[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\notechie: # Replace with a single Otechie username\ncustom: https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/art/donate.png\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Make AndroidUtilCode more perfect!\nlabels: bug\nassignees: Blankj\n\n---\n\n## Describe the bug\n\nA clear and concise description of what the bug is.\n\n- The version of AndroidUtilCode: <!-- e.g. utilcode:1.16.3 or utilcodex:1.16.3 -->\n- The device: <!-- e.g. Nexus 5X -->\n- The version of device: <!-- API 27 -->\n\n## The code of bug\n\n<!-- e.g. \n```java\nCrashUtils.init();\n```\n-->\n```\nput your code here\n```\n\n## The stack of crash\n\n<!-- e.g. \n```\nCaused by: java.lang.NullPointerException: u should init first\n   at com.blankj.utilcode.util.Utils.getApp(Utils.java:98)\n   at com.blankj.utilcode.util.CrashUtils.<clinit>(CrashUtils.java:55)\n   at com.blankj.utilcode.util.CrashUtils.init(CrashUtils.java:168) \n   at com.blankj.androidutilcode.UtilsApp.initCrash(UtilsApp.java:71) \n   at com.blankj.androidutilcode.UtilsApp.onCreate(UtilsApp.java:33) \n```\n-->\n\n```\nput the stack of crash here\n```\n\n## Screenshots\n\nIf applicable, add screenshots to help explain your problem.\n\n\n## Please delete the current line and the following.\n\nThank you for supporting [AndroidUtilCode](https://github.com/Blankj/AndroidUtilCode).\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report_cn.md",
    "content": "---\nname: 提交 Bug\nabout: 让工具类更完美！\nlabels: bug\nassignees: Blankj\n\n---\n\n## 描述 Bug\n\n简洁地描述下 Bug。\n\n- AndroidUtilCode 的版本：<!-- 例如 utilcode:1.16.3 或 utilcodex:1.16.3（这里面是注释，内容需要放在外面） -->\n- 出现 Bug 的设备型号：<!-- 例如 Nexus 5X -->\n- 设备的 Android 版本：<!-- 例如 API 27 -->\n\n## 相关代码\n\n<!-- 例如\n```java\nCrashUtils.init();\n```\n-->\n```\nput your code here\n```\n\n## 异常堆栈\n\n<!-- 例如\n```\nCaused by: java.lang.NullPointerException: u should init first\n   at com.blankj.utilcode.util.Utils.getApp(Utils.java:98)\n   at com.blankj.utilcode.util.CrashUtils.<clinit>(CrashUtils.java:55)\n   at com.blankj.utilcode.util.CrashUtils.init(CrashUtils.java:168) \n   at com.blankj.androidutilcode.UtilsApp.initCrash(UtilsApp.java:71) \n   at com.blankj.androidutilcode.UtilsApp.onCreate(UtilsApp.java:33) \n```\n-->\n\n```\nput the stack of crash here\n```\n\n## 截图\n\n如果有的话请添加屏幕截图以帮助解释问题。\n\n\n## 请删除当前行及以下内容\n\n感谢支持 [AndroidUtilCode](https://github.com/Blankj/AndroidUtilCode).\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request.md",
    "content": "---\nname: Feature request\nabout: Make AndroidUtilCode more perfect!\nlabels: help wanted\nassignees: Blankj\n\n---\n\n## Describe the feature\n\nA clear and concise description of what the feature is.\n\n\n## Reference\n\nHope to give some reference articles, links, code, if any.\n\n\n## Please delete the current line and the following\n\nThank you for supporting [AndroidUtilCode](https://github.com/Blankj/AndroidUtilCode).\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature-request_cn.md",
    "content": "---\nname: 提交需求\nabout: 让工具类更健全！\nlabels: help wanted\nassignees: Blankj\n\n---\n\n## 描述需求\n\n简洁地描述下需求。\n\n\n## 可借鉴的\n\n如果有的话，可以给出一些参考文章、链接、代码\n\n\n## 请删除当前行及以下内容\n\n感谢支持 [AndroidUtilCode](https://github.com/Blankj/AndroidUtilCode).\n"
  },
  {
    "path": ".github/workflows/android.yml",
    "content": "name: Android CI\n\non: [push]\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v2\n    - name: set up JDK 1.8\n      uses: actions/setup-java@v1\n      with:\n        java-version: 1.8\n    - name: Build with Gradle\n      run: ./gradlew build aR\n"
  },
  {
    "path": ".gitignore",
    "content": "*.iml\n__api__.json\n__bus__.json\n.gradle\nlocal.properties\n.idea\n.DS_Store\n/build\n/captures\n.externalNativeBuild\n/apk\n*.phrof\n/mavenLocal\n/reports\n*/reports"
  },
  {
    "path": "CHANGELOG.md",
    "content": "* `22/10/15` [add] Fix some issue. Publish v1.31.1\n* `21/12/06` [add] Publish v1.31.0\n* `21/05/13` [add] Support publish mavenCentral.\n* `21/02/22` [add] Fix ToastUtils rtl bug. Publish v1.30.6.\n* `20/11/16` [add] Add ImageUtils#save2Album support param of dirName.\n* `20/11/13` [add] Fix MessengerUtils ANR. Add NetworkUtils#getWifiScanResult, [add|remove]OnWifiChangedConsumer. Publish v1.30.5.\n* `20/10/29` [add] Fix MessengerUtils startService IllegalStateException. Publish v1.30.4.\n* `20/10/28` [add] Fix BusUtils ConcurrentModificationException. Publish v1.30.3.\n* `20/10/27` [add] Fix AppUtils#getAppSignatures. Add DeviceUtils#isDevelopmentSettingsEnabled. Publish v1.30.2.\n* `20/10/26` [add] Fix AppUtils#isAppForeground. Publish v1.30.1.\n* `20/10/24` [add] Publish v1.30.0.\n* `20/10/23` [fix] LanguageUtils crash on some device.\n* `20/10/21` [add] LogUtils.Config#setOnConsoleOutputListener, setOnFileOutputListener, addFileExtraHead. LogUtils.getCurrentLogFilePath.\n* `20/10/20` [opt] ToastUtils.\n* `20/10/12` [add] PermissionUtils#explain.\n* `20/10/10` [add] ClipboardUtils.\n* `20/10/08` [add] VolumeUtils.\n* `20/09/06` [add] DebouncingUtils#isValid.\n* `20/09/04` [fix] ToastUtils adapt SDK 30.\n* `20/05/28` [fix] IntentUtils#getInstallAppIntent file exist wrong. Publish v1.29.0.\n* `20/05/23` [fix] BusUtils#postSticky times not right. Publish v1.28.6.\n* `20/05/22` [add] IntentUtils#getInstallAppIntent support Uri param.\n* `20/05/21` [add] Publish bus plugin v2.6. Publish api plugin v1.4. Publish. Publish v1.28.5.\n* `20/05/19` [fix] FileUtils#copyOrMoveDird NPE.\n* `20/05/18` [add] IntentUtils#getLaunchAppDetailsSettingsIntent support isNewTask.\n* `20/05/17` [add] ImageUtils#save2Album, NetworkUtils#getSSID, UtilsTransActivity4MainProcess.\n* `20/05/03` [add] Publish bus plugin v2.5. Publish api plugin v1.3. Publish. Publish v1.28.4.\n* `20/04/30` [add] BaseItem support partialUpdate.\n* `20/04/29` [add] Publish plugin lib com.blankj:base-transform:1.0.\n* `20/04/28` [fix] LanguageUtils#applyLanguage.\n* `20/04/27` [fix] BarUtils#isNavBarVisible.\n* `20/04/26` [fix] Utils#init fit tinker. Publish v1.28.3.\n* `20/04/25` [fix] UriUtils#uri2File Unknown URI. Publish 1.28.2.\n* `20/04/24` [add] SnackbarUtils support show on the top; UriUtils#uri2InputStream.\n* `20/04/23` [fix] UriUtils#uri2File not support HW; TransActivity crash below 21.\n* `20/04/23` [fix] PhoneUtils#getSerial, PhoneUtils#getSerial crash on Android 10.\n* `20/04/20` [fix] ImageUtils#isImage.\n* `20/04/18` [fix] PermissionUtils#callback. Publish v1.28.1.\n* `20/04/17` [fix] ImageUtils#view2Bitmap, ImageUtils.getBitmap(InputStream).\n* `20/04/16` [add] ConvertUtils#int2HexString, hexString2Int.\n* `20/04/15` [add] UiMessageUtils' demo.\n* `20/04/13` [add] NumberUtils. Publish v1.28.0.\n* `20/04/12` [opt] TimeUtils#SDF_THREAD_LOCAL.\n* `20/04/11` [add] SDCardUtils#getXxTotalSize, SDCardUtils#getXxAvailableSize. FileUtils#getFsTotalSize, FileUtils#getFsAvailableSize.\n* `20/04/10` [fix] FileUtils#isFileExists; FragmentUtils#getTop bug. Publish v1.27.6.\n* `20/04/09` [add] UriUtils#res2Uri, UriUtils#uri2File support QQBrowser; ThreadUtils#getMainHandler; PathUtils#getxxPathExternalFirst.\n* `20/04/08` [fix] ActivityUtils#finish bug. Publish v1.27.5.\n* `20/04/08` [fix] CleanUtils clean dir not work. FileUtils#isFileExists. Publish v1.27.4.\n* `20/04/08` [fix] CrashUtils DefaultUncaughtExceptionHandler is wrong; LogUtils write file failed; Utils#getApp failed run on remote process. Publish v1.27.3.\n* `20/04/07` [mdf] GsonUtils#getGson() method public.\n* `20/04/04` [fix] ShadowUtils bug running on lower version devices. Publish v1.27.2.\n* `20/04/03` [fix] UtilsActivityLifecycleImpl#HashMap#remove IllegalStateException bug.\n* `20/04/02` [fix] PathUtils sdcard enable state is wrong; ActivityUtils finish activity wrong; Publish v1.27.1.\n* `20/03/31` [add] Publish v1.27.0.\n* `20/03/30` [add] BatteryUtils in subutil.\n* `20/03/27` [add] publish.gradle.\n* `20/03/24` [add] UtilsBridge to clean the utils.\n* `20/03/22` [upd] GsonUtils support custom gson.\n* `20/03/20` [add] ActivityUtils#addActivityLifecycleCallbacks, ActivityUtils#removeActivityLifecycleCallbacks.\n* `20/01/17` [upd] Leak Canary to v2.1.\n* `20/01/06` [add] ClickUtils#expandClickArea, ClickUtils#back2HomeFriendly\n* `19/11/30` [add] Publish bus plugin v2.4. Publish api plugin v1.2.\n* `19/11/28` [add] Publish v1.26.0.\n* `19/11/27` [add] Shadow demo.\n* `19/11/26` [add] MVP demo.\n* `19/11/22` [fix] Adapt the project for Gradle version of 6.0.\n* `19/10/30` [add] Publish bus plugin v2.3. Publish api plugin v1.1.\n* `19/10/24` [upd] Demo's UI.\n* `19/10/22` [add] NotificationUtils and demo.\n* `19/10/20` [add] UiMessageUtils.\n* `19/09/20` [add] ShadowUtils.\n* `19/08/27` [add] DebugUtils.\n* `19/08/26` [fix] PermissionUtils NPE.\n* `19/08/25` [upd] ImageUtils#getImageType. [add] LogUtils#getLogFiles. Publish v1.25.9.\n* `19/08/24` [fix] PhoneUtils#getIMEI crash on SDK 29.\n* `19/08/23` [add] ViewUtils#isLayoutRtl.\n* `19/08/22` [add] LogUtils#getLogFiles.\n* `19/08/13` [add] MapUtils and MapUtilsTest. Publish v1.25.8.\n* `19/08/12` [add] CollectionUtils and CollectionUtilsTest.\n* `19/08/11` [add] ArrayUtils and ArrayUtilsTest.\n* `19/08/09` [fix] https://www.virustotal.com/gui/home/upload with ESET-NOD32. Publish v1.25.7.\n* `19/08/08` [add] BusUtils#post tag support one-to-many. Publish v1.25.6.\n* `19/08/04` [add] ThreadUtils#Task support timeout.\n* `19/08/01` [upd] EncryptUtils#rsa.\n* `19/07/31` [add] DeviceUtils#getUniqueDeviceId, DeviceUtils#isSameDevice. Publish v1.25.5.\n* `19/07/30` [fix] ThreadUtils's task can only be executed once. PhoneUtils#getIMEI wrong.\n* `19/07/29` [fix] BusUtils post father class useless. KeyboardUtils#hideSoft bug. Publish v1.25.4.\n* `19/07/28` [add] NetworkUtils#(un)registerNetworkStatusChangedListener. Publish v1.25.3.\n* `19/07/27` [fix] ThreadUtils memory leak.\n* `19/07/26` [add] ContainerUtils. Publish v1.25.2.\n* `19/07/25` [fix] PermissionUtils' NullPointException.\n* `19/07/24` [fix] ZipUtils#unzipFile.\n* `19/07/23` [fix] ThreadUtils of cache pool. Publish v1.25.1.\n* `19/07/18` [add] README of ApiUtils and BusUtils.\n* `19/07/15` [add] Publish v1.25.0.\n* `19/07/14` [upd] Bus plugin for use BusUtils. Publish bus plugin v2.0.\n* `19/07/13` [add] Api plugin for use ApiUtils. Publish api plugin v1.0.\n* `19/07/09` [upd] The frame of project.\n* `19/07/06` [upd] BusUtils which behave same as EventBus.\n* `19/07/03` [add] ApiUtils which decoupling modules.\n* `19/06/30` [add] LanguageUtils support activity's class name.\n* `19/06/29` [add] ClickUtils#OnMultiClickListener, and remove dangerous function. Publish v1.24.6.\n* `19/06/28` [add] LanguageUtils. Publish v1.24.5.\n* `19/06/20` [fix] BusUtils' permission. Publish v1.24.4.\n* `19/06/19` [fix] UriUtils. Publish v1.24.3.\n* `19/06/18` [add] ClickUtils, ViewUtils.\n* `19/06/07` [fix] LogUtils file name contains ':'. Publish v1.24.2.\n* `19/06/06` [fix] LogUtils write to file. Publish v1.24.1.\n* `19/06/03` [fix] Refactoring framework. Publish v1.24.0.\n* `19/04/25` [fix] LogUtils delete due log.\n* `19/04/24` [upd] The swipe panel.\n* `19/03/17` [fix] The ugly UI.\n* `19/03/14` [fix] AdaptScreenUtils didn't work on some HaWei tablet.\n* `19/03/09` [fix] UriUtils#uri2File.\n* `19/03/08` [add] LogUtils support multi process. Publish v1.23.7.\n* `19/03/02` [fix] LogUtils#file.\n* `19/02/28` [fix] ImageUtils#calculateInSampleSize. Publish v1.23.6.\n* `19/02/26` [fix] UriUtils#uri2File. Publish v1.23.5.\n* `19/01/31` [add] HttpUtils.\n* `19/01/30` [add] RomUtils. Publish v1.23.4.\n* `19/01/29` [fix] LogUtils format json when json not start with '{'. Publish v1.23.3.\n* `19/01/28` [fix] KeyboardUtils#fixSoftInputLeaks don't work on the device of HuaWei.\n* `19/01/26` [fix] NetworkUtils#getNetworkType.\n* `19/01/25` [add] CloneUtils, PermissionUtils support request permission of WRITE_SETTINGS and DRAW_OVERLAYS. Publish v1.23.2.\n* `19/01/24` [add] BrightnessUtils and FlashlightUtils.\n* `19/01/23` [add] Modify the demo of utilcode use kotlin. Publish v1.23.1.\n* `19/01/22` [fix] AppUtils#installApp.\n* `19/01/17` [fix] Publish v1.23.0.\n* `19/01/16` [fix] BarUtils get Activity from view and delete the function of set status bar alpha.\n* `19/01/15` [add] ColorUtils.\n* `19/01/04` [add] CacheDiskStaticUtils, CacheDoubleStaticUtils, CacheMemoryStaticUtils.\n* `19/01/03` [add] SPStaticUtils.\n* `19/01/02` [fix] LogUtils log object. Publish v1.22.10.\n* `19/01/01` [add] GsonUtils.\n* `18/12/29` [add] AntiShakeUtils and VibrateUtils. Publish v1.22.9.\n* `18/12/28` [fix] ToastUtils show behind the dialog when close notification.\n* `18/12/27` [fix] LogUtils print StringBuilder failed.\n* `18/12/24` [fix] Utils$ActivityLifecycleImpl.consumeOnActivityDestroyedListener ConcurrentModificationException. Publish v1.22.7.\n* `18/12/22` [fix] AdaptScreenUtils#pt2px don't work when start webview. Publish v1.22.6.\n* `18/12/21` [add] LogUtils support print Map, Collection and Object to String.\n* `18/12/19` [fix] AdaptScreenUtils don't work in MIUI on Android 5.1.1. Publish v1.22.5.\n* `18/12/18` [fix] ToastUtils multi show crash when run API 25. Publish v1.22.4.\n* `18/12/18` [fix] ImageUtils recycle ret equals src. Publish v1.22.3.\n* `18/12/17` [fix] Utils$FileProvider4UtilCode not found. Publish v1.22.3.\n* `18/12/17` [fix] ToastUtils leak. Publish v1.22.2.\n* `18/12/09` [add] Component for the project.\n* `18/12/04` [add] BusUtils. Publish v1.22.1.\n* `18/11/18` [fix] ToastUtils don't show in the devices grater than API 24 when close the permission of notification. Publish v1.22.0.\n* `18/11/17` [fix] AppUtils#isAppInstalled don't work in no launcher app.\n* `18/11/16` [fix] ThreadUtils#cancel block the main thread.\n* `18/11/15` [add] module of bus-gradle-plugin and change style of gradle.\n* `18/11/14` [add] BusUtils.\n* `18/11/13` [add] AdaptScreenUtils.\n* `18/11/12` [fix] SPUtils bug when use in multi threads.\n* `18/10/25` [fix] SpanUtils#setLineHeight bug of multi lines. Publish v1.21.2.\n* `18/10/24` [fix] SpanUtils#appendImage on VIVO. Publish v1.21.1.\n* `18/10/16` [add] BusUtils, DeviceUtils#isAdbEnabled. Publish v1.21.0.\n* `18/09/29` [fix] ToastUtils which causes crash in the some devices of Xiaomi. Publish v1.20.4.\n* `18/09/13` 修复 ToastUtils 在小米手机显示 Toast 带有 App 名，发布 1.20.3\n* `18/09/12` 修复 KeyBoardUtils#fixAndroidBug5497，完善 ToastUtils，发布 1.20.2\n* `18/09/11` 新增 BarUtils#isSupportNavBar，删除 BarUtils#setNavBarImmersive\n* `18/09/10` 修复 KeyboardUtils 中 getWindowVisibleDisplayFrame 空指针异常，更新 BarUtils#isNavBarVisible\n* `18/09/06` 新增 PathUtils，发布 1.20.1\n* `18/09/05` 新增 KeyboardUtils#showSoftInputUsingToggle 和 KeyboardUtils#hideSoftInputUsingToggle\n* `18/09/04` 修复 SHA 算法名\n* `18/09/03` 新增 MetaDataUtils，发布 1.20.0\n* `18/08/30` 修复 PermissionUtils$PermissionActivity 的 window 背景为黑色的问题，发布 1.19.4\n* `18/08/28` 新增 RegexUtils#isIDCard18Exact\n* `18/08/26` 新增 AppUtils#getAppSignatureSHA256 和 AppUtils#getAppSignatureMD5，发布 1.19.3\n* `18/08/24` 新增 ScreenUtils#restoreAdaptScreen，利用 FileProvider4UtilCode 不再需要初始化，发布 1.19.2\n* `18/08/23` 修复 适配后 ToastUtils 原生 Toast 尺寸发生改变的问题，修复 KeyboardUtils#fixSoftInputLeaks，发布 1.19.1\n* `18/08/10` 修复 ScreenUtils#adaptxx 导致获取状态栏和导航栏尺寸不对问题，发布 1.19.0\n* `18/08/09` 新增 IntentUtils#isIntentAvailable，ToastUtils 传入空显示 null，发布 1.18.6\n* `18/08/08` 修复 ScreenUtils#adaptxx 在第三方 SDK 会出现的问题，发布 1.18.5\n* `18/08/07` 修复 ScreenUtils#adaptxx 在 API 26 以下无效的 bug，发布 1.18.4\n* `18/08/06` 修复 ScreenUtils#screenShot 中 decorView.getDrawingCache() 为空的问题，发布 1.18.3\n* `18/08/05` 修复 1.18.0 版本删去 `if (activity.getClass() == PermissionUtils.PermissionActivity.class) return;` 造成 PermissionUtils 获取栈顶 Activity 问题，发布 1.18.2\n* `18/08/04` 新增 LogUtils#Config#setSaveDays，发布 1.18.1\n* `18/08/03` 新增 LogUtils#Config#addFormatter，并新增 Array, Throwable, Bundle, Intent 的格式化输出\n* `18/08/02` 修复 TimeUtils 中的 SimpleDateFormat 为 ThreadLocal 实现\n* `18/08/01` 删除 标记废弃的 CacheUtils, AppUtils#installApp, TimeUtils#getWeekIndex，发布 1.18.0\n* `18/07/30` 替换 ScreenUtils#adaptPortraitScreen 和 ScreenUtils#adaptLandscapeScreen，为 ScreenUtils#adaptScreen4VerticalSlide 和 ScreenUtils#adaptScreen4HorizontalSlide\n* `18/07/28` 修复 NetworkUtils#getIPAddress\n* `18/07/16` 新增 ScreenUtils#adaptPortraitScreen 和 ScreenUtils#adaptLandscapeScreen，发布 1.17.3\n* `18/07/13` 修复 IntentUtils 分享图片判断逻辑，CacheDiskUtils 可放入 byte[0]\n* `18/06/29` 修复 FragmentUtils 中 getFragmentManager 空指针错误，发布 1.17.2\n* `18/06/27` 新增 UriUtils#uri2File\n* `18/06/25` 新增 KeyboardUtils#fixAndroidBug5497，发布 1.17.1 版本\n* `18/06/21` 修复 FragmentUtils#add 死循环的 BUG\n* `18/06/14` 替换 CacheUtils 为 CacheDiskUtils，CacheUtils 标记 deprecated，发布 1.17.0 版本\n* `18/06/13` 新增 CacheMemoryUtils 和 CacheDoubleUtils\n* `18/06/12` 完善 FragmentUtils#add 和 replace 新增 tag\n* `18/05/30` 完善 DeviceUtils#getMacAddress，发布 1.16.4 版本\n* `18/05/30` 修复 ToastUtils 在 targetSdkVersion 为 27 在 api 25 机器快速 show 两次崩溃的异常，发布 1.16.3 版本\n* `18/05/29` 完善 TimeUtils 的 timeSpan 带符号位，ToastUtils 去除弱引用，发布 1.16.2 版本\n* `18/05/25` 新增 AppUtils#registerAppStatusChangedListener 和 AppUtils#unregisterAppStatusChangedListener，发布 1.16.1 版本\n* `18/05/22` 新增 ThreadUtils，发布 1.16.0 版本\n* `18/05/15` 新增 MetaDataUtils 和 ActivityUtils#startActivityForResult，发布 1.15.1 版本\n* `18/05/08` 新增 ResourceUtils，发布 1.15.0 版本\n* `18/05/07` 修复 ZipUtils 漏洞，发布 1.14.4 版本\n* `18/05/03` 修复 ToastUtils 默认字体大小问题，发布 1.14.3 版本\n* `18/05/02` 修复 PermissionUtils 空异常，发布 1.14.2 版本\n* `18/04/28` 新增 FlashlightUtils，发布 1.14.1 版本\n* `18/04/26` 修复 KeyboardUtils 全屏 NO_LIMIT 的 bug\n* `18/04/25` 修复 多个空异常\n* `18/04/24` 修复 多 FileProvider 带来的问题，发布 1.14.0 版本\n* `18/04/23` 新增 RSA 加解密，发布 1.13.16 版本\n* `18/04/22` 新增 LogUtils 设置栈偏移\n* `18/04/21` 新增 AppUtils#relaunchApp、DeviceUtils#getABIs，发布 1.13.15 版本\n* `18/04/20` 新增 BarUtils#setNavBarColor、BarUtils#getNavBarColor\n* `18/04/19` 新增 Process#isMainProcess、Process#getCurrentProcessName，发布 1.13.14 版本\n* `18/04/18` 修复 LogUtils 头部空指针异常，SPUtils、CacheUtils 存储空值异常，发布 1.13.13 版本\n* `18/04/17` 修复 ToastUtils 内存泄漏问题，感谢 [LambertCoding](https://github.com/LambertCoding)，发布 1.13.12 版本\n* `18/04/16` 完善 AppUtils#installAppSilent 路径包含空格问题，发布 1.13.11 版本\n* `18/04/10` 完善 OnCrashListener 回调崩溃信息，发布 1.13.10 版本\n* `18/04/09` 修复 静默安装重载错误，发布 1.13.9 版本\n* `18/04/08` 修复 获取栈顶 Activity 链表为空的异常，获取栈顶 Activity 放到 Utils 中，发布 1.13.8 版本\n* `18/04/06` 新增 GsonUtils 及单元测试\n* `18/04/05` 完善 README 文档\n* `18/04/03` 修复 LogUtils 在 Android Studio 3.1 版本日志丑陋的问题，发布 1.13.7 版本\n* `18/03/29` 兼容 Utils 的初始化传入 application，发布 1.13.6 版本\n* `18/03/20` 修复 PermissionUtils 子进程的问题，发布 1.13.5 版本\n* `18/03/16` 新增 gradle 插件来格式化 README\n* `18/03/14` 修复 KeyboardUtils#getContentViewInvisibleHeight，发布 1.13.4 版本\n* `18/03/10` 完善 AppUtils#installAppSilent 和 DeviceUtils#getMacAddress，发布 1.13.3 版本\n* `18/03/09` 完善 ActivityUtils#getTopActivity\n* `18/03/08` 新增 反射获取栈顶 Activity 的方法，发布 1.13.2 版本\n* `18/03/07` 修复 PermissionUtils 请求权限为 0 的 崩溃\n* `18/03/05` 修复 Library Source does not match the bytecode for class LogUtils 问题，发布 1.13.1 版本\n* `18/03/04` 完善 Javadoc 中文版为英文版，发布 1.13.0 版本\n* `18/03/04` 完善 Javadoc 中文版为英文版\n* `18/03/03` 完善 Javadoc 中文版为英文版\n* `18/03/02` 完善 Javadoc 中文版为英文版\n* `18/03/01` 完善 Javadoc 中文版为英文版\n* `18/02/28` 完善 Javadoc 中文版为英文版\n* `18/02/27` 完善 Javadoc 中文版为英文版\n* `18/02/26` 完善 Javadoc 中文版为英文版\n* `18/02/25` 完善 Javadoc 中文版为英文版\n* `18/02/24` 完善 Javadoc 中文版为英文版\n* `18/02/23` 完善 Javadoc 中文版为英文版\n* `18/02/22` 完善 Javadoc 中文版为英文版\n* `18/02/21` 完善 Javadoc 中文版为英文版\n* `18/02/10` 完善 Javadoc 中文版为英文版\n* `18/02/09` 完善 非空转换插件 traute 的使用方式\n* `18/02/08` 修复 ActivityUtils option 低版本为空的异常\n* `18/01/31` 修复 default 相关的逻辑错误，发布 1.12.4，修复 ToastUtils 在 kotlin 中转义失败，发布 1.12.5\n* `18/01/28` 修复 ToastUtils 默认样式问题，发布 1.12.2，新增 DeviceUtils#getSDKVersionName，发布 1.12.3\n* `18/01/27` 修复 PermissionUtils 某些机型闪烁问题，发布 1.12.1\n* `18/01/17` 完善 ReflectUtils 及单元测试，发布 1.12.0 版本\n* `18/01/16` 完善 ReflectUtils 及单元测试\n* `18/01/15` 完善 ReflectUtils 及单元测试\n* `18/01/14` 完善 ReflectUtils 及单元测试\n* `18/01/13` 完善 ReflectUtils 及单元测试\n* `18/01/12` 完善 ReflectUtils 及单元测试\n* `18/01/11` 修复 ImageUtils 的 fastBlur radius 为 1 recycle 的 bug，新增 CrashUtils 初始化崩溃监听事件，发布 1.11.1 版本\n* `18/01/10` 完善 PermissionUtils 及 readme，发布 1.11.0 版本\n* `18/01/09` 完善 demo 动态权限适配\n* `18/01/08` 新增 SPActivity，删除 SPUtils 的单元测试\n* `18/01/08` 修复 ToastUtils 在 SDK 为 18 的自定义 toast 崩溃问题\n* `18/01/07` 新增 PermissionUtils 的 Demo\n* `18/01/06` 修复 权限相关工具类内存泄漏问题\n* `18/01/05` 新增 获取 Activity icon 和 logo\n* `18/01/04` 完善 6.0 动态权限相关工具类\n* `18/01/03` 完善 6.0 动态权限相关工具类\n* `18/01/02` 完善 6.0 动态权限相关工具类\n* `18/01/01` 新增 6.0 动态权限相关工具类\n* `17/12/30` 删除 SpanUtils 中设置图标\n* `17/12/29` 完善 SpanUtils 的 appendImage 对齐方式\n* `17/12/28` 完善 ScreenUtils 设置全屏的方式，发布 1.10.0\n* `17/12/26` 新增 状态栏、导航栏设置是否可见和判断是否可见\n* `17/12/22` 新增 注册软键盘改变监听器、注册导航栏改变监听器方法\n* `17/12/21` 完善 获取屏幕宽高，修复行宽度大于 100 字符\n* `17/12/20` 修复 SpanUtils 图标的 bug，不高于 6.0 的版本不支持居中和底部对齐\n* `17/12/19` 修复 SpanUtils 多图的 bug\n* `17/12/15` 新增 ReflectUtils\n* `17/12/14` 完善 手机号（精确）正则，发布 1.9.12\n* `17/12/12` 完善 LogUtils，当最终日志长度为 0 时，输出 log nothing\n* `17/12/11` 完善 ActivityUtils 的 finish 系列，发布 1.9.11\n* `17/12/04` 完善 LogUtils 边框改为单线清爽型\n* `17/11/30` 修复 ToastUtils 背景问题，发布 1.9.10\n* `17/11/30` 修复 ToastUtils 获取背景为空，发布 1.9.9\n* `17/11/28` 修复 EmptyUtils 对 CharSequence 的判断，感谢 jiezigg\n* `17/11/24` 新增 readme 格式化的 gradle 脚本\n* `17/11/15` 完善 资源分包位置，使其更合理\n* `17/11/10` 完善 LogUtils 新增日志头部，感谢 Kanade\n* `17/11/07` 完善 LogUtils 无 tag 的多参数\n* `17/11/06` 修复 LogUtils 多参数打印失败的问题\n* `17/11/01` 完善 ShellUtil 的 Msg 换行，感谢香脆的大鸡排\n* `17/10/30` 完善 README\n* `17/10/29` 修复 6.0 内部存储安装失败问题\n* `17/10/28` 完善 compile 为 implementation, provided 为 compileOnly\n* `17/10/27` 修复 兼容 AS3.0\n* `17/10/27` 修复 LogUtils 在 kotlin 中使用的问题\n* `17/10/25` 修复 LogUtils 边框，修复 getBitmap 从流获取\n* `17/09/30` 完善 FragmentUtils，发布 1.9.2\n* `17/09/29` 完善 FragmentUtils 和 isInstallApp\n* `17/09/28` 完善 FragmentUtils\n* `17/09/27` 完善 FragmentUtils\n* `17/09/26` 完善 ActivityUtils 及 Demo，发布 1.9.1\n* `17/09/25` 完善 ActivityUtils 及 Demo\n* `17/09/24` 完善 ActivityUtils 及 Demo\n* `17/09/23` 完善 FragmentUtils\n* `17/09/19` 修复 CrashUtils 自定义路径错误\n* `17/09/18` 完善 ImageUtils 的 Demo\n* `17/09/17` 完善 ImageUtils 的 compress\n* `17/09/13` 完善 ImageUtils 的 addBorder\n* `17/09/13` 完善 ImageUtils 的 toRound\n* `17/09/13` 完善 ImageUtils 和 LogUtils\n* `17/09/12` 完善 ImageUtils\n* `17/09/10` 完善 单元测试\n* `17/09/08` 完善 单元测试\n* `17/09/06` 完善 SDCardUtils 获取 SD 卡路径，完善 SPUtils 新增 commit\n* `17/09/05` 完善 LogUtils，发布版本 1.9.0\n* `17/09/04` 完善 ToastUtils，去除相关 safe 函数，都改为 safe 实现，新增 CustomToast 的 Demo\n* `17/09/02` 完善 ToastUtils，去除引入 view 带来的问题，发布版本 1.8.6\n* `17/08/30` 修复 ToastUtils 弱引用带来的问题，修复 CacheUtils 异步问题，发布版本 1.8.5\n* `17/08/28` 修复 ToastUtils 内存泄露，新增 toast 可根据系统字体显示不同字体，发布版本 1.8.4\n* `17/08/20` 新增 监听 Activity 生命周期，退出 App，发布版本 1.8.3\n* `17/08/11` 完善 LogUtils 的 Builder 改为 Config，发布版本 1.8.2\n* `17/08/10` 完善 FileUtils 的 deleteFilesInDir 和 listFilesInDir\n* `17/08/08` 新增 反射工具类 ReflectUtils\n* `17/08/06` 完善 为按功能分包，增加 subutil 的 Demo\n* `17/07/31` 修复 NetworkUtils 的 isAvailableByPing 循环递归，发布 1.8.1\n* `17/07/31` 完善 BarUtils，发布 1.8.0\n* `17/07/31` 完善 BarUtils\n* `17/07/30` 完善 BarUtils\n* `17/07/29` 完善 BarUtils\n* `17/07/28` 完善 BarUtils\n* `17/07/27` 完善 BarUtils\n* `17/07/26` 完善 ActivityUtils\n* `17/07/25` 完善 BarUtils，更新布局文件\n* `17/07/24` 完善 BarUtils\n* `17/07/23` 完善 BarUtils\n* `17/07/22` 完善 BarUtils\n* `17/07/21` 完善 xml 文件的格式化\n* `17/07/17` 完善 NetworkUtils 的 isAvailableByPing 函数新增 ip 参数\n* `17/07/14` 修复 FragmentUtils 的 FragmentNode 为 public\n* `17/07/11` 完善 将不常用的工具类放在 subutil 中\n* `17/07/10` 新增 subutil 库\n* `17/07/07` 修复 TimeUtils 中获取当天零点的 bug\n* `17/07/02` 完善 BarUtils 的 Demo\n* `17/07/01` 完善 BarUtils 的 Demo\n* `17/06/30` 完善 BarUtils 的 Demo\n* `17/06/29` 新增 README logo\n* `17/06/28` 新增 返回键及右划返回\n* `17/06/27` 新增 Toolbar\n* `17/06/26` 新增 final 参数\n* `17/06/23` 完善 Demo 主页\n* `17/06/20` 完善 ToastUtil, SnackbarUtils 新增设置底边距\n* `17/06/17` 删除 HandlerUtils\n* `17/06/16` 新增 insight.io 的 bandage\n* `17/06/14` 完善 LogUtils 回退栈，发布 1.7.1 版本\n* `17/06/13` 完善 Snackbar 和 Toast 的 Demo\n* `17/06/12` 完善 Snackbar 为建造者模式\n* `17/06/11` 完善 SpanUtils，发布版本 1.7.0\n* `17/06/08` 完善 SpanUtils\n* `17/06/07` 完善 SpannableStringUtils 改名为 SpanUtils，即将完工\n* `17/06/06` 完善 SpannableStringUtils\n* `17/06/05` 完善 SpannableStringUtils\n* `17/06/04` 完善 SpannableStringUtils\n* `17/06/03` 完善 SpannableStringUtils\n* `17/06/02` 完善 SpannableStringUtils\n* `17/06/01` 完善 KeyBoardUtils 及 Demo\n* `17/05/30` 完善 CrashUtils，发布 1.6.4\n* `17/05/28` 修复 CacheUtils 的 bug，发布 1.6.3\n* `17/05/27` 修复 CacheUtils 的 bug，发布 1.6.2\n* `17/05/26` 完善 CacheUtils，发布 1.6.0 和 1.6.1\n* `17/05/25` 完善 FileIOUtils 和 CacheUtils\n* `17/05/23` 新增 读取文件到字符数组中两种方式\n* `17/05/19` 新增 LogUtils 文件过滤和控制台开关\n* `17/05/16` 新增 ActivityUtils 动画\n* `17/05/12` 新增 base 系列\n* `17/05/11` 修复 SpannableStringUtils 的 setDrawable 的 bug，发布 1.5.1\n* `17/05/10` 完善 7.0 安装 App，完善 AppActivity\n* `17/05/09` 完善 TimeUtils 单元测试\n* `17/05/08` 更新 BarUtils, LogUtils 新增配置文件，TimeUtils 将 pattern 改为 format，发布 1.5.0\n* `17/05/04` 新增 签名\n* `17/05/03` 修复 对齐头部日期\n* `17/05/02` 完善 Demo 的 string 字符串变更，完善 ToastUtils 和 SnackbarUtils\n* `17/04/27` 新增 Travis CI，使用 shields，发布 1.4.1\n* `17/04/26` 完善 HandlerUtils 使用 Handler#CallBack 的回调接口及 SpannableStringUtils 图片对齐\n* `17/04/24` 修复 拼写错误，修复 StringUtils 的 equalsIgnoreCase\n* `17/04/23` 完善 README\n* `17/04/21` 完善 TimeUtils，发布 1.4.0\n* `17/04/20` 新增 SpannableStringUtils 设置字体尺寸\n* `17/03/29` 修改 README\n* `17/03/27` 更新 LogUtils\n* `17/03/26` 更新 LogUtils\n* `17/03/25` 更新 LogUtils\n* `17/03/24` 完善 StringUtils\n* `17/03/20` 修复 链接错误\n* `17/03/19` 新增 LogUtils 栈回溯\n* `17/03/14` 新增 常量包\n* `17/02/14` 完善 FragmentUtils 中，Demo 测试中\n* `17/02/13` 完善 FragmentUtils 中\n* `17/02/12` 完善 FragmentUtils 中\n* `17/02/11` 完善 FragmentUtils 中\n* `17/02/10` 完善 FragmentUtils 中，LogUtils 对长度进行分割\n* `17/02/09` 完善 FragmentUtils 中\n* `17/02/08` 完善 FragmentUtils 中\n* `17/02/07` 完善 FragmentUtils 中\n* `17/02/06` 完善 FragmentUtils 中，炸断肠\n* `17/02/05` 完善 FragmentUtils 中\n* `17/02/04` 完善 FragmentUtils 中\n* `17/02/03` 完善 FragmentUtils 中\n* `17/02/02` 完善 FragmentUtils 中\n* `17/02/01` 完善 FragmentUtils 中\n* `17/01/24` 完善 并发布版本 1.3.6\n* `17/01/16` 新增 LogUtils 打印类名函数名及所在行\n* `17/12/26` 新增 阴历相关工具类\n* `17/12/21` 完善 SpannableStringUtils\n* `16/12/19` 完善 SpannableStringUtils\n* `16/12/18` 完善 SpannableStringUtils，采用构造者模式\n* `16/12/17` 完善 SpannableStringUtils\n* `16/12/16` 完善 拼音工具类\n* `16/12/15` 完善 拼音工具类\n* `16/12/14` 新增 不低于 7.0 的 Html 解码\n* `16/12/13` 新增 获取文件最后修改时间\n* `16/12/12` 新增 Utils 来做初始化 context\n* `16/12/10` 完善 权限中\n* `16/12/09` 新增 6.0 以上权限判断\n* `16/12/07` 修复升级到 6.0 bug 中\n* `16/12/06` 完善 FlashlightUtils 中\n* `16/12/05` 完善 FlashlightUtils 兼容 Api21 之后\n* `16/12/04` 新增 FlashlightUtils\n* `16/12/03` 完善 时间工具类\n* `16/12/02` 新增 获取合适型时间差\n* `16/12/01` 新增 获取生肖和星座\n* `16/11/30` 新增 获取友好型时间差\n* `16/11/23` 完善 LocationUtils 测试，发布 1.3.4\n* `16/11/22` 修复 LocationActivity 内存泄漏\n* `16/11/21` 完善 README\n* `16/11/20` 完善 LocationUtils\n* `16/11/19` 完善 SizeUtils\n* `16/11/18` 完善 LocationUtils\n* `16/11/17` 完善 LocationUtils\n* `16/11/16` 新增 拼音工具类，单独拎出来做了整理\n* `16/11/15` 完善 正则工具类\n* `16/11/14` 新增 启动服务\n* `16/11/13` 新增 判断 sim 卡是否准备好\n* `16/11/12` 新增 重启到 recovery 和 bootloader，新增获取 launcher activity，最近一直在博客搬家，所以更得有点少\n* `16/11/04` 修复 README 的缺少 process 的 bug\n* `16/11/03` 修复 SnackbarUtils 中 Snackbar 持有弱引用来消除内存泄漏\n* `16/11/02` 修复 内存泄漏中\n* `16/11/01` 完善 发布版本 1.3.3 内存泄漏检测中\n* `16/10/31` 完善 发布版本 1.3.1 和 1.3.2\n* `16/10/30` 修复 获取 IpAddress 对于小米手机的 Bug\n* `16/10/29` 新增 文件重命名和完善 root\n* `16/10/23` 完善 测试中\n* `16/10/22` 完善 测试中\n* `16/10/21` 完善 测试中\n* `16/10/20` 完善 测试中\n* `16/10/19` 修复 判断网络是否可用\n* `16/10/18` 完善 是否前台应用，完善网络状态\n* `16/10/17` 修复 获取签名，完善是否前台应用，完善网络状态\n* `16/10/16` 新增 SnackbarUtils\n* `16/10/15` 完善 AppUtils 的 isAppForeground\n* `16/10/14` 完善 README-CN 排版（强迫症一定要对齐）\n* `16/10/13` 完善 测试\n* `16/10/12` 新增 LogUtils 建造者模式，新增获取星期，发布版本 1.3.0，cheer\n* `16/10/11` 新增 EncryptUtils 的 Hmac 系列加密\n* `16/10/10` 完善 LogUtils\n* `16/10/09` 完善 ToastUtils\n* `16/10/08` 新增 AppUtils 静默安装和静默卸载\n* `16/10/07` 完善 EmptyUtils，新增很多判空\n* `16/10/05` 完善 Happy Wedding!\n* `16/10/04` 完善 Readme\n* `16/10/03` 修复 ConvertUtils\n* `16/10/02` 完善 CrashUtils 完毕\n* `16/10/01` 完善 Happy National Day!\n* `16/09/30` 完善 CrashUtils\n* `16/09/29` 完善 CleanUtils 测试完毕\n* `16/09/28` 新增 EmptyUtils，完善 AppUtils 完毕\n* `16/09/27` 新增 CleanUtils，完善 AppUtils\n* `16/09/26` 新增 根据域名获取 ip 地址（在此感谢 jp1017），新增 ClipboardUtils 单元测试，对 ImageUtils 进行了 bug 修复\n* `16/09/25` 新增 ClipboardUtils\n* `16/09/24` 完善 AppUtils\n* `16/09/23` 完善 工具类，新增 ActivityUtils、BarUtils、IntentUtils\n* `16/09/22` 完善 LogUtils 中\n* `16/09/21` 新增 LogUtils\n* `16/09/20` 完善 昨天的单元测试\n* `16/09/19` 新增 CameraUtils，新增获取中文首字母\n* `16/09/18` 修复 少许代码，发布 1.2.1\n* `16/09/15` 完善 Happy Mid-Autumn Festival!\n* `16/09/14` 完善 ImageUtils 完毕，完善了 6.0 及以上版本安装 App 的问题，发布版本 1.2.0\n* `16/09/13` 新增 英文版 README\n* `16/09/12` 完善 ZipUtils 及单元测试完美谢幕（支持空文件夹）\n* `16/09/11` 完善 不断更\n* `16/09/10` 完善 ZipUtils 和单元测试中\n* `16/09/09` 新增 字符串反转，ImageUtils 单元测试卡住中，暂时换为真机测试\n* `16/09/08` 修复 NetworkUtils 报空，ImageUtils 单元测试卡住中\n* `16/08/31` 完善 ImageUtils 单元测试中，之后 7 天鸡儿岭放假，停更\n* `16/08/30` 完善 ImageUtils 单元测试（获取保存图片有问题，卡卡卡住了）\n* `16/08/29` 完善 ImageUtils，新增 stack 模糊算法和快速模糊\n* `16/08/28` 完善 ImageUtils\n* `16/08/27` 完善 ConvertUtils，新增 ZipUtils\n* `16/08/26` 完善 ThreadPoolUtils 线程池相关工具类\n* `16/08/25` 完善 ConstUtils 时间和存储相关常量新增枚举，传参改为枚举更为友好，新增 ThreadPoolUtils 线程池相关工具类\n* `16/08/24` 新增 ConvertUtils 的 InputStream 与 byte[]和 String 相互转换，应用在 FileUtils 中读文件\n* `16/08/23` 修复 bug，接下来完善 SDCardUtils 和 ImageUtils\n* `16/08/22` 完善 SPUtils 将 commit 改为 apply 提高效率，将 SPUtils 改为构造函数法创建，FileUtils 新增查找函数，规范 JavaDoc\n* `16/08/21` 完善 FileUtils 单元测试，修复 FileUtils 的 bug，发布版本 1.1.2\n* `16/08/20` 完善 目录、FileUtils 单元测试，发布版本 1.1.1\n* `16/08/19` 完善 FileUtils 及单元测试，及其他小修小补（在此感谢 vpop 的三次 Pr）\n* `16/08/18` 完善 FileUtils 及单元测试，完善 ImageUtils\n* `16/08/17` 完善 FileUtils\n* `16/08/16` 新增 StringUtils 及单元测试，完善正则工具类，版本更新 1.1.0\n* `16/08/15` 新增 3DES 和 AES 加密及单元检测，加密解密工具类基本完善，目录更新\n* `16/08/14` 新增 DES 加密及单元检测\n* `16/08/13` 新增 MD2，SHA224，SHA256，SHA384，SHA512 加密及单元测试，正折腾 DES 加密\n* `16/08/12` 新增 Base64 和 Html 编码解码及他们的单元测试，新增 TimeUtils 单元测试，更新 md\n* `16/08/11` 新增 SDCardUtils, UnitUtils，单元测试慢慢完善中\n* `16/08/09` 修复 目录排版，新增 Download, Proguard 和 License\n* `16/08/08` 新增 Shell 工具类，已传 jcenter 遇到好多坑，javaDoc 惹的祸，注释一定要规范\n* `16/08/07` 新增 6.0 获取 Mac 地址方法，新增对 HTML 转义，新增编码解码工具类，新增 SP 工具类\n* `16/08/06` 完善 名包名，新增加密相关的单元测试，MD5 加密新增文件加密重载\n* `16/08/05` 新增 MD5 盐加密，完善 NetworkUtils，新增判断状态栏是否存在（在此感谢 tiandawu）\n* `16/08/04` 新增 时间工具类（在此感谢 yi520000 给的补充），手机正则分简单和精确（在此感谢 MIkeeJY），新增判断是否锁屏，注释分段落，目录按首字母排序\n* `16/08/03` 修复 onCreate 中获取 view 尺寸的 bug, MD5 和 SHA 的 Bug 修复完成（在此感谢 ssyijiu）\n* `16/08/02` 修复 wifi 设置界面 bug，注释排版还在修改，获取 mac 地址增加判空，新增 QQ群：74721490，欢迎加入，新增隐藏状态栏，注释更加全面，工具类已封装，写的时候真的是一个一个测试过去的，宝宝心里苦\n* `16/08/01` 新增 获取 SD 卡路径，手机和设备进行分类，代码 bug 修改部分，小修排版，正在封装类，新增目录中显示方法名，新增获取当前 App 版本 Code\n* `16/07/31` 新增 点击屏幕空白区域隐藏软键盘，未能成功增加本页目录跳转功能（不支持）\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright 2017 Blankj\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README-CN.md",
    "content": "[![logo][logo]](https://github.com/Blankj/AndroidUtilCode)\n\n[![frame][frame]](https://github.com/Blankj/AucFrameTemplate)\n\n[![auc][aucSvg]][auc] [![result][apiSvg]][result] [![build][buildSvg]][build] [![License][licenseSvg]][license]\n\n## [README of English][readme]\n\n## About\n\n**[AndroidUtilCode][readme]** :fire: 是一个强大易用的安卓工具类库，它合理地封装了安卓开发中常用的函数，具有完善的 Demo 和单元测试，利用其封装好的 APIs 可以大大提高开发效率，如今它主要包括两部分模块，其一是主工具类模块：**[utilcode][utilcode-cn]**，其中的工具类是开发中常用到的；其二是子工具类模块：**[subutil][subutil-cn]**，它包含的工具类并不是很常用，它的出现是为了防止主工具类的臃肿。 :fire:\n\n\n## Documentation\n\n### utilcode\n\n* [README of English][utilcode]\n* [README of Chinese][utilcode-cn]\n\n\n### subutil\n\n* [README of English][subutil]\n* [README of Chinese][subutil-cn]\n\n\n## Donations\n\n如果它对你帮助很大，并且你很想支持库的后续开发和维护，那么你可以扫下方二维码随意打赏我，就当是请我喝杯咖啡或是啤酒，我将不胜感激 :-)\n\n![donate][donate]\n\n\n## Contact\n\n[![Blog][blogSvg]][blog] [![jianshu][jianshuSvg]][jianshu] [![weibo][weiboSvg]][weibo] [![QQGroup][qqgroupSvg]][qqgroup]\n\n\n## [Change Log][changeLog.md]\n\n\n## 打个小广告\n\n欢迎加入我的小专栏「**[基你太美](https://xiaozhuanlan.com/Blankj)**」一起学习。\n\n\n[logo]: https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/art/logo.png\n\n[frame]: https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/art/auc_frame_cn.png\n\n[aucSvg]: https://github.com/Blankj/AndroidUtilCode/workflows/Android%20CI/badge.svg?branch=master\n[auc]: https://github.com/Blankj/AndroidUtilCode\n\n[apiSvg]: https://img.shields.io/badge/API-14+-brightgreen.svg\n[result]: https://android-arsenal.com/result?level=14\n\n[buildSvg]: https://travis-ci.org/Blankj/AndroidUtilCode.svg?branch=master\n[build]: https://travis-ci.org/Blankj/AndroidUtilCode\n\n[licenseSvg]: https://img.shields.io/badge/License-Apache--2.0-brightgreen.svg\n[license]: https://github.com/Blankj/AndroidUtilCode/blob/master/LICENSE\n\n[readme]: https://github.com/Blankj/AndroidUtilCode\n[readme-cn]: https://github.com/Blankj/AndroidUtilCode/blob/master/README-CN.md\n\n[utilcode]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/README.md\n[utilcode-cn]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/README-CN.md\n\n[subutil]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/subutil/README.md\n[subutil-cn]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/subutil/README-CN.md\n\n[changeLog.md]: https://github.com/Blankj/AndroidUtilCode/blob/master/CHANGELOG.md\n\n[donate]: https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/art/donate.png\n\n[blogSvg]: https://img.shields.io/badge/Blog-@Blankj-34a48e.svg\n[blog]: http://blankj.com\n\n[jianshuSvg]: https://img.shields.io/badge/简书-@Blankj-34a48e.svg\n[jianshu]: http://www.jianshu.com/u/46702d5c6978\n\n[weiboSvg]: https://img.shields.io/badge/weibo-@__Blankj-34a48e.svg\n[weibo]: http://weibo.com/3076228982\n\n[qqgroupSvg]: https://img.shields.io/badge/QQ群-25206533-34a48e.svg\n[qqgroup]: https://shang.qq.com/wpa/qunwpa?idkey=d906789f84484465e2736f7b524366b4c23afeda38733d5c7b10fc3f6e406e9b\n"
  },
  {
    "path": "README.md",
    "content": "[![logo][logo]](https://github.com/Blankj/AndroidUtilCode)\n\n[![frame][frame]](https://github.com/Blankj/AucFrameTemplate)\n\n[![auc][aucSvg]][auc] [![result][apiSvg]][result] [![build][buildSvg]][build] [![License][licenseSvg]][license]\n\n## [README of Chinese][readme-cn]\n\n## About\n\n**[AndroidUtilCode][readme]** :fire: is a powerful & easy to use library for Android. This library encapsulates the functions that commonly used in Android development which have complete demo and unit test. By using it's encapsulated APIs, you can greatly improve the development efficiency. The program mainly consists of two modules which is **[utilcode][utilcode-cn]**, which is commonly used in development, and **[subutil][subutil-cn]** which is rarely used in development, but the utils can be beneficial to simplify the main module. :fire:\n\n\n## Documentation\n\n### utilcode\n\n* [README of English][utilcode]\n* [README of Chinese][utilcode-cn]\n\n\n### subutil\n\n* [README of English][subutil]\n* [README of Chinese][subutil-cn]\n\n\n## Donations\n\nIf this project helps you a lot and you want to support the project's development and maintenance of this project, feel free to scan the following QR code for donation. Your donation is highly appreciated. Thank you!\n\n![donate][donate]\n\n\n## Contact\n\n[![Blog][blogSvg]][blog] [![jianshu][jianshuSvg]][jianshu] [![weibo][weiboSvg]][weibo] [![QQGroup][qqgroupSvg]][qqgroup]\n\n\n## [Change Log][changeLog.md]\n\n\n## 打个小广告\n\n欢迎加入我的小专栏「**[基你太美](https://xiaozhuanlan.com/Blankj)**」一起学习。\n\n\n[logo]: https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/art/logo.png\n\n[frame]: https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/art/auc_frame.png\n\n[aucSvg]: https://github.com/Blankj/AndroidUtilCode/workflows/Android%20CI/badge.svg?branch=master\n[auc]: https://github.com/Blankj/AndroidUtilCode\n\n[apiSvg]: https://img.shields.io/badge/API-14+-brightgreen.svg\n[result]: https://android-arsenal.com/result?level=14\n\n[buildSvg]: https://travis-ci.org/Blankj/AndroidUtilCode.svg?branch=master\n[build]: https://travis-ci.org/Blankj/AndroidUtilCode\n\n[licenseSvg]: https://img.shields.io/badge/License-Apache--2.0-brightgreen.svg\n[license]: https://github.com/Blankj/AndroidUtilCode/blob/master/LICENSE\n\n[readme]: https://github.com/Blankj/AndroidUtilCode\n[readme-cn]: https://github.com/Blankj/AndroidUtilCode/blob/master/README-CN.md\n\n[utilcode]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/README.md\n[utilcode-cn]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/README-CN.md\n\n[subutil]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/subutil/README.md\n[subutil-cn]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/subutil/README-CN.md\n\n[changeLog.md]: https://github.com/Blankj/AndroidUtilCode/blob/master/CHANGELOG.md\n\n[donate]: https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/art/donate.png\n\n[blogSvg]: https://img.shields.io/badge/Blog-@Blankj-34a48e.svg\n[blog]: http://blankj.com\n\n[jianshuSvg]: https://img.shields.io/badge/简书-@Blankj-34a48e.svg\n[jianshu]: http://www.jianshu.com/u/46702d5c6978\n\n[weiboSvg]: https://img.shields.io/badge/weibo-@__Blankj-34a48e.svg\n[weibo]: http://weibo.com/3076228982\n\n[qqgroupSvg]: https://img.shields.io/badge/QQ群-25206533-34a48e.svg\n[qqgroup]: https://shang.qq.com/wpa/qunwpa?idkey=d906789f84484465e2736f7b524366b4c23afeda38733d5c7b10fc3f6e406e9b\n"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\nbuildscript {\n    ConfigUtils.init(gradle)\n    repositories {\n        mavenLocal()\n        google()\n        mavenCentral()\n        jcenter()\n    }\n\n    dependencies {\n        for (def entrySet : ConfigUtils.getApplyPlugins().entrySet()) {\n            classpath entrySet.value.path\n        }\n    }\n}\n\nallprojects {\n    repositories {\n        mavenLocal()\n        maven { url \"https://jitpack.io\" }\n        google()\n        mavenCentral()\n        jcenter()\n    }\n\n    configurations.all {\n        resolutionStrategy.cacheChangingModulesFor 0, 'seconds'\n\n        resolutionStrategy.eachDependency {\n            if (it.requested.group == 'com.android.support' && !it.requested.name.contains(\n                    'multidex')) {\n                it.useVersion Config.supportVersion\n            }\n        }\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}"
  },
  {
    "path": "buildApp.gradle",
    "content": "apply plugin: \"com.android.application\"\n\napply {\n    from \"${rootDir.path}/buildCommon.gradle\"\n    from \"${rootDir.path}/config/flavor.gradle\"\n    if (Config.plugins.plugin_api.isApply) {\n        plugin Config.plugins.plugin_api.id\n    }\n    if (Config.plugins.plugin_bus.isApply) {\n        plugin Config.plugins.plugin_bus.id\n    }\n}\n\nconfigSigning()\nconfigApkName()\n\n//if (PluginConfig.plugin_bus.isApply) {\n//    bus {\n//        onlyScanLibRegex = '^([:]|(com\\\\.blankj)).+$'\n//    }\n//}\n//\n//if (PluginConfig.plugin_api.isApply) {\n//    api {\n//        onlyScanLibRegex = '^([:]|(com\\\\.blankj)).+$'\n//    }\n//}\n\nandroid {\n    defaultConfig {\n        applicationId Config.applicationId + suffix\n        targetSdkVersion Config.targetSdkVersion\n        multiDexEnabled true\n    }\n\n    buildTypes {\n        debug {}\n\n        release {\n            minifyEnabled true\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    packagingOptions {\n        exclude 'META-INF/*'\n    }\n\n    dexOptions {\n        preDexLibraries true\n        javaMaxHeapSize \"8g\"\n        maxProcessCount 8\n        dexInProcess = true\n    }\n\n    productFlavors {\n        dev {\n            applicationIdSuffix \".dev\"\n            versionNameSuffix \"-dev\"\n            resValue \"string\", \"app_name\", Config.appName + suffix + \"-dev\"\n        }\n\n        production {\n            resValue \"string\", \"app_name\", Config.appName + suffix\n        }\n    }\n}\n\ndependencies {\n    // LeakCanary\n    debugImplementation Config.libs.leakcanary.path\n\n    debugImplementation Config.modules.lib_utildebug.dep\n    releaseImplementation Config.modules.lib_utildebug_no_op.dep\n\n    // 根据 Config.pkgConfig 来依赖所有 pkg\n    for (def entrySet : ConfigUtils.getApplyPkgs().entrySet()) {\n        api entrySet.value.dep\n    }\n\n    if (Config.modules.feature_mock.isApply) {\n        api ModuleConfig.modules.feature_mock.dep\n    }\n}\n\ndef getSuffix() {\n    if (project.name == \"feature_launcher_app\") return \"\"\n    return \".\" + project.\n        name.\n        substring(\"feature_\".length(), project.name.length() - \"_app\".length())\n}\n\ndef configSigning() {\n\n    File signPropertiesFile = file(\"${rootDir.path}/sign/keystore.properties\")\n    if (!signPropertiesFile.exists()) return\n\n    GLog.d(\"${project.toString()} sign start...\")\n    project.android {\n        Properties properties = new Properties()\n        properties.load(new FileInputStream(signPropertiesFile))\n        signingConfigs {\n            release {\n                storeFile new File(signPropertiesFile.getParent(), properties['keystore'])\n                storePassword properties['storePassword']\n                keyAlias properties['keyAlias']\n                keyPassword properties['keyPassword']\n            }\n        }\n        buildTypes.release.signingConfig signingConfigs.release\n    }\n    GLog.d(\"${project.toString()} sign end...\")\n}\n\ndef configApkName() {\n    project.android.applicationVariants.all { variant ->\n        if (variant.buildType.name != \"debug\") {\n            def artifact = variant.getPackageApplicationProvider().get()\n            artifact.outputDirectory = new File(\"${rootDir.path}/apk\")\n            variant.outputs.each {\n                it.outputFileName = \"util\" + suffix +\n                    (variant.flavorName == \"\" ? \"\" : (\"_\" + variant.flavorName)) +\n                    \"_\" +\n                    variant.versionName.replace(\".\", \"_\") +\n                    \"_\" +\n                    variant.buildType.name +\n                    \".apk\"\n            }\n        }\n    }\n}"
  },
  {
    "path": "buildCommon.gradle",
    "content": "apply {\n    plugin \"kotlin-android\"\n    plugin \"kotlin-android-extensions\"\n}\n\nandroid {\n    compileSdkVersion Config.compileSdkVersion\n    defaultConfig {\n        minSdkVersion Config.minSdkVersion\n        versionCode Config.versionCode\n        versionName Config.versionName\n        consumerProguardFiles 'proguard-rules.pro'\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled true\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n\n    lintOptions {\n        abortOnError false\n    }\n}"
  },
  {
    "path": "buildLib.gradle",
    "content": "apply plugin: \"com.android.library\"\napply from: \"${rootDir.path}/buildCommon.gradle\"\n\ndependencies {\n    if (project.name.endsWith(\"_pkg\") || project.name.endsWith(\"_mock\")) {\n        // if module's name equals 'pkg', api all of export\n        for (def entrySet : ConfigUtils.getApplyExports().entrySet()) {\n            api entrySet.value.dep\n        }\n    } else if (project.name.endsWith(\"_export\")) {\n        api Config.modules.lib_common.dep\n    }\n}"
  },
  {
    "path": "buildSrc/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "buildSrc/build.gradle",
    "content": "repositories {\n    google()\n    jcenter()\n}\n\napply {\n    plugin 'groovy'\n    plugin 'java-gradle-plugin'\n}\n\ngradlePlugin {\n    plugins {\n        readmeCore {\n            id = 'readme-core'\n            implementationClass = 'com.blankj.plugin.readme.ReadmeCorePlugin'\n        }\n\n        readmeSub {\n            id = 'readme-sub'\n            implementationClass = 'com.blankj.plugin.readme.ReadmeSubPlugin'\n        }\n    }\n}\n\ndependencies {\n    implementation gradleApi()\n    implementation localGroovy()\n    implementation \"commons-io:commons-io:2.6\"\n}\n\nsourceSets {\n    main {\n        groovy {\n            srcDirs += 'src/main/java'\n        }\n    }\n}"
  },
  {
    "path": "buildSrc/settings.gradle",
    "content": "//dependencyResolutionManagement {\n//    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n//    repositories {\n//        google()\n//        mavenCentral()\n//        jcenter() // Warning: this repository is going to shut down soon\n//    }\n//}"
  },
  {
    "path": "buildSrc/src/main/groovy/Config.groovy",
    "content": "class Config {\n\n    static applicationId = 'com.blankj.androidutilcode'\n    static appName = 'Util'\n\n    static compileSdkVersion = 29\n    static minSdkVersion = 14\n    static targetSdkVersion = 29\n    static versionCode = 1_031_001\n    static versionName = '1.31.1'// E.g. 1.9.72 => 1,009,072\n\n    // lib version\n    static gradlePluginVersion = '4.1.0'\n    static kotlinVersion = '1.3.72'\n    static androidxVersion = '1.0.0'\n\n    static modules = [\n            /*Don't delete this line*/\n            /*Generated by \"module_config.json\"*/\n            plugin_api_gradle_plugin   : new ModuleConfig(isApply: true , useLocal: true , localPath: \"./plugin/api-gradle-plugin\"),\n            plugin_bus_gradle_plugin   : new ModuleConfig(isApply: true , useLocal: true , localPath: \"./plugin/bus-gradle-plugin\"),\n            plugin_lib_base_transform  : new ModuleConfig(isApply: true , useLocal: true , localPath: \"./plugin/lib/base-transform\", remotePath: \"com.blankj:base-transform:1.0\"),\n            plugin_buildSrc_plugin     : new ModuleConfig(isApply: true , useLocal: true , localPath: \"./plugin/buildSrc-plugin\"),\n            feature_mock               : new ModuleConfig(isApply: false, useLocal: true , localPath: \"./feature/mock\"),\n            feature_launcher_app       : new ModuleConfig(isApply: true , useLocal: true , localPath: \"./feature/launcher/app\"),\n            feature_main_app           : new ModuleConfig(isApply: false, useLocal: true , localPath: \"./feature/main/app\"),\n            feature_main_pkg           : new ModuleConfig(isApply: true , useLocal: true , localPath: \"./feature/main/pkg\"),\n            feature_subutil_app        : new ModuleConfig(isApply: false, useLocal: true , localPath: \"./feature/subutil/app\"),\n            feature_subutil_pkg        : new ModuleConfig(isApply: true , useLocal: true , localPath: \"./feature/subutil/pkg\"),\n            feature_subutil_export     : new ModuleConfig(isApply: true , useLocal: true , localPath: \"./feature/subutil/export\"),\n            feature_utilcode_app       : new ModuleConfig(isApply: false, useLocal: true , localPath: \"./feature/utilcode/app\"),\n            feature_utilcode_pkg       : new ModuleConfig(isApply: true , useLocal: true , localPath: \"./feature/utilcode/pkg\"),\n            feature_utilcode_export    : new ModuleConfig(isApply: true , useLocal: true , localPath: \"./feature/utilcode/export\", remotePath: \"com.blankj:utilcode-export:1.1\"),\n            lib_base                   : new ModuleConfig(isApply: true , useLocal: true , localPath: \"./lib/base\"),\n            lib_common                 : new ModuleConfig(isApply: true , useLocal: true , localPath: \"./lib/common\"),\n            lib_subutil                : new ModuleConfig(isApply: true , useLocal: true , localPath: \"./lib/subutil\"),\n            lib_utilcode               : new ModuleConfig(isApply: true , useLocal: true , localPath: \"./lib/utilcode\", remotePath: \"com.blankj:utilcodex:$Config.versionName\"),\n            lib_utildebug              : new ModuleConfig(isApply: true , useLocal: true , localPath: \"./lib/utildebug\"),\n            lib_utildebug_no_op        : new ModuleConfig(isApply: true , useLocal: true , localPath: \"./lib/utildebug-no-op\"),\n            /*Don't delete this line*/\n    ]\n\n    static plugins = [\n            plugin_gradle  : new PluginConfig(path: \"com.android.tools.build:gradle:$gradlePluginVersion\"),\n            plugin_kotlin  : new PluginConfig(path: \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion\"),\n            // 上传到 maven\n            plugin_maven   : new PluginConfig(path: \"com.github.dcendents:android-maven-gradle-plugin:2.1\", id: \"com.github.dcendents.android-maven\"),\n\n            // 上传新版本插件更新 path 中的版本号，并设置 isApply = false\n            // 通过 mavenLocal 上传本地版本，设置 isApply = true 即可应用插件来调试，最后通过 bintrayUpload 来发布插件\n            plugin_api     : new PluginConfig(isApply: true, useLocal: false, path: \"com.blankj:api-gradle-plugin:1.5\", id: \"com.blankj.api\"),\n            //./gradlew clean :plugin_api-gradle-plugin:mavenLocal     // 上传到本地 mavenLocal\n            //./gradlew clean :plugin_api-gradle-plugin:bintrayUpload  // 上传到 jcenter\n            plugin_bus     : new PluginConfig(isApply: true, useLocal: false, path: \"com.blankj:bus-gradle-plugin:2.6\", id: \"com.blankj.bus\"),\n            //./gradlew clean :plugin_bus-gradle-plugin:mavenLocal     // 上传到本地 mavenLocal\n            //./gradlew clean :plugin_bus-gradle-plugin:bintrayUpload  // 上传到 jcenter\n            plugin_buildSrc: new PluginConfig(isApply: false, useLocal: false, path: \"com.blankj:buildSrc-plugin:1.0\", id: \"com.blankj.buildSrc\"),\n            //./gradlew clean :plugin_bus-gradle-plugin:mavenLocal     // 上传到本地 mavenLocal\n            //./gradlew clean :plugin_bus-gradle-plugin:bintrayUpload  // 上传到 jcenter\n    ]\n\n    static libs = [\n            androidx_appcompat : new LibConfig(path: \"androidx.appcompat:appcompat:$androidxVersion\"),\n            androidx_material  : new LibConfig(path: \"com.google.android.material:material:$androidxVersion\"),\n            androidx_multidex  : new LibConfig(path: \"androidx.multidex:multidex:2.0.0\"),\n            androidx_constraint: new LibConfig(path: \"androidx.constraintlayout:constraintlayout:1.1.3\"),\n\n            kotlin             : new LibConfig(path: \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlinVersion\"),\n\n            leakcanary         : new LibConfig(path: \"com.squareup.leakcanary:leakcanary-android:2.1\"),\n\n            free_proguard      : new LibConfig(path: \"com.blankj:free-proguard:1.0.2\"),\n            swipe_panel        : new LibConfig(path: \"com.blankj:swipe-panel:1.2\"),\n\n            gson               : new LibConfig(path: \"com.google.code.gson:gson:2.8.5\"),\n            glide              : new LibConfig(path: \"com.github.bumptech.glide:glide:4.7.1\"),\n            retrofit           : new LibConfig(path: \"com.squareup.retrofit2:retrofit:2.4.0\"),\n            commons_io         : new LibConfig(path: \"commons-io:commons-io:2.6\"),\n\n            eventbus_lib       : new LibConfig(path: \"org.greenrobot:eventbus:3.1.1\"),\n            eventbus_processor : new LibConfig(path: \"org.greenrobot:eventbus-annotation-processor:3.0.1\"),\n\n            photo_view         : new LibConfig(path: \"com.github.chrisbanes:PhotoView:2.0.0\"),\n\n            test_junit         : new LibConfig(path: \"junit:junit:4.12\"),\n            test_robolectric   : new LibConfig(path: \"org.robolectric:robolectric:4.3.1\"),\n    ]\n}\n//./gradlew clean :lib_utilcode:bintrayUpload"
  },
  {
    "path": "buildSrc/src/main/groovy/ConfigUtils.groovy",
    "content": "import org.gradle.api.Project\nimport org.gradle.api.ProjectEvaluationListener\nimport org.gradle.api.ProjectState\nimport org.gradle.api.invocation.Gradle\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/13\n *     desc  :\n * </pre>\n */\nclass ConfigUtils {\n\n    static init(Gradle gradle) {\n        generateDep(gradle)\n        addCommonGradle(gradle)\n        TaskDurationUtils.init(gradle)\n    }\n\n    /**\n     * 根据 depConfig 生成 dep\n     */\n    private static void generateDep(Gradle gradle) {\n        def configs = [:]\n        for (Map.Entry<String, ModuleConfig> entry : Config.modules.entrySet()) {\n            def (name, config) = [entry.key, entry.value]\n            if (config.useLocal) {\n                config.dep = gradle.rootProject.findProject(name)\n            } else {\n                config.dep = config.remotePath\n            }\n            configs.put(name, config)\n        }\n        GLog.l(\"generateDep = ${GLog.object2String(configs)}\")\n    }\n\n    private static addCommonGradle(Gradle gradle) {\n        gradle.addProjectEvaluationListener(new ProjectEvaluationListener() {\n            @Override\n            void beforeEvaluate(Project project) {\n                // 在 project 的 build.gradle 前 do sth.\n                if (project.name.contains(\"plugin\")) {\n                    return\n                }\n                if (project.name.endsWith(\"_app\")) {\n                    GLog.l(project.toString() + \" applies buildApp.gradle\")\n                    project.apply {\n                        from \"${project.rootDir.path}/buildApp.gradle\"\n                    }\n                } else {\n                    GLog.l(project.toString() + \" applies buildLib.gradle\")\n                    project.apply {\n                        from \"${project.rootDir.path}/buildLib.gradle\"\n                    }\n                }\n            }\n\n            @Override\n            void afterEvaluate(Project project, ProjectState state) {\n                // 在 project 的 build.gradle 末 do sth.\n            }\n        })\n    }\n\n    static getApplyPlugins() {\n        def plugins = [:]\n        for (Map.Entry<String, PluginConfig> entry : Config.plugins.entrySet()) {\n            if (entry.value.isApply) {\n                plugins.put(entry.key, entry.value)\n            }\n        }\n        GLog.d(\"getApplyPlugins = ${GLog.object2String(plugins)}\")\n        return plugins\n    }\n\n    static getApplyPkgs() {\n        def pkgs = [:]\n        for (Map.Entry<String, ModuleConfig> entry : Config.modules.entrySet()) {\n            if (entry.value.isApply && entry.key.endsWith(\"_pkg\")) {\n                pkgs.put(entry.key, entry.value)\n            }\n        }\n        GLog.d(\"getApplyPkgs = ${GLog.object2String(pkgs)}\")\n        return pkgs\n    }\n\n    static getApplyExports() {\n        def exports = [:]\n        for (Map.Entry<String, ModuleConfig> entry : Config.modules.entrySet()) {\n            if (entry.value.isApply && entry.key.endsWith(\"_export\")) {\n                exports.put(entry.key, entry.value)\n            }\n        }\n        GLog.d(\"getApplyExports = ${GLog.object2String(exports)}\")\n        return exports\n    }\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/GLog.groovy",
    "content": "/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/13\n *     desc  :\n * </pre>\n */\nclass GLog {\n\n    def static debugSwitch = true\n\n    static d(Object... contents) {\n        if (!debugSwitch) return contents\n        return l(contents)\n    }\n\n    static l(Object... contents) {\n        StringBuilder sb = new StringBuilder()\n        sb.append(LogConst.BORDER_TOP)\n        sb.append(borderMsg(processContents(contents)))\n        sb.append(LogConst.BORDER_BTM)\n        print sb.toString()\n        return contents\n    }\n\n    private static borderMsg(String msg) {\n        StringBuilder sb = new StringBuilder()\n        object2String(msg).split(LogConst.LINE_SEP).each { line ->\n            sb.append(LogConst.BORDER_LFT).append(line).append(LogConst.LINE_SEP)\n        }\n        return sb\n    }\n\n    private static processContents(final Object... contents) {\n        String body = LogConst.NULL\n        if (contents != null) {\n            if (contents.length == 1) {\n                body = object2String(contents[0])\n            } else {\n                StringBuilder sb = new StringBuilder()\n                int i = 0\n                for (int len = contents.length; i < len; ++i) {\n                    Object content = contents[i]\n                    sb.append(\"args[$i] = \")\n                            .append(object2String(content))\n                            .append(LogConst.LINE_SEP)\n                }\n                body = sb.toString()\n            }\n        }\n        return body.length() == 0 ? LogConst.NOTHING : body\n    }\n\n    static String object2String(Object object) {\n        if (object == null) return \"null\";\n        if (object.getClass().isArray()) return LogFormatter.array2String(object);\n        if (object instanceof List) return LogFormatter.list2String(object);\n        if (object instanceof Map) return LogFormatter.map2String(object);\n        if (object instanceof Throwable) return LogFormatter.throwable2String(object);\n        return object.toString();\n    }\n\n    static class LogFormatter {\n\n        private static array2String(Object object) {\n            if (object instanceof Object[]) {\n                return Arrays.deepToString((Object[]) object);\n            } else if (object instanceof boolean[]) {\n                return Arrays.toString((boolean[]) object);\n            } else if (object instanceof byte[]) {\n                return Arrays.toString((byte[]) object);\n            } else if (object instanceof char[]) {\n                return Arrays.toString((char[]) object);\n            } else if (object instanceof double[]) {\n                return Arrays.toString((double[]) object);\n            } else if (object instanceof float[]) {\n                return Arrays.toString((float[]) object);\n            } else if (object instanceof int[]) {\n                return Arrays.toString((int[]) object);\n            } else if (object instanceof long[]) {\n                return Arrays.toString((long[]) object);\n            } else if (object instanceof short[]) {\n                return Arrays.toString((short[]) object);\n            }\n            throw new IllegalArgumentException(\"Array has incompatible type: \" + object.getClass());\n        }\n\n        private static list2String(List list) {\n            StringBuilder sb = new StringBuilder()\n            sb.append(\"[\")\n            list.each { v ->\n                if (v instanceof Map || v instanceof List) {\n                    sb.append(String.format(\"$LogConst.LINE_SEP%${deep++ * 8}s${object2String(v)},\", \"\"))\n                    deep--\n                } else {\n                    sb.append(String.format(\"$LogConst.LINE_SEP%${deep * 8}s$v,\", \"\"))\n                }\n            }\n            sb.deleteCharAt(sb.length() - 1)\n            if (deep - 1 == 0) {\n                sb.append(\"$LogConst.LINE_SEP]\")\n            } else {\n                sb.append(String.format(\"$LogConst.LINE_SEP%${(deep - 1) * 8}s]\", \"\"))\n            }\n            return sb.toString()\n        }\n\n        private static deep = 1;\n\n        private static map2String(Map map) {\n            StringBuilder sb = new StringBuilder()\n            sb.append(\"[\")\n            map.each { k, v ->\n                if (v instanceof Map || v instanceof List) {\n                    sb.append(String.format(\"$LogConst.LINE_SEP%${deep++ * 8}s%-26s: ${object2String(v)},\", \"\", k))\n                    deep--\n                } else {\n                    sb.append(String.format(\"$LogConst.LINE_SEP%${deep * 8}s%-26s: $v,\", \"\", k))\n                }\n            }\n            sb.deleteCharAt(sb.length() - 1)\n            if (deep - 1 == 0) {\n                sb.append(\"$LogConst.LINE_SEP]\")\n            } else {\n                sb.append(String.format(\"$LogConst.LINE_SEP%${(deep - 1) * 8}s]\", \"\"))\n            }\n            return sb.toString()\n        }\n\n        private static throwable2String(Throwable throwable) {\n            final List<Throwable> throwableList = new ArrayList<>();\n            while (throwable != null && !throwableList.contains(throwable)) {\n                throwableList.add(throwable);\n                throwable = throwable.getCause();\n            }\n            final int size = throwableList.size();\n            final List<String> frames = new ArrayList<>();\n            List<String> nextTrace = getStackFrameList(throwableList.get(size - 1));\n            for (int i = size; --i >= 0;) {\n                final List<String> trace = nextTrace;\n                if (i != 0) {\n                    nextTrace = getStackFrameList(throwableList.get(i - 1));\n                    removeCommonFrames(trace, nextTrace);\n                }\n                if (i == size - 1) {\n                    frames.add(throwableList.get(i).toString());\n                } else {\n                    frames.add(\" Caused by: \" + throwableList.get(i).toString());\n                }\n                frames.addAll(trace);\n            }\n            StringBuilder sb = new StringBuilder();\n            for (final String element : frames) {\n                sb.append(element).append(LogConst.LINE_SEP);\n            }\n            return sb.toString();\n        }\n\n        private static List<String> getStackFrameList(final Throwable throwable) {\n            final StringWriter sw = new StringWriter();\n            final PrintWriter pw = new PrintWriter(sw, true);\n            throwable.printStackTrace(pw);\n            final String stackTrace = sw.toString();\n            final StringTokenizer frames = new StringTokenizer(stackTrace, LogConst.LINE_SEP);\n            final List<String> list = new ArrayList<>();\n            boolean traceStarted = false;\n            while (frames.hasMoreTokens()) {\n                final String token = frames.nextToken();\n                // Determine if the line starts with <whitespace>at\n                final int at = token.indexOf(\"at\");\n                if (at != -1 && token.substring(0, at).trim().isEmpty()) {\n                    traceStarted = true;\n                    list.add(token);\n                } else if (traceStarted) {\n                    break;\n                }\n            }\n            return list;\n        }\n\n        private static void removeCommonFrames(final List<String> causeFrames, final List<String> wrapperFrames) {\n            int causeFrameIndex = causeFrames.size() - 1;\n            int wrapperFrameIndex = wrapperFrames.size() - 1;\n            while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) {\n                // Remove the frame from the cause trace if it is the same\n                // as in the wrapper trace\n                final String causeFrame = causeFrames.get(causeFrameIndex);\n                final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex);\n                if (causeFrame.equals(wrapperFrame)) {\n                    causeFrames.remove(causeFrameIndex);\n                }\n                causeFrameIndex--;\n                wrapperFrameIndex--;\n            }\n        }\n    }\n\n    static class LogConst {\n        static LINE_SEP = System.getProperty(\"line.separator\");\n        static BORDER_TOP = \"┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────\" + LINE_SEP\n        static BORDER_LFT = \"│ \";\n        static BORDER_BTM = \"└────────────────────────────────────────────────────────────────────────────────────────────────────────────────\" + LINE_SEP\n\n        static final NOTHING = \"log nothing\";\n        static final NULL = \"null\";\n    }\n}\n"
  },
  {
    "path": "buildSrc/src/main/groovy/LibConfig.groovy",
    "content": "class LibConfig {\n\n    String path\n\n    String getGroupId() {\n        String[] splits = path.split(\":\")\n        return splits.length == 3 ? splits[0] : null\n    }\n\n    String getArtifactId() {\n        String[] splits = path.split(\":\")\n        return splits.length == 3 ? splits[1] : null\n    }\n\n    String getVersion() {\n        String[] splits = path.split(\":\")\n        return splits.length == 3 ? splits[2] : null\n    }\n\n    @Override\n    String toString() {\n        return \"LibConfig { path = $path }\"\n    }\n\n    static String getFlag(boolean b) {\n        return b ? \"✅\" : \"❌\"\n    }\n}\n\n"
  },
  {
    "path": "buildSrc/src/main/groovy/ModuleConfig.groovy",
    "content": "class ModuleConfig {\n\n    boolean isApply   // 是否应用\n    boolean useLocal  // 是否使用本地的\n    String localPath  // 本地路径\n    String remotePath // 远程路径\n    def dep          // 根据条件生成项目最终的依赖项\n\n    String getGroupId() {\n        String[] splits = remotePath.split(\":\")\n        return splits.length == 3 ? splits[0] : null\n    }\n\n    String getArtifactId() {\n        String[] splits = remotePath.split(\":\")\n        return splits.length == 3 ? splits[1] : null\n    }\n\n    String getVersion() {\n        String[] splits = remotePath.split(\":\")\n        return splits.length == 3 ? splits[2] : null\n    }\n\n    @Override\n    String toString() {\n        return \"ModuleConfig { isApply = ${getFlag(isApply)}\" +\n                \", dep = \" + dep +\n                \" }\"\n    }\n\n    static String getFlag(boolean b) {\n        return b ? \"✅\" : \"❌\"\n    }\n}\n\n"
  },
  {
    "path": "buildSrc/src/main/groovy/PluginConfig.groovy",
    "content": "final class PluginConfig {\n\n    boolean isApply = true  // 是否应用\n    boolean useLocal        // 是否使用本地的\n    String path             // 插件路径\n    String id               // 插件 ID\n\n    String getGroupId() {\n        String[] splits = path.split(\":\")\n        return splits.length == 3 ? splits[0] : null\n    }\n\n    String getArtifactId() {\n        String[] splits = path.split(\":\")\n        return splits.length == 3 ? splits[1] : null\n    }\n\n    String getVersion() {\n        String[] splits = path.split(\":\")\n        return splits.length == 3 ? splits[2] : null\n    }\n\n    @Override\n    String toString() {\n        return \"PluginConfig { isApply = ${getFlag(isApply)}\" +\n                \", useLocal = ${getFlag(useLocal)}\" +\n                \", path = \" + path +\n                \", id = \" + id +\n                \" }\"\n    }\n\n    static String getFlag(boolean b) {\n        return b ? \"✅\" : \"❌\"\n    }\n}"
  },
  {
    "path": "buildSrc/src/main/groovy/TaskDurationUtils.groovy",
    "content": "import org.gradle.BuildListener\nimport org.gradle.BuildResult\nimport org.gradle.api.Task\nimport org.gradle.api.execution.TaskExecutionListener\nimport org.gradle.api.initialization.Settings\nimport org.gradle.api.invocation.Gradle\nimport org.gradle.api.tasks.TaskState\n\nimport java.text.SimpleDateFormat\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/11/22\n *     desc  :\n * </pre>\n */\nclass TaskDurationUtils {\n\n    static List<TaskInfo> taskInfoList = []\n    static long startMillis\n\n    static init(Gradle grd) {\n        startMillis = System.currentTimeMillis()\n        grd.addListener(new TaskExecutionListener() {\n            @Override\n            void beforeExecute(Task task) {\n                task.ext.startTime = System.currentTimeMillis()\n            }\n\n            @Override\n            void afterExecute(Task task, TaskState state) {\n                def exeDuration = System.currentTimeMillis() - task.ext.startTime\n                if (exeDuration >= 500) {\n                    taskInfoList.add(new TaskInfo(task: task, exeDuration: exeDuration))\n                }\n            }\n        })\n        grd.addBuildListener(new BuildListener() {\n            @Override\n            void beforeSettings(Settings settings) {\n                super.beforeSettings(settings)\n            }\n\n            @Override\n            void buildStarted(Gradle gradle) {}\n\n            @Override\n            void settingsEvaluated(Settings settings) {}\n\n            @Override\n            void projectsLoaded(Gradle gradle) {}\n\n            @Override\n            void projectsEvaluated(Gradle gradle) {}\n\n            @Override\n            void buildFinished(BuildResult buildResult) {\n                if (!taskInfoList.isEmpty()) {\n                    Collections.sort(taskInfoList, new Comparator<TaskInfo>() {\n                        @Override\n                        int compare(TaskInfo t, TaskInfo t1) {\n                            return t1.exeDuration - t.exeDuration\n                        }\n                    })\n                    StringBuilder sb = new StringBuilder()\n                    int buildSec = (System.currentTimeMillis() - startMillis) / 1000;\n                    int m = buildSec / 60;\n                    int s = buildSec % 60;\n                    def timeInfo = (m == 0 ? \"${s}s\" : \"${m}m ${s}s (${buildSec}s)\")\n                    sb.append(\"BUILD FINISHED in $timeInfo\\n\")\n                    taskInfoList.each {\n                        sb.append(String.format(\"%7sms %s\\n\", it.exeDuration, it.task.path))\n                    }\n                    def content = sb.toString()\n                    GLog.d(content)\n                    File file = new File(grd.rootProject.buildDir.getAbsolutePath(),\n                            \"build_time_records_\" + new SimpleDateFormat(\"yyyy-MM-dd-HH-mm-ss\").format(new Date()) + \".txt\")\n                    file.getParentFile().mkdirs()\n                    file.write(content)\n                }\n            }\n        })\n    }\n\n    private static class TaskInfo {\n        Task task\n        long exeDuration\n    }\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/com/blankj/plugin/readme/FormatUtils.groovy",
    "content": "package com.blankj.plugin.readme\n\nclass FormatUtils {\n\n    static def LINE_SEP = System.getProperty(\"line.separator\")\n    static def LONG_SPACE = \"                                                                  \"\n\n    static def format(File readmeCN) {\n        def sb = new StringBuilder(),\n            lines = readmeCN.readLines(\"UTF-8\"),\n            i = 0,\n            size = lines.size()\n        for (; i < size; ++i) {\n            String line = lines.get(i)\n            if (line.contains(\"* ###\")) {\n                sb.append(line).append(LINE_SEP)\n                        .append(\"```\").append(LINE_SEP)\n                def maxLen = 0\n                line = lines.get(i += 2)\n                // get the max length of space\n                for (def j = i; !line.equals(\"```\"); line = lines.get(++j)) {\n                    maxLen = Math.max(maxLen, line.replace(\" \", \"\").replace(\",\", \", \").indexOf(':'))\n                }\n                line = lines.get(i)\n                for (; !line.equals(\"```\"); line = lines.get(++i)) {\n                    def noSpaceLine = line.replace(\" \", \"\")\n                    def spaceLen = maxLen - line.replace(\" \", \"\").replace(\",\", \", \").indexOf(':')\n                    sb.append(noSpaceLine.substring(0, noSpaceLine.indexOf(':')).replace(\",\", \", \"))\n                            .append(LONG_SPACE.substring(0, spaceLen))// add the space\n                            .append(': ')\n                            .append(line.substring(line.indexOf(':') + 1).trim())\n                            .append(LINE_SEP)\n                }\n                sb.append(\"```\")\n            } else {\n                sb.append(line)\n            }\n            sb.append(LINE_SEP)\n        }\n        readmeCN.write(sb.toString(), \"UTF-8\")\n    }\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/com/blankj/plugin/readme/ReadmeCorePlugin.groovy",
    "content": "package com.blankj.plugin.readme\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\n\nclass ReadmeCorePlugin implements Plugin<Project> {\n\n    @Override\n    void apply(Project project) {\n        project.extensions.create('readme', ReadmeExtension)\n\n        project.task('readmeTask') {\n            doLast {\n                println \"readmeTask start...\"\n\n                def ext = project['readme'] as ReadmeExtension\n                def readmeCN = ext.readmeCnFile\n                def readmeEng = ext.readmeFile\n\n                readmeOfUtilCode2Eng(readmeCN, readmeEng)\n\n                println \"readmeTask finished.\"\n            }\n        }\n    }\n\n    static def readmeOfUtilCode2Eng(File readmeCN, File readmeEng) {\n        FormatUtils.format(readmeCN)\n        def lines = readmeCN.readLines(\"UTF-8\")\n        def sb = new StringBuilder()\n        readmeCN.eachLine { line ->\n            if (line.contains(\"* ###\")) {\n                if (line.contains(\"UtilsTransActivity\")) {\n                    sb.append(line)\n                } else {\n                    String utilsName = line.substring(line.indexOf(\"[\") + 1, line.indexOf(\"Utils\"))\n                    sb.append(\"* ### About \").append(utilsName).append(line.substring(line.indexOf(\" -> \")))\n                }\n            } else if (line.contains(\": \") && !line.contains(\"[\")) {\n                sb.append(line.substring(0, line.indexOf(':')).trim())\n            } else if (line.contains(\"打个小广告\") || line.contains(\"基你太美\")) {\n                return\n            } else {\n                sb.append(line)\n            }\n            sb.append(FormatUtils.LINE_SEP)\n        }\n        readmeEng.write(sb.toString(), \"UTF-8\")\n    }\n}\n\n\n"
  },
  {
    "path": "buildSrc/src/main/java/com/blankj/plugin/readme/ReadmeExtension.groovy",
    "content": "package com.blankj.plugin.readme\n\nclass ReadmeExtension {\n\n    File readmeFile\n    File readmeCnFile\n\n}\n"
  },
  {
    "path": "buildSrc/src/main/java/com/blankj/plugin/readme/ReadmeSubPlugin.groovy",
    "content": "package com.blankj.plugin.readme\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\n\nclass ReadmeSubPlugin implements Plugin<Project> {\n\n    @Override\n    void apply(Project project) {\n        project.extensions.create('readme', ReadmeExtension)\n\n        project.task('readmeTask') {\n            doLast {\n                println \"readmeTask start...\"\n\n                def ext = project['readme'] as ReadmeExtension\n                def readmeCN = ext.readmeCnFile\n                def readmeEng = ext.readmeFile\n\n                readmeOfSubUtil2Eng(readmeCN, readmeEng)\n\n                println \"readmeTask finished.\"\n            }\n        }\n    }\n\n    static def readmeOfSubUtil2Eng(File readmeCN, File readmeEng) {\n        FormatUtils.format(readmeCN)\n        def lines = readmeCN.readLines(\"UTF-8\"),\n            sb = new StringBuilder(\"## How to use\" + FormatUtils.LINE_SEP\n                    + FormatUtils.LINE_SEP +\n                    \"You should copy the following classes which you want to use in your project.\" + FormatUtils.LINE_SEP),\n            i = 3,\n            size = lines.size()\n        for (; i < size; ++i) {\n            String line = lines.get(i)\n            if (line.contains(\"* ###\")) {\n                String utilsName = line.substring(line.indexOf(\"[\") + 1, line.indexOf(\"Utils\"))\n                sb.append(\"* ### About \").append(utilsName).append(line.substring(line.indexOf(\" -> \")))\n            } else if (line.contains(\": \") && !line.contains(\"[\")) {\n                sb.append(line.substring(0, line.indexOf(':')).trim())\n            } else {\n                sb.append(line)\n            }\n            sb.append(FormatUtils.LINE_SEP)\n        }\n        readmeEng.write(sb.toString(), \"UTF-8\")\n    }\n}\n"
  },
  {
    "path": "config/flavor.gradle",
    "content": "android {\n    flavorDimensions \"env\"\n    productFlavors {\n        dev {\n            dimension \"env\"\n        }\n\n        production {\n            dimension \"env\"\n        }\n    }\n\n    variantFilter { variant ->\n        def flavorNames = variant.flavors*.name\n        def buildTypeName = variant.buildType.name\n\n        // production 包不允许 debug 构建\n        if (flavorNames.contains(\"production\") && buildTypeName.contains(\"debug\")) {\n            variant.setIgnore(true)\n        }\n    }\n}"
  },
  {
    "path": "config/publish.gradle",
    "content": "/*\r\n 1. add\r\n    signing.keyId=xx\r\n    signing.password=xx\r\n    signing.secretKeyRingFile=/Users/xx/secring.gpg\r\n    ossrhUsername=xx\r\n    ossrhPassword=xx\r\n    in root local.properties\r\n\r\n 2. copy the file to the directory of gradle, and apply the file in the module\r\n ext {\r\n    groupId = Config.modules.lib_utilcode.groupId\r\n    artifactId = Config.modules.lib_utilcode.artifactId\r\n    version = Config.modules.lib_utilcode.version\r\n    website = \"https://github.com/Blankj/AndroidUtilCode\"\r\n}\r\n apply from: \"${rootDir.path}/config/publish.gradle\"\r\n\r\n 3. execute following command to publish\r\n ./gradlew :xxmodule:publish2Local  -> upload to mavenLocal\r\n ./gradlew :xxmodule:publish2Remote -> upload to mavenCentral\r\n*/\r\n\r\napply plugin: 'maven-publish'\r\napply plugin: 'signing'\r\n\r\next.multiPublishMode = true\r\n\r\nFile localPropertiesFile = project.rootProject.file(\"local.properties\");\r\nif (!localPropertiesFile.exists()) {\r\n    return\r\n}\r\n\r\nProperties properties = new Properties()\r\nproperties.load(new FileInputStream(localPropertiesFile))\r\nproperties.each { name, value -> ext[name] = value }\r\n\r\nafterEvaluate {\r\n    def ext = project.ext\r\n    publishing {\r\n        publications {\r\n            release(MavenPublication) {\r\n                groupId ext.groupId\r\n                artifactId ext.artifactId\r\n                version ext.version\r\n\r\n                if (isAndroidEnv(project)) {\r\n                    if (project.ext.multiPublishMode) {\r\n                        artifact(\"$buildDir/outputs/aar/${project.getName()}-release.aar\")\r\n                        artifact sourcesJar\r\n                    } else {\r\n                        from project.components.release\r\n                    }\r\n                } else {\r\n                    from project.components.java\r\n                }\r\n\r\n                pom {\r\n                    name = ext.artifactId\r\n                    description = ext.artifactId\r\n                    url = ext.website\r\n\r\n                    licenses {\r\n                        license {\r\n                            name = 'The Apache Software License, Version 2.0'\r\n                            url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'\r\n                        }\r\n                    }\r\n                    developers {\r\n                        developer {\r\n                            id = ext.ossrhUsername\r\n                            name = ext.ossrhUsername\r\n                        }\r\n                    }\r\n                    scm {\r\n                        url = ext.website\r\n                        connection = ext.website\r\n                        developerConnection = ext.website + \".git\"\r\n                    }\r\n\r\n                    if (project.ext.multiPublishMode) {\r\n                        withXml {\r\n                            def dependenciesNode = asNode().getAt('dependencies')[0] ?:\r\n                                    asNode().appendNode('dependencies')\r\n\r\n                            configurations.api.getDependencies().each {\r\n                                dep -> addDependency(project, dependenciesNode, dep, \"compile\")\r\n                            }\r\n                            configurations.implementation.getDependencies().each {\r\n                                dep -> addDependency(project, dependenciesNode, dep, \"runtime\")\r\n                            }\r\n                        }\r\n                    }\r\n                }\r\n            }\r\n        }\r\n\r\n        repositories {\r\n            maven {\r\n                // s01 is newest\r\n                def releasesUrl = \"https://s01.oss.sonatype.org/content/repositories/releases/\"\r\n                def snapshotUrl = \"https://s01.oss.sonatype.org/content/repositories/snapshots/\"\r\n                url = version.toUpperCase().endsWith('SNAPSHOT') ? snapshotUrl : releasesUrl\r\n\r\n                credentials {\r\n                    username ossrhUsername\r\n                    password ossrhPassword\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    signing {\r\n        sign publishing.publications\r\n    }\r\n}\r\n\r\nprivate void addDependency(Project project, def dependenciesNode, Dependency dep, String scope) {\r\n    if (dep.group == null || dep.version == null || dep.name == null || dep.name == \"unspecified\") {\r\n        return\r\n    }\r\n\r\n    final dependencyNode = dependenciesNode.appendNode('dependency')\r\n    dependencyNode.appendNode('scope', scope)\r\n\r\n    if (dep.version == 'unspecified') {\r\n        // 检测 module 中的 dependencies 是否有源码依赖\r\n        // 如果是源码依赖，而且没有在 config 中配置 remotePath，\r\n        // 那么发布到仓库，其他地方依赖该库时会找不到源码的那个库\r\n        println \"publish -> module(unspecified) <${dep.group}:${dep.name}:${dep.version}>\"\r\n        if (project.ext.groupId || project.ext.version) {\r\n            throw new GradleException(\"The module of <\" + dep.name + \"> should set groupId & version.\")\r\n        }\r\n        // 源码依赖，但配置了 remotePath，让 pom 中写入 remotePath\r\n        println(\"publish -> module(wrapped) <${project.ext.groupId}:${name}:${project.ext.version}>\")\r\n\r\n        dependencyNode.appendNode('groupId', project.ext.pomGroupID)\r\n        dependencyNode.appendNode('artifactId', dep.name)\r\n        dependencyNode.appendNode('version', project.ext.pomVersion)\r\n    } else {\r\n        dependencyNode.appendNode('groupId', dep.group)\r\n        dependencyNode.appendNode('artifactId', dep.name)\r\n        dependencyNode.appendNode('version', dep.version)\r\n        println(\"publish -> library <${dep.group}:${dep.name}:${dep.version}>\")\r\n    }\r\n\r\n    if (!dep.transitive) {\r\n        // In case of non transitive dependency,\r\n        // all its dependencies should be force excluded from them POM file\r\n        final exclusionNode = dependencyNode.appendNode('exclusions').appendNode('exclusion')\r\n        exclusionNode.appendNode('groupId', '*')\r\n        exclusionNode.appendNode('artifactId', '*')\r\n    } else if (!dep.properties.excludeRules.empty) {\r\n        // For transitive with exclusions, all exclude rules should be added to the POM file\r\n        final exclusions = dependencyNode.appendNode('exclusions')\r\n        dep.properties.excludeRules.each { ExcludeRule rule ->\r\n            final exclusionNode = exclusions.appendNode('exclusion')\r\n            exclusionNode.appendNode('groupId', rule.group ?: '*')\r\n            exclusionNode.appendNode('artifactId', rule.module ?: '*')\r\n        }\r\n    }\r\n}\r\n\r\nif (isAndroidEnv(project)) {\r\n    // This generates sources.jar\r\n    task sourcesJar(type: Jar) {\r\n        classifier = 'sources'\r\n        from android.sourceSets.main.java.source\r\n    }\r\n\r\n    task javadoc(type: Javadoc) {\r\n        source = android.sourceSets.main.java.source\r\n        classpath += configurations.compile\r\n        classpath += project.files(android.getBootClasspath().join(File.pathSeparator))\r\n    }\r\n\r\n    task javadocJar(type: Jar, dependsOn: javadoc) {\r\n        classifier = 'javadoc'\r\n        from javadoc.destinationDir\r\n    }\r\n} else {\r\n    task sourcesJar(type: Jar, dependsOn: classes) {\r\n        classifier = 'sources'\r\n        from sourceSets.main.allSource\r\n    }\r\n\r\n    task javadocJar(type: Jar, dependsOn: javadoc) {\r\n        classifier = 'javadoc'\r\n        from javadoc.destinationDir\r\n    }\r\n}\r\n\r\nif (project.hasProperty(\"kotlin\")) {\r\n    // Disable creating javadocs\r\n    project.tasks.withType(Javadoc) {\r\n        enabled = false\r\n    }\r\n}\r\n\r\njavadoc {\r\n    options {\r\n        encoding \"UTF-8\"\r\n        charSet 'UTF-8'\r\n        author true\r\n        version project.ext.version\r\n        links \"http://docs.oracle.com/javase/7/docs/api\"\r\n        title \"${project.ext.artifactId} ${project.ext.version}\"\r\n    }\r\n}\r\n\r\nartifacts {\r\n    archives javadocJar\r\n    archives sourcesJar\r\n}\r\n\r\nstatic def isAndroidEnv(Project project) {\r\n    return project.getPlugins().hasPlugin('com.android.application') || project.getPlugins().hasPlugin('com.android.library')\r\n}\r\n\r\ntask publish2Local(type: GradleBuild) {\r\n    tasks = ['assemble', 'publishReleasePublicationToMavenLocal']\r\n}\r\n\r\ntask publish2Remote(type: GradleBuild) {\r\n    tasks = ['assemble', 'publishReleasePublicationToMavenRepository']\r\n}"
  },
  {
    "path": "feature/launcher/app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "feature/launcher/app/build.gradle",
    "content": "apply plugin: 'kotlin-kapt'\n\ndependencies {\n    kapt Config.libs.eventbus_processor.path\n}"
  },
  {
    "path": "feature/launcher/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "feature/launcher/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.blankj.launcher.app\">\n\n    <application\n        android:name=\".LauncherApp\"\n        android:allowBackup=\"false\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\">\n\n        <activity\n            android:name=\"com.blankj.main.pkg.MainActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:screenOrientation=\"user\"\n            android:theme=\"@style/SplashTheme\"\n            android:windowSoftInputMode=\"stateHidden\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n    </application>\n\n</manifest>"
  },
  {
    "path": "feature/launcher/app/src/main/java/com/blankj/launcher/app/LauncherApp.java",
    "content": "package com.blankj.launcher.app;\n\nimport com.blankj.common.CommonApplication;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/10/12\n *     desc  :\n * </pre>\n */\npublic class LauncherApp extends CommonApplication {\n\n    private static LauncherApp sInstance;\n\n    public static LauncherApp getInstance() {\n        return sInstance;\n    }\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        sInstance = this;\n    }\n}\n\n\n"
  },
  {
    "path": "feature/main/app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "feature/main/app/build.gradle",
    "content": ""
  },
  {
    "path": "feature/main/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "feature/main/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.blankj.main.app\">\n\n    <application\n        android:name=\"com.blankj.main.app.MainApp\"\n        android:allowBackup=\"false\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\">\n\n        <activity\n            android:name=\"com.blankj.main.pkg.MainActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:screenOrientation=\"user\"\n            android:theme=\"@style/SplashTheme\"\n            android:windowSoftInputMode=\"stateHidden\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n    </application>\n\n</manifest>"
  },
  {
    "path": "feature/main/app/src/main/java/com/blankj/main/app/MainApp.java",
    "content": "package com.blankj.main.app;\n\nimport android.content.Context;\n\nimport com.blankj.common.CommonApplication;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/10/12\n *     desc  :\n * </pre>\n */\npublic class MainApp extends CommonApplication {\n\n    private static MainApp sInstance;\n\n    public static MainApp getInstance() {\n        return sInstance;\n    }\n\n    @Override\n    protected void attachBaseContext(Context base) {\n        super.attachBaseContext(base);\n    }\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        sInstance = this;\n    }\n}\n\n\n"
  },
  {
    "path": "feature/main/pkg/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "feature/main/pkg/build.gradle",
    "content": ""
  },
  {
    "path": "feature/main/pkg/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "feature/main/pkg/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.blankj.main.pkg\">\n\n    <application>\n        <activity\n            android:name=\".MainActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:screenOrientation=\"user\"\n            android:theme=\"@style/SplashTheme\"\n            android:windowSoftInputMode=\"stateHidden\" />\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "feature/main/pkg/src/main/java/com/blankj/main/pkg/MainActivity.kt",
    "content": "package com.blankj.main.pkg\n\nimport android.graphics.Color\nimport android.os.Bundle\nimport android.view.View\nimport androidx.appcompat.app.ActionBarDrawerToggle\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.subutil.export.api.SubUtilApi\nimport com.blankj.utilcode.export.api.UtilCodeApi\nimport com.blankj.utilcode.util.ApiUtils\nimport com.blankj.utilcode.util.BarUtils\nimport com.blankj.utilcode.util.ClickUtils\nimport com.blankj.utilcode.util.CollectionUtils\nimport kotlinx.android.synthetic.main.activity_main.*\n\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/09/29\n * desc  : MainActivity\n * ```\n */\nclass MainActivity : CommonActivity() {\n\n    override fun isSwipeBack(): Boolean {\n        return false\n    }\n\n    override fun bindDrawer(): Boolean {\n        return true\n    }\n\n    override fun bindLayout(): Int {\n        return R.layout.activity_main\n    }\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        window.setBackgroundDrawable(null)\n        super.onCreate(savedInstanceState)\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        setCommonItems(mainRv, CollectionUtils.newArrayList<CommonItem<*>>(\n                CommonItemClick(R.string.core_util, true) {\n                    ApiUtils.getApi(UtilCodeApi::class.java)?.startUtilCodeActivity(this)\n                },\n                CommonItemClick(R.string.sub_util, true) {\n                    ApiUtils.getApi(SubUtilApi::class.java)?.startSubUtilActivity(this)\n                }\n        ))\n\n        launcherMainCtl.setExpandedTitleColor(Color.TRANSPARENT)\n        setSupportActionBar(launcherMainToolbar)\n        val toggle = ActionBarDrawerToggle(this,\n                drawerView.mBaseDrawerRootLayout,\n                launcherMainToolbar,\n                R.string.navigation_drawer_open,\n                R.string.navigation_drawer_close)\n        drawerView.mBaseDrawerRootLayout.addDrawerListener(toggle)\n        toggle.syncState()\n\n        BarUtils.setStatusBarColor4Drawer(drawerView.mBaseDrawerRootLayout, launcherMainFakeStatusBar, Color.TRANSPARENT, false)\n        BarUtils.addMarginTopEqualStatusBarHeight(launcherMainToolbar)\n    }\n\n    override fun onBackPressed() {\n        ClickUtils.back2HomeFriendly(\"Press again to exit.\")\n    }\n}\n"
  },
  {
    "path": "feature/main/pkg/src/main/java/com/blankj/main/pkg/SplashActivity.kt",
    "content": "package com.blankj.main.pkg\n\nimport com.blankj.common.activity.CommonActivity\n\nclass SplashActivity : CommonActivity() {\n\n}"
  },
  {
    "path": "feature/main/pkg/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"192dp\"\n        android:theme=\"@style/ThemeOverlay.AppCompat.Dark.ActionBar\"\n        app:popupTheme=\"@style/ThemeOverlay.AppCompat.Light\">\n\n        <com.google.android.material.appbar.CollapsingToolbarLayout\n            android:id=\"@+id/launcherMainCtl\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\"\n            app:contentScrim=\"@color/colorPrimary\"\n            app:layout_scrollFlags=\"scroll|exitUntilCollapsed|snap\">\n\n            <ImageView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:padding=\"@dimen/spacing_16\"\n                android:scaleType=\"fitCenter\"\n                android:src=\"@mipmap/ic_launcher\"\n                app:layout_collapseMode=\"parallax\"\n                app:layout_collapseParallaxMultiplier=\"0.6\" />\n\n            <androidx.appcompat.widget.Toolbar\n                android:id=\"@+id/launcherMainToolbar\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"?attr/actionBarSize\"\n                app:layout_collapseMode=\"pin\" />\n\n        </com.google.android.material.appbar.CollapsingToolbarLayout>\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <View\n        android:id=\"@+id/launcherMainFakeStatusBar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/mainRv\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\" />\n\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "feature/main/pkg/src/main/res/values/strings.xml",
    "content": "<resources></resources>\n"
  },
  {
    "path": "feature/mock/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "feature/mock/build.gradle",
    "content": ""
  },
  {
    "path": "feature/mock/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "feature/mock/src/main/AndroidManifest.xml",
    "content": "<manifest package=\"com.blankj.mock\" />"
  },
  {
    "path": "feature/mock/src/main/java/com/blankj/mock/subutil/SubUtilApiMock.java",
    "content": "package com.blankj.mock.subutil;\n\nimport android.content.Context;\n\nimport com.blankj.subutil.export.api.SubUtilApi;\nimport com.blankj.utilcode.util.ApiUtils;\nimport com.blankj.utilcode.util.ToastUtils;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/10\n *     desc  :\n * </pre>\n */\n@ApiUtils.Api(isMock = true)\npublic class SubUtilApiMock extends SubUtilApi {\n\n    @Override\n    public void startSubUtilActivity(Context context) {\n        ToastUtils.showShort(\"startSubUtilActivity\");\n    }\n\n}\n"
  },
  {
    "path": "feature/mock/src/main/java/com/blankj/mock/utilcode/UtilCodeApiMock.java",
    "content": "package com.blankj.mock.utilcode;\n\nimport android.content.Context;\n\nimport com.blankj.utilcode.export.api.UtilCodeApi;\nimport com.blankj.utilcode.util.ApiUtils;\nimport com.blankj.utilcode.util.ToastUtils;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/10\n *     desc  :\n * </pre>\n */\n@ApiUtils.Api(isMock = true)\npublic class UtilCodeApiMock extends UtilCodeApi {\n\n    @Override\n    public void startUtilCodeActivity(Context context) {\n        ToastUtils.showShort(\"startUtilCodeActivity\");\n    }\n\n    @Override\n    public void testCallback(Callback callback) {\n        if (callback != null) {\n            callback.call();\n        }\n    }\n\n}\n"
  },
  {
    "path": "feature/subutil/app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "feature/subutil/app/build.gradle",
    "content": ""
  },
  {
    "path": "feature/subutil/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "feature/subutil/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.blankj.subutil.app\">\n\n    <application\n        android:name=\".SubUtilApp\"\n        android:allowBackup=\"false\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\">\n\n        <activity\n            android:name=\"com.blankj.subutil.pkg.feature.SubUtilActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:screenOrientation=\"user\"\n            android:windowSoftInputMode=\"stateHidden\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n\n    </application>\n\n</manifest>"
  },
  {
    "path": "feature/subutil/app/src/main/java/com/blankj/subutil/app/SubUtilApp.kt",
    "content": "package com.blankj.subutil.app\n\nimport android.content.Context\nimport com.blankj.common.CommonApplication\n\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/10/12\n * desc  : app about utils\n * ```\n */\nclass SubUtilApp : CommonApplication() {\n\n    companion object {\n        var instance: SubUtilApp? = null\n            private set\n    }\n\n    override fun attachBaseContext(base: Context) {\n        super.attachBaseContext(base)\n    }\n\n    override fun onCreate() {\n        super.onCreate()\n        instance = this\n    }\n}\n\n\n"
  },
  {
    "path": "feature/subutil/export/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "feature/subutil/export/build.gradle",
    "content": ""
  },
  {
    "path": "feature/subutil/export/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "feature/subutil/export/src/main/AndroidManifest.xml",
    "content": "<manifest package=\"com.blankj.subutil.export\" />\n"
  },
  {
    "path": "feature/subutil/export/src/main/java/com/blankj/subutil/export/api/SubUtilApi.java",
    "content": "package com.blankj.subutil.export.api;\n\nimport android.content.Context;\n\nimport com.blankj.utilcode.util.ApiUtils;\n\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/06/09\n *     desc  :\n * </pre>\n */\npublic abstract class SubUtilApi extends ApiUtils.BaseApi {\n\n    public abstract void startSubUtilActivity(Context context);\n\n}\n"
  },
  {
    "path": "feature/subutil/pkg/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "feature/subutil/pkg/build.gradle",
    "content": ""
  },
  {
    "path": "feature/subutil/pkg/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "feature/subutil/pkg/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.blankj.subutil.pkg\"\n    android:sharedUserId=\"android.uid.shell\">\n\n    <!--dangerous-->\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.SEND_SMS\" />\n\n    <uses-permission android:name=\"android.permission.INSTALL_PACKAGES\" /><!--installAppSilent-->\n    <uses-permission android:name=\"android.permission.DELETE_PACKAGES\" /><!--uninstallAppSilent-->\n    <uses-permission android:name=\"android.permission.SHUTDOWN\" /><!--shutdown-->\n    <uses-permission android:name=\"android.permission.REBOOT\" /><!--reboot-->\n    <uses-permission android:name=\"android.permission.MODIFY_PHONE_STATE\" /><!--setMobileDataEnabled-->\n\n    <!-- location -->\n    <uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\" />\n    <uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />\n\n    <application>\n\n        <activity\n            android:name=\".feature.SubUtilActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.appStore.AppStoreActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.battery.BatteryActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.country.CountryActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.dangerous.DangerousActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.location.LocationActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n\n        <service android:name=\".feature.location.LocationService\" />\n        <activity\n            android:name=\".feature.pinyin.PinyinActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/Config.kt",
    "content": "package com.blankj.subutil.pkg\n\nimport android.os.Environment\nimport com.blankj.utilcode.util.Utils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2017/05/10\n * desc  : config about constants\n * ```\n */\nobject Config {\n\n    val FILE_SEP = System.getProperty(\"file.separator\")\n    val LINE_SEP = System.getProperty(\"line.separator\")\n    const val TEST_PKG = \"com.blankj.testinstall\"\n    private val CACHE_PATH: String\n    val TEST_APK_PATH: String\n\n    init {\n        val cacheDir = Utils.getApp().externalCacheDir\n        CACHE_PATH = if (cacheDir != null) {\n            cacheDir.absolutePath\n        } else {\n            Environment.getExternalStorageDirectory().absolutePath\n        } + FILE_SEP\n        TEST_APK_PATH = CACHE_PATH + \"test_install.apk\"\n    }\n}\n"
  },
  {
    "path": "feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/SubUtilApiImpl.java",
    "content": "package com.blankj.subutil.pkg;\n\nimport android.content.Context;\n\nimport com.blankj.subutil.export.api.SubUtilApi;\nimport com.blankj.subutil.pkg.feature.SubUtilActivity;\nimport com.blankj.utilcode.util.ApiUtils;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/02\n *     desc  :\n * </pre>\n */\n@ApiUtils.Api\npublic class SubUtilApiImpl extends SubUtilApi {\n\n    @Override\n    public void startSubUtilActivity(Context context) {\n        SubUtilActivity.Companion.start(context);\n    }\n\n}\n"
  },
  {
    "path": "feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/feature/SubUtilActivity.kt",
    "content": "package com.blankj.subutil.pkg.feature\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.subutil.pkg.R\nimport com.blankj.subutil.pkg.feature.appStore.AppStoreActivity\nimport com.blankj.subutil.pkg.feature.battery.BatteryActivity\nimport com.blankj.subutil.pkg.feature.country.CountryActivity\nimport com.blankj.subutil.pkg.feature.dangerous.DangerousActivity\nimport com.blankj.subutil.pkg.feature.location.LocationActivity\nimport com.blankj.subutil.pkg.feature.pinyin.PinyinActivity\nimport com.blankj.utilcode.util.CollectionUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/09/29\n * desc  : MainActivity\n * ```\n */\nclass SubUtilActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, SubUtilActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n\n    override fun bindTitleRes(): Int {\n        return R.string.sub_util\n    }\n\n    override fun bindItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemClick(R.string.demo_app_store, true) {\n                    AppStoreActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_battery, true) {\n                    BatteryActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_country, true) {\n                    CountryActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_dangerous, true) {\n                    DangerousActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_location, true) {\n                    LocationActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_pinyin, true) {\n                    PinyinActivity.start(this)\n                }\n        )\n    }\n}\n"
  },
  {
    "path": "feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/feature/appStore/AppStoreActivity.kt",
    "content": "package com.blankj.subutil.pkg.feature.appStore\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.subutil.pkg.R\nimport com.blankj.subutil.util.AppStoreUtils\nimport com.blankj.utilcode.util.ActivityUtils\nimport com.blankj.utilcode.util.CollectionUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 17/02/01\n * desc  : demo about AppStore\n * ```\n */\nclass AppStoreActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, AppStoreActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_app_store\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemClick(R.string.app_store_system, true) {\n                    AppStoreUtils.getAppStoreIntent(\"com.tencent.mm\").apply {\n                        ActivityUtils.startActivity(this)\n                    }\n                }\n        )\n    }\n}\n"
  },
  {
    "path": "feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/feature/battery/BatteryActivity.kt",
    "content": "package com.blankj.subutil.pkg.feature.battery\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Bundle\nimport android.view.View\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.subutil.pkg.R\nimport com.blankj.subutil.util.BatteryUtils\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.ToastUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 20/03/31\n * desc  : demo about Battery\n * ```\n */\nclass BatteryActivity : CommonActivity(), BatteryUtils.OnBatteryStatusChangedListener {\n\n    private val titleItem: CommonItemTitle = CommonItemTitle(\"\", true);\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, BatteryActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_battery\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(titleItem)\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        BatteryUtils.registerBatteryStatusChangedListener(this)\n    }\n\n    override fun onBatteryStatusChanged(status: BatteryUtils.Status) {\n        titleItem.title = status.toString()\n        ToastUtils.showShort(status.toString())\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        BatteryUtils.unregisterBatteryStatusChangedListener(this)\n    }\n}\n"
  },
  {
    "path": "feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/feature/country/CountryActivity.kt",
    "content": "package com.blankj.subutil.pkg.feature.country\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.subutil.pkg.R\nimport com.blankj.subutil.util.CountryUtils\nimport com.blankj.utilcode.util.CollectionUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 17/02/01\n * desc  : demo about Country\n * ```\n */\nclass CountryActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, CountryActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_country\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemTitle(\"getCountryCodeBySim\", CountryUtils.getCountryCodeBySim(\"Default\")),\n                CommonItemTitle(\"getCountryCodeByLanguage\", CountryUtils.getCountryCodeByLanguage(\"Default\")),\n                CommonItemTitle(\"getCountryBySim\", CountryUtils.getCountryBySim()),\n                CommonItemTitle(\"getCountryByLanguage\", CountryUtils.getCountryByLanguage())\n        )\n    }\n}\n"
  },
  {
    "path": "feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/feature/dangerous/DangerousActivity.kt",
    "content": "package com.blankj.subutil.pkg.feature.dangerous\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.helper.PermissionHelper\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemSwitch\nimport com.blankj.subutil.pkg.Config\nimport com.blankj.subutil.pkg.R\nimport com.blankj.subutil.util.DangerousUtils\nimport com.blankj.utilcode.constant.PermissionConstants\nimport com.blankj.utilcode.util.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 17/02/01\n * desc  : demo about dangerous\n * ```\n */\nclass DangerousActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            PermissionHelper.request(context, object : PermissionUtils.SimpleCallback {\n                override fun onGranted() {\n                    val starter = Intent(context, DangerousActivity::class.java)\n                    context.startActivity(starter)\n                }\n\n                override fun onDenied() {\n                }\n            }, PermissionConstants.STORAGE, PermissionConstants.SMS)\n        }\n    }\n\n    private val listener = object : OnReleasedListener {\n        override fun onReleased() {\n            if (DangerousUtils.installAppSilent(Config.TEST_APK_PATH)) {\n                ToastUtils.showShort(R.string.dangerous_install_successfully)\n            } else {\n                ToastUtils.showShort(R.string.dangerous_install_unsuccessfully)\n            }\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_dangerous\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemClick(R.string.dangerous_install_silent) {\n                    if (AppUtils.isAppInstalled(Config.TEST_PKG)) {\n                        ToastUtils.showShort(R.string.dangerous_app_install_tips)\n                    } else {\n                        if (!FileUtils.isFileExists(Config.TEST_APK_PATH)) {\n                            ReleaseInstallApkTask(listener).execute()\n                        } else {\n                            listener.onReleased()\n                        }\n                    }\n                },\n                CommonItemClick(R.string.dangerous_uninstall_silent) {\n                    if (AppUtils.isAppInstalled(Config.TEST_PKG)) {\n                        if (DangerousUtils.uninstallAppSilent(Config.TEST_PKG, false)) {\n                            ToastUtils.showShort(R.string.dangerous_uninstall_successfully)\n                        } else {\n                            ToastUtils.showShort(R.string.dangerous_uninstall_unsuccessfully)\n                        }\n                    } else {\n                        ToastUtils.showShort(R.string.dangerous_app_uninstall_tips)\n                    }\n                },\n                CommonItemClick(R.string.dangerous_shutdown) {\n                    ToastUtils.showShort(DangerousUtils.shutdown().toString())\n                },\n                CommonItemClick(R.string.dangerous_reboot) {\n                    ToastUtils.showShort(DangerousUtils.reboot().toString())\n                },\n                CommonItemClick(R.string.dangerous_reboot_to_recovery) {\n                    ToastUtils.showShort(DangerousUtils.reboot2Recovery().toString())\n                },\n                CommonItemClick(R.string.dangerous_reboot_to_bootloader) {\n                    ToastUtils.showShort(DangerousUtils.reboot2Bootloader().toString())\n                },\n                CommonItemSwitch(\n                        R.string.dangerous_data_enabled,\n                        { NetworkUtils.getMobileDataEnabled() },\n                        {\n                            if (AppUtils.isAppSystem()) {\n                                DangerousUtils.setMobileDataEnabled(it)\n                            }\n                        }\n                ),\n                CommonItemClick(R.string.dangerous_send_sms_silent) {\n                    DangerousUtils.sendSmsSilent(\"10000\", \"sendSmsSilent\")\n                }\n        )\n    }\n}\n\nclass ReleaseInstallApkTask(private val mListener: OnReleasedListener) : ThreadUtils.SimpleTask<Unit>() {\n\n    override fun doInBackground() {\n        ResourceUtils.copyFileFromAssets(\"test_install\", Config.TEST_APK_PATH)\n    }\n\n    override fun onSuccess(result: Unit) {\n        mListener.onReleased()\n    }\n\n    fun execute() {\n        ThreadUtils.executeByIo(this)\n    }\n}\n\ninterface OnReleasedListener {\n    fun onReleased()\n}"
  },
  {
    "path": "feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/feature/location/LocationActivity.kt",
    "content": "package com.blankj.subutil.pkg.feature.location\n\nimport android.content.ComponentName\nimport android.content.Context\nimport android.content.Intent\nimport android.content.ServiceConnection\nimport android.os.IBinder\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.helper.PermissionHelper\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.subutil.pkg.R\nimport com.blankj.utilcode.constant.PermissionConstants\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.PermissionUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/10/13\n * desc  : demo about LocationUtils\n * ```\n */\nclass LocationActivity : CommonActivity() {\n\n    private var lastLatitude: String = \"unknown\"\n    private var lastLongitude: String = \"unknown\"\n    private var latitude: String = \"unknown\"\n    private var longitude: String = \"unknown\"\n    private var country: String = \"unknown\"\n    private var locality: String = \"unknown\"\n    private var street: String = \"unknown\"\n\n    companion object {\n        fun start(context: Context) {\n            PermissionHelper.request(context, object : PermissionUtils.SimpleCallback {\n                override fun onGranted() {\n                    val starter = Intent(context, LocationActivity::class.java)\n                    context.startActivity(starter)\n                }\n\n                override fun onDenied() {\n                }\n            }, PermissionConstants.LOCATION)\n        }\n    }\n\n    private lateinit var mLocationService: LocationService\n\n    private var conn: ServiceConnection = object : ServiceConnection {\n        override fun onServiceDisconnected(name: ComponentName) {}\n\n        override fun onServiceConnected(name: ComponentName, service: IBinder) {\n            mLocationService = (service as LocationService.LocationBinder).service\n            mLocationService.setOnGetLocationListener(object : LocationService.OnGetLocationListener {\n                override fun getLocation(lastLatitude: String, lastLongitude: String, latitude: String,\n                                         longitude: String, country: String, locality: String, street: String) {\n                    this@LocationActivity.apply {\n                        this.lastLatitude = lastLatitude\n                        this.lastLongitude = lastLongitude\n                        this.latitude = latitude\n                        this.longitude = longitude\n                        this.country = country\n                        this.locality = locality\n                        this.street = street\n                    }\n                    runOnUiThread {\n                        itemsView.updateItems(bindItems())\n                    }\n                }\n            })\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_location\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemTitle(\"lastLatitude\", lastLatitude),\n                CommonItemTitle(\"lastLongitude\", lastLongitude),\n                CommonItemTitle(\"latitude\", latitude),\n                CommonItemTitle(\"longitude\", longitude),\n                CommonItemTitle(\"getCountryName\", country),\n                CommonItemTitle(\"getLocality\", locality),\n                CommonItemTitle(\"getStreet\", street)\n        )\n    }\n\n    override fun doBusiness() {\n        bindService(Intent(this, LocationService::class.java), conn, Context.BIND_AUTO_CREATE)\n    }\n\n    override fun onDestroy() {\n        unbindService(conn)\n        super.onDestroy()\n    }\n}\n"
  },
  {
    "path": "feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/feature/location/LocationService.kt",
    "content": "package com.blankj.subutil.pkg.feature.location\n\nimport android.app.Service\nimport android.content.Intent\nimport android.location.Location\nimport android.os.Binder\nimport android.os.Bundle\nimport android.os.IBinder\nimport android.os.Looper\n\nimport com.blankj.subutil.util.LocationUtils\nimport com.blankj.utilcode.util.ToastUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/11/21\n * desc  : demo about LocationUtils\n * ```\n */\nclass LocationService : Service() {\n\n    private var isSuccess: Boolean = false\n    private var lastLatitude = \"loading...\"\n    private var lastLongitude = \"loading...\"\n    private var latitude = \"loading...\"\n    private var longitude = \"loading...\"\n    private var country = \"loading...\"\n    private var locality = \"loading...\"\n    private var street = \"loading...\"\n\n    private var mOnGetLocationListener: OnGetLocationListener? = null\n\n    private val mOnLocationChangeListener = object : LocationUtils.OnLocationChangeListener {\n        override fun getLastKnownLocation(location: Location) {\n            lastLatitude = location.latitude.toString()\n            lastLongitude = location.longitude.toString()\n            mOnGetLocationListener?.getLocation(lastLatitude, lastLongitude, latitude, longitude, country, locality, street)\n        }\n\n        override fun onLocationChanged(location: Location) {\n            latitude = location.latitude.toString()\n            longitude = location.longitude.toString()\n            mOnGetLocationListener?.getLocation(lastLatitude, lastLongitude, latitude, longitude, country, locality, street)\n            country = LocationUtils.getCountryName(java.lang.Double.parseDouble(latitude), java.lang.Double.parseDouble(longitude))\n            locality = LocationUtils.getLocality(java.lang.Double.parseDouble(latitude), java.lang.Double.parseDouble(longitude))\n            street = LocationUtils.getStreet(java.lang.Double.parseDouble(latitude), java.lang.Double.parseDouble(longitude))\n            mOnGetLocationListener?.getLocation(lastLatitude, lastLongitude, latitude, longitude, country, locality, street)\n        }\n\n        override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}\n    }\n\n    fun setOnGetLocationListener(onGetLocationListener: OnGetLocationListener) {\n        mOnGetLocationListener = onGetLocationListener\n    }\n\n    override fun onCreate() {\n        super.onCreate()\n        Thread(Runnable {\n            Looper.prepare()\n            isSuccess = LocationUtils.register(0, 0, mOnLocationChangeListener)\n            if (isSuccess) ToastUtils.showShort(\"init success\")\n            Looper.loop()\n        }).start()\n    }\n\n    override fun onBind(intent: Intent): IBinder? {\n        return LocationBinder()\n    }\n\n    inner class LocationBinder : Binder() {\n        val service: LocationService\n            get() = this@LocationService\n    }\n\n    override fun onDestroy() {\n        LocationUtils.unregister()\n        // 一定要制空，否则内存泄漏\n        mOnGetLocationListener = null\n        super.onDestroy()\n    }\n\n    /**\n     * 获取位置监听器\n     */\n    interface OnGetLocationListener {\n        fun getLocation(\n                lastLatitude: String, lastLongitude: String,\n                latitude: String, longitude: String,\n                country: String, locality: String, street: String\n        )\n    }\n}\n"
  },
  {
    "path": "feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/feature/pinyin/PinyinActivity.kt",
    "content": "package com.blankj.subutil.pkg.feature.pinyin\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.subutil.pkg.R\nimport com.blankj.subutil.util.PinyinUtils\nimport com.blankj.utilcode.util.CollectionUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 17/02/01\n * desc  : demo about PinyinUtils\n * ```\n */\nclass PinyinActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, PinyinActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_pinyin\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        val surnames = \"乐乘乜仇会便区单参句召员宓弗折曾朴查洗盖祭种秘繁缪能蕃覃解谌适都阿难黑\"\n        val size = surnames.length\n        val sb = StringBuilder(\"澹台: \" + PinyinUtils.getSurnamePinyin(\"澹台\")\n                + \"\\n尉迟: \" + PinyinUtils.getSurnamePinyin(\"尉迟\")\n                + \"\\n万俟: \" + PinyinUtils.getSurnamePinyin(\"万俟\")\n                + \"\\n单于: \" + PinyinUtils.getSurnamePinyin(\"单于\"))\n        for (i in 0 until size) {\n            val surname = surnames[i].toString()\n            sb.append(String.format(\n                    \"\\n%s 正确: %-8s 错误: %-8s\",\n                    surname,\n                    PinyinUtils.getSurnamePinyin(surname),\n                    PinyinUtils.ccs2Pinyin(surname)\n            ))\n        }\n        return CollectionUtils.newArrayList(\n                CommonItemTitle(\"汉字转拼音\", PinyinUtils.ccs2Pinyin(\"汉字转拼音\", \" \")),\n                CommonItemTitle(\"获取首字母\", PinyinUtils.getPinyinFirstLetters(\"获取首字母\", \" \")),\n                CommonItemTitle(\"测试姓氏\", sb.toString())\n\n        )\n    }\n}"
  },
  {
    "path": "feature/subutil/pkg/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"demo_app_store\">App Store Demo</string>\n    <string name=\"demo_battery\">Battery Demo</string>\n    <string name=\"demo_country\">Country Demo</string>\n    <string name=\"demo_dangerous\">Dangerous Demo</string>\n    <string name=\"demo_location\">LocationUtils Demo</string>\n    <string name=\"demo_pinyin\">PinyinUtils Demo</string>\n\n    <!--App Store 相关-->\n    <string name=\"app_store_system\">Go System App Store In WeChat Page</string>\n\n    <string name=\"dangerous_install_silent\">Install Test App Silently</string>\n    <string name=\"dangerous_uninstall_silent\">Uninstall App Silently</string>\n    <string name=\"dangerous_app_install_tips\">Test app have installed</string>\n    <string name=\"dangerous_install_successfully\">Install successfully</string>\n    <string name=\"dangerous_install_unsuccessfully\">Install unsuccessfully</string>\n    <string name=\"dangerous_app_uninstall_tips\">Please install test app first</string>\n    <string name=\"dangerous_uninstall_successfully\">Uninstall successfully</string>\n    <string name=\"dangerous_uninstall_unsuccessfully\">Uninstall unsuccessfully</string>\n    <string name=\"dangerous_shutdown\">Shutdown</string>\n    <string name=\"dangerous_reboot\">Reboot</string>\n    <string name=\"dangerous_reboot_to_recovery\">Reboot To Recovery</string>\n    <string name=\"dangerous_reboot_to_bootloader\">Reboot To Bootloader</string>\n    <string name=\"dangerous_send_sms_silent\">Send SMS Silent</string>\n    <string name=\"dangerous_data_enabled\">Mobile Data Enabled</string>\n\n\n</resources>"
  },
  {
    "path": "feature/utilcode/app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "feature/utilcode/app/build.gradle",
    "content": ""
  },
  {
    "path": "feature/utilcode/app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "feature/utilcode/app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.blankj.utilcode.app\">\n\n    <application\n        android:name=\".UtilCodeApp\"\n        android:allowBackup=\"false\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\">\n\n        <activity\n            android:name=\"com.blankj.utilcode.pkg.feature.CoreUtilActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "feature/utilcode/app/src/main/java/com/blankj/utilcode/app/UtilCodeApp.kt",
    "content": "package com.blankj.utilcode.app\n\nimport com.blankj.common.CommonApplication\nimport com.blankj.utilcode.util.Utils\n\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/10/12\n * desc  : app about utils\n * ```\n */\nclass UtilCodeApp : CommonApplication() {\n\n    companion object {\n        lateinit var instance: UtilCodeApp\n            private set\n    }\n\n    override fun onCreate() {\n        Utils.init(this)\n        super.onCreate()\n        instance = this\n//        BusUtils.register(\"com.blankj.androidutilcode\")\n    }\n}\n\n\n"
  },
  {
    "path": "feature/utilcode/export/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "feature/utilcode/export/build.gradle",
    "content": "ext {\n    groupId = Config.modules.feature_utilcode_export.groupId\n    artifactId = Config.modules.feature_utilcode_export.artifactId\n    version = Config.modules.feature_utilcode_export.version\n    website = \"https://github.com/Blankj/AndroidUtilCode\"\n}\n//apply from: \"${rootDir.path}/config/publish.gradle\"\n//./gradlew :feature_utilcode_export:mavenLocal   // 上传到本地 mavenLocal"
  },
  {
    "path": "feature/utilcode/export/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "feature/utilcode/export/src/main/AndroidManifest.xml",
    "content": "<manifest package=\"com.blankj.utilcode.export\" />\n"
  },
  {
    "path": "feature/utilcode/export/src/main/java/com/blankj/utilcode/export/api/UtilCodeApi.java",
    "content": "package com.blankj.utilcode.export.api;\n\nimport android.content.Context;\n\nimport com.blankj.utilcode.util.ApiUtils;\n\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/01\n *     desc  :\n * </pre>\n */\npublic abstract class UtilCodeApi extends ApiUtils.BaseApi {\n\n    public abstract void startUtilCodeActivity(Context context);\n\n    public abstract void testCallback(Callback callback);\n\n    public interface Callback {\n        void call();\n    }\n\n}"
  },
  {
    "path": "feature/utilcode/pkg/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "feature/utilcode/pkg/build.gradle",
    "content": ""
  },
  {
    "path": "feature/utilcode/pkg/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.blankj.utilcode.pkg\">\n\n    <!-- app -->\n    <uses-permission android:name=\"android.permission.WRITE_SETTINGS\" />\n    <uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\" />\n    <uses-permission android:name=\"android.permission.REQUEST_DELETE_PACKAGES\" />\n\n    <!-- bar -->\n    <uses-permission android:name=\"android.permission.EXPAND_STATUS_BAR\" />\n\n    <!-- 读写内存 -->\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n\n    <!-- flashlight -->\n    <uses-permission android:name=\"android.permission.CAMERA\" />\n    <uses-permission android:name=\"android.permission.FLASHLIGHT\" />\n\n    <!-- network -->\n    <uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\" />\n    <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />\n    <uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\" />\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n\n    <!-- permission -->\n    <uses-permission android:name=\"android.permission.READ_CALENDAR\" />\n    <uses-permission android:name=\"android.permission.RECORD_AUDIO\" />\n    <uses-permission android:name=\"android.permission.SYSTEM_ALERT_WINDOW\" />\n\n    <!-- phone -->\n    <uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />\n    <uses-permission android:name=\"android.permission.CALL_PHONE\" />\n    <uses-permission android:name=\"android.permission.READ_CONTACTS\" />\n\n    <!-- process -->\n    <uses-permission android:name=\"android.permission.KILL_BACKGROUND_PROCESSES\" />\n\n    <!-- vibrate -->\n    <uses-permission android:name=\"android.permission.VIBRATE\" />\n\n    <application>\n        <activity\n            android:name=\".feature.CoreUtilActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n\n        <activity\n            android:name=\".feature.activity.ActivityActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:icon=\"@drawable/activity_activity_icon\"\n            android:launchMode=\"singleTop\"\n            android:logo=\"@drawable/activity_activity_logo\" />\n        <activity\n            android:name=\".feature.activity.SubActivityActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.adaptScreen.AdaptScreenActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.adaptScreen.AdaptCloseActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.adaptScreen.AdaptHeightActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.adaptScreen.AdaptWidthActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.api.ApiActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.app.AppActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.bar.BarActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.bar.nav.BarNavActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.bar.notification.BarNotificationActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.bar.status.BarStatusActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.bar.status.BarStatusActivityAlpha\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.bar.status.BarStatusActivityColor\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.bar.status.BarStatusActivityCustom\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.bar.status.BarStatusActivityDrawer\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.bar.status.fragment.BarStatusFragmentActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.bar.status.BarStatusActivityImageView\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.brightness.BrightnessActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.bus.BusActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.bus.BusCompareActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.clean.CleanActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.click.ClickActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.clipboard.ClipboardActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.device.DeviceActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.file.FileActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.flashlight.FlashlightActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.fragment.FragmentActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.image.ImageActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.intent.IntentActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.keyboard.KeyboardActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.language.LanguageActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.log.LogActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.messenger.MessengerActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.messenger.MessengerRemoteActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\"\n            android:process=\":remote\" />\n        <activity\n            android:name=\".feature.metaData.MetaDataActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\">\n            <meta-data\n                android:name=\"activity_meta_data\"\n                android:value=\"D1234567890123456789012345678901234567890\" />\n        </activity>\n        <activity\n            android:name=\".feature.mvp.MvpActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.network.NetworkActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.notification.NotificationActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.path.PathActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.permission.PermissionActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.phone.PhoneActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.process.ProcessActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.reflect.ReflectActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.resource.ResourceActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.rom.RomActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.screen.ScreenActivity\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.sdcard.SDCardActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.shadow.ShadowActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.snackbar.SnackbarActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.spStatic.SPStaticActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.span.SpanActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.toast.ToastActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\" />\n        <activity\n            android:name=\".feature.uiMessage.UiMessageActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.vibrate.VibrateActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n        <activity\n            android:name=\".feature.volume.VolumeActivity\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:launchMode=\"singleTop\" />\n\n        <meta-data\n            android:name=\"app_meta_data\"\n            android:value=\"AppMetaData\" />\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/assets/test/sub/test.txt",
    "content": "1st line\n2nd line"
  },
  {
    "path": "feature/utilcode/pkg/src/main/assets/test/test.txt",
    "content": "1st line\n2nd line"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/Config.kt",
    "content": "package com.blankj.utilcode.pkg\n\nimport com.blankj.utilcode.util.PathUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2017/05/10\n * desc  : config about constants\n * ```\n */\nobject Config {\n    val FILE_SEP = System.getProperty(\"file.separator\")\n    val LINE_SEP = System.getProperty(\"line.separator\")\n    const val TEST_PKG = \"com.blankj.testinstall\"\n    val CACHE_PATH = PathUtils.getCachePathExternalFirst() + FILE_SEP\n    val TEST_APK_PATH: String = CACHE_PATH + \"test_install.apk\"\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/UtilCodeApiImpl.java",
    "content": "package com.blankj.utilcode.pkg;\n\nimport android.content.Context;\n\nimport com.blankj.utilcode.export.api.UtilCodeApi;\nimport com.blankj.utilcode.pkg.feature.CoreUtilActivity;\nimport com.blankj.utilcode.util.ApiUtils;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/01\n *     desc  :\n * </pre>\n */\n@ApiUtils.Api\npublic class UtilCodeApiImpl extends UtilCodeApi {\n\n    @Override\n    public void startUtilCodeActivity(Context context) {\n        CoreUtilActivity.Companion.start(context);\n    }\n\n    @Override\n    public void testCallback(Callback callback) {\n        if (callback != null) {\n            callback.call();\n        }\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/CoreUtilActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Bundle\nimport android.widget.TextView\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.pkg.feature.activity.ActivityActivity\nimport com.blankj.utilcode.pkg.feature.adaptScreen.AdaptScreenActivity\nimport com.blankj.utilcode.pkg.feature.api.ApiActivity\nimport com.blankj.utilcode.pkg.feature.app.AppActivity\nimport com.blankj.utilcode.pkg.feature.bar.BarActivity\nimport com.blankj.utilcode.pkg.feature.brightness.BrightnessActivity\nimport com.blankj.utilcode.pkg.feature.bus.BusActivity\nimport com.blankj.utilcode.pkg.feature.clean.CleanActivity\nimport com.blankj.utilcode.pkg.feature.click.ClickActivity\nimport com.blankj.utilcode.pkg.feature.clipboard.ClipboardActivity\nimport com.blankj.utilcode.pkg.feature.device.DeviceActivity\nimport com.blankj.utilcode.pkg.feature.file.FileActivity\nimport com.blankj.utilcode.pkg.feature.flashlight.FlashlightActivity\nimport com.blankj.utilcode.pkg.feature.fragment.FragmentActivity\nimport com.blankj.utilcode.pkg.feature.image.ImageActivity\nimport com.blankj.utilcode.pkg.feature.intent.IntentActivity\nimport com.blankj.utilcode.pkg.feature.keyboard.KeyboardActivity\nimport com.blankj.utilcode.pkg.feature.language.LanguageActivity\nimport com.blankj.utilcode.pkg.feature.log.LogActivity\nimport com.blankj.utilcode.pkg.feature.messenger.MessengerActivity\nimport com.blankj.utilcode.pkg.feature.metaData.MetaDataActivity\nimport com.blankj.utilcode.pkg.feature.mvp.MvpActivity\nimport com.blankj.utilcode.pkg.feature.network.NetworkActivity\nimport com.blankj.utilcode.pkg.feature.notification.NotificationActivity\nimport com.blankj.utilcode.pkg.feature.path.PathActivity\nimport com.blankj.utilcode.pkg.feature.permission.PermissionActivity\nimport com.blankj.utilcode.pkg.feature.phone.PhoneActivity\nimport com.blankj.utilcode.pkg.feature.process.ProcessActivity\nimport com.blankj.utilcode.pkg.feature.reflect.ReflectActivity\nimport com.blankj.utilcode.pkg.feature.resource.ResourceActivity\nimport com.blankj.utilcode.pkg.feature.rom.RomActivity\nimport com.blankj.utilcode.pkg.feature.screen.ScreenActivity\nimport com.blankj.utilcode.pkg.feature.sdcard.SDCardActivity\nimport com.blankj.utilcode.pkg.feature.shadow.ShadowActivity\nimport com.blankj.utilcode.pkg.feature.snackbar.SnackbarActivity\nimport com.blankj.utilcode.pkg.feature.spStatic.SPStaticActivity\nimport com.blankj.utilcode.pkg.feature.span.SpanActivity\nimport com.blankj.utilcode.pkg.feature.toast.ToastActivity\nimport com.blankj.utilcode.pkg.feature.uiMessage.UiMessageActivity\nimport com.blankj.utilcode.pkg.feature.vibrate.VibrateActivity\nimport com.blankj.utilcode.pkg.feature.volume.VolumeActivity\nimport com.blankj.utilcode.pkg.helper.DialogHelper\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.LogUtils\nimport com.blankj.utilcode.util.ThreadUtils\nimport com.blankj.utilcode.util.UtilsTransActivity\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/09/29\n * desc  :\n * ```\n */\nclass CoreUtilActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, CoreUtilActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.core_util\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemClick(R.string.demo_activity, true) {\n                    ActivityActivity.start(this)\n                    ThreadUtils.runOnUiThreadDelayed(Runnable {\n\n                    }, 2000)\n                },\n                CommonItemClick(R.string.demo_adapt_screen, true) {\n                    AdaptScreenActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_api, true) {\n                    ApiActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_app, true) {\n                    AppActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_bar, true) {\n                    BarActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_brightness, true) {\n                    BrightnessActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_bus, true) {\n                    BusActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_clean, true) {\n                    CleanActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_click, true) {\n                    ClickActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_clipboard, true) {\n                    ClipboardActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_crash) {\n                    throw NullPointerException(\"crash test\")\n                },\n                CommonItemClick(R.string.demo_device, true) {\n                    DeviceActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_file, true) {\n                    FileActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_flashlight, true) {\n                    FlashlightActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_fragment, true) {\n                    FragmentActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_image, true) {\n                    ImageActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_intent, true) {\n                    IntentActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_keyboard, true) {\n                    KeyboardActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_language, true) {\n                    LanguageActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_log, true) {\n                    LogActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_messenger, true) {\n                    MessengerActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_meta_data, true) {\n                    MetaDataActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_mvp, true) {\n                    MvpActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_network, true) {\n                    NetworkActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_notification, true) {\n                    NotificationActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_path, true) {\n                    PathActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_permission, true) {\n                    PermissionActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_phone, true) {\n                    PhoneActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_process, true) {\n                    ProcessActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_reflect, true) {\n                    ReflectActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_resource, true) {\n                    ResourceActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_rom, true) {\n                    RomActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_screen, true) {\n                    ScreenActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_sdcard, true) {\n                    SDCardActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_shadow, true) {\n                    ShadowActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_snackbar, true) {\n                    SnackbarActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_spStatic, true) {\n                    SPStaticActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_span, true) {\n                    SpanActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_toast, true) {\n                    ToastActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_trans_activity, true) {\n                    UtilsTransActivity.start(this, object : UtilsTransActivity.TransActivityDelegate() {\n                        override fun onCreated(activity: UtilsTransActivity, savedInstanceState: Bundle?) {\n                            super.onCreated(activity, savedInstanceState)\n                            activity.setContentView(R.layout.common_dialog_loading)\n                            activity.findViewById<TextView>(R.id.utilActionLoadingMsgTv).text = \"Trans Activity is showing...\"\n                        }\n                    })\n                },\n                CommonItemClick(R.string.demo_uiMessage, true) {\n                    UiMessageActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_vibrate, true) {\n                    VibrateActivity.start(this)\n                },\n                CommonItemClick(R.string.demo_volume, true) {\n                    VolumeActivity.start(this)\n                }\n        )\n    }\n\n    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {\n        super.onActivityResult(requestCode, resultCode, data)\n        LogUtils.e(requestCode, requestCode)\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/activity/ActivityActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.activity\n\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.drawable.BitmapDrawable\nimport android.os.Bundle\nimport android.widget.ImageView\nimport androidx.core.app.ActivityOptionsCompat\nimport com.blankj.base.rv.ItemViewHolder\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemImage\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.pkg.feature.CoreUtilActivity\nimport com.blankj.utilcode.util.ActivityUtils\nimport com.blankj.utilcode.util.AppUtils\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.StringUtils\nimport java.util.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/10/13\n * desc  : demo about ActivityUtils\n * ```\n */\nclass ActivityActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, ActivityActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_activity\n    }\n\n    override fun bindItems(): List<CommonItem<*>> {\n        val elementItem = ActivityItem()\n        val intent = Intent(this, SubActivityActivity::class.java)\n        val intents = arrayOfNulls<Intent>(2)\n        intents[0] = intent\n        intents[1] = Intent(this, SubActivityActivity::class.java)\n\n        return CollectionUtils.newArrayList(\n                elementItem,\n                CommonItemTitle(\"isActivityExists(${SubActivityActivity::class.java.name})\", ActivityUtils.isActivityExists(AppUtils.getAppPackageName(), SubActivityActivity::class.java.name).toString()),\n                CommonItemTitle(\"getLauncherActivity\", ActivityUtils.getLauncherActivity(AppUtils.getAppPackageName())),\n                CommonItemTitle(\"getMainActivities\", ActivityUtils.getMainActivities().toString()),\n                CommonItemTitle(\"getActivityList\", CollectionUtils.collect(ActivityUtils.getActivityList()) { input -> input.javaClass.simpleName }.toString()),\n                CommonItemTitle(\"getTopActivity\", ActivityUtils.getTopActivity().toString()),\n                CommonItemTitle(\"isActivityExistsInStack\", ActivityUtils.isActivityExistsInStack(CoreUtilActivity::class.java).toString()),\n                CommonItemImage(\"getActivityIcon\") {\n                    it.setImageDrawable(ActivityUtils.getActivityIcon(ActivityActivity::class.java))\n                },\n                CommonItemImage(\"getActivityLogo\") {\n                    it.setImageDrawable(ActivityUtils.getActivityLogo(ActivityActivity::class.java))\n                },\n\n                CommonItemClick(R.string.activity_clz, true) {\n                    ActivityUtils.startActivity(SubActivityActivity::class.java)\n                },\n                CommonItemClick(R.string.activity_clz_opt, true) {\n                    ActivityUtils.startActivity(SubActivityActivity::class.java, getOption(elementItem))\n                },\n                CommonItemClick(R.string.activity_clz_anim, true) {\n                    ActivityUtils.startActivity(SubActivityActivity::class.java, R.anim.fade_in_1000, R.anim.fade_out_1000)\n                },\n                CommonItemClick(R.string.activity_act_clz, true) {\n                    ActivityUtils.startActivity(this, SubActivityActivity::class.java)\n                },\n                CommonItemClick(R.string.activity_act_clz_shared_element, true) {\n                    ActivityUtils.startActivity(this, SubActivityActivity::class.java, elementItem.element)\n                },\n                CommonItemClick(R.string.activity_act_clz_anim, true) {\n                    ActivityUtils.startActivity(this, SubActivityActivity::class.java, R.anim.fade_in_1000, R.anim.fade_out_1000)\n                },\n                CommonItemClick(R.string.activity_pkg_cls, true) {\n                    ActivityUtils.startActivity(this.packageName, SubActivityActivity::class.java.name)\n                },\n                CommonItemClick(R.string.activity_pkg_cls_opt, true) {\n                    ActivityUtils.startActivity(this.packageName, SubActivityActivity::class.java.name, getOption(elementItem))\n                },\n                CommonItemClick(R.string.activity_pkg_cls_anim, true) {\n                    ActivityUtils.startActivity(this.packageName, SubActivityActivity::class.java.name, R.anim.fade_in_1000, R.anim.fade_out_1000)\n                },\n                CommonItemClick(R.string.activity_act_pkg_cls, true) {\n                    ActivityUtils.startActivity(this, this.packageName, SubActivityActivity::class.java.name)\n                },\n                CommonItemClick(R.string.activity_act_pkg_cls_opt, true) {\n                    ActivityUtils.startActivity(this, this.packageName, SubActivityActivity::class.java.name, getOption(elementItem))\n                },\n                CommonItemClick(R.string.activity_act_pkg_cls_shared_element, true) {\n                    ActivityUtils.startActivity(this, this.packageName, SubActivityActivity::class.java.name, elementItem.element)\n                },\n                CommonItemClick(R.string.activity_act_pkg_cls_anim, true) {\n                    ActivityUtils.startActivity(this, this.packageName, SubActivityActivity::class.java.name, R.anim.fade_in_1000, R.anim.fade_out_1000)\n                },\n                CommonItemClick(R.string.activity_intent, true) {\n                    ActivityUtils.startActivity(this, intent)\n                },\n                CommonItemClick(R.string.activity_intent_opt, true) {\n                    ActivityUtils.startActivity(this, intent, getOption(elementItem))\n                },\n                CommonItemClick(R.string.activity_intent_shared_element, true) {\n                    ActivityUtils.startActivity(this, intent, elementItem.element)\n                },\n                CommonItemClick(R.string.activity_intent_anim, true) {\n                    ActivityUtils.startActivity(this, intent, R.anim.fade_in_1000, R.anim.fade_out_1000)\n                },\n                CommonItemClick(R.string.activity_intents, true) {\n                    ActivityUtils.startActivities(intents)\n                },\n                CommonItemClick(R.string.activity_intents_opt, true) {\n                    ActivityUtils.startActivities(intents, getOption(elementItem))\n                },\n                CommonItemClick(R.string.activity_intents_anim, true) {\n                    ActivityUtils.startActivities(intents, R.anim.fade_in_1000, R.anim.fade_out_1000)\n                },\n                CommonItemClick(R.string.activity_act_intents, true) {\n                    ActivityUtils.startActivities(this, intents, R.anim.fade_in_1000, R.anim.fade_out_1000)\n                },\n                CommonItemClick(R.string.activity_act_intents_opt, true) {\n                    ActivityUtils.startActivities(this, intents, getOption(elementItem))\n                },\n                CommonItemClick(R.string.activity_act_intents_anim, true) {\n                    ActivityUtils.startActivities(this, intents, R.anim.fade_in_1000, R.anim.fade_out_1000)\n                },\n                CommonItemClick(R.string.activity_start_home_activity, true) {\n                    ActivityUtils.startHomeActivity()\n                },\n                CommonItemClick(R.string.activity_start_launcher_activity, true) {\n                    ActivityUtils.startLauncherActivity()\n                },\n                CommonItemClick(R.string.activity_finish_activity, false) {\n                    ActivityUtils.finishActivity(CoreUtilActivity::class.java)\n                },\n                CommonItemClick(R.string.activity_finish_to_activity, true) {\n                    ActivityUtils.finishToActivity(CoreUtilActivity::class.java, false, true)\n                },\n                CommonItemClick(R.string.activity_finish_all_activities_except_newest, true) {\n                    ActivityUtils.finishAllActivitiesExceptNewest()\n                },\n                CommonItemClick(R.string.activity_finish_all_activities, true) {\n                    ActivityUtils.finishAllActivities()\n                }\n        )\n    }\n\n    private fun getOption(activityItem: ActivityItem): Bundle? {\n        when (Random().nextInt(5)) {\n            0 -> return ActivityOptionsCompat.makeCustomAnimation(this,\n                    R.anim.slide_right_in_1000,\n                    R.anim.slide_left_out_1000)\n                    .toBundle()\n            1 -> return ActivityOptionsCompat.makeScaleUpAnimation(activityItem.element,\n                    activityItem.element.width / 2,\n                    activityItem.element.height / 2,\n                    0, 0)\n                    .toBundle()\n            2 -> return ActivityOptionsCompat.makeThumbnailScaleUpAnimation(activityItem.element,\n                    (activityItem.element.drawable as BitmapDrawable).bitmap,\n                    0, 0)\n                    .toBundle()\n            3 -> return ActivityOptionsCompat.makeSceneTransitionAnimation(this,\n                    activityItem.element,\n                    StringUtils.getString(R.string.activity_shared_element))\n                    .toBundle()\n            else -> return ActivityOptionsCompat.makeClipRevealAnimation(activityItem.element,\n                    activityItem.element.width / 2,\n                    activityItem.element.height / 2,\n                    0, 0)\n                    .toBundle()\n        }\n    }\n}\n\nclass ActivityItem : CommonItem<ActivityItem> {\n\n    lateinit var element: ImageView;\n\n    constructor() : super(R.layout.activity_item_shared_element_activity)\n\n    override fun bind(holder: ItemViewHolder, position: Int) {\n        super.bind(holder, position)\n        element = holder.findViewById(R.id.activityViewSharedElement)\n    }\n}"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/activity/SubActivityActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.activity\n\nimport android.app.Activity\nimport android.content.Intent\nimport android.os.Bundle\nimport androidx.core.app.ActivityCompat\nimport android.view.View\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.ColorUtils\nimport kotlinx.android.synthetic.main.activity_sub_activity.*\n\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/10/13\n * desc  : demo about ActivityUtils\n * ```\n */\nclass SubActivityActivity : CommonActivity() {\n\n    override fun bindLayout(): Int {\n        return R.layout.activity_sub_activity\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_activity\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        contentView?.setBackgroundColor(ColorUtils.getRandomColor(false))\n        activityViewSharedElement.setOnClickListener {\n            val result = Intent()\n            result.putExtra(\"data\", \"data\")\n            this@SubActivityActivity.setResult(Activity.RESULT_OK, result)\n            this@SubActivityActivity.finish()\n        }\n    }\n\n    override fun onBackPressed() {\n        super.onBackPressed()\n        ActivityCompat.finishAfterTransition(this)\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/adaptScreen/AdaptCloseActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.adaptScreen\n\nimport android.content.Context\nimport android.content.Intent\nimport android.content.res.Resources\nimport android.os.Bundle\nimport android.view.View\nimport android.view.WindowManager\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.AdaptScreenUtils\n\nclass AdaptCloseActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, AdaptCloseActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindLayout(): Int {\n        return R.layout.adaptscreen_close_activity\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)\n    }\n\n    override fun getResources(): Resources {\n        return AdaptScreenUtils.closeAdapt(super.getResources())\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/adaptScreen/AdaptHeightActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.adaptScreen\n\nimport android.content.Context\nimport android.content.Intent\nimport android.content.res.Resources\nimport android.os.Bundle\nimport android.view.View\nimport android.view.WindowManager\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.AdaptScreenUtils\nimport com.blankj.utilcode.util.BarUtils\nimport com.blankj.utilcode.util.LogUtils\n\nclass AdaptHeightActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, AdaptHeightActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindLayout(): Int {\n        return R.layout.adaptscreen_height_activity\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)\n    }\n\n    override fun onResume() {\n        super.onResume()\n        LogUtils.e(BarUtils.getStatusBarHeight())\n    }\n\n    override fun getResources(): Resources {\n        return AdaptScreenUtils.adaptHeight(super.getResources(), 1920)\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/adaptScreen/AdaptScreenActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.adaptScreen\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.CollectionUtils\n\nclass AdaptScreenActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, AdaptScreenActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_adapt_screen\n    }\n\n    override fun bindItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemClick(R.string.adaptScreen_adapt_width, true) {\n                    AdaptWidthActivity.start(this)\n                },\n                CommonItemClick(R.string.adaptScreen_adapt_height, true) {\n                    AdaptHeightActivity.start(this)\n                },\n                CommonItemClick(R.string.adaptScreen_adapt_close, true) {\n                    AdaptCloseActivity.start(this)\n                }\n        )\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/adaptScreen/AdaptWidthActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.adaptScreen\n\nimport android.content.Context\nimport android.content.Intent\nimport android.content.res.Resources\nimport android.graphics.Color\nimport android.os.Bundle\nimport android.view.View\nimport android.view.WindowManager\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.AdaptScreenUtils\nimport kotlinx.android.synthetic.main.adaptscreen_width_activity.*\n\nclass AdaptWidthActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, AdaptWidthActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindLayout(): Int {\n        return R.layout.adaptscreen_width_activity\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)\n        adaptScreenWidthWebView.setBackgroundColor(Color.parseColor(\"#f0d26d\"))\n    }\n\n    override fun getResources(): Resources {\n        return AdaptScreenUtils.adaptWidth(super.getResources(), 1080)\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/api/ApiActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.api\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.pkg.feature.api.other.export.OtherModuleApi\nimport com.blankj.utilcode.util.ApiUtils\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.ToastUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2019/03/12\n * desc  : demo about ApiUtils\n * ```\n */\nclass ApiActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, ApiActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_api\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemClick(R.string.api_invoke_with_params) {\n                    ApiUtils.getApi(OtherModuleApi::class.java)?.invokeWithParams(OtherModuleApi.ApiBean(\"params\"))\n                },\n                CommonItemClick(R.string.api_invoke_with_return_value) {\n                    ToastUtils.showShort(ApiUtils.getApi(OtherModuleApi::class.java)?.invokeWithReturnValue()?.name)\n                }\n        );\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/api/other/export/OtherModuleApi.java",
    "content": "package com.blankj.utilcode.pkg.feature.api.other.export;\n\nimport com.blankj.utilcode.util.ApiUtils;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/10\n *     desc  : demo about ApiUtils\n * </pre>\n */\npublic abstract class OtherModuleApi extends ApiUtils.BaseApi {\n\n    public abstract void invokeWithParams(ApiBean bean);\n\n    public abstract ApiBean invokeWithReturnValue();\n\n    public static class ApiBean {\n\n        public String name;\n\n        public ApiBean(String name) {\n            this.name = name;\n        }\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/api/other/pkg/OtherPkgApiImpl.java",
    "content": "package com.blankj.utilcode.pkg.feature.api.other.pkg;\n\nimport com.blankj.utilcode.pkg.feature.api.other.export.OtherModuleApi;\nimport com.blankj.utilcode.util.ApiUtils;\nimport com.blankj.utilcode.util.ToastUtils;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/10\n *     desc  : demo about ApiUtils\n * </pre>\n */\n@ApiUtils.Api\npublic class OtherPkgApiImpl extends OtherModuleApi {\n\n    @Override\n    public void invokeWithParams(ApiBean bean) {\n        ToastUtils.showShort(bean.name);\n    }\n\n    @Override\n    public ApiBean invokeWithReturnValue() {\n        String value = \"value\";\n        return new ApiBean(value);\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/app/AppActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.app\n\nimport android.app.Activity\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.helper.PermissionHelper\nimport com.blankj.common.item.*\nimport com.blankj.utilcode.constant.PermissionConstants\nimport com.blankj.utilcode.pkg.Config\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/10/13\n * desc  : demo about AppUtils\n * ```\n */\nclass AppActivity : CommonActivity(), Utils.OnAppStatusChangedListener {\n\n    var isRegisterAppStatusChangedListener: Boolean = false\n\n    companion object {\n        fun start(context: Context) {\n            PermissionHelper.request(context, object : PermissionUtils.SimpleCallback {\n                override fun onGranted() {\n                    val starter = Intent(context, AppActivity::class.java)\n                    context.startActivity(starter)\n                }\n\n                override fun onDenied() {\n                }\n            }, PermissionConstants.STORAGE)\n        }\n    }\n\n    private val listener = object : OnReleasedListener {\n        override fun onReleased() {\n            return AppUtils.installApp(Config.TEST_APK_PATH)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_app\n    }\n\n    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {\n        super.onActivityResult(requestCode, resultCode, data)\n        LogUtils.e(requestCode, resultCode)\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n            CommonItemSwitch(\n                \"registerAppStatusChangedListener\",\n                { isRegisterAppStatusChangedListener },\n                {\n                    isRegisterAppStatusChangedListener = it\n                    if (it) {\n                        AppUtils.registerAppStatusChangedListener(this)\n                    } else {\n                        AppUtils.unregisterAppStatusChangedListener(this)\n                    }\n                }),\n            CommonItemTitle(\"isAppRoot\", AppUtils.isAppRoot().toString()),\n            CommonItemTitle(\"isAppDebug\", AppUtils.isAppDebug().toString()),\n            CommonItemTitle(\"isAppSystem\", AppUtils.isAppSystem().toString()),\n            CommonItemTitle(\n                \"isAppForeground\",\n                AppUtils.isAppForeground(AppUtils.getAppPackageName()).toString()\n            ),\n            CommonItemTitle(\n                \"isAppRunning\",\n                AppUtils.isAppRunning(AppUtils.getAppPackageName()).toString()\n            ),\n            CommonItemImage(\"getAppIcon\") {\n                it.setImageDrawable(AppUtils.getAppIcon())\n            },\n            CommonItemTitle(\"getAppPackageName\", AppUtils.getAppPackageName()),\n            CommonItemTitle(\"getAppName\", AppUtils.getAppName()),\n            CommonItemTitle(\"getAppPath\", AppUtils.getAppPath()),\n            CommonItemTitle(\"getAppVersionName\", AppUtils.getAppVersionName()),\n            CommonItemTitle(\"getAppVersionCode\", AppUtils.getAppVersionCode().toString()),\n            CommonItemTitle(\"getAppMinSdkVersion\", AppUtils.getAppMinSdkVersion().toString()),\n            CommonItemTitle(\"getAppTargetSdkVersion\", AppUtils.getAppTargetSdkVersion().toString()),\n            CommonItemTitle(\"getAppSignaturesSHA1\", AppUtils.getAppSignaturesSHA1().toString()),\n            CommonItemTitle(\"getAppSignaturesSHA256\", AppUtils.getAppSignaturesSHA256().toString()),\n            CommonItemTitle(\"getAppSignaturesMD5\", AppUtils.getAppSignaturesMD5().toString()),\n            CommonItemTitle(\"getAppUid\", AppUtils.getAppUid().toString()),\n            CommonItemTitle(\"getApkInfo\", AppUtils.getApkInfo(AppUtils.getAppPath()).toString()),\n\n            CommonItemClick(R.string.app_install) {\n                if (AppUtils.isAppInstalled(Config.TEST_PKG)) {\n                    ToastUtils.showShort(R.string.app_install_tips)\n                } else {\n                    if (!FileUtils.isFileExists(Config.TEST_APK_PATH)) {\n                        ReleaseInstallApkTask(listener).execute()\n                    } else {\n                        listener.onReleased()\n                    }\n                }\n            },\n            CommonItemClick(R.string.app_uninstall) {\n                if (AppUtils.isAppInstalled(Config.TEST_PKG)) {\n                    AppUtils.uninstallApp(Config.TEST_PKG)\n                } else {\n                    ToastUtils.showShort(R.string.app_uninstall_tips)\n                }\n            },\n            CommonItemClick(R.string.app_launch) {\n                AppUtils.launchApp(this.packageName)\n            },\n            CommonItemClick(R.string.app_relaunch) {\n                AppUtils.relaunchApp()\n            },\n            CommonItemClick(R.string.app_launch_details_settings, true) {\n                AppUtils.launchAppDetailsSettings()\n            },\n            CommonItemClick(R.string.app_exit) {\n                AppUtils.exitApp()\n            }\n        )\n    }\n\n    override fun onForeground(activity: Activity) {\n        ToastUtils.showShort(\"onForeground\\n${activity.javaClass.simpleName}\")\n    }\n\n    override fun onBackground(activity: Activity) {\n        ToastUtils.showShort(\"onBackground\\n${activity.javaClass.simpleName}\")\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        if (isRegisterAppStatusChangedListener) {\n            AppUtils.unregisterAppStatusChangedListener(this)\n        }\n    }\n}\n\nclass ReleaseInstallApkTask(private val mListener: OnReleasedListener) :\n    ThreadUtils.SimpleTask<Unit>() {\n\n    override fun doInBackground() {\n        ResourceUtils.copyFileFromAssets(\"test_install\", Config.TEST_APK_PATH)\n    }\n\n    override fun onSuccess(result: Unit) {\n        mListener.onReleased()\n    }\n\n    fun execute() {\n        ThreadUtils.executeByIo(this)\n    }\n}\n\ninterface OnReleasedListener {\n    fun onReleased()\n}"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/bar/BarActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.bar\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.pkg.feature.bar.nav.BarNavActivity\nimport com.blankj.utilcode.pkg.feature.bar.notification.BarNotificationActivity\nimport com.blankj.utilcode.pkg.feature.bar.status.*\nimport com.blankj.utilcode.pkg.feature.bar.status.fragment.BarStatusFragmentActivity\nimport com.blankj.utilcode.util.CollectionUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2017/05/27\n * desc  : demo about BarUtils\n * ```\n */\nclass BarActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, BarActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_bar\n    }\n\n    override fun bindItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemTitle(R.string.bar_about_status_bar, true),\n                CommonItemClick(R.string.bar_status_about, true) {\n                    BarStatusActivity.start(this)\n                },\n                CommonItemClick(R.string.bar_status_set_color, true) {\n                    BarStatusActivityColor.start(this)\n                },\n                CommonItemClick(R.string.bar_status_set_alpha, true) {\n                    BarStatusActivityAlpha.start(this)\n                },\n                CommonItemClick(R.string.bar_status_set_image_view, true) {\n                    BarStatusActivityImageView.start(this)\n                },\n                CommonItemClick(R.string.bar_status_set_custom, true) {\n                    BarStatusActivityCustom.start(this)\n                },\n                CommonItemClick(R.string.bar_status_set_fragment, true) {\n                    BarStatusFragmentActivity.start(this)\n                },\n                CommonItemClick(R.string.bar_status_set_drawer, true) {\n                    BarStatusActivityDrawer.start(this)\n                },\n                CommonItemTitle(R.string.bar_about_notification_bar, true),\n                CommonItemClick(R.string.bar_notification_about, true) {\n                    BarNotificationActivity.start(this)\n                },\n                CommonItemTitle(R.string.bar_about_nav_bar, true),\n                CommonItemClick(R.string.bar_nav_about, true) {\n                    BarNavActivity.start(this)\n                }\n        )\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/bar/nav/BarNavActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.bar.nav\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Build\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemSwitch\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.BarUtils\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.ColorUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/10/13\n * desc  : demo about BarUtils\n * ```\n */\nclass BarNavActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, BarNavActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_bar\n    }\n\n    override fun bindItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList<CommonItem<*>>().apply {\n            add(CommonItemTitle(\"navHeight\", BarUtils.getNavBarHeight().toString()))\n            add(CommonItemTitle(\"isSupportNavBar\", BarUtils.isSupportNavBar().toString()))\n            if (BarUtils.isSupportNavBar()) {\n                add(CommonItemSwitch(\n                    R.string.bar_nav_visibility,\n                    { BarUtils.isNavBarVisible(this@BarNavActivity) },\n                    { BarUtils.setNavBarVisibility(this@BarNavActivity, it) }\n                ))\n\n                add(CommonItemSwitch(\n                    R.string.bar_nav_light_mode,\n                    { BarUtils.isNavBarLightMode(this@BarNavActivity) },\n                    { BarUtils.setNavBarLightMode(this@BarNavActivity, it) }\n                ))\n\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n                    add(\n                        CommonItemClick(\n                            \"getNavBarColor: ${\n                                ColorUtils.int2ArgbString(\n                                    BarUtils.getNavBarColor(\n                                        this@BarNavActivity\n                                    )\n                                )\n                            }\"\n                        ).setOnItemClickListener() { _, item, _ ->\n                            BarUtils.setNavBarColor(\n                                this@BarNavActivity,\n                                ColorUtils.getRandomColor()\n                            )\n                            itemsView.updateItems(bindItems())\n                            item.title = \"getNavBarColor: ${\n                                ColorUtils.int2ArgbString(\n                                    BarUtils.getNavBarColor(this@BarNavActivity)\n                                )\n                            }\"\n                        })\n                }\n                add(CommonItemClick(\"transparentNavBar\").setOnItemClickListener() { _, item, _ ->\n                    BarUtils.transparentNavBar(this@BarNavActivity)\n                })\n            }\n        }\n    }\n\n    override fun onWindowFocusChanged(hasFocus: Boolean) {\n        super.onWindowFocusChanged(hasFocus)\n        itemsView.updateItems(bindItems())\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/bar/notification/BarNotificationActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.bar.notification\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Handler\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.BarUtils\nimport com.blankj.utilcode.util.CollectionUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/10/13\n * desc  : demo about BarUtils\n * ```\n */\nclass BarNotificationActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, BarNotificationActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    private val mHandler = Handler()\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_bar\n    }\n\n    override fun bindItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemClick(R.string.bar_notification_show) {\n                    BarUtils.setNotificationBarVisibility(true)\n                    mHandler.postDelayed({ BarUtils.setNotificationBarVisibility(false) }, 2000)\n                }\n        )\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        mHandler.removeCallbacksAndMessages(null)\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/bar/status/BarStatusActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.bar.status\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemSwitch\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.BarUtils\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.Utils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/10/13\n * desc  : demo about BarUtils\n * ```\n */\nclass BarStatusActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, BarStatusActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_bar\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemTitle(\"getStatusBarHeight\", BarUtils.getStatusBarHeight().toString()),\n                CommonItemSwitch(\n                        R.string.bar_status_visibility,\n                        { BarUtils.isStatusBarVisible(this) },\n                        { BarUtils.setStatusBarVisibility(this, it) }\n                ),\n                CommonItemSwitch(\n                        R.string.bar_status_light_mode,\n                        { BarUtils.isStatusBarLightMode(this) },\n                        { BarUtils.setStatusBarLightMode(this, it) }\n                )\n        )\n    }\n\n    override fun onResume() {\n        super.onResume()\n        itemsView.updateItems(bindItems())\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/bar/status/BarStatusActivityAlpha.kt",
    "content": "package com.blankj.utilcode.pkg.feature.bar.status\n\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.Color\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.SeekBar\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemSeekBar\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.BarUtils\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.ColorUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2017/05/27\n * desc  : demo about BarUtils\n * ```\n */\nclass BarStatusActivityAlpha : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, BarStatusActivityAlpha::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    private var mAlpha: Int = 112\n\n    override fun bindLayout(): Int {\n        return R.layout.bar_status_alpha_activity\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        setCommonItems(findViewById(R.id.commonItemRv), getItems())\n        updateStatusBar()\n    }\n\n    private fun getItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList<CommonItem<*>>(\n                CommonItemSeekBar(\"Status Bar Alpha\", 255, object : CommonItemSeekBar.ProgressListener() {\n                    override fun getCurValue(): Int {\n                        return mAlpha\n                    }\n\n                    override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {\n                        mAlpha = progress\n                        updateStatusBar()\n                    }\n                }).apply {\n                    backgroundColor = ColorUtils.setAlphaComponent(backgroundColor, 0.5f)\n                }\n        )\n    }\n\n    private fun updateStatusBar() {\n        BarUtils.setStatusBarColor(this, Color.argb(mAlpha, 0, 0, 0))\n        BarUtils.addMarginTopEqualStatusBarHeight(findViewById(R.id.commonItemRv))// 其实这个只需要调用一次即可\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/bar/status/BarStatusActivityColor.kt",
    "content": "package com.blankj.utilcode.pkg.feature.bar.status\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Bundle\nimport android.view.View\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.BarUtils\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.ColorUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2017/05/27\n * desc  : demo about BarUtils\n * ```\n */\nclass BarStatusActivityColor : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, BarStatusActivityColor::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    private var mColor: Int = ColorUtils.getColor(R.color.colorPrimary)\n\n    override fun bindItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList<CommonItem<*>>(\n                CommonItemClick(R.string.bar_status_random_color, ColorUtils.int2ArgbString(mColor)).setOnClickUpdateContentListener {\n                    mColor = ColorUtils.getRandomColor()\n                    updateStatusBar()\n                    return@setOnClickUpdateContentListener ColorUtils.int2ArgbString(mColor)\n                }\n        )\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        updateStatusBar()\n    }\n\n    private fun updateStatusBar() {\n        BarUtils.setStatusBarColor(this, mColor)\n        BarUtils.addMarginTopEqualStatusBarHeight(findViewById(R.id.commonItemRv))// 其实这个只需要调用一次即可\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/bar/status/BarStatusActivityCustom.kt",
    "content": "package com.blankj.utilcode.pkg.feature.bar.status\n\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.Color\nimport android.os.Bundle\nimport android.view.View\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.BarUtils\n\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2019/01/14\n * desc  : demo about BarUtils\n * ```\n */\nclass BarStatusActivityCustom : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, BarStatusActivityCustom::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        BarUtils.setStatusBarColor(this, Color.TRANSPARENT).setBackgroundResource(R.drawable.bar_status_custom)\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/bar/status/BarStatusActivityDrawer.kt",
    "content": "package com.blankj.utilcode.pkg.feature.bar.status\n\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.Color\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.SeekBar\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemSeekBar\nimport com.blankj.common.item.CommonItemSwitch\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.BarUtils\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.ColorUtils\nimport kotlinx.android.synthetic.main.bar_status_drawer_activity.*\n\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2017/05/27\n * desc  : demo about BarUtils\n * ```\n */\nclass BarStatusActivityDrawer : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, BarStatusActivityDrawer::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    private var mColor: Int = ColorUtils.getColor(R.color.colorPrimary)\n    private var mAlpha: Int = 112\n\n    private var mAlphaStatus: Boolean = false\n    private var mFrontStatus: Boolean = false\n\n    override fun isSwipeBack(): Boolean {\n        return false\n    }\n\n    override fun bindDrawer(): Boolean {\n        return true\n    }\n\n    override fun bindLayout(): Int {\n        return R.layout.bar_status_drawer_activity\n    }\n\n    private fun getItems(): MutableList<CommonItem<*>> {\n        val randomColorItem = CommonItemClick(R.string.bar_status_random_color, ColorUtils.int2ArgbString(mColor)).setOnClickUpdateContentListener {\n            mColor = ColorUtils.getRandomColor()\n            updateStatusBar()\n            return@setOnClickUpdateContentListener ColorUtils.int2ArgbString(mColor)\n        }\n\n        val alphaItem: CommonItem<*> = CommonItemSeekBar(\"Status Bar Alpha\", 255, object : CommonItemSeekBar.ProgressListener() {\n            override fun getCurValue(): Int {\n                return mAlpha\n            }\n\n            override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {\n                mAlpha = progress\n                updateStatusBar()\n            }\n        })\n\n        return CollectionUtils.newArrayList(\n                CommonItemSwitch(\n                        R.string.bar_status_title_alpha,\n                        {\n                            updateStatusBar()\n                            mAlphaStatus\n                        },\n                        {\n                            mAlphaStatus = it\n                            if (mAlphaStatus) {\n                                barStatusDrawerRootLl.setBackgroundResource(R.drawable.image_lena)\n                                commonItemAdapter.replaceItem(2, alphaItem, true)\n                            } else {\n                                barStatusDrawerRootLl.setBackgroundColor(Color.TRANSPARENT)\n                                commonItemAdapter.replaceItem(2, randomColorItem, true)\n                            }\n                        }\n                ),\n                CommonItemSwitch(\n                        R.string.bar_status_is_front,\n                        { mFrontStatus },\n                        {\n                            mFrontStatus = it\n                            updateStatusBar()\n                        }\n                ),\n                randomColorItem\n        )\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        setCommonItems(findViewById(R.id.commonItemRv), getItems())\n    }\n\n    private fun updateStatusBar() {\n        if (mAlphaStatus) {\n            BarUtils.setStatusBarColor4Drawer(drawerView.mBaseDrawerRootLayout, barStatusDrawerFakeStatusBar, Color.argb(mAlpha, 0, 0, 0), mFrontStatus)\n        } else {\n            BarUtils.setStatusBarColor4Drawer(drawerView.mBaseDrawerRootLayout, barStatusDrawerFakeStatusBar, mColor, mFrontStatus)\n        }\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/bar/status/BarStatusActivityImageView.kt",
    "content": "package com.blankj.utilcode.pkg.feature.bar.status\n\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.Color\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.SeekBar\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemSeekBar\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.BarUtils\nimport com.blankj.utilcode.util.CollectionUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2017/05/27\n * desc  : demo about BarUtils\n * ```\n */\nclass BarStatusActivityImageView : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, BarStatusActivityImageView::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    private var mAlpha: Int = 112\n\n    override fun bindLayout(): Int {\n        return R.layout.bar_status_image_view_activity\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        setCommonItems(findViewById(R.id.commonItemRv), getItems())\n        updateStatusBar()\n    }\n\n    private fun getItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList<CommonItem<*>>(\n                CommonItemSeekBar(\"Status Bar Alpha\", 255, object : CommonItemSeekBar.ProgressListener() {\n                    override fun getCurValue(): Int {\n                        return mAlpha\n                    }\n\n                    override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {\n                        mAlpha = progress\n                        updateStatusBar()\n                    }\n                })\n        )\n    }\n\n    private fun updateStatusBar() {\n        BarUtils.setStatusBarColor(this, Color.argb(mAlpha, 0, 0, 0), true)\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/bar/status/fragment/BarStatusFragmentActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.bar.status.fragment\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.Fragment\nimport androidx.fragment.app.FragmentPagerAdapter\nimport androidx.viewpager.widget.ViewPager\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.utilcode.pkg.R\nimport com.google.android.material.bottomnavigation.BottomNavigationView\nimport kotlinx.android.synthetic.main.bar_status_fragment_activity.*\nimport java.util.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2017/05/27\n * desc  : demo about BarUtils\n * ```\n */\nclass BarStatusFragmentActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, BarStatusFragmentActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    private val itemIds = intArrayOf(\n            R.id.barStatusFragmentNavigationColor,\n            R.id.barStatusFragmentNavigationAlpha,\n            R.id.barStatusFragmentNavigationImageView,\n            R.id.barStatusFragmentNavigationCustom\n    )\n\n    private val mFragmentList = ArrayList<androidx.fragment.app.Fragment>()\n\n    private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener l@{ item ->\n        when (item.itemId) {\n            R.id.barStatusFragmentNavigationColor -> {\n                barStatusFragmentVp.currentItem = 0\n                return@l true\n            }\n            R.id.barStatusFragmentNavigationAlpha -> {\n                barStatusFragmentVp.currentItem = 1\n                return@l true\n            }\n            R.id.barStatusFragmentNavigationImageView -> {\n                barStatusFragmentVp.currentItem = 2\n                return@l true\n            }\n            R.id.barStatusFragmentNavigationCustom -> {\n                barStatusFragmentVp.currentItem = 3\n                return@l true\n            }\n            else -> false\n        }\n    }\n\n    override fun isSwipeBack(): Boolean {\n        return false\n    }\n\n    override fun bindLayout(): Int {\n        return R.layout.bar_status_fragment_activity\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        mFragmentList.add(BarStatusFragmentColor.newInstance())\n        mFragmentList.add(BarStatusFragmentAlpha.newInstance())\n        mFragmentList.add(BarStatusFragmentImageView.newInstance())\n        mFragmentList.add(BarStatusFragmentCustom.newInstance())\n\n        barStatusFragmentVp.offscreenPageLimit = mFragmentList.size - 1\n        barStatusFragmentVp.adapter = object : FragmentPagerAdapter(supportFragmentManager) {\n            override fun getItem(position: Int): Fragment {\n                return mFragmentList[position]\n            }\n\n            override fun getCount(): Int {\n                return mFragmentList.size\n            }\n        }\n        barStatusFragmentVp.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {\n            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}\n\n            override fun onPageSelected(position: Int) {\n                barStatusFragmentNav.selectedItemId = itemIds[position]\n            }\n\n            override fun onPageScrollStateChanged(state: Int) {}\n        })\n\n        barStatusFragmentNav.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/bar/status/fragment/BarStatusFragmentAlpha.kt",
    "content": "package com.blankj.utilcode.pkg.feature.bar.status.fragment\n\nimport android.graphics.Color\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.SeekBar\nimport com.blankj.common.fragment.CommonFragment\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemSeekBar\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.BarUtils\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.ColorUtils\nimport kotlinx.android.synthetic.main.bar_status_alpha_fragment.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2017/07/01\n * desc  : demo about BarUtils\n * ```\n */\nclass BarStatusFragmentAlpha : CommonFragment() {\n\n    companion object {\n        fun newInstance(): BarStatusFragmentAlpha {\n            return BarStatusFragmentAlpha()\n        }\n    }\n\n    override fun isLazy(): Boolean {\n        return true\n    }\n\n    private var mAlpha: Int = 112\n\n    override fun bindLayout(): Int {\n        return R.layout.bar_status_alpha_fragment\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        setCommonItems(findViewById(R.id.commonItemRv), getItems())\n        updateFakeStatusBar()\n    }\n\n    private fun getItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList<CommonItem<*>>(\n                CommonItemSeekBar(\"Status Bar Alpha\", 255, object : CommonItemSeekBar.ProgressListener() {\n                    override fun getCurValue(): Int {\n                        return mAlpha\n                    }\n\n                    override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {\n                        mAlpha = progress\n                        updateFakeStatusBar()\n                    }\n                }).apply {\n                    backgroundColor = ColorUtils.setAlphaComponent(backgroundColor, 0.5f)\n                }\n        )\n    }\n\n    fun updateFakeStatusBar() {\n        BarUtils.setStatusBarColor(barStatusAlphaFragmentFakeStatusBar, Color.argb(mAlpha, 0, 0, 0))\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/bar/status/fragment/BarStatusFragmentColor.kt",
    "content": "package com.blankj.utilcode.pkg.feature.bar.status.fragment\n\nimport android.os.Bundle\nimport android.view.View\nimport com.blankj.common.fragment.CommonFragment\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.BarUtils\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.ColorUtils\nimport kotlinx.android.synthetic.main.bar_status_color_fragment.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2017/07/01\n * desc  : demo about BarUtils\n * ```\n */\nclass BarStatusFragmentColor : CommonFragment() {\n\n    companion object {\n        fun newInstance(): BarStatusFragmentColor {\n            return BarStatusFragmentColor()\n        }\n    }\n\n    private var mColor: Int = ColorUtils.getColor(R.color.colorPrimary)\n\n    override fun isLazy(): Boolean {\n        return true\n    }\n\n    override fun bindLayout(): Int {\n        return R.layout.bar_status_color_fragment\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        setCommonItems(findViewById(R.id.commonItemRv), getItems())\n        updateFakeStatusBar()\n    }\n\n    private fun getItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList<CommonItem<*>>(\n                CommonItemClick(R.string.bar_status_random_color, ColorUtils.int2ArgbString(mColor)).setOnClickUpdateContentListener {\n                    mColor = ColorUtils.getRandomColor()\n                    updateFakeStatusBar()\n                    return@setOnClickUpdateContentListener ColorUtils.int2ArgbString(mColor)\n                }\n        )\n    }\n\n    private fun updateFakeStatusBar() {\n        BarUtils.setStatusBarColor(barStatusColorFragmentFakeStatusBar, mColor)\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/bar/status/fragment/BarStatusFragmentCustom.kt",
    "content": "package com.blankj.utilcode.pkg.feature.bar.status.fragment\n\nimport android.os.Bundle\nimport android.view.View\nimport com.blankj.common.fragment.CommonFragment\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.BarUtils\nimport kotlinx.android.synthetic.main.bar_status_custom_fragment.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2017/07/01\n * desc  : demo about BarUtils\n * ```\n */\nclass BarStatusFragmentCustom : CommonFragment() {\n\n    companion object {\n        fun newInstance(): BarStatusFragmentCustom {\n            return BarStatusFragmentCustom()\n        }\n    }\n\n    override fun isLazy(): Boolean {\n        return true\n    }\n\n    override fun bindLayout(): Int {\n        return R.layout.bar_status_custom_fragment\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        BarUtils.setStatusBarCustom(barStatusCustomFragmentFakeStatusBar)\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/bar/status/fragment/BarStatusFragmentImageView.kt",
    "content": "package com.blankj.utilcode.pkg.feature.bar.status.fragment\n\nimport android.graphics.Color\nimport android.os.Bundle\nimport android.view.View\nimport android.widget.SeekBar\nimport com.blankj.common.fragment.CommonFragment\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemSeekBar\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.BarUtils\nimport com.blankj.utilcode.util.CollectionUtils\nimport kotlinx.android.synthetic.main.bar_status_image_view_fragment.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2017/07/01\n * desc  : demo about BarUtils\n * ```\n */\nclass BarStatusFragmentImageView : CommonFragment() {\n\n    companion object {\n        fun newInstance(): BarStatusFragmentImageView {\n            return BarStatusFragmentImageView()\n        }\n    }\n\n    private var mAlpha: Int = 112\n\n    override fun isLazy(): Boolean {\n        return true\n    }\n\n    override fun bindLayout(): Int {\n        return R.layout.bar_status_image_view_fragment\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        setCommonItems(findViewById(R.id.commonItemRv), getItems())\n        updateFakeStatusBar()\n    }\n\n    private fun getItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList<CommonItem<*>>(\n                CommonItemSeekBar(\"Status Bar Alpha\", 255, object : CommonItemSeekBar.ProgressListener() {\n                    override fun getCurValue(): Int {\n                        return mAlpha\n                    }\n\n                    override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {\n                        mAlpha = progress\n                        updateFakeStatusBar()\n                    }\n                })\n        )\n    }\n\n    fun updateFakeStatusBar() {\n        BarUtils.setStatusBarColor(barStatusImageViewFragmentFakeStatusBar, Color.argb(mAlpha, 0, 0, 0))\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/brightness/BrightnessActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.brightness\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Build\nimport android.widget.SeekBar\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemSeekBar\nimport com.blankj.common.item.CommonItemSwitch\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2018/02/08\n * desc  : demo about BrightnessUtils\n * ```\n */\nclass BrightnessActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n                PermissionUtils.requestWriteSettings(object : PermissionUtils.SimpleCallback {\n                    override fun onGranted() {\n                        val starter = Intent(context, BrightnessActivity::class.java)\n                        context.startActivity(starter)\n                    }\n\n                    override fun onDenied() {\n                        ToastUtils.showLong(\"No permission of write settings.\")\n                    }\n                })\n            } else {\n                val starter = Intent(context, BrightnessActivity::class.java)\n                context.startActivity(starter)\n            }\n        }\n    }\n\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_brightness\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemSeekBar(\"getBrightness\", 255, object : CommonItemSeekBar.ProgressListener() {\n                    override fun getCurValue(): Int {\n                        return BrightnessUtils.getBrightness()\n                    }\n\n                    override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {\n                        BrightnessUtils.setBrightness(progress)\n                    }\n                }),\n                CommonItemSeekBar(\"getWindowBrightness\", 255, object : CommonItemSeekBar.ProgressListener() {\n                    override fun getCurValue(): Int {\n                        return BrightnessUtils.getWindowBrightness(window)\n                    }\n\n                    override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {\n                        BrightnessUtils.setWindowBrightness(window, progress)\n                    }\n                }),\n                CommonItemSwitch(\n                        R.string.brightness_auto_brightness,\n                        { BrightnessUtils.isAutoBrightnessEnabled() },\n                        { BrightnessUtils.setAutoBrightnessEnabled(it) }\n                )\n        )\n    }\n}"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/bus/BusActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.bus\n\nimport android.content.Context\nimport android.content.Intent\nimport androidx.annotation.Keep\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.BusUtils\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.ThreadUtils\nimport kotlin.random.Random\n\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2019/03/12\n * desc  : demo about BusUtils\n * ```\n */\nclass BusActivity : CommonActivity() {\n\n    private val titleItem: CommonItemTitle = CommonItemTitle(\"\", true);\n\n    @BusUtils.Bus(tag = TAG_BASIC_TYPE)\n    fun test(param: Int) {\n        titleItem.title = param.toString()\n    }\n\n    @BusUtils.Bus(tag = TAG_BUS, priority = 5)\n    fun test(param: String) {\n        titleItem.title = param\n    }\n\n    @BusUtils.Bus(tag = TAG_BUS, priority = 1)\n    fun testSameTag(param: String) {\n        if (titleItem.title.toString() == TAG_BUS) {\n            titleItem.title = \"${titleItem.title} * 2\"\n        }\n    }\n\n    @BusUtils.Bus(tag = TAG_STICKY_BUS, sticky = true)\n    fun testSticky(callback: Callback) {\n        titleItem.title = callback.call()\n    }\n\n    @BusUtils.Bus(tag = TAG_IO, threadMode = BusUtils.ThreadMode.IO)\n    fun testIo() {\n        val currentThread = Thread.currentThread().toString()\n        ThreadUtils.runOnUiThread(Runnable {\n            titleItem.title = currentThread\n        })\n    }\n\n    companion object {\n        const val TAG_BASIC_TYPE = \"tag_basic_type\"\n        const val TAG_BUS = \"tag_bus\"\n        const val TAG_STICKY_BUS = \"tag_sticky_bus\"\n        const val TAG_IO = \"tag_io\"\n\n        fun start(context: Context) {\n            val starter = Intent(context, BusActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_bus\n    }\n\n    override fun bindItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                titleItem,\n                CommonItemClick(R.string.bus_register) {\n                    BusUtils.register(this)\n                },\n                CommonItemClick(R.string.bus_unregister) {\n                    BusUtils.unregister(this)\n                },\n                CommonItemClick(R.string.bus_post) {\n                    BusUtils.post(TAG_BUS, TAG_BUS)\n                },\n                CommonItemClick(R.string.bus_post_basic_type) {\n                    BusUtils.post(TAG_BASIC_TYPE, Random(System.currentTimeMillis()).nextInt())\n                },\n                CommonItemClick(R.string.bus_post_sticky) {\n                    BusUtils.postSticky(TAG_STICKY_BUS, object : Callback {\n                        override fun call(): String {\n                            return TAG_STICKY_BUS\n                        }\n                    })\n                },\n                CommonItemClick(R.string.bus_post_to_io_thread) {\n                    BusUtils.post(TAG_IO)\n                },\n                CommonItemClick(R.string.bus_remove_sticky) {\n                    BusUtils.removeSticky(TAG_STICKY_BUS)\n                },\n                CommonItemClick(R.string.bus_start_compare, true) {\n                    BusCompareActivity.start(this)\n                }\n        )\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        BusUtils.removeSticky(TAG_STICKY_BUS)\n        BusUtils.unregister(this)\n    }\n}\n\n@Keep\ninterface Callback {\n    fun call(): String\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/bus/BusCompareActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.bus\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.BusUtils\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.ThreadUtils\nimport org.greenrobot.eventbus.EventBus\nimport org.greenrobot.eventbus.Subscribe\nimport java.util.*\nimport java.util.concurrent.CopyOnWriteArrayList\n\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2019/07/14\n * desc  : demo about BusUtils\n * ```\n */\nclass BusCompareActivity : CommonActivity() {\n\n    private val titleItem: CommonItemTitle = CommonItemTitle(\"\", true)\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, BusCompareActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_bus\n    }\n\n    override fun bindItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                titleItem,\n                CommonItemClick(R.string.bus_compare_register_10000_times) {\n                    compareRegister10000Times()\n                },\n                CommonItemClick(R.string.bus_compare_post_to_1_subscriber_1000000_times) {\n                    comparePostTo1Subscriber1000000Times()\n                },\n                CommonItemClick(R.string.bus_compare_post_to_100_subscriber_100000_times) {\n                    comparePostTo100Subscribers100000Times()\n                },\n                CommonItemClick(R.string.bus_compare_unregister_10000_times) {\n                    compareUnregister10000Times()\n                }\n        )\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        ThreadUtils.cancel(ThreadUtils.getCpuPool())\n    }\n\n    /**\n     * 注册 10000 个订阅者，共执行 10 次取平均值\n     */\n    private fun compareRegister10000Times() {\n        val eventBusTests = CopyOnWriteArrayList<BusEvent>()\n        val busUtilsTests = CopyOnWriteArrayList<BusEvent>()\n\n        compareWithEventBus(\"Register 10000 times.\", 10, 10000, object : CompareCallback {\n            override fun runEventBus() {\n                val test = BusEvent()\n                EventBus.getDefault().register(test)\n                eventBusTests.add(test)\n            }\n\n            override fun runBusUtils() {\n                val test = BusEvent()\n                BusUtils.register(test)\n                busUtilsTests.add(test)\n            }\n\n            override fun restState() {\n                for (test in eventBusTests) {\n                    EventBus.getDefault().unregister(test)\n                }\n                eventBusTests.clear()\n\n                for (test in busUtilsTests) {\n                    BusUtils.unregister(test)\n                }\n                busUtilsTests.clear()\n            }\n        }, object : OnFinishCallback {\n            override fun onFinish() {\n                for (test in eventBusTests) {\n                    EventBus.getDefault().unregister(test)\n                }\n                eventBusTests.clear()\n\n                for (test in busUtilsTests) {\n                    BusUtils.unregister(test)\n                }\n                busUtilsTests.clear()\n            }\n        })\n    }\n\n    /**\n     * 向 1 个订阅者发送 * 1000000 次，共执行 10 次取平均值\n     */\n    private fun comparePostTo1Subscriber1000000Times() {\n        comparePostTemplate(\"Post to 1 subscriber 1000000 times.\", 1, 1000000)\n    }\n\n    /**\n     * 向 100 个订阅者发送 * 100000 次，共执行 10 次取平均值\n     */\n    private fun comparePostTo100Subscribers100000Times() {\n        comparePostTemplate(\"Post to 100 subscribers 100000 times.\", 100, 100000)\n    }\n\n    private fun comparePostTemplate(name: String, subscribeNum: Int, postTimes: Int) {\n        val tests = java.util.ArrayList<BusEvent>()\n        for (i in 0 until subscribeNum) {\n            val test = BusEvent()\n            EventBus.getDefault().register(test)\n            BusUtils.register(test)\n            tests.add(test)\n        }\n\n        compareWithEventBus(name, 10, postTimes, object : CompareCallback {\n            override fun runEventBus() {\n                EventBus.getDefault().post(\"EventBus\")\n            }\n\n            override fun runBusUtils() {\n                BusUtils.post(\"busUtilsFun\", \"BusUtils\")\n            }\n\n            override fun restState() {\n\n            }\n        }, object : OnFinishCallback {\n            override fun onFinish() {\n                for (test in tests) {\n                    EventBus.getDefault().unregister(test)\n                    BusUtils.unregister(test)\n                }\n            }\n        })\n    }\n\n    /**\n     * 注销 10000 个订阅者，共执行 10 次取平均值\n     */\n    private fun compareUnregister10000Times() {\n        showLoading()\n        ThreadUtils.executeBySingle(object : ThreadUtils.SimpleTask<List<BusEvent>>() {\n            override fun doInBackground(): List<BusEvent> {\n                val tests = ArrayList<BusEvent>()\n                for (i in 0..9999) {\n                    val test = BusEvent()\n                    EventBus.getDefault().register(test)\n                    BusUtils.register(test)\n                    tests.add(test)\n                }\n                return tests\n            }\n\n            override fun onSuccess(tests: List<BusEvent>) {\n                compareWithEventBus(\"Unregister 10000 times.\", 10, 1, object : CompareCallback {\n                    override fun runEventBus() {\n                        for (test in tests) {\n                            EventBus.getDefault().unregister(test)\n                        }\n                    }\n\n                    override fun runBusUtils() {\n                        for (test in tests) {\n                            BusUtils.unregister(test)\n                        }\n                    }\n\n                    override fun restState() {\n                        for (test in tests) {\n                            EventBus.getDefault().register(test)\n                            BusUtils.register(test)\n                        }\n                    }\n                }, object : OnFinishCallback {\n                    override fun onFinish() {\n                        for (test in tests) {\n                            EventBus.getDefault().unregister(test)\n                            BusUtils.unregister(test)\n                        }\n                    }\n                })\n            }\n        })\n    }\n\n    /**\n     * @param name             传入的测试函数名\n     * @param sampleSize       样本数\n     * @param times            每次执行的次数\n     * @param callback         比较的回调函数\n     * @param onFinishCallback 执行结束的回调\n     */\n    private fun compareWithEventBus(name: String, sampleSize: Int, times: Int,\n                                    callback: CompareCallback, onFinishCallback: OnFinishCallback) {\n        showLoading()\n        ThreadUtils.executeByCpu(object : ThreadUtils.Task<String>() {\n            override fun doInBackground(): String {\n                val dur = Array(2) { LongArray(sampleSize) }\n                for (i in 0 until sampleSize) {\n                    var cur = System.currentTimeMillis()\n                    for (j in 0 until times) {\n                        callback.runEventBus()\n                    }\n                    dur[0][i] = System.currentTimeMillis() - cur\n                    cur = System.currentTimeMillis()\n                    for (j in 0 until times) {\n                        callback.runBusUtils()\n                    }\n                    dur[1][i] = System.currentTimeMillis() - cur\n                    callback.restState()\n                }\n                var eventBusAverageTime: Long = 0\n                var busUtilsAverageTime: Long = 0\n                for (i in 0 until sampleSize) {\n                    eventBusAverageTime += dur[0][i]\n                    busUtilsAverageTime += dur[1][i]\n                }\n                return name +\n                        \"\\nEventBusCostTime: \" + eventBusAverageTime / sampleSize +\n                        \"\\nBusUtilsCostTime: \" + busUtilsAverageTime / sampleSize;\n            }\n\n            override fun onSuccess(result: String?) {\n                onFinishCallback.onFinish()\n                dismissLoading()\n                titleItem?.title = result\n            }\n\n            override fun onCancel() {\n                onFinishCallback.onFinish()\n                dismissLoading()\n            }\n\n            override fun onFail(t: Throwable?) {\n                onFinishCallback.onFinish()\n                dismissLoading()\n            }\n        })\n    }\n}\n\ninterface CompareCallback {\n    fun runEventBus()\n\n    fun runBusUtils()\n\n    fun restState()\n}\n\ninterface OnFinishCallback {\n    fun onFinish()\n}\n\nclass BusEvent {\n    @Subscribe\n    fun eventBusFun(param: String) {\n    }\n\n    @BusUtils.Bus(tag = \"busUtilsFun\")\n    fun busUtilsFun(param: String) {\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/clean/CleanActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.clean\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.CleanUtils\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.SDCardUtils\nimport com.blankj.utilcode.util.SnackbarUtils\nimport java.io.File\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/09/29\n * desc  : demo about CleanUtils\n * ```\n */\nclass CleanActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, CleanActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_clean\n    }\n\n    override fun bindItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList<CommonItem<*>>().apply {\n            add(CommonItemClick(R.string.clean_internal_cache) {\n                showSnackbar(CleanUtils.cleanInternalCache(), cacheDir.path)\n            })\n            add(CommonItemClick(R.string.clean_internal_files) {\n                showSnackbar(CleanUtils.cleanInternalFiles(), filesDir.path)\n            })\n            add(CommonItemClick(R.string.clean_internal_databases) {\n                showSnackbar(CleanUtils.cleanInternalDbs(), filesDir.parent + File.separator + \"databases\")\n            })\n            add(CommonItemClick(R.string.clean_internal_sp) {\n                showSnackbar(CleanUtils.cleanInternalSp(), filesDir.parent + File.separator + \"shared_prefs\")\n            })\n            if (SDCardUtils.isSDCardEnableByEnvironment()) {\n                add(CommonItemClick(R.string.clean_external_cache) {\n                    showSnackbar(CleanUtils.cleanExternalCache(), externalCacheDir?.absolutePath)\n                })\n            }\n            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {\n                add(CommonItemClick(R.string.clean_app_user_data) {\n                    CleanUtils.cleanAppUserData()\n                })\n            }\n        }\n    }\n\n    private fun showSnackbar(isSuccess: Boolean, path: String?) {\n        SnackbarUtils.with(mContentView)\n                .setDuration(SnackbarUtils.LENGTH_LONG)\n                .apply {\n                    if (isSuccess) {\n                        setMessage(\"clean \\\"$path\\\" dir successful.\")\n                        showSuccess()\n                    } else {\n                        setMessage(\"clean \\\"$path\\\" dir failed.\")\n                        showError()\n                    }\n                }\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/click/ClickActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.click\n\nimport android.content.Context\nimport android.content.Intent\nimport android.view.View\nimport android.widget.TextView\nimport androidx.annotation.StringRes\nimport com.blankj.base.rv.ItemViewHolder\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/09/29\n * desc  : demo about ClickUtils\n * ```\n */\nclass ClickActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, ClickActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_click\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                ClickItem(R.string.click_view_scale_default, Utils.Consumer {\n                    ClickUtils.applyPressedViewScale(it)\n                }),\n                ClickItem(R.string.click_view_scale_half, Utils.Consumer {\n                    ClickUtils.applyPressedViewScale(it, -0.5f)\n                }),\n                ClickItem(R.string.click_view_alpha_default, Utils.Consumer {\n                    ClickUtils.applyPressedViewAlpha(it)\n                }),\n                ClickItem(R.string.click_bg_alpha_default, Utils.Consumer {\n                    ClickUtils.applyPressedBgAlpha(it, 0.6f)\n                }),\n                ClickItem(R.string.click_bg_dark_default, Utils.Consumer {\n                    ClickUtils.applyPressedBgDark(it)\n                }),\n                ClickItem(R.string.click_single_debouncing, Utils.Consumer {\n                    ClickUtils.applyPressedBgDark(it)\n                    ClickUtils.applySingleDebouncing(it, 5000) {\n                        SnackbarUtils.with(mContentView)\n                                .setMessage(StringUtils.getString(R.string.click_single_tip))\n                                .setBgColor(ColorUtils.getRandomColor(false))\n                                .setDuration(SnackbarUtils.LENGTH_LONG)\n                                .show()\n                    }\n                }),\n                ClickItem(R.string.click_global_debouncing, Utils.Consumer {\n                    ClickUtils.applyPressedBgDark(it)\n                    ClickUtils.applySingleDebouncing(it, 5000) {\n                        SnackbarUtils.with(mContentView)\n                                .setMessage(StringUtils.getString(R.string.click_global_tip))\n                                .setBgColor(ColorUtils.getRandomColor(false))\n                                .setDuration(SnackbarUtils.LENGTH_LONG)\n                                .show()\n                    }\n                }),\n                ClickItem(R.string.click_multi, Utils.Consumer {\n                    ClickUtils.applyPressedBgDark(it)\n                    it.setOnClickListener(object : ClickUtils.OnMultiClickListener(5) {\n                        override fun onTriggerClick(v: View) {\n                            ToastUtils.showShort(\"onTriggerClick\")\n                        }\n\n                        override fun onBeforeTriggerClick(v: View, count: Int) {\n                            ToastUtils.showShort(count)\n                        }\n                    })\n                })\n        )\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        SnackbarUtils.dismiss()\n    }\n}\n\nclass ClickItem : CommonItem<ClickItem> {\n\n    private val mConsumer: Utils.Consumer<View>;\n    private val mTitle: String\n\n    constructor(@StringRes title: Int, consumer: Utils.Consumer<View>) : super(R.layout.common_item_title_click) {\n        mConsumer = consumer\n        mTitle = StringUtils.getString(title)\n    }\n\n    override fun bind(holder: ItemViewHolder, position: Int) {\n        super.bind(holder, position)\n        holder.findViewById<TextView>(R.id.commonItemTitleTv).text = mTitle\n        holder.itemView.setOnClickListener() {\n            SnackbarUtils.with(it)\n                    .setMessage(mTitle)\n                    .setBgColor(ColorUtils.getRandomColor(false))\n                    .setDuration(SnackbarUtils.LENGTH_LONG)\n                    .show()\n        }\n        mConsumer.accept(holder.itemView)\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/clipboard/ClipboardActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.clipboard\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemSwitch\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.ClipboardUtils\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.ToastUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2020/09/11\n * desc  : demo about ClipboardUtils\n * ```\n */\nclass ClipboardActivity : CommonActivity() {\n\n    private var index: Int = 0\n    private var isAddListener: Boolean = false\n    private var listener = {\n        ToastUtils.showShort(ClipboardUtils.getText())\n    }\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, ClipboardActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_clipboard\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemTitle(\"getText\", ClipboardUtils.getText()),\n                CommonItemTitle(\"getLabel\", ClipboardUtils.getLabel()),\n                CommonItemClick(\"copyText: value{$index}\").setOnItemClickListener { _, _, _ ->\n                    ClipboardUtils.copyText(\"value{${index++}}\")\n                    itemsView.updateItems(bindItems())\n                },\n                CommonItemClick(\"clear\").setOnItemClickListener { _, _, _ ->\n                    ClipboardUtils.clear()\n                    itemsView.updateItems(bindItems())\n                },\n                CommonItemSwitch(\"clipChangeListener\", { isAddListener }, {\n                    isAddListener = it\n                    if (isAddListener) {\n                        ClipboardUtils.addChangedListener(listener)\n                    } else {\n                        ClipboardUtils.removeChangedListener(listener)\n                    }\n                })\n        )\n    }\n\n    override fun onResume() {\n        super.onResume()\n        itemsView.updateItems(bindItems())\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        if (isAddListener) {\n            ClipboardUtils.removeChangedListener(listener)\n        }\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/device/DeviceActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.device\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Build\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.DeviceUtils\nimport java.util.*\n\n/**\n * ```\n * author: Blankj\n * blog : http://blankj.com\n * time : 2016/09/27\n * desc : demo about DeviceUtils\n * ```\n */\nclass DeviceActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, DeviceActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_device\n    }\n\n    override fun bindItems(): List<CommonItem<*>> {\n        return arrayListOf<CommonItem<*>>().apply {\n            add(CommonItemTitle(\"isRoot\", DeviceUtils.isDeviceRooted().toString()))\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n                add(CommonItemTitle(\"isAdbEnabled\", DeviceUtils.isAdbEnabled().toString()))\n            }\n            add(CommonItemTitle(\"getSDKVersionName\", DeviceUtils.getSDKVersionName()))\n            add(CommonItemTitle(\"getSDKVersionCode\", DeviceUtils.getSDKVersionCode().toString()))\n            add(CommonItemTitle(\"getAndroidID\", DeviceUtils.getAndroidID()))\n            add(CommonItemTitle(\"getMacAddress\", DeviceUtils.getMacAddress()))\n            add(CommonItemTitle(\"getManufacturer\", DeviceUtils.getManufacturer()))\n            add(CommonItemTitle(\"getModel\", DeviceUtils.getModel()))\n            add(CommonItemTitle(\"getABIs\", Arrays.asList(*DeviceUtils.getABIs()).toString()))\n            add(CommonItemTitle(\"isTablet\", DeviceUtils.isTablet().toString()))\n            add(CommonItemTitle(\"isEmulator\", DeviceUtils.isEmulator().toString()))\n            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) {\n                add(CommonItemTitle(\"isDevelopmentSettingsEnabled\", DeviceUtils.isDevelopmentSettingsEnabled().toString()))\n            }\n            add(CommonItemTitle(\"getUniqueDeviceId\", DeviceUtils.getUniqueDeviceId(\"util\")))\n            add(CommonItemTitle(\"isSameDevice\", DeviceUtils.isSameDevice(DeviceUtils.getUniqueDeviceId()).toString()))\n        }\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/file/FileActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.file\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.Config.CACHE_PATH\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.FileUtils\nimport com.blankj.utilcode.util.PathUtils\nimport com.blankj.utilcode.util.SnackbarUtils\nimport java.io.File\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/09/29\n * desc  : demo about FileUtils\n * ```\n */\nclass FileActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, FileActivity::class.java)\n            context.startActivity(starter)\n        }\n\n        val TEST_FILE_PATH: String = CACHE_PATH + \"test_file.txt\"\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_file\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemTitle(\"isFileExists: \" + PathUtils.getInternalAppFilesPath(), \"\" + FileUtils.isFileExists(PathUtils.getInternalAppFilesPath())),\n                CommonItemTitle(\"isFileExists: \" + PathUtils.getExternalAppFilesPath(), \"\" + FileUtils.isFileExists(PathUtils.getExternalAppFilesPath())),\n                CommonItemTitle(\"isFileExists: \" + PathUtils.getExternalStoragePath(), \"\" + FileUtils.isFileExists(PathUtils.getExternalStoragePath())),\n                CommonItemTitle(\"isFileExists: \" + PathUtils.getDownloadCachePath(), \"\" + FileUtils.isFileExists(PathUtils.getDownloadCachePath())),\n                CommonItemTitle(\"isFileExists: \" + PathUtils.getExternalDownloadsPath(), \"\" + FileUtils.isFileExists(PathUtils.getExternalDownloadsPath())),\n\n                CommonItemTitle(\"isFileExists: \" + PathUtils.getInternalAppFilesPath(), \"\" + FileUtils.isFileExists(File(PathUtils.getInternalAppFilesPath()))),\n                CommonItemTitle(\"isFileExists: \" + PathUtils.getExternalAppFilesPath(), \"\" + FileUtils.isFileExists(File(PathUtils.getExternalAppFilesPath()))),\n                CommonItemTitle(\"isFileExists: \" + PathUtils.getExternalStoragePath(), \"\" + FileUtils.isFileExists(File(PathUtils.getExternalStoragePath()))),\n                CommonItemTitle(\"isFileExists: \" + PathUtils.getDownloadCachePath(), \"\" + FileUtils.isFileExists(File(PathUtils.getDownloadCachePath()))),\n                CommonItemTitle(\"isFileExists: \" + PathUtils.getExternalDownloadsPath(), \"\" + FileUtils.isFileExists(File(PathUtils.getExternalDownloadsPath())))\n        )\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        SnackbarUtils.dismiss()\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/flashlight/FlashlightActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.flashlight\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.helper.PermissionHelper\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemSwitch\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.constant.PermissionConstants\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2018/04/27\n * desc  : demo about FlashlightUtils\n * ```\n */\nclass FlashlightActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            if (!FlashlightUtils.isFlashlightEnable()) {\n                ToastUtils.showLong(\"Didn't support flashlight.\")\n                return\n            }\n            PermissionHelper.request(context, object : PermissionUtils.SimpleCallback {\n                override fun onGranted() {\n                    val starter = Intent(context, FlashlightActivity::class.java)\n                    context.startActivity(starter)\n                }\n\n                override fun onDenied() {\n                    LogUtils.e(\"permission denied\")\n                }\n            }, PermissionConstants.CAMERA)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_flashlight\n    }\n\n    override fun bindItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList<CommonItem<*>>().apply {\n            add(CommonItemTitle(\"isFlashlightEnable\", FlashlightUtils.isFlashlightEnable().toString()))\n            if (FlashlightUtils.isFlashlightEnable()) {\n                add(CommonItemSwitch(\n                        R.string.flashlight_status,\n                        { FlashlightUtils.isFlashlightOn() },\n                        { FlashlightUtils.setFlashlightStatus(it) }\n                ))\n            }\n        }\n    }\n\n    override fun onDestroy() {\n        FlashlightUtils.destroy()\n        super.onDestroy()\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/fragment/ChildFragment.kt",
    "content": "package com.blankj.utilcode.pkg.feature.fragment\n\nimport android.os.Bundle\nimport android.view.View\nimport androidx.fragment.app.FragmentManager\nimport com.blankj.common.fragment.CommonFragment\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.pkg.helper.DialogHelper\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.ColorUtils\nimport com.blankj.utilcode.util.FragmentUtils\nimport com.blankj.utilcode.util.SpanUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 17/02/02\n * desc  : demo about FragmentUtils\n * ```\n */\nclass ChildFragment : CommonFragment() {\n\n    companion object {\n        fun newInstance(): ChildFragment {\n            val args = Bundle()\n            val fragment = ChildFragment()\n            fragment.arguments = args\n            return fragment\n        }\n    }\n\n    private lateinit var fm: FragmentManager\n    private val mBgColor = ColorUtils.getRandomColor(false)\n\n    override fun bindLayout(): Int {\n        return R.layout.fragment_child\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        FragmentUtils.setBackgroundColor(this, mBgColor)\n        fm = fragmentManager!!\n        setCommonItems(findViewById(R.id.commonItemRv), getItems())\n    }\n\n    private fun getItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList<CommonItem<*>>(\n                CommonItemClick(R.string.fragment_show_stack) {\n                    DialogHelper.showFragmentDialog(\n                            SpanUtils().appendLine(\"top: \" + FragmentUtils.getSimpleName(FragmentUtils.getTop(fm)))\n                                    .appendLine(\"topInStack: \" + FragmentUtils.getSimpleName(FragmentUtils.getTopInStack(fm)))\n                                    .appendLine(\"topShow: \" + FragmentUtils.getSimpleName(FragmentUtils.getTopShow(fm)))\n                                    .appendLine(\"topShowInStack: \" + FragmentUtils.getSimpleName(FragmentUtils.getTopShowInStack(fm)))\n                                    .appendLine()\n                                    .appendLine(\"---all of fragments---\")\n                                    .appendLine(FragmentUtils.getAllFragments(fm).toString())\n                                    .appendLine(\"----------------------\")\n                                    .appendLine()\n                                    .appendLine(\"---stack top---\")\n                                    .appendLine(FragmentUtils.getAllFragmentsInStack(fm).toString())\n                                    .appendLine(\"---stack bottom---\")\n                                    .create()\n                    )\n                },\n                CommonItemClick(R.string.fragment_pop) {\n                    FragmentUtils.pop(fm)\n                },\n                CommonItemClick(R.string.fragment_remove) {\n                    FragmentUtils.remove(this)\n                },\n                SharedElementItem()\n        ).apply {\n            for (ci: CommonItem<*> in this) {\n                ci.backgroundColor = mBgColor\n            }\n        }\n    }\n}"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/fragment/ContainerFragment.kt",
    "content": "package com.blankj.utilcode.pkg.feature.fragment\n\nimport android.os.Build\nimport android.os.Bundle\nimport android.transition.*\nimport android.view.View\nimport android.widget.ImageView\nimport androidx.annotation.RequiresApi\nimport androidx.fragment.app.FragmentManager\nimport com.blankj.base.rv.ItemViewHolder\nimport com.blankj.common.fragment.CommonFragment\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.pkg.helper.DialogHelper\nimport com.blankj.utilcode.util.*\nimport java.util.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 17/02/02\n * desc  : demo about FragmentUtils\n * ```\n */\nclass ContainerFragment : CommonFragment(), FragmentUtils.OnBackClickListener {\n\n    companion object {\n        fun newInstance(): ContainerFragment {\n            val args = Bundle()\n            val fragment = ContainerFragment()\n            fragment.arguments = args\n            return fragment\n        }\n    }\n\n    private lateinit var fm: FragmentManager\n    private val mBgColor = ColorUtils.getRandomColor(false)\n\n    override fun bindLayout(): Int {\n        return R.layout.fragment_container\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        mContentView.setBackgroundColor(mBgColor)\n        fm = fragmentManager!!\n        setCommonItems(findViewById(R.id.commonItemRv), getItems())\n    }\n\n    private fun getItems(): ArrayList<CommonItem<*>>? {\n        val item = SharedElementItem()\n        return CollectionUtils.newArrayList<CommonItem<*>>(\n                CommonItemClick(R.string.fragment_show_stack) {\n                    DialogHelper.showFragmentDialog(\n                            SpanUtils().appendLine(\"top: \" + FragmentUtils.getSimpleName(FragmentUtils.getTop(fm)))\n                                    .appendLine(\"topInStack: \" + FragmentUtils.getSimpleName(FragmentUtils.getTopInStack(fm)))\n                                    .appendLine(\"topShow: \" + FragmentUtils.getSimpleName(FragmentUtils.getTopShow(fm)))\n                                    .appendLine(\"topShowInStack: \" + FragmentUtils.getSimpleName(FragmentUtils.getTopShowInStack(fm)))\n                                    .appendLine()\n                                    .appendLine(\"---all of fragments---\")\n                                    .appendLine(FragmentUtils.getAllFragments(fm).toString())\n                                    .appendLine(\"----------------------\")\n                                    .appendLine()\n                                    .appendLine(\"---stack top---\")\n                                    .appendLine(FragmentUtils.getAllFragmentsInStack(fm).toString())\n                                    .appendLine(\"---stack bottom---\")\n                                    .create()\n                    )\n                },\n                CommonItemClick(R.string.fragment_add_child) {\n                    FragmentUtils.add(\n                            fm,\n                            ChildFragment.newInstance(),\n                            id\n                    )\n                },\n                CommonItemClick(R.string.fragment_add_child_stack) {\n                    FragmentUtils.add(\n                            fm,\n                            ChildFragment.newInstance(),\n                            id,\n                            false,\n                            true\n                    )\n                },\n                CommonItemClick(R.string.fragment_add_hide) {\n                    FragmentUtils.add(\n                            fm,\n                            ChildFragment.newInstance(),\n                            id,\n                            true\n                    )\n                },\n                CommonItemClick(R.string.fragment_add_hide_stack) {\n                    FragmentUtils.add(\n                            fm,\n                            ChildFragment.newInstance(),\n                            id,\n                            true,\n                            true\n                    )\n                },\n                CommonItemClick(R.string.fragment_add_demo1_show) {\n                    FragmentUtils.add(\n                            fm,\n                            addSharedElement(ChildFragment.newInstance()),\n                            id,\n                            false,\n                            false\n                    )\n                },\n                CommonItemClick(R.string.fragment_pop_to_root) {\n                    FragmentUtils.popTo(\n                            fm,\n                            ChildFragment::class.java,\n                            true\n                    )\n                },\n                CommonItemClick(R.string.fragment_hide_demo0_show_demo1) {\n                    val fragment1 = FragmentUtils.findFragment(fm, ChildFragment::class.java)\n                    if (fragment1 != null) {\n                        FragmentUtils.showHide(this, fragment1)\n                    } else {\n                        ToastUtils.showLong(\"please add demo1 first!\")\n                    }\n                },\n                CommonItemClick(R.string.fragment_replace) {\n                    FragmentUtils.replace(\n                            fm,\n                            addSharedElement(ChildFragment.newInstance()),\n                            id,\n                            true,\n                            item.element\n                    )\n                },\n                item\n        ).apply {\n            for (ci: CommonItem<*> in this) {\n                ci.backgroundColor = mBgColor\n            }\n        }\n    }\n\n    private fun addSharedElement(fragment: androidx.fragment.app.Fragment): androidx.fragment.app.Fragment {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            fragment.sharedElementEnterTransition = DetailTransition()\n            fragment.enterTransition = Fade()\n            fragment.sharedElementReturnTransition = DetailTransition()\n        }\n        return fragment\n    }\n\n    override fun onBackClick(): Boolean {\n        return false\n    }\n}\n\nclass SharedElementItem : CommonItem<SharedElementItem> {\n\n    lateinit var element: ImageView;\n\n    constructor() : super(R.layout.fragment_item_shared_element)\n\n    override fun bind(holder: ItemViewHolder, position: Int) {\n        super.bind(holder, position)\n        element = holder.findViewById(R.id.fragmentRootSharedElementIv)\n    }\n}\n\n@RequiresApi(Build.VERSION_CODES.LOLLIPOP)\nclass DetailTransition() : TransitionSet() {\n    init {\n        ordering = ORDERING_TOGETHER\n        addTransition(ChangeBounds()).addTransition(ChangeTransform()).addTransition(ChangeImageTransform())\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/fragment/FragmentActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.fragment\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Bundle\nimport android.os.PersistableBundle\nimport com.google.android.material.bottomnavigation.BottomNavigationView\nimport androidx.fragment.app.Fragment\nimport android.view.View\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.FragmentUtils\nimport kotlinx.android.synthetic.main.fragment_activity.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 17/02/01\n * desc  : demo about FragmentUtils\n * ```\n */\nclass FragmentActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, FragmentActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    private val mFragments = arrayListOf<androidx.fragment.app.Fragment>()\n    private var curIndex: Int = 0\n\n    private val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item ->\n        when (item.itemId) {\n            R.id.fragmentNavigation0 -> {\n                showCurrentFragment(0)\n                return@OnNavigationItemSelectedListener true\n            }\n            R.id.fragmentNavigation1 -> {\n                showCurrentFragment(1)\n                return@OnNavigationItemSelectedListener true\n            }\n            R.id.fragmentNavigation2 -> {\n                showCurrentFragment(2)\n                return@OnNavigationItemSelectedListener true\n            }\n            else -> false\n        }\n    }\n\n    override fun bindLayout(): Int {\n        return R.layout.fragment_activity\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        if (savedInstanceState != null) {\n            curIndex = savedInstanceState.getInt(\"curIndex\")\n        }\n        fragmentNav.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)\n\n        mFragments.add(RootFragment.newInstance())\n        mFragments.add(RootFragment.newInstance())\n        mFragments.add(RootFragment.newInstance())\n        FragmentUtils.add(\n                supportFragmentManager,\n                mFragments,\n                R.id.fragmentContainer,\n                arrayOf(\"RootFragment0\", \"RootFragment1\", \"RootFragment2\"),\n                curIndex\n        )\n    }\n\n    override fun onBackPressed() {\n        if (!FragmentUtils.dispatchBackPress(mFragments[curIndex])) {\n            super.onBackPressed()\n        }\n    }\n\n    private fun showCurrentFragment(index: Int) {\n        curIndex = index\n        FragmentUtils.showHide(index, mFragments)\n    }\n\n    override fun onSaveInstanceState(outState: Bundle, outPersistentState: PersistableBundle) {\n        super.onSaveInstanceState(outState, outPersistentState)\n        outState.putInt(\"curIndex\", curIndex)\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/fragment/RootFragment.kt",
    "content": "package com.blankj.utilcode.pkg.feature.fragment\n\nimport android.os.Bundle\nimport android.view.View\nimport com.blankj.common.fragment.CommonFragment\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.BarUtils\nimport com.blankj.utilcode.util.ColorUtils\nimport com.blankj.utilcode.util.FragmentUtils\nimport kotlinx.android.synthetic.main.fragment_root.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 17/02/02\n * desc  : demo about FragmentUtils\n * ```\n */\nclass RootFragment : CommonFragment(), FragmentUtils.OnBackClickListener {\n\n    companion object {\n        fun newInstance(): RootFragment {\n            val args = Bundle()\n            val fragment = RootFragment()\n            fragment.arguments = args\n            return fragment\n        }\n    }\n\n    override fun bindLayout(): Int {\n        return R.layout.fragment_root\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        BarUtils.setStatusBarColor(rootFragmentFakeStatusBar, ColorUtils.getColor(R.color.colorPrimary))\n        FragmentUtils.add(\n                childFragmentManager,\n                ContainerFragment.newInstance(),\n                R.id.rootFragmentContainer\n        )\n    }\n\n    override fun onBackClick(): Boolean {\n        if (FragmentUtils.dispatchBackPress(childFragmentManager)) return true\n        return if (childFragmentManager.backStackEntryCount == 0) {\n            false\n        } else {\n            childFragmentManager.popBackStack()\n            true\n        }\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/image/ImageActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.image\n\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.Bitmap\nimport android.graphics.Color\nimport android.os.Build\nimport android.os.Bundle\nimport android.view.View\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.helper.PermissionHelper\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemImage\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.constant.PermissionConstants\nimport com.blankj.utilcode.pkg.Config\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.*\nimport java.io.File\nimport java.util.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/09/26\n * desc  : demo about ImageUtils\n * ```\n */\nclass ImageActivity : CommonActivity() {\n\n    private val savePath = Config.CACHE_PATH + \"lena.jpg\"\n    private val titleItem: CommonItemTitle = CommonItemTitle(\"isImage: $savePath\", \"\");\n\n    companion object {\n        fun start(context: Context) {\n            PermissionHelper.request(context, object : PermissionUtils.SimpleCallback {\n                override fun onGranted() {\n                    val starter = Intent(context, ImageActivity::class.java)\n                    context.startActivity(starter)\n                }\n\n                override fun onDenied() {\n                }\n            }, PermissionConstants.STORAGE)\n        }\n    }\n\n    private val bgTask: ThreadUtils.SimpleTask<List<CommonItem<*>>> = object : ThreadUtils.SimpleTask<List<CommonItem<*>>>() {\n        override fun doInBackground(): List<CommonItem<*>> {\n            return bindItems()\n        }\n\n        override fun onSuccess(result: List<CommonItem<*>>) {\n            dismissLoading()\n            itemsView.updateItems(result)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_image\n    }\n\n    override fun bindItems(): ArrayList<CommonItem<*>> {\n        if (ThreadUtils.isMainThread()) return arrayListOf()\n        val src = ImageUtils.getBitmap(R.drawable.image_lena)\n        val round = ImageUtils.getBitmap(R.drawable.common_avatar_round)\n        val watermark = ImageUtils.getBitmap(R.mipmap.ic_launcher)\n\n        val width = src.width\n        val height = src.height\n\n        titleItem.setContent(ImageUtils.isImage(savePath).toString())\n\n        return CollectionUtils.newArrayList<CommonItem<*>>().apply {\n            add(titleItem)\n            add(CommonItemClick(\"Save to $savePath\") {\n                ThreadUtils.executeBySingle(object : ThreadUtils.SimpleTask<Boolean>() {\n                    override fun doInBackground(): Boolean {\n                        return ImageUtils.save(src, savePath, Bitmap.CompressFormat.JPEG)\n                    }\n\n                    override fun onSuccess(result: Boolean) {\n                        titleItem.setContent(ImageUtils.isImage(savePath).toString())\n                        titleItem.update()\n                        SnackbarUtils.with(mContentView)\n                                .setDuration(SnackbarUtils.LENGTH_LONG)\n                                .apply {\n                                    if (result) {\n                                        setMessage(\"save successful.\")\n                                                .showSuccess(true)\n                                    } else {\n                                        setMessage(\"save failed.\")\n                                                .showError(true)\n                                    }\n                                }\n                    }\n                })\n            })\n            add(CommonItemClick(\"Save to Album\") {\n                ThreadUtils.executeBySingle(object : ThreadUtils.SimpleTask<File?>() {\n                    override fun doInBackground(): File? {\n                        return ImageUtils.save2Album(src, Bitmap.CompressFormat.JPEG)\n                    }\n\n                    override fun onSuccess(result: File?) {\n                        SnackbarUtils.with(mContentView)\n                                .setDuration(SnackbarUtils.LENGTH_LONG)\n                                .apply {\n                                    if (result != null) {\n                                        setMessage(\"save successful.\")\n                                                .showSuccess(true)\n                                    } else {\n                                        setMessage(\"save failed.\")\n                                                .showError(true)\n                                    }\n                                }\n                    }\n                })\n            })\n            add(CommonItemImage(R.string.image_src) {\n                it.setImageBitmap(src)\n            })\n            add(CommonItemImage(R.string.image_add_color) {\n                it.setImageBitmap(ImageUtils.drawColor(src, Color.parseColor(\"#8000FF00\")))\n            })\n            add(CommonItemImage(R.string.image_scale) {\n                it.setImageBitmap(ImageUtils.scale(src, width / 2, height / 2))\n            })\n            add(CommonItemImage(R.string.image_clip) {\n                it.setImageBitmap(ImageUtils.clip(src, 0, 0, width / 2, height / 2))\n            })\n            add(CommonItemImage(R.string.image_skew) {\n                it.setImageBitmap(ImageUtils.skew(src, 0.2f, 0.1f))\n            })\n            add(CommonItemImage(R.string.image_rotate) {\n                it.setImageBitmap(ImageUtils.rotate(src, 90, (width / 2).toFloat(), (height / 2).toFloat()))\n            })\n            add(CommonItemImage(R.string.image_to_round) {\n                it.setImageBitmap(ImageUtils.toRound(src))\n            })\n            add(CommonItemImage(R.string.image_to_round_border) {\n                it.setImageBitmap(ImageUtils.toRound(src, 16, Color.GREEN))\n            })\n            add(CommonItemImage(R.string.image_to_round_corner) {\n                it.setImageBitmap(ImageUtils.toRoundCorner(src, 80f))\n            })\n            add(CommonItemImage(R.string.image_to_round_corner_border) {\n                it.setImageBitmap(ImageUtils.toRoundCorner(src, 80f, 16f, Color.GREEN))\n            })\n            add(CommonItemImage(R.string.image_to_round_corner_border) {\n                it.setImageBitmap(ImageUtils.toRoundCorner(src, floatArrayOf(0f, 0f, 80f, 80f, 0f, 0f, 80f, 80f), 16f, Color.GREEN))\n            })\n            add(CommonItemImage(R.string.image_add_corner_border) {\n                it.setImageBitmap(ImageUtils.addCornerBorder(src, 16f, Color.GREEN, 80f))\n            })\n            add(CommonItemImage(R.string.image_add_corner_border) {\n                it.setImageBitmap(ImageUtils.addCornerBorder(src, 16f, Color.GREEN, floatArrayOf(0f, 0f, 80f, 80f, 0f, 0f, 80f, 80f)))\n            })\n            add(CommonItemImage(R.string.image_add_circle_border) {\n                it.setImageBitmap(ImageUtils.addCircleBorder(src, 16f, Color.GREEN))\n            })\n            add(CommonItemImage(R.string.image_add_reflection) {\n                it.setImageBitmap(ImageUtils.addReflection(src, 80))\n            })\n            add(CommonItemImage(R.string.image_add_text_watermark) {\n                it.setImageBitmap(ImageUtils.addTextWatermark(src, \"blankj\", 40, Color.GREEN, 0f, 0f))\n            })\n            add(CommonItemImage(R.string.image_add_image_watermark) {\n                it.setImageBitmap(ImageUtils.addImageWatermark(src, watermark, 0, 0, 0x88))\n            })\n            add(CommonItemImage(R.string.image_to_gray) {\n                it.setImageBitmap(ImageUtils.toGray(src))\n            })\n            add(CommonItemImage(R.string.image_fast_blur) {\n                it.setImageBitmap(ImageUtils.fastBlur(src, 0.1f, 5f))\n            })\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n                add(CommonItemImage(R.string.image_render_script_blur) {\n                    it.setImageBitmap(ImageUtils.renderScriptBlur(src, 10f))\n                })\n            }\n            add(CommonItemImage(R.string.image_stack_blur) {\n                it.setImageBitmap(ImageUtils.stackBlur(src, 10))\n            })\n            add(CommonItemImage(R.string.image_compress_by_scale) {\n                it.setImageBitmap(ImageUtils.compressByScale(src, 0.5f, 0.5f))\n            })\n            add(CommonItemImage(R.string.image_compress_by_sample_size) {\n                it.setImageBitmap(ImageUtils.compressBySampleSize(src, 2))\n            })\n        }\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        showLoading()\n        ThreadUtils.executeByIo(bgTask)\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        ThreadUtils.cancel(bgTask)\n    }\n}"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/intent/IntentActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.intent\n\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.Bitmap\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.utilcode.pkg.Config\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.*\nimport java.util.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2020/05/29\n * desc  : demo about IntentUtils\n * ```\n */\nclass IntentActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, IntentActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_intent\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemClick(\"LaunchApp\") {\n                    startActivity(IntentUtils.getLaunchAppIntent(packageName))\n                },\n                CommonItemClick(\"LaunchAppDetailsSettings\") {\n                    startActivityForResult(IntentUtils.getLaunchAppDetailsSettingsIntent(packageName), 1)\n                },\n                CommonItemClick(\"ShareText\") {\n                    startActivity(IntentUtils.getShareTextIntent(\"share content\"))\n                },\n                CommonItemClick(\"ShareImage\") {\n                    startActivity(IntentUtils.getShareImageIntent(getShareImagePath()[0]));\n                },\n                CommonItemClick(\"ShareTextImage\") {\n                    startActivity(IntentUtils.getShareTextImageIntent(\"share content\", getShareImagePath()[0]));\n                },\n                CommonItemClick(\"ShareImages\") {\n                    startActivity(IntentUtils.getShareImageIntent(getShareImagePath()));\n                },\n                CommonItemClick(\"ShareTextImages\") {\n                    startActivity(IntentUtils.getShareTextImageIntent(\"share content\", getShareImagePath()));\n                }\n        )\n    }\n\n    private fun getShareImagePath(): LinkedList<String> {\n        val shareImagePath0 = Config.CACHE_PATH + \"share.jpg\"\n        if (!FileUtils.isFile(shareImagePath0)) {\n            ImageUtils.save(ImageUtils.getBitmap(R.drawable.image_lena), shareImagePath0, Bitmap.CompressFormat.JPEG)\n        }\n        val shareImagePath1 = Config.CACHE_PATH + \"cheetah.jpg\"\n        if (!FileUtils.isFile(shareImagePath1)) {\n            ImageUtils.save(ImageUtils.getBitmap(R.drawable.span_cheetah), shareImagePath1, Bitmap.CompressFormat.JPEG)\n        }\n        return CollectionUtils.newLinkedList(shareImagePath0, shareImagePath1)\n    }\n\n    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {\n        super.onActivityResult(requestCode, resultCode, data)\n        LogUtils.d(\"onActivityResult() called with: requestCode = $requestCode, resultCode = $resultCode, data = $data\")\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/keyboard/KeyboardActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.keyboard\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Bundle\nimport android.view.View\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.pkg.helper.DialogHelper\nimport com.blankj.utilcode.util.*\nimport kotlinx.android.synthetic.main.keyboard_activity.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/09/27\n * desc  : demo about KeyboardUtils\n * ```\n */\nclass KeyboardActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, KeyboardActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    private var titleItem: CommonItemTitle = CommonItemTitle(\"\", true)\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_keyboard\n    }\n\n    override fun bindLayout(): Int {\n        return R.layout.keyboard_activity\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        KeyboardUtils.fixAndroidBug5497(this)\n        setCommonItems(findViewById(R.id.commonItemRv), getItems())\n        KeyboardUtils.registerSoftInputChangedListener(this) { height ->\n            titleItem.title = \"isSoftInputVisible: \" + KeyboardUtils.isSoftInputVisible(this@KeyboardActivity) + \"\\nkeyboardHeight: $height\"\n            if (height > 0) {\n                keyboardEt.requestFocus()\n            }\n        }\n    }\n\n    private fun getItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                titleItem,\n                CommonItemClick(R.string.keyboard_hide_soft_input) {\n                    KeyboardUtils.hideSoftInput(this)\n                },\n                CommonItemClick(R.string.keyboard_show_soft_input) {\n                    KeyboardUtils.showSoftInput(this)\n                },\n                CommonItemClick(R.string.keyboard_toggle_soft_input) {\n                    KeyboardUtils.toggleSoftInput()\n                },\n                CommonItemClick(R.string.keyboard_show_dialog) {\n                    keyboardEt.clearFocus()\n                    DialogHelper.showKeyboardDialog(this)\n                }\n        )\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/language/LanguageActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.language\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemSwitch\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.LanguageUtils\nimport com.blankj.utilcode.util.SPStaticUtils\nimport com.blankj.utilcode.util.StringUtils\nimport java.util.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2018/12/29\n * desc  : demo about VibrateUtils\n * ```\n */\nclass LanguageActivity : CommonActivity() {\n\n    companion object {\n\n        const val SP_KEY_IS_RELAUNCH_APP = \"SP_KEY_IS_RELAUNCH_APP\"\n\n        fun start(context: Context) {\n            val starter = Intent(context, LanguageActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_language\n    }\n\n    override fun bindItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemTitle(\"isAppliedLanguage\", LanguageUtils.isAppliedLanguage().toString()),\n                CommonItemTitle(\"isAppliedLanguage(SIMPLIFIED_CHINESE)\", LanguageUtils.isAppliedLanguage(Locale.SIMPLIFIED_CHINESE).toString()),\n                CommonItemTitle(\"getAppliedLanguage\", (LanguageUtils.getAppliedLanguage() ?: \"null\").toString()),\n                CommonItemTitle(\"getActivityContextLanguage\", LanguageUtils.getContextLanguage(this).toString()),\n                CommonItemTitle(\"getAppContextLanguage\", LanguageUtils.getAppContextLanguage().toString()),\n                CommonItemTitle(\"getSystemLanguage\", LanguageUtils.getSystemLanguage().toString()),\n                CommonItemSwitch(\n                        StringUtils.getString(R.string.language_relaunch_app),\n                        { isRelaunchApp() },\n                        { SPStaticUtils.put(SP_KEY_IS_RELAUNCH_APP, it) }\n                ),\n                CommonItemClick(R.string.language_apply_simple_chinese) {\n                    LanguageUtils.applyLanguage(Locale.SIMPLIFIED_CHINESE, isRelaunchApp())\n                },\n                CommonItemClick(R.string.language_apply_american) {\n                    LanguageUtils.applyLanguage(Locale.US, isRelaunchApp())\n                },\n                CommonItemClick(R.string.language_apply_english) {\n                    LanguageUtils.applyLanguage(Locale.ENGLISH, isRelaunchApp())\n                },\n                CommonItemClick(R.string.language_apply_arabic) {\n                    LanguageUtils.applyLanguage(Locale(\"ar\"), isRelaunchApp())\n                },\n                CommonItemClick(R.string.language_apply_system) {\n                    LanguageUtils.applySystemLanguage(isRelaunchApp())\n                }\n        )\n    }\n\n    private fun isRelaunchApp() = SPStaticUtils.getBoolean(SP_KEY_IS_RELAUNCH_APP)\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/log/LogActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.log\n\nimport android.content.ComponentName\nimport android.content.Context\nimport android.content.Intent\nimport android.net.Uri\nimport android.os.Bundle\nimport android.util.Log\nimport com.blankj.base.BaseApplication\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemSwitch\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.*\nimport java.io.File\nimport java.util.*\n\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2017/03/22\n * desc  : demo about LogUtils\n * ```\n */\nclass LogActivity : CommonActivity() {\n\n    companion object {\n        private const val TAG = \"CMJ\"\n        private const val JSON = \"{\\\"tools\\\": [{ \\\"name\\\":\\\"css format\\\" , \\\"site\\\":\\\"http://tools.w3cschool.cn/code/css\\\" },{ \\\"name\\\":\\\"JSON format\\\" , \\\"site\\\":\\\"http://tools.w3cschool.cn/code/JSON\\\" },{ \\\"name\\\":\\\"pwd check\\\" , \\\"site\\\":\\\"http://tools.w3cschool.cn/password/my_password_safe\\\" }]}\"\n        private const val XML = \"<books><book><author>Jack Herrington</author><title>PHP Hacks</title><publisher>O'Reilly</publisher></book><book><author>Jack Herrington</author><title>Podcasting Hacks</title><publisher>O'Reilly</publisher></book></books>\"\n        private val ONE_D_ARRAY = intArrayOf(1, 2, 3)\n        private val TWO_D_ARRAY = arrayOf(intArrayOf(1, 2, 3), intArrayOf(4, 5, 6), intArrayOf(7, 8, 9))\n        private val THROWABLE = NullPointerException()\n        private val BUNDLE = Bundle()\n        private val INTENT = Intent()\n        private val LIST = ArrayList<String>()\n        private val MAP = HashMap<String, String>()\n\n        private val LONG_STR: String\n\n        init {\n            val sb = StringBuilder()\n            sb.append(\"len = 10400\\ncontent = \\\"\")\n            for (i in 0..1024) {\n                sb.append(\"Hello world. \")\n            }\n            sb.append(\"\\\"\")\n            LONG_STR = sb.toString()\n\n            BUNDLE.putByte(\"byte\", (-1).toByte())\n            BUNDLE.putChar(\"char\", 'c')\n            BUNDLE.putCharArray(\"charArray\", charArrayOf('c', 'h', 'a', 'r', 'A', 'r', 'r', 'a', 'y'))\n            BUNDLE.putCharSequence(\"charSequence\", \"charSequence\")\n            BUNDLE.putCharSequenceArray(\"charSequenceArray\", arrayOf<CharSequence>(\"char\", \"Sequence\", \"Array\"))\n            BUNDLE.putBundle(\"bundle\", BUNDLE)\n            BUNDLE.putBoolean(\"boolean\", true)\n            BUNDLE.putInt(\"int\", 1)\n            BUNDLE.putFloat(\"float\", 1f)\n            BUNDLE.putLong(\"long\", 1L)\n            BUNDLE.putShort(\"short\", 1.toShort())\n\n            INTENT.action = \"LogUtils action\"\n            INTENT.addCategory(\"LogUtils category\")\n            INTENT.data = Uri.parse(\"intent data\")\n            INTENT.type = \"intent type\"\n            INTENT.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)\n            INTENT.setPackage(AppUtils.getAppPackageName())\n            INTENT.component = ComponentName(AppUtils.getAppPackageName(), LogActivity::class.java.toString())\n            INTENT.putExtra(\"int\", 1)\n            INTENT.putExtra(\"float\", 1f)\n            INTENT.putExtra(\"char\", 'c')\n            INTENT.putExtra(\"string\", \"string\")\n            INTENT.putExtra(\"intArray\", ONE_D_ARRAY)\n            val list = ArrayList<String>()\n            list.add(\"ArrayList\")\n            list.add(\"is\")\n            list.add(\"serializable\")\n            INTENT.putExtra(\"serializable\", list)\n            INTENT.putExtra(\"bundle\", BUNDLE)\n\n            LIST.add(\"hello\")\n            LIST.add(\"log\")\n            LIST.add(\"utils\")\n\n            MAP[\"name\"] = \"AndroidUtilCode\"\n            MAP[\"class\"] = \"LogUtils\"\n        }\n\n        fun start(context: Context) {\n            val starter = Intent(context, LogActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    private val mConfig = LogUtils.getConfig()\n\n    private val mRunnable = Runnable {\n        LogUtils.v(\"verbose\")\n        LogUtils.d(\"debug\")\n        LogUtils.i(\"info\")\n        LogUtils.w(\"warn\")\n        LogUtils.e(\"error\")\n        LogUtils.a(\"assert\")\n    }\n\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_log\n    }\n\n    override fun bindItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemTitle(\"getLogFiles\", LogUtils.getLogFiles().toString()),\n                CommonItemSwitch(\n                        R.string.log_switch,\n                        { mConfig.isLogSwitch },\n                        { mConfig.isLogSwitch = it }\n                ),\n                CommonItemSwitch(\n                        R.string.log_console_switch,\n                        { mConfig.isLog2ConsoleSwitch },\n                        { mConfig.setConsoleSwitch(it) }\n                ),\n                CommonItemSwitch(\n                        R.string.log_console_listener_switch,\n                        { mConfig.haveSetOnConsoleOutputListener() },\n                        { mConfig.setOnConsoleOutputListener { type, tag, content -> Log.println(type, tag, content) } }\n                ),\n                CommonItemClick(\"Global Tag\", if (mConfig.globalTag == \"\") \"null\" else mConfig.globalTag).setOnClickUpdateContentListener {\n                    if (StringUtils.isSpace(mConfig.globalTag)) {\n                        mConfig.globalTag = TAG\n                    } else {\n                        mConfig.globalTag = \"\"\n                    }\n                    return@setOnClickUpdateContentListener if (mConfig.globalTag == \"\") \"\\\"\\\"\" else mConfig.globalTag\n                },\n                CommonItemSwitch(\n                        R.string.log_head_switch,\n                        { mConfig.isLogHeadSwitch },\n                        { mConfig.isLogHeadSwitch = it }\n                ),\n                CommonItemSwitch(\n                        R.string.log_file_switch,\n                        { mConfig.isLog2FileSwitch },\n                        { mConfig.isLog2FileSwitch = it }\n                ),\n                CommonItemSwitch(\n                        R.string.log_file_listener_switch,\n                        { mConfig.haveSetOnFileOutputListener() },\n                        { mConfig.setOnFileOutputListener { filePath, content -> Log.d(\"LogActivity\", filePath + \"\\n\" + content) } }\n                ),\n                CommonItemClick(\"Dir\", mConfig.dir).setOnClickUpdateContentListener {\n                    if (mConfig.dir != mConfig.defaultDir) {\n                        mConfig.dir = mConfig.defaultDir\n                    } else {\n                        mConfig.setDir(File(PathUtils.getExternalAppFilesPath(), \"log\"))\n                    }\n                    return@setOnClickUpdateContentListener mConfig.dir\n                },\n                CommonItemSwitch(\n                        R.string.log_border_switch,\n                        { mConfig.isLogBorderSwitch },\n                        { mConfig.setBorderSwitch(it) }\n                ),\n                CommonItemSwitch(\n                        R.string.log_single_tag_switch,\n                        { mConfig.isSingleTagSwitch },\n                        { mConfig.setSingleTagSwitch(it) }\n                ),\n                CommonItemSwitch(\n                        R.string.log_single_tag_switch,\n                        { mConfig.isSingleTagSwitch },\n                        { mConfig.setSingleTagSwitch(it) }\n                ),\n                CommonItemClick(\"ConsoleFilter\", mConfig.consoleFilter.toString()).setOnClickUpdateContentListener {\n                    mConfig.setConsoleFilter(if (mConfig.consoleFilter == 'V') LogUtils.W else LogUtils.V)\n                    return@setOnClickUpdateContentListener mConfig.consoleFilter.toString()\n                },\n                CommonItemClick(\"FileFilter\", mConfig.fileFilter.toString()).setOnClickUpdateContentListener {\n                    mConfig.setFileFilter(if (mConfig.fileFilter == 'V') LogUtils.W else LogUtils.V)\n                    return@setOnClickUpdateContentListener mConfig.fileFilter.toString()\n                },\n                CommonItemClick(R.string.log_with_no_tag) {\n                    LogUtils.v(\"verbose\")\n                    LogUtils.d(\"debug\")\n                    LogUtils.i(\"info\")\n                    LogUtils.w(\"warn\")\n                    LogUtils.e(\"error\")\n                    LogUtils.a(\"assert\")\n                },\n                CommonItemClick(R.string.log_with_tag) {\n                    LogUtils.vTag(\"customTag\", \"verbose\")\n                    LogUtils.dTag(\"customTag\", \"debug\")\n                    LogUtils.iTag(\"customTag\", \"info\")\n                    LogUtils.wTag(\"customTag\", \"warn\")\n                    LogUtils.eTag(\"customTag\", \"error\")\n                    LogUtils.aTag(\"customTag\", \"assert\")\n                },\n                CommonItemClick(R.string.log_in_new_thread) {\n                    val thread = Thread(mRunnable)\n                    thread.start()\n                },\n                CommonItemClick(R.string.log_null) {\n                    LogUtils.v(null)\n                    LogUtils.d(null)\n                    LogUtils.i(null)\n                    LogUtils.w(null)\n                    LogUtils.e(null)\n                    LogUtils.a(null)\n                },\n                CommonItemClick(R.string.log_many_params) {\n                    LogUtils.v(\"verbose0\", \"verbose1\")\n                    LogUtils.vTag(\"customTag\", \"verbose0\", \"verbose1\")\n                    LogUtils.d(\"debug0\", \"debug1\")\n                    LogUtils.dTag(\"customTag\", \"debug0\", \"debug1\")\n                    LogUtils.i(\"info0\", \"info1\")\n                    LogUtils.iTag(\"customTag\", \"info0\", \"info1\")\n                    LogUtils.w(\"warn0\", \"warn1\")\n                    LogUtils.wTag(\"customTag\", \"warn0\", \"warn1\")\n                    LogUtils.e(\"error0\", \"error1\")\n                    LogUtils.eTag(\"customTag\", \"error0\", \"error1\")\n                    LogUtils.a(\"assert0\", \"assert1\")\n                    LogUtils.aTag(\"customTag\", \"assert0\", \"assert1\")\n                },\n                CommonItemClick(R.string.log_long_string) {\n                    LogUtils.d(LONG_STR)\n                },\n                CommonItemClick(R.string.log_to_file) {\n                    LogUtils.file(\"test0 log to file\")\n                    LogUtils.file(LogUtils.I, \"test0 log to file\")\n                },\n                CommonItemClick(R.string.log_json) {\n                    LogUtils.json(JSON)\n                    LogUtils.json(LogUtils.I, JSON)\n                },\n                CommonItemClick(R.string.log_xml) {\n                    LogUtils.xml(XML)\n                    LogUtils.xml(LogUtils.I, XML)\n                },\n                CommonItemClick(R.string.log_array) {\n                    LogUtils.e(ONE_D_ARRAY)\n                    LogUtils.e(TWO_D_ARRAY)\n                },\n                CommonItemClick(R.string.log_throwable) {\n                    LogUtils.e(THROWABLE)\n                },\n                CommonItemClick(R.string.log_bundle) {\n                    LogUtils.e(BUNDLE)\n                },\n                CommonItemClick(R.string.log_intent) {\n                    LogUtils.e(INTENT)\n                },\n                CommonItemClick(R.string.log_array_list) {\n                    LogUtils.e(LIST)\n                },\n                CommonItemClick(R.string.log_map) {\n                    LogUtils.e(MAP)\n                }\n        )\n    }\n\n    override fun onDestroy() {\n        BaseApplication.getInstance().initLog()\n        super.onDestroy()\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/messenger/MessengerActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.messenger\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Bundle\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.activity.CommonActivityItemsView\nimport com.blankj.common.activity.CommonActivityTitleView\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.MessengerUtils\nimport com.blankj.utilcode.util.SnackbarUtils\nimport com.blankj.utilcode.util.ToastUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2019/03/12\n * desc  : demo about MessengerUtils\n * ```\n */\nclass MessengerActivity : CommonActivity() {\n\n    companion object {\n        const val MESSENGER_KEY = \"MessengerActivity\"\n\n        fun start(context: Context) {\n            val starter = Intent(context, MessengerActivity::class.java)\n            context.startActivity(starter)\n            MessengerUtils.register()\n        }\n\n        val BUNDLE = Bundle()\n\n        init {\n            BUNDLE.putString(MESSENGER_KEY, \"MessengerActivity\")\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_messenger\n    }\n\n    override fun bindItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemClick(R.string.messenger_post_to_main_server) {\n                    MessengerUtils.post(MESSENGER_KEY, BUNDLE)\n                },\n                CommonItemClick(R.string.messenger_start_remote) {\n                    MessengerRemoteActivity.start(this)\n                }\n        )\n    }\n\n    override fun doBusiness() {\n        MessengerUtils.subscribe(MESSENGER_KEY) { data ->\n            SnackbarUtils.with(mContentView)\n                    .setMessage(data.getString(MESSENGER_KEY) ?: \"\")\n                    .setDuration(SnackbarUtils.LENGTH_INDEFINITE)\n                    .show()\n        }\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        MessengerUtils.unregister()\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/messenger/MessengerRemoteActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.messenger\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Bundle\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.activity.CommonActivityItemsView\nimport com.blankj.common.activity.CommonActivityTitleView\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.MessengerUtils\nimport com.blankj.utilcode.util.SnackbarUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2019/03/12\n * desc  : demo about MessengerUtils\n * ```\n */\nclass MessengerRemoteActivity : CommonActivity() {\n\n    companion object {\n        const val MESSENGER_KEY = \"MessengerRemoteActivity\"\n\n        fun start(context: Context) {\n            val starter = Intent(context, MessengerRemoteActivity::class.java)\n            context.startActivity(starter)\n        }\n\n        val BUNDLE = Bundle()\n\n        init {\n            BUNDLE.putString(MESSENGER_KEY, \"MessengerRemoteActivity\")\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_messenger\n    }\n\n    override fun bindItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemClick(R.string.messenger_register_remote_client) {\n                    MessengerUtils.register()\n                },\n                CommonItemClick(R.string.messenger_unregister_remote_client) {\n                    MessengerUtils.unregister()\n                },\n                CommonItemClick(R.string.messenger_post_to_self_client) {\n                    MessengerUtils.post(MESSENGER_KEY, BUNDLE)\n                },\n                CommonItemClick(R.string.messenger_post_to_main_server) {\n                    MessengerUtils.post(MessengerActivity.MESSENGER_KEY, MessengerActivity.BUNDLE)\n                }\n        )\n    }\n\n    override fun doBusiness() {\n        MessengerUtils.subscribe(MESSENGER_KEY) { data ->\n            SnackbarUtils.with(mContentView)\n                    .setMessage(data.getString(MESSENGER_KEY) ?: \"\")\n                    .setDuration(SnackbarUtils.LENGTH_INDEFINITE)\n                    .show()\n        }\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        MessengerUtils.unsubscribe(MESSENGER_KEY)\n        MessengerUtils.unregister()\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/metaData/MetaDataActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.metaData\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.MetaDataUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2018/05/15\n * desc  : demo about MetaDataUtils\n * ```\n */\nclass MetaDataActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, MetaDataActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_meta_data\n    }\n\n    override fun bindItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemTitle(\"getMetaDataInApp\", MetaDataUtils.getMetaDataInApp(\"app_meta_data\")),\n                CommonItemTitle(\"getMetaDataInActivity\", MetaDataUtils.getMetaDataInActivity(this, \"activity_meta_data\").substring(1))\n        )\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/mvp/MvpActivity.java",
    "content": "package com.blankj.utilcode.pkg.feature.mvp;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.view.View;\n\nimport com.blankj.common.activity.CommonActivity;\nimport com.blankj.utilcode.pkg.R;\n\nimport androidx.annotation.Nullable;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/11/09\n *     desc  :\n * </pre>\n */\npublic class MvpActivity extends CommonActivity {\n\n    public static void start(Context context) {\n        Intent starter = new Intent(context, MvpActivity.class);\n        context.startActivity(starter);\n    }\n\n    @Override\n    public int bindTitleRes() {\n        return R.string.demo_mvp;\n    }\n\n    @Override\n    public int bindLayout() {\n        return R.layout.mvp_activity;\n    }\n\n    @Override\n    public void initView(@Nullable Bundle savedInstanceState, @Nullable View contentView) {\n        super.initView(savedInstanceState, contentView);\n        new MvpView(this).addPresenter(new MvpPresenter());\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/mvp/MvpModel.java",
    "content": "package com.blankj.utilcode.pkg.feature.mvp;\n\nimport com.blankj.base.mvp.BaseModel;\nimport com.blankj.utilcode.util.ThreadUtils;\nimport com.blankj.utilcode.util.Utils;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/11/26\n *     desc  :\n * </pre>\n */\npublic class MvpModel extends BaseModel implements MvpMvp.Model {\n\n    private int index;\n\n    @Override\n    public void onCreate() {\n        index = 0;\n    }\n\n    @Override\n    public void requestUpdateMsg(final Utils.Consumer<String> consumer) {\n        ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<String>() {\n            @Override\n            public String doInBackground() throws Throwable {\n                Thread.sleep(2000);\n                return \"msg: \" + index++;\n            }\n\n            @Override\n            public void onSuccess(String result) {\n                consumer.accept(result);\n            }\n        });\n    }\n\n    @Override\n    public void onDestroy() {\n        super.onDestroy();\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/mvp/MvpMvp.java",
    "content": "package com.blankj.utilcode.pkg.feature.mvp;\n\nimport com.blankj.utilcode.util.Utils;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/11/26\n *     desc  :\n * </pre>\n */\npublic interface MvpMvp {\n\n    interface View {\n        void setLoadingVisible(boolean visible);\n\n        void showMsg(CharSequence msg);\n    }\n\n    interface Presenter {\n        void updateMsg();\n    }\n\n    interface Model {\n        void requestUpdateMsg(final Utils.Consumer<String> consumer);\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/mvp/MvpPresenter.java",
    "content": "package com.blankj.utilcode.pkg.feature.mvp;\n\nimport com.blankj.base.mvp.BasePresenter;\nimport com.blankj.utilcode.util.LogUtils;\nimport com.blankj.utilcode.util.Utils;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/11/26\n *     desc  :\n * </pre>\n */\npublic class MvpPresenter extends BasePresenter<MvpView>\n        implements MvpMvp.Presenter {\n\n    @Override\n    public void onBindView() {\n    }\n\n    @Override\n    public void updateMsg() {\n        getView().setLoadingVisible(true);\n        getModel(MvpModel.class).requestUpdateMsg(new Utils.Consumer<String>() {\n            @Override\n            public void accept(String s) {\n                if (isAlive()) {\n                    getView().showMsg(s);\n                    getView().setLoadingVisible(false);\n                } else {\n                    LogUtils.iTag(MvpView.TAG, \"destroyed\");\n                }\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/mvp/MvpView.java",
    "content": "package com.blankj.utilcode.pkg.feature.mvp;\n\nimport android.text.Layout;\nimport android.view.View;\nimport android.widget.TextView;\n\nimport com.blankj.base.mvp.BaseView;\nimport com.blankj.utilcode.pkg.R;\nimport com.blankj.utilcode.util.ClickUtils;\nimport com.blankj.utilcode.util.LogUtils;\nimport com.blankj.utilcode.util.SizeUtils;\nimport com.blankj.utilcode.util.ThreadUtils;\nimport com.blankj.utilcode.util.ToastUtils;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/11/26\n *     desc  :\n * </pre>\n */\npublic class MvpView extends BaseView<MvpView>\n        implements MvpMvp.View {\n\n    private TextView mvpTv;\n    private TextView mvpMeasureWidthTv;\n    private int      i = 0;\n\n    public MvpView(MvpActivity activity) {\n        super(activity);\n        mvpTv = activity.findViewById(R.id.mvpUpdateTv);\n        ClickUtils.applyPressedBgDark(mvpTv);\n        mvpTv.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                getPresenter(MvpPresenter.class).updateMsg();\n            }\n        });\n\n        mvpMeasureWidthTv = activity.findViewById(R.id.mvpMeasureWidthTv);\n\n        measure();\n    }\n\n    private void measure() {\n        ThreadUtils.runOnUiThreadDelayed(new Runnable() {\n            @Override\n            public void run() {\n                float textWidth = Layout.getDesiredWidth(mvpMeasureWidthTv.getText(), mvpMeasureWidthTv.getPaint()) + SizeUtils.dp2px(16);\n                float textWidth2 = mvpMeasureWidthTv.getPaint().measureText(mvpMeasureWidthTv.getText().toString()) + SizeUtils.dp2px(16);\n                LogUtils.i(mvpMeasureWidthTv.getWidth(), textWidth, textWidth2);\n                mvpMeasureWidthTv.setText(mvpMeasureWidthTv.getText().toString() + i);\n                measure();\n            }\n        }, 1000);\n    }\n\n    @Override\n    public void setLoadingVisible(boolean visible) {\n        final MvpActivity activity = getActivity();\n        if (visible) {\n            activity.showLoading(new Runnable() {\n                @Override\n                public void run() {\n                    activity.finish();\n                }\n            });\n        } else {\n            activity.dismissLoading();\n        }\n    }\n\n    @Override\n    public void showMsg(CharSequence msg) {\n        ToastUtils.showLong(msg);\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/network/NetworkActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.network\n\nimport android.content.Context\nimport android.content.Intent\nimport android.net.wifi.ScanResult\nimport android.net.wifi.WifiManager\nimport android.os.Bundle\nimport android.view.View\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.helper.PermissionHelper\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemSwitch\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.constant.PermissionConstants\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/10/13\n * desc  : demo about NetworkUtils\n * ```\n */\nclass NetworkActivity : CommonActivity(), NetworkUtils.OnNetworkStatusChangedListener {\n\n    companion object {\n        fun start(context: Context) {\n            PermissionHelper.request(context, object : PermissionUtils.SimpleCallback {\n                override fun onGranted() {\n                    val starter = Intent(context, NetworkActivity::class.java)\n                    context.startActivity(starter)\n                }\n\n                override fun onDenied() {\n                }\n            }, PermissionConstants.LOCATION)\n        }\n    }\n\n    private lateinit var itemsTask: ThreadUtils.SimpleTask<List<CommonItem<*>>>\n    private lateinit var wifiScanResultItem: CommonItemTitle\n    private val consumer = Utils.Consumer<NetworkUtils.WifiScanResults> { t ->\n        wifiScanResultItem.setContent(scanResults2String(t.filterResults))\n        wifiScanResultItem.update()\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_network\n    }\n\n    private fun getItemsTask(): ThreadUtils.SimpleTask<List<CommonItem<*>>> {\n        itemsTask = object : ThreadUtils.SimpleTask<List<CommonItem<*>>>() {\n            override fun doInBackground(): List<CommonItem<*>> {\n                return bindItems()\n            }\n\n            override fun onSuccess(result: List<CommonItem<*>>) {\n                dismissLoading()\n                itemsView.updateItems(result)\n            }\n        }\n        return itemsTask\n    }\n\n    override fun bindItems(): List<CommonItem<*>> {\n        if (ThreadUtils.isMainThread()) return arrayListOf()\n        wifiScanResultItem = CommonItemTitle(\"getWifiScanResult\", scanResults2String(NetworkUtils.getWifiScanResult().filterResults))\n        return CollectionUtils.newArrayList(\n                CommonItemTitle(\"isConnected\", NetworkUtils.isConnected().toString()),\n                CommonItemTitle(\"getMobileDataEnabled\", NetworkUtils.getMobileDataEnabled().toString()),\n                CommonItemTitle(\"isMobileData\", NetworkUtils.isMobileData().toString()),\n                CommonItemTitle(\"is4G\", NetworkUtils.is4G().toString()),\n                CommonItemTitle(\"is5G\", NetworkUtils.is5G().toString()),\n                CommonItemTitle(\"isWifiConnected\", NetworkUtils.isWifiConnected().toString()),\n                CommonItemTitle(\"getNetworkOperatorName\", NetworkUtils.getNetworkOperatorName()),\n                CommonItemTitle(\"getNetworkTypeName\", NetworkUtils.getNetworkType().toString()),\n                CommonItemTitle(\"getBroadcastIpAddress\", NetworkUtils.getBroadcastIpAddress()),\n                CommonItemTitle(\"getIpAddressByWifi\", NetworkUtils.getIpAddressByWifi()),\n                CommonItemTitle(\"getGatewayByWifi\", NetworkUtils.getGatewayByWifi()),\n                CommonItemTitle(\"getNetMaskByWifi\", NetworkUtils.getNetMaskByWifi()),\n                CommonItemTitle(\"getServerAddressByWifi\", NetworkUtils.getServerAddressByWifi()),\n                CommonItemTitle(\"getSSID\", NetworkUtils.getSSID()),\n\n                CommonItemTitle(\"getIPv4Address\", NetworkUtils.getIPAddress(true)),\n                CommonItemTitle(\"getIPv6Address\", NetworkUtils.getIPAddress(false)),\n                CommonItemTitle(\"isWifiAvailable\", NetworkUtils.isWifiAvailable().toString()),\n                CommonItemTitle(\"isAvailable\", NetworkUtils.isAvailable().toString()),\n                CommonItemTitle(\"getBaiduDomainAddress\", NetworkUtils.getDomainAddress(\"baidu.com\")),\n                wifiScanResultItem,\n\n                CommonItemSwitch(\n                        R.string.network_wifi_enabled,\n                        {\n                            val wifiEnabled = NetworkUtils.getWifiEnabled()\n                            if (wifiEnabled) {\n                                NetworkUtils.addOnWifiChangedConsumer(consumer)\n                            } else {\n                                NetworkUtils.removeOnWifiChangedConsumer(consumer)\n                            }\n                            wifiEnabled\n                        },\n                        {\n                            NetworkUtils.setWifiEnabled(it)\n                            ThreadUtils.executeByIo(getItemsTask())\n                        }\n                ),\n                CommonItemClick(R.string.network_open_wireless_settings) {\n                    NetworkUtils.openWirelessSettings()\n                }\n        )\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        NetworkUtils.registerNetworkStatusChangedListener(this)\n        updateItems()\n    }\n\n    override fun onDisconnected() {\n        ToastUtils.showLong(\"onDisconnected\")\n        updateItems()\n    }\n\n    override fun onConnected(networkType: NetworkUtils.NetworkType) {\n        ToastUtils.showLong(\"onConnected: ${networkType.name}\")\n        updateItems()\n    }\n\n    private fun updateItems() {\n        showLoading()\n        ThreadUtils.executeByIo(getItemsTask())\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        ThreadUtils.cancel(itemsTask)\n        NetworkUtils.unregisterNetworkStatusChangedListener(this)\n        NetworkUtils.removeOnWifiChangedConsumer(consumer)\n    }\n\n    private fun scanResults2String(results: List<ScanResult>): String {\n        val sb: StringBuilder = StringBuilder()\n        for (result in results) {\n            sb.append(String.format(\"${result.SSID}, Level: ${WifiManager.calculateSignalLevel(result.level, 4)}\\n\"))\n        }\n        return sb.toString()\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/notification/NotificationActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.notification\n\nimport android.app.PendingIntent\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.NotificationUtils\nimport com.blankj.utilcode.util.ToastUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2019/10/22\n * desc  : demo about NotificationUtils\n * ```\n */\nclass NotificationActivity : CommonActivity() {\n\n    private var id: Int = 0\n    private var cancelId: Int = 0\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, NotificationActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_notification\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemTitle(\"areNotificationsEnabled\", NotificationUtils.areNotificationsEnabled().toString()),\n                CommonItemClick(R.string.notification_notify) {\n                    NotificationUtils.notify(id++) { param ->\n                        intent.putExtra(\"id\", id);\n                        param.setSmallIcon(R.mipmap.ic_launcher)\n                                .setContentTitle(\"title\")\n                                .setContentText(\"content text: $id\")\n                                .setContentIntent(PendingIntent.getActivity(mActivity, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT))\n                                .setAutoCancel(true)\n                        null\n                    }\n                },\n                CommonItemClick(R.string.notification_cancel) {\n                    if (cancelId < id) {\n                        NotificationUtils.cancel(cancelId++)\n                    } else {\n                        ToastUtils.showShort(\"No notification.\")\n                    }\n                },\n                CommonItemClick(R.string.notification_cancel_all) {\n                    NotificationUtils.cancelAll()\n                    cancelId = id;\n                },\n                CommonItemClick(R.string.notification_show) {\n                    NotificationUtils.setNotificationBarVisibility(true)\n                }\n        )\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/path/PathActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.path\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.PathUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/10/13\n * desc  : demo about PathUtils\n * ```\n */\nclass PathActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, PathActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_path\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemTitle(\"getRootPath\", PathUtils.getRootPath()),\n                CommonItemTitle(\"getDataPath\", PathUtils.getDataPath()),\n                CommonItemTitle(\"getDownloadCachePath\", PathUtils.getDownloadCachePath()),\n\n                CommonItemTitle(\"getInternalAppDataPath\", PathUtils.getInternalAppDataPath()),\n                CommonItemTitle(\"getInternalAppCodeCacheDir\", PathUtils.getInternalAppCodeCacheDir()),\n                CommonItemTitle(\"getInternalAppCachePath\", PathUtils.getInternalAppCachePath()),\n                CommonItemTitle(\"getInternalAppDbsPath\", PathUtils.getInternalAppDbsPath()),\n                CommonItemTitle(\"getInternalAppDbPath\", PathUtils.getInternalAppDbPath(\"demo\")),\n                CommonItemTitle(\"getInternalAppFilesPath\", PathUtils.getInternalAppFilesPath()),\n                CommonItemTitle(\"getInternalAppSpPath\", PathUtils.getInternalAppSpPath()),\n                CommonItemTitle(\"getInternalAppNoBackupFilesPath\", PathUtils.getInternalAppNoBackupFilesPath()),\n\n                CommonItemTitle(\"getExternalStoragePath\", PathUtils.getExternalStoragePath()),\n                CommonItemTitle(\"getExternalMusicPath\", PathUtils.getExternalMusicPath()),\n                CommonItemTitle(\"getExternalPodcastsPath\", PathUtils.getExternalPodcastsPath()),\n                CommonItemTitle(\"getExternalRingtonesPath\", PathUtils.getExternalRingtonesPath()),\n                CommonItemTitle(\"getExternalAlarmsPath\", PathUtils.getExternalAlarmsPath()),\n                CommonItemTitle(\"getExternalNotificationsPath\", PathUtils.getExternalNotificationsPath()),\n                CommonItemTitle(\"getExternalPicturesPath\", PathUtils.getExternalPicturesPath()),\n                CommonItemTitle(\"getExternalMoviesPath\", PathUtils.getExternalMoviesPath()),\n                CommonItemTitle(\"getExternalDownloadsPath\", PathUtils.getExternalDownloadsPath()),\n                CommonItemTitle(\"getExternalDcimPath\", PathUtils.getExternalDcimPath()),\n                CommonItemTitle(\"getExternalDocumentsPath\", PathUtils.getExternalDocumentsPath()),\n\n                CommonItemTitle(\"getExternalAppDataPath\", PathUtils.getExternalAppDataPath()),\n                CommonItemTitle(\"getExternalAppCachePath\", PathUtils.getExternalAppCachePath()),\n                CommonItemTitle(\"getExternalAppFilesPath\", PathUtils.getExternalAppFilesPath()),\n                CommonItemTitle(\"getExternalAppMusicPath\", PathUtils.getExternalAppMusicPath()),\n                CommonItemTitle(\"getExternalAppPodcastsPath\", PathUtils.getExternalAppPodcastsPath()),\n                CommonItemTitle(\"getExternalAppRingtonesPath\", PathUtils.getExternalAppRingtonesPath()),\n                CommonItemTitle(\"getExternalAppAlarmsPath\", PathUtils.getExternalAppAlarmsPath()),\n                CommonItemTitle(\"getExternalAppNotificationsPath\", PathUtils.getExternalAppNotificationsPath()),\n                CommonItemTitle(\"getExternalAppPicturesPath\", PathUtils.getExternalAppPicturesPath()),\n                CommonItemTitle(\"getExternalAppMoviesPath\", PathUtils.getExternalAppMoviesPath()),\n                CommonItemTitle(\"getExternalAppDownloadPath\", PathUtils.getExternalAppDownloadPath()),\n                CommonItemTitle(\"getExternalAppDcimPath\", PathUtils.getExternalAppDcimPath()),\n                CommonItemTitle(\"getExternalAppDocumentsPath\", PathUtils.getExternalAppDocumentsPath()),\n                CommonItemTitle(\"getExternalAppObbPath\", PathUtils.getExternalAppObbPath())\n        )\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/permission/PermissionActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.permission\n\nimport android.Manifest.permission\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Build\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.helper.PermissionHelper\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemSwitch\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.constant.PermissionConstants\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2018/01/01\n * desc  : demo about PermissionUtils\n * ```\n */\nclass PermissionActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, PermissionActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    private val permissions: String\n\n    init {\n        val permissionList = PermissionUtils.getPermissions()\n        if (permissionList.isEmpty()) {\n            permissions = \"\"\n        } else {\n            val sb = StringBuilder()\n            for (permission in permissionList) {\n                sb.append(\"\\n\").append(permission.substring(permission.lastIndexOf('.') + 1))\n            }\n            permissions = sb.deleteCharAt(0).toString()\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_permission\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList<CommonItem<*>>().apply {\n            add(CommonItemTitle(\"Permissions\", permissions))\n            add(CommonItemClick(R.string.permission_open_app_settings, true) { PermissionUtils.launchAppDetailsSettings() })\n            add(CommonItemSwitch(\n                    R.string.permission_calendar_status,\n                    { PermissionUtils.isGranted(PermissionConstants.CALENDAR) },\n                    { requestCalendar() }\n            ))\n            add(CommonItemSwitch(\n                    R.string.permission_record_audio_status,\n                    { PermissionUtils.isGranted(PermissionConstants.MICROPHONE) },\n                    { requestRecordAudio() }\n            ))\n            add(CommonItemSwitch(\n                    R.string.permission_calendar_and_record_audio_status,\n                    { PermissionUtils.isGranted(PermissionConstants.CALENDAR, PermissionConstants.MICROPHONE) },\n                    { requestCalendarAndRecordAudio() }\n            ))\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n                add(CommonItemSwitch(\n                        R.string.permission_write_settings_status,\n                        { PermissionUtils.isGrantedWriteSettings() },\n                        { requestWriteSettings() }\n                ))\n                add(CommonItemSwitch(\n                        R.string.permission_write_settings_status,\n                        { PermissionUtils.isGrantedDrawOverlays() },\n                        { requestDrawOverlays() }\n                ))\n            }\n        }\n    }\n\n    private fun requestCalendar() {\n        PermissionUtils.permissionGroup(PermissionConstants.CALENDAR)\n                .rationale { activity, shouldRequest -> PermissionHelper.showRationaleDialog(activity, shouldRequest) }\n                .callback(object : PermissionUtils.FullCallback {\n                    override fun onGranted(permissionsGranted: List<String>) {\n                        LogUtils.d(permissionsGranted)\n                        showSnackbar(true, \"Calendar is granted\")\n                        itemsView.updateItems(bindItems())\n                    }\n\n                    override fun onDenied(permissionsDeniedForever: List<String>,\n                                          permissionsDenied: List<String>) {\n                        LogUtils.d(permissionsDeniedForever, permissionsDenied)\n                        if (permissionsDeniedForever.isNotEmpty()) {\n                            showSnackbar(false, \"Calendar is denied forever\")\n                        } else {\n                            showSnackbar(false, \"Calendar is denied\")\n                        }\n                        itemsView.updateItems(bindItems())\n                    }\n                })\n                .theme { activity -> ScreenUtils.setFullScreen(activity) }\n                .request()\n    }\n\n    private fun requestRecordAudio() {\n        PermissionUtils.permissionGroup(PermissionConstants.MICROPHONE)\n                .rationale { activity, shouldRequest -> PermissionHelper.showRationaleDialog(activity, shouldRequest) }\n                .callback(object : PermissionUtils.FullCallback {\n                    override fun onGranted(permissionsGranted: List<String>) {\n                        LogUtils.d(permissionsGranted)\n                        showSnackbar(true, \"Microphone is granted\")\n                        itemsView.updateItems(bindItems())\n                    }\n\n                    override fun onDenied(permissionsDeniedForever: List<String>,\n                                          permissionsDenied: List<String>) {\n                        LogUtils.d(permissionsDeniedForever, permissionsDenied)\n                        if (permissionsDeniedForever.isNotEmpty()) {\n                            showSnackbar(false, \"Microphone is denied forever\")\n                        } else {\n                            showSnackbar(false, \"Microphone is denied\")\n                        }\n                        itemsView.updateItems(bindItems())\n                    }\n                })\n                .request()\n    }\n\n    private fun requestCalendarAndRecordAudio() {\n        PermissionUtils.permission(permission.READ_CALENDAR, permission.RECORD_AUDIO)\n                .explain { activity, denied, shouldRequest -> PermissionHelper.showExplainDialog(activity, denied, shouldRequest) }\n                .callback { isAllGranted, granted, deniedForever, denied ->\n                    LogUtils.d(granted, deniedForever, denied)\n                    itemsView.updateItems(bindItems())\n                    if (isAllGranted) {\n                        showSnackbar(true, \"Calendar and Microphone are granted\")\n                        return@callback\n                    }\n                    if (deniedForever.isNotEmpty()) {\n                        showSnackbar(false, \"Calendar or Microphone is denied forever\")\n                    } else {\n                        showSnackbar(false, \"Calendar or Microphone is denied\")\n                    }\n                }\n                .request()\n    }\n\n    private fun requestWriteSettings() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            PermissionUtils.requestWriteSettings(object : PermissionUtils.SimpleCallback {\n                override fun onGranted() {\n                    showSnackbar(true, \"Write Settings is granted\")\n                    itemsView.updateItems(bindItems())\n                }\n\n                override fun onDenied() {\n                    showSnackbar(false, \"Write Settings is denied\")\n                    itemsView.updateItems(bindItems())\n                }\n            })\n        }\n    }\n\n    private fun requestDrawOverlays() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            PermissionUtils.requestDrawOverlays(object : PermissionUtils.SimpleCallback {\n                override fun onGranted() {\n                    showSnackbar(true, \"Draw Overlays is granted\")\n                    itemsView.updateItems(bindItems())\n                }\n\n                override fun onDenied() {\n                    showSnackbar(false, \"Draw Overlays is denied\")\n                    itemsView.updateItems(bindItems())\n                }\n            })\n        }\n    }\n\n\n    private fun showSnackbar(isSuccess: Boolean, msg: String) {\n        SnackbarUtils.with(mContentView)\n                .setDuration(SnackbarUtils.LENGTH_LONG)\n                .setMessage(msg)\n                .apply {\n                    if (isSuccess) {\n                        showSuccess()\n                    } else {\n                        showError()\n                    }\n                }\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/phone/PhoneActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.phone\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.helper.PermissionHelper\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.constant.PermissionConstants\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.PermissionUtils\nimport com.blankj.utilcode.util.PhoneUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/10/13\n * desc  : demo about PhoneUtils\n * ```\n */\nclass PhoneActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            PermissionHelper.request(context, object : PermissionUtils.SimpleCallback {\n                override fun onGranted() {\n                    val starter = Intent(context, PhoneActivity::class.java)\n                    context.startActivity(starter)\n                }\n\n                override fun onDenied() {\n                }\n            }, PermissionConstants.PHONE)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_phone\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemTitle(\"isPhone\", PhoneUtils.isPhone().toString()),\n                CommonItemTitle(\"getDeviceId\", PhoneUtils.getDeviceId()),\n                CommonItemTitle(\"getSerial\", PhoneUtils.getSerial()),\n                CommonItemTitle(\"getIMEI\", PhoneUtils.getIMEI()),\n                CommonItemTitle(\"getMEID\", PhoneUtils.getMEID()),\n                CommonItemTitle(\"getIMSI\", PhoneUtils.getIMSI()),\n                CommonItemTitle(\"getPhoneType\", PhoneUtils.getPhoneType().toString()),\n                CommonItemTitle(\"isSimCardReady\", PhoneUtils.isSimCardReady().toString()),\n                CommonItemTitle(\"getSimOperatorName\", PhoneUtils.getSimOperatorName()),\n                CommonItemTitle(\"getSimOperatorByMnc\", PhoneUtils.getSimOperatorByMnc()),\n\n                CommonItemClick(R.string.phone_dial) { PhoneUtils.dial(\"*10000#haha\") },\n                CommonItemClick(R.string.phone_call) { PhoneUtils.call(\"*10000#haha\") },\n                CommonItemClick(R.string.phone_send_sms) { PhoneUtils.sendSms(\"10000\", \"sendSms\") }\n        )\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/process/ProcessActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.process\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.ProcessUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/10/13\n * desc  : demo about ProcessUtils\n * ```\n */\nclass ProcessActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, ProcessActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_process\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        val set = ProcessUtils.getAllBackgroundProcesses()\n        return CollectionUtils.newArrayList(\n                CommonItemTitle(\"getForegroundProcessName\", ProcessUtils.getForegroundProcessName()!!),\n                CommonItemTitle(\"getAllBackgroundProcesses -> ${set.size}\", getSetItems(set)),\n                CommonItemTitle(\"isMainProcess\", ProcessUtils.isMainProcess().toString()),\n                CommonItemTitle(\"getCurrentProcessName\", ProcessUtils.getCurrentProcessName()),\n\n                CommonItemClick(R.string.process_kill_all_background).setOnItemClickListener { _, item, _ ->\n                    val bgSet = ProcessUtils.getAllBackgroundProcesses()\n                    val killedSet = ProcessUtils.killAllBackgroundProcesses()\n                    itemsView.updateItems(\n                            CollectionUtils.newArrayList(\n                                    CommonItemTitle(\"getForegroundProcessName\", ProcessUtils.getForegroundProcessName()),\n                                    CommonItemTitle(\"getAllBackgroundProcesses -> ${bgSet.size}\", getSetItems(bgSet)),\n                                    CommonItemTitle(\"killAllBackgroundProcesses -> ${killedSet.size}\", getSetItems(killedSet)),\n                                    CommonItemTitle(\"isMainProcess\", ProcessUtils.isMainProcess().toString()),\n                                    CommonItemTitle(\"getCurrentProcessName\", ProcessUtils.getCurrentProcessName()),\n                                    item\n                            )\n                    )\n                }\n        )\n    }\n\n    private fun getSetItems(set: Set<String>): String {\n        val iterator = set.iterator()\n        val sb = StringBuilder()\n        while (iterator.hasNext()) {\n            sb.append(\"\\n\").append(iterator.next())\n        }\n        return if (sb.isNotEmpty()) sb.deleteCharAt(0).toString() else \"\"\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/reflect/ReflectActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.reflect\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.ReflectUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2018/01/29\n * desc  : demo about ReflectUtils\n * ```\n */\nclass ReflectActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, ReflectActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_reflect\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemTitle(\"source value\", TestPrivateStaticFinal.STR),\n                CommonItemTitle(\"reflect get\", ReflectUtils.reflect(TestPrivateStaticFinal::class.java).field(\"STR\").get<String>()),\n                CommonItemTitle(\"after reflect get\", ReflectUtils.reflect(TestPrivateStaticFinal::class.java).field(\"STR\", \"reflect success\").field(\"STR\").get<String>()),\n                CommonItemTitle(\"source value\", TestPrivateStaticFinal.STR)\n        )\n    }\n}"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/reflect/TestPrivateStaticFinal.java",
    "content": "package com.blankj.utilcode.pkg.feature.reflect;\n\nimport androidx.annotation.Keep;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/09\n *     desc  :\n * </pre>\n */\n@Keep\npublic class TestPrivateStaticFinal {\n    public static final String STR = \"str\";\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/resource/ResourceActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.resource\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.Config\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.ResourceUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2018/05/07\n * desc  : demo about ResourceUtils\n * ```\n */\nclass ResourceActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, ResourceActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_resource\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemTitle(\"readAssets2String\", ResourceUtils.readAssets2String(\"test/test.txt\")),\n                CommonItemTitle(\"readAssets2List\", ResourceUtils.readAssets2List(\"test/test.txt\").toString()),\n                CommonItemTitle(\"readRaw2List\", ResourceUtils.readRaw2List(R.raw.test).toString()),\n\n                CommonItemClick(R.string.resource_copy_file_from_assets_2_cache) {\n                    ResourceUtils.copyFileFromAssets(\"test\", Config.CACHE_PATH + \"assets/test\")\n                },\n                CommonItemClick(R.string.resource_copy_file_from_raw_2_cache) {\n                    ResourceUtils.copyFileFromRaw(R.raw.test, Config.CACHE_PATH + \"raw/test.txt\")\n                }\n        )\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/rom/RomActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.rom\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.RomUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2019/01/29\n * desc  : demo about RomUtils\n * ```\n */\nclass RomActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, RomActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_rom\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        val romInfo = RomUtils.getRomInfo()\n        return CollectionUtils.newArrayList(\n                CommonItemTitle(\"Rom Name\", romInfo.name),\n                CommonItemTitle(\"Rom Version\", romInfo.version)\n        )\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/screen/ScreenActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.screen\n\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Build\nimport android.widget.ImageView\nimport android.widget.TextView\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemSwitch\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.pkg.helper.DialogHelper\nimport com.blankj.utilcode.util.*\n\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2019/01/29\n * desc  : demo about RomUtils\n * ```\n */\nclass ScreenActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n                PermissionUtils.requestWriteSettings(object : PermissionUtils.SimpleCallback {\n                    override fun onGranted() {\n                        val starter = Intent(context, ScreenActivity::class.java)\n                        context.startActivity(starter)\n                    }\n\n                    override fun onDenied() {\n                        ToastUtils.showLong(\"No permission of write settings.\")\n                    }\n                })\n            } else {\n                val starter = Intent(context, ScreenActivity::class.java)\n                context.startActivity(starter)\n            }\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_screen\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemTitle(\"getScreenWidth\", ScreenUtils.getScreenWidth().toString()),\n                CommonItemTitle(\"getScreenHeight\", ScreenUtils.getScreenHeight().toString()),\n                CommonItemTitle(\"getAppScreenWidth\", ScreenUtils.getAppScreenWidth().toString()),\n                CommonItemTitle(\"getAppScreenHeight\", ScreenUtils.getAppScreenHeight().toString()),\n                CommonItemTitle(\"getScreenDensity\", ScreenUtils.getScreenDensity().toString()),\n                CommonItemTitle(\"getScreenDensityDpi\", ScreenUtils.getScreenDensityDpi().toString()),\n                CommonItemTitle(\"getScreenRotation\", ScreenUtils.getScreenRotation(this).toString()),\n                CommonItemTitle(\"isScreenLock\", ScreenUtils.isScreenLock().toString()),\n                CommonItemTitle(\"getSleepDuration\", ScreenUtils.getSleepDuration().toString()),\n\n                CommonItemSwitch(\n                        \"isFullScreen\",\n                        { ScreenUtils.isFullScreen(this) },\n                        {\n                            if (it) {\n                                ScreenUtils.setFullScreen(this)\n                                BarUtils.setStatusBarVisibility(this, false)\n                            } else {\n                                ScreenUtils.setNonFullScreen(this)\n                                BarUtils.setStatusBarVisibility(this, true)\n                            }\n                        }\n                ),\n                CommonItemSwitch(\n                        \"isLandscape\",\n                        { ScreenUtils.isLandscape() },\n                        {\n                            if (it) {\n                                ScreenUtils.setLandscape(this)\n                            } else {\n                                ScreenUtils.setPortrait(this)\n                            }\n                        }\n                ),\n                CommonItemClick(R.string.screen_screenshot) {\n                    val iv :ImageView = ImageView(this)\n                    iv.setImageResource(R.mipmap.ic_launcher)\n\n                    val tv: TextView = TextView(this)\n                    tv.setText(\"wowowowwowo\")\n\n                    DialogHelper.showScreenshotDialog(ImageUtils.view2Bitmap(tv))\n\n//                    DialogHelper.showScreenshotDialog(ScreenUtils.screenShot(this))\n                }\n        )\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/sdcard/SDCardActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.sdcard\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.ConvertUtils\nimport com.blankj.utilcode.util.SDCardUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/09/27\n * desc  : demo about SDCardUtils\n * ```\n */\nclass SDCardActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, SDCardActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_sdcard\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemTitle(\"isSDCardEnableByEnvironment\", SDCardUtils.isSDCardEnableByEnvironment().toString()),\n                CommonItemTitle(\"getSDCardPathByEnvironment\", SDCardUtils.getSDCardPathByEnvironment()),\n                CommonItemTitle(\"getSDCardInfo\", SDCardUtils.getSDCardInfo().toString()),\n                CommonItemTitle(\"getMountedSDCardPath\", SDCardUtils.getMountedSDCardPath().toString()),\n                CommonItemTitle(\"getExternalTotalSize\", ConvertUtils.byte2FitMemorySize(SDCardUtils.getExternalTotalSize(), 2)),\n                CommonItemTitle(\"getExternalAvailableSize\", ConvertUtils.byte2FitMemorySize(SDCardUtils.getExternalAvailableSize(), 2)),\n                CommonItemTitle(\"getInternalTotalSize\", ConvertUtils.byte2FitMemorySize(SDCardUtils.getInternalTotalSize(), 2)),\n                CommonItemTitle(\"getInternalAvailableSize\", ConvertUtils.byte2FitMemorySize(SDCardUtils.getInternalAvailableSize(), 2))\n        )\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/shadow/ShadowActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.shadow\n\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.Color\nimport android.os.Bundle\nimport android.view.View\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.ShadowUtils\nimport com.blankj.utilcode.util.ShadowUtils.Config\nimport com.blankj.utilcode.util.SizeUtils\nimport kotlinx.android.synthetic.main.shadow_activity.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2019/10/22\n * desc  : demo about ShadowUtils\n * ```\n */\nclass ShadowActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, ShadowActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_shadow\n    }\n\n    override fun bindLayout(): Int {\n        return R.layout.shadow_activity\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        ShadowUtils.apply(shadowRectView, Config().setShadowColor(0x44000000, 0x55000000))\n        ShadowUtils.apply(shadowRoundRectView, Config().setShadowColor(0x44000000, 0x55000000).setShadowRadius(\n                SizeUtils.dp2px(16f).toFloat()))\n        ShadowUtils.apply(shadowCircleView, Config().setCircle().setShadowColor(0x44000000, 0x55000000))\n\n        ShadowUtils.apply(shadowRectView1, Config().setShadowColor(0x44000000, 0x55000000))\n        ShadowUtils.apply(shadowRoundRectView1, Config().setShadowColor(0x44000000, 0x55000000).setShadowRadius(\n                SizeUtils.dp2px(16f).toFloat()))\n        ShadowUtils.apply(shadowCircleView1, Config().setCircle().setShadowColor(0x44000000, 0x55000000))\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/snackbar/SnackbarActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.snackbar\n\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.Color\nimport android.text.SpannableStringBuilder\nimport android.view.ViewGroup\nimport android.widget.TextView\nimport androidx.annotation.StringRes\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.SnackbarUtils\nimport com.blankj.utilcode.util.SpanUtils\nimport com.blankj.utilcode.util.ToastUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/10/17\n * desc  : demo about SnackbarUtils\n * ```\n */\nclass SnackbarActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, SnackbarActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_snackbar\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemClick(R.string.snackbar_short) {\n                    SnackbarUtils.with(mContentView)\n                            .setMessage(getMsg(R.string.snackbar_short))\n                            .setMessageColor(Color.WHITE)\n                            .setBgResource(R.drawable.snackbar_custom_bg)\n                            .show()\n                },\n                CommonItemClick(R.string.snackbar_short_top) {\n                    SnackbarUtils.with(mContentView)\n                            .setMessage(getMsg(R.string.snackbar_short_top))\n                            .setMessageColor(Color.WHITE)\n                            .setBgResource(R.drawable.snackbar_custom_bg)\n                            .show(true)\n                },\n                CommonItemClick(R.string.snackbar_short_with_action) {\n                    SnackbarUtils.with(mContentView)\n                            .setMessage(getMsg(R.string.snackbar_short_with_action))\n                            .setMessageColor(Color.WHITE)\n                            .setBgResource(R.drawable.snackbar_custom_bg)\n                            .setAction(getString(R.string.snackbar_click), Color.YELLOW) { ToastUtils.showShort(getString(R.string.snackbar_click)) }\n                            .show()\n                },\n                CommonItemClick(R.string.snackbar_short_with_action_top) {\n                    SnackbarUtils.with(mContentView)\n                            .setMessage(getMsg(R.string.snackbar_short_with_action_top))\n                            .setMessageColor(Color.WHITE)\n                            .setBgResource(R.drawable.snackbar_custom_bg)\n                            .setAction(getString(R.string.snackbar_click), Color.YELLOW) { ToastUtils.showShort(getString(R.string.snackbar_click)) }\n                            .show(true)\n                },\n                CommonItemClick(R.string.snackbar_long) {\n                    SnackbarUtils.with(mContentView)\n                            .setMessage(getMsg(R.string.snackbar_long))\n                            .setMessageColor(Color.WHITE)\n                            .setDuration(SnackbarUtils.LENGTH_LONG)\n                            .setBgResource(R.drawable.snackbar_custom_bg)\n                            .show()\n                },\n                CommonItemClick(R.string.snackbar_long_with_action) {\n                    SnackbarUtils.with(mContentView)\n                            .setMessage(getMsg(R.string.snackbar_long_with_action))\n                            .setMessageColor(Color.WHITE)\n                            .setBgResource(R.drawable.snackbar_custom_bg)\n                            .setDuration(SnackbarUtils.LENGTH_LONG)\n                            .setAction(getString(R.string.snackbar_click), Color.YELLOW) { ToastUtils.showShort(getString(R.string.snackbar_click)) }\n                            .show()\n                },\n                CommonItemClick(R.string.snackbar_indefinite) {\n                    SnackbarUtils.with(mContentView)\n                            .setMessage(getMsg(R.string.snackbar_indefinite))\n                            .setMessageColor(Color.WHITE)\n                            .setDuration(SnackbarUtils.LENGTH_INDEFINITE)\n                            .setBgResource(R.drawable.snackbar_custom_bg)\n                            .show()\n                },\n                CommonItemClick(R.string.snackbar_indefinite_with_action) {\n                    SnackbarUtils.with(mContentView)\n                            .setMessage(getMsg(R.string.snackbar_indefinite_with_action))\n                            .setMessageColor(Color.WHITE)\n                            .setDuration(SnackbarUtils.LENGTH_INDEFINITE)\n                            .setBgResource(R.drawable.snackbar_custom_bg)\n                            .setAction(getString(R.string.snackbar_click), Color.YELLOW) { ToastUtils.showShort(getString(R.string.snackbar_click)) }\n                            .show()\n                },\n                CommonItemClick(R.string.snackbar_add_view) {\n                    val params = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)\n                    SnackbarUtils.with(mContentView)\n                            .setBgColor(Color.TRANSPARENT)\n                            .setDuration(SnackbarUtils.LENGTH_INDEFINITE)\n                            .show()\n                    SnackbarUtils.addView(R.layout.snackbar_custom, params)\n                },\n                CommonItemClick(R.string.snackbar_add_view_with_action) {\n                    val params: ViewGroup.LayoutParams = ViewGroup.LayoutParams(\n                            ViewGroup.LayoutParams.MATCH_PARENT,\n                            ViewGroup.LayoutParams.MATCH_PARENT\n                    )\n                    SnackbarUtils.with(mContentView)\n                            .setBgColor(Color.TRANSPARENT)\n                            .setDuration(SnackbarUtils.LENGTH_INDEFINITE)\n                            .show()\n                    SnackbarUtils.addView(R.layout.snackbar_custom, params)\n                    val snackbarView = SnackbarUtils.getView()\n                    if (snackbarView != null) {\n                        val tvSnackbarCustom = snackbarView.findViewById<TextView>(R.id.snackbarCustomTv)\n                        tvSnackbarCustom.setText(R.string.snackbar_click_to_dismiss)\n                        snackbarView.setOnClickListener { SnackbarUtils.dismiss() }\n                    }\n                },\n                CommonItemClick(R.string.snackbar_success) {\n                    SnackbarUtils.with(mContentView)\n                            .setMessage(getMsg(R.string.snackbar_success))\n                            .showSuccess()\n                },\n                CommonItemClick(R.string.snackbar_warning) {\n                    SnackbarUtils.with(mContentView)\n                            .setMessage(getMsg(R.string.snackbar_warning))\n                            .showWarning()\n                },\n                CommonItemClick(R.string.snackbar_error) {\n                    SnackbarUtils.with(mContentView)\n                            .setMessage(getMsg(R.string.snackbar_error))\n                            .showError()\n                },\n                CommonItemClick(R.string.snackbar_dismiss) {\n                    SnackbarUtils.dismiss()\n                }\n        )\n    }\n\n    private fun getMsg(@StringRes resId: Int): SpannableStringBuilder {\n        return SpanUtils()\n                .appendImage(R.mipmap.ic_launcher, SpanUtils.ALIGN_CENTER)\n                .appendSpace(32)\n                .append(getString(resId)).setFontSize(24, true)\n                .create()\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/spStatic/SPStaticActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.spStatic\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.SPStaticUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2018/01/08\n * desc  : demo about SPUtils\n * ```\n */\nclass SPStaticActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, SPStaticActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_spStatic\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        val itemTitle = CommonItemTitle(sp2String(), true)\n        return CollectionUtils.newArrayList(\n                itemTitle,\n                CommonItemClick(R.string.sp_put_string) {\n                    SPStaticUtils.put(\"STRING\", \"string\")\n                    itemTitle.title = sp2String()\n                },\n                CommonItemClick(R.string.sp_put_int) {\n                    SPStaticUtils.put(\"INT\", 21)\n                    itemTitle.title = sp2String()\n                },\n                CommonItemClick(R.string.sp_put_long) {\n                    SPStaticUtils.put(\"LONG\", java.lang.Long.MAX_VALUE)\n                    itemTitle.title = sp2String()\n                },\n                CommonItemClick(R.string.sp_put_float) {\n                    SPStaticUtils.put(\"FLOAT\", Math.PI.toFloat())\n                    itemTitle.title = sp2String()\n                },\n                CommonItemClick(R.string.sp_put_boolean) {\n                    SPStaticUtils.put(\"BOOLEAN\", true)\n                    itemTitle.title = sp2String()\n                },\n                CommonItemClick(R.string.sp_put_string_set) {\n                    SPStaticUtils.put(\"SET\", setOf(\"1\", \"2\"))\n                    itemTitle.title = sp2String()\n                },\n                CommonItemClick(R.string.sp_clear) {\n                    SPStaticUtils.clear()\n                    itemTitle.title = sp2String()\n                }\n        )\n    }\n\n    private fun sp2String(): String {\n        val sb = StringBuilder()\n        val map = SPStaticUtils.getAll()\n        if (map.isEmpty()) return \"\"\n        for ((key, value) in map) {\n            sb.append(\"\\n\")\n                    .append(key)\n                    .append(\": \")\n                    .append(value)\n\n        }\n        return sb.deleteCharAt(0).toString()\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/span/SpanActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.span\n\nimport android.animation.ValueAnimator\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.*\nimport android.os.Bundle\nimport android.text.Layout\nimport android.text.SpannableStringBuilder\nimport android.text.TextPaint\nimport android.text.style.CharacterStyle\nimport android.text.style.ClickableSpan\nimport android.text.style.UpdateAppearance\nimport android.view.View\nimport android.view.animation.LinearInterpolator\nimport androidx.annotation.ColorInt\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.SpanUtils\nimport com.blankj.utilcode.util.ToastUtils\nimport kotlinx.android.synthetic.main.span_activity.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/09/27\n * desc  : demo about SpanUtils\n * ```\n */\nclass SpanActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, SpanActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    private lateinit var mSpanUtils: SpanUtils\n    private lateinit var animSsb: SpannableStringBuilder\n\n    private var lineHeight: Int = 0\n    private var textSize: Float = 0f\n    private lateinit var valueAnimator: ValueAnimator\n    private lateinit var mShader: Shader\n    private var mShaderWidth: Float = 0f\n    private lateinit var matrix: Matrix\n    private lateinit var mBlurMaskFilterSpan: BlurMaskFilterSpan\n    private lateinit var mShadowSpan: ShadowSpan\n    private lateinit var mForegroundAlphaColorSpan: ForegroundAlphaColorSpan\n    private lateinit var mForegroundAlphaColorSpanGroup: ForegroundAlphaColorSpanGroup\n    private lateinit var mPrinterString: String\n    private var density: Float = 0f\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_span\n    }\n\n    override fun bindLayout(): Int {\n        return R.layout.span_activity\n    }\n\n    override fun initView(savedInstanceState: Bundle?, contentView: View?) {\n        super.initView(savedInstanceState, contentView)\n        val clickableSpan = object : ClickableSpan() {\n            override fun onClick(widget: View) {\n                ToastUtils.showShort(\"事件触发了\")\n            }\n\n            override fun updateDrawState(ds: TextPaint) {\n                ds.color = Color.BLUE\n                ds.isUnderlineText = false\n            }\n        }\n\n        lineHeight = spanAboutTv.lineHeight\n        textSize = spanAboutTv.textSize\n        density = resources.displayMetrics.density\n\n        SpanUtils.with(spanAboutTv)\n                .appendLine(\"SpanUtils\").setBackgroundColor(Color.LTGRAY).setBold().setForegroundColor(Color.YELLOW).setHorizontalAlign(Layout.Alignment.ALIGN_CENTER)\n                .appendLine(\"前景色\").setForegroundColor(Color.GREEN)\n//                .appendLine(\"测试哈哈\").setForegroundColor(Color.RED).setBackgroundColor(Color.LTGRAY).setFontSize(10).setLineHeight(280, SpanUtils.ALIGN_BOTTOM)\n                .appendLine(\"背景色\").setBackgroundColor(Color.LTGRAY)\n                .appendLine(\"行高居中对齐\").setLineHeight(2 * lineHeight, SpanUtils.ALIGN_CENTER).setBackgroundColor(Color.LTGRAY)\n                .appendLine(\"行高底部对齐\").setLineHeight(2 * lineHeight, SpanUtils.ALIGN_BOTTOM).setBackgroundColor(Color.GREEN)\n                .appendLine(\"测试段落缩，首行缩进两字，其他行不缩进\").setLeadingMargin(textSize.toInt() * 2, 10).setBackgroundColor(Color.GREEN)\n                .appendLine(\"测试引用，后面的字是为了凑到两行的效果\").setQuoteColor(Color.GREEN, 10, 10).setBackgroundColor(Color.LTGRAY)\n                .appendLine(\"测试列表项，后面的字是为了凑到两行的效果\").setBullet(Color.GREEN, 20, 10).setBackgroundColor(Color.LTGRAY).setBackgroundColor(Color.GREEN)\n                .appendLine(\"32dp 字体\").setFontSize(32, true)\n                .appendLine(\"2 倍字体\").setFontProportion(2f)\n                .appendLine(\"横向 2 倍字体\").setFontXProportion(1.5f)\n                .appendLine(\"删除线\").setStrikethrough()\n                .appendLine(\"下划线\").setUnderline()\n                .append(\"测试\").appendLine(\"上标\").setSuperscript()\n                .append(\"测试\").appendLine(\"下标\").setSubscript()\n                .appendLine(\"粗体\").setBold()\n                .appendLine(\"斜体\").setItalic()\n                .appendLine(\"粗斜体\").setBoldItalic()\n                .appendLine(\"monospace 字体\").setFontFamily(\"monospace\")\n                .appendLine(\"自定义字体\").setTypeface(Typeface.createFromAsset(assets, \"fonts/dnmbhs.ttf\"))\n                .appendLine(\"相反对齐\").setHorizontalAlign(Layout.Alignment.ALIGN_OPPOSITE)\n                .appendLine(\"居中对齐\").setHorizontalAlign(Layout.Alignment.ALIGN_CENTER)\n                .appendLine(\"正常对齐\").setHorizontalAlign(Layout.Alignment.ALIGN_NORMAL)\n                .append(\"测试\").appendLine(\"点击事件\").setClickSpan(clickableSpan)\n                .append(\"测试\").appendLine(\"Url\").setUrl(\"https://github.com/Blankj/AndroidUtilCode\")\n                .append(\"测试\").appendLine(\"模糊\").setBlur(3f, BlurMaskFilter.Blur.NORMAL)\n                .appendLine(\"颜色渐变\").setShader(LinearGradient(0f, 0f, 64f * density * 4f, 0f, resources.getIntArray(R.array.rainbow), null, Shader.TileMode.REPEAT)).setFontSize(64, true)\n                .appendLine(\"图片着色\").setFontSize(64, true).setShader(BitmapShader(BitmapFactory.decodeResource(resources, R.drawable.span_cheetah), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT))\n                .appendLine(\"阴影效果\").setFontSize(64, true).setBackgroundColor(Color.BLACK).setShadow(24f, 8f, 8f, Color.WHITE)\n\n                .append(\"小图\").setBackgroundColor(Color.GREEN)\n                .appendImage(R.drawable.span_block_low, SpanUtils.ALIGN_TOP)\n                .append(\"顶部\").setBackgroundColor(Color.GREEN)\n                .appendImage(R.drawable.span_block_low, SpanUtils.ALIGN_CENTER)\n                .append(\"居中\").setBackgroundColor(Color.GREEN)\n                .appendImage(R.drawable.span_block_low, SpanUtils.ALIGN_BASELINE)\n                .append(\"底部\").setBackgroundColor(Color.GREEN)\n                .appendImage(R.drawable.span_block_low, SpanUtils.ALIGN_BOTTOM)\n                .appendLine(\"对齐\").setBackgroundColor(Color.GREEN)\n                .appendImage(R.drawable.span_block_high, SpanUtils.ALIGN_TOP)\n                .append(\"大图\").setBackgroundColor(Color.LTGRAY)\n                .appendImage(R.drawable.span_block_high, SpanUtils.ALIGN_TOP)\n                .append(\"顶部\").setBackgroundColor(Color.LTGRAY)\n                .appendImage(R.drawable.span_block_high, SpanUtils.ALIGN_TOP)\n                .appendLine(\"对齐\").setBackgroundColor(Color.LTGRAY)\n\n                .appendImage(R.drawable.span_block_high, SpanUtils.ALIGN_CENTER)\n                .append(\"大图\").setBackgroundColor(Color.GREEN)\n                .appendImage(R.drawable.span_block_high, SpanUtils.ALIGN_CENTER)\n                .append(\"居中\").setBackgroundColor(Color.GREEN)\n                .appendImage(R.drawable.span_block_high, SpanUtils.ALIGN_CENTER)\n                .appendLine(\"对齐\").setBackgroundColor(Color.GREEN)\n\n                .appendImage(R.drawable.span_block_high, SpanUtils.ALIGN_BOTTOM)\n                .append(\"大图\").setBackgroundColor(Color.LTGRAY)\n                .appendImage(R.drawable.span_block_high, SpanUtils.ALIGN_BOTTOM)\n                .append(\"底部\").setBackgroundColor(Color.LTGRAY)\n                .appendImage(R.drawable.span_block_high, SpanUtils.ALIGN_BOTTOM)\n                .appendLine(\"对齐\").setBackgroundColor(Color.LTGRAY)\n\n                .append(\"测试空格\").appendSpace(30, Color.LTGRAY).appendSpace(50, Color.GREEN).appendSpace(100).appendSpace(30, Color.LTGRAY).appendSpace(50, Color.GREEN)\n                .create()\n\n        //        initAnimSpan();\n        //        startAnim();\n    }\n\n    private fun initAnimSpan() {\n        mShaderWidth = 64f * density * 4f\n        mShader = LinearGradient(0f, 0f,\n                mShaderWidth, 0f,\n                resources.getIntArray(R.array.rainbow), null,\n                Shader.TileMode.REPEAT)\n        matrix = Matrix()\n\n        mBlurMaskFilterSpan = BlurMaskFilterSpan(25f)\n\n        mShadowSpan = ShadowSpan(8f, 8f, 8f, Color.WHITE)\n\n        mForegroundAlphaColorSpan = ForegroundAlphaColorSpan(Color.TRANSPARENT)\n\n        mForegroundAlphaColorSpanGroup = ForegroundAlphaColorSpanGroup(0f)\n\n        mPrinterString = \"打印动画，后面的文字是为了测试打印效果...\"\n\n        mSpanUtils = SpanUtils()\n                .appendLine(\"彩虹动画\").setFontSize(64, true).setShader(mShader)\n                .appendLine(\"模糊动画\").setFontSize(64, true).setSpans(mBlurMaskFilterSpan)\n                .appendLine(\"阴影动画\").setFontSize(64, true).setBackgroundColor(Color.BLACK).setSpans(mShadowSpan)\n                .appendLine(\"透明动画\").setFontSize(64, true).setSpans(mForegroundAlphaColorSpan)\n        var i = 0\n        val len = mPrinterString.length\n        while (i < len) {\n            val span = ForegroundAlphaColorSpan(Color.TRANSPARENT)\n            mSpanUtils.append(mPrinterString.substring(i, i + 1)).setSpans(span)\n            mForegroundAlphaColorSpanGroup.addSpan(span)\n            ++i\n        }\n        animSsb = mSpanUtils.create()\n    }\n\n    private fun startAnim() {\n        valueAnimator = ValueAnimator.ofFloat(0f, 1f)\n        valueAnimator.addUpdateListener { animation ->\n            // shader\n            matrix.reset()\n            matrix.setTranslate(animation.animatedValue as Float * mShaderWidth, 0f)\n            mShader.setLocalMatrix(matrix)\n\n            // blur\n            mBlurMaskFilterSpan.radius = 25 * (1.00001f - animation.animatedValue as Float)\n\n            // shadow\n            mShadowSpan.dx = 16 * (0.5f - animation.animatedValue as Float)\n            mShadowSpan.dy = 16 * (0.5f - animation.animatedValue as Float)\n\n            // alpha\n            mForegroundAlphaColorSpan.setAlpha((255 * animation.animatedValue as Float).toInt())\n\n            // printer\n            mForegroundAlphaColorSpanGroup.alpha = animation.animatedValue as Float\n\n            // showMsg\n            spanAboutAnimTv.text = animSsb\n        }\n\n        valueAnimator.interpolator = LinearInterpolator()\n        valueAnimator.duration = (600 * 3).toLong()\n        valueAnimator.repeatCount = ValueAnimator.INFINITE\n        valueAnimator.start()\n    }\n\n    override fun doBusiness() {}\n\n    override fun onDebouncingClick(view: View) {}\n\n//    override fun onDestroy() {\n//        if (valueAnimator.isRunning) {\n//            valueAnimator.cancel()\n//        }\n//        super.onDestroy()\n//    }\n}\n\nclass BlurMaskFilterSpan(private var mRadius: Float) : CharacterStyle(), UpdateAppearance {\n    private var mFilter: MaskFilter? = null\n\n    var radius: Float\n        get() = mRadius\n        set(radius) {\n            mRadius = radius\n            mFilter = BlurMaskFilter(mRadius, BlurMaskFilter.Blur.NORMAL)\n        }\n\n    override fun updateDrawState(ds: TextPaint) {\n        ds.maskFilter = mFilter\n    }\n}\n\nclass ForegroundAlphaColorSpan(@param:ColorInt private var mColor: Int) : CharacterStyle(), UpdateAppearance {\n\n    fun setAlpha(alpha: Int) {\n        mColor = Color.argb(alpha, Color.red(mColor), Color.green(mColor), Color.blue(mColor))\n    }\n\n    override fun updateDrawState(ds: TextPaint) {\n        ds.color = mColor\n    }\n}\n\nclass ForegroundAlphaColorSpanGroup(private val mAlpha: Float) {\n\n    private val mSpans: ArrayList<ForegroundAlphaColorSpan> = ArrayList()\n\n    var alpha: Float\n        get() = mAlpha\n        set(alpha) {\n            val size = mSpans.size\n            var total = 1.0f * size.toFloat() * alpha\n            for (index in 0 until size) {\n                val span = mSpans[index]\n                if (total >= 1.0f) {\n                    span.setAlpha(255)\n                    total -= 1.0f\n                } else {\n                    span.setAlpha((total * 255).toInt())\n                    total = 0.0f\n                }\n            }\n        }\n\n    fun addSpan(span: ForegroundAlphaColorSpan) {\n        span.setAlpha((mAlpha * 255).toInt())\n        mSpans.add(span)\n    }\n}\n\nclass ShadowSpan(private val radius: Float, var dx: Float, var dy: Float, private val shadowColor: Int) : CharacterStyle(), UpdateAppearance {\n\n    override fun updateDrawState(tp: TextPaint) {\n        tp.setShadowLayer(radius, dx, dy, shadowColor)\n    }\n}"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/toast/CustomToast.kt",
    "content": "package com.blankj.utilcode.pkg.feature.toast\n\nimport android.widget.TextView\nimport androidx.annotation.StringRes\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.StringUtils\nimport com.blankj.utilcode.util.ToastUtils\nimport com.blankj.utilcode.util.ViewUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2017/08/31\n * desc  : demo about ToastUtils\n * ```\n */\nobject CustomToast {\n\n    fun showShort(text: CharSequence) {\n        show(text, false)\n    }\n\n    fun showShort(@StringRes resId: Int) {\n        show(StringUtils.getString(resId), false)\n    }\n\n    fun showShort(@StringRes resId: Int, vararg args: Any) {\n        show(StringUtils.getString(resId, args), false)\n    }\n\n    fun showShort(format: String, vararg args: Any) {\n        show(StringUtils.format(format, args), false)\n    }\n\n    fun showLong(text: CharSequence) {\n        show(text, true)\n    }\n\n    fun showLong(@StringRes resId: Int) {\n        show(StringUtils.getString(resId), true)\n    }\n\n    fun showLong(@StringRes resId: Int, vararg args: Any) {\n        show(StringUtils.getString(resId, args), true)\n    }\n\n    fun showLong(format: String, vararg args: Any) {\n        show(StringUtils.format(format, args), true)\n    }\n\n    private fun show(text: CharSequence, isLong: Boolean) {\n        val textView = ViewUtils.layoutId2View(R.layout.toast_custom) as TextView\n        textView.text = text\n        ToastUtils.make().setDurationIsLong(isLong).show(textView)\n    }\n\n    fun cancel() {\n        ToastUtils.cancel()\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/toast/ToastActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.toast\n\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.Color\nimport android.view.Gravity\nimport androidx.core.content.ContextCompat\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.pkg.helper.DialogHelper\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.ColorUtils\nimport com.blankj.utilcode.util.SpanUtils\nimport com.blankj.utilcode.util.ToastUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2016/09/29\n * desc  : demo about ToastUtils\n * ```\n */\nclass ToastActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, ToastActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_toast\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemClick(R.string.toast_show_short) {\n                    Thread(Runnable { ToastUtils.showShort(R.string.toast_short) }).start()\n                },\n                CommonItemClick(R.string.toast_show_long) {\n                    Thread(Runnable { ToastUtils.showLong(R.string.toast_long) }).start()\n                },\n                CommonItemClick(R.string.toast_show_null) {\n                    ToastUtils.showLong(null)\n                },\n                CommonItemClick(R.string.toast_show_empty) {\n                    ToastUtils.showLong(\"\")\n                },\n                CommonItemClick(R.string.toast_show_span) {\n                    ToastUtils.showLong(\n                            SpanUtils()\n                                    .appendImage(R.mipmap.ic_launcher, SpanUtils.ALIGN_CENTER)\n                                    .appendSpace(32)\n                                    .append(getString(R.string.toast_span)).setFontSize(24, true)\n                                    .create()\n                    )\n                },\n                CommonItemClick(R.string.toast_show_long_string) {\n                    ToastUtils.showLong(R.string.toast_long_string)\n                },\n                CommonItemClick(R.string.toast_show_green_font) {\n                    ToastUtils.make().setTextColor(Color.GREEN).setDurationIsLong(true).show(R.string.toast_green_font)\n                },\n                CommonItemClick(R.string.toast_show_bg_color) {\n                    ToastUtils.make().setBgColor(ColorUtils.getColor(R.color.colorAccent)).show(R.string.toast_bg_color)\n                },\n                CommonItemClick(R.string.toast_show_bg_resource) {\n                    ToastUtils.make().setBgResource(R.drawable.toast_round_rect).show(R.string.toast_custom_bg)\n                },\n                CommonItemClick(R.string.toast_show_left_icon) {\n                    ToastUtils.make().setLeftIcon(R.mipmap.ic_launcher).show(R.string.toast_show_left_icon)\n                },\n                CommonItemClick(R.string.toast_show_dark_mode) {\n                    ToastUtils.make().setTopIcon(R.mipmap.ic_launcher).setMode(ToastUtils.MODE.DARK).show(R.string.toast_show_dark_mode)\n                },\n                CommonItemClick(R.string.toast_show_middle) {\n                    ToastUtils.make().setGravity(Gravity.CENTER, 0, 0).show(R.string.toast_middle)\n                },\n                CommonItemClick(R.string.toast_show_top) {\n                    ToastUtils.make().setGravity(Gravity.TOP or Gravity.CENTER_HORIZONTAL, 0, 0).show(R.string.toast_top)\n                },\n                CommonItemClick(R.string.toast_show_custom_view) {\n                    Thread(Runnable { CustomToast.showLong(R.string.toast_custom_view) }).start()\n                },\n                CommonItemClick(R.string.toast_cancel) {\n                    ToastUtils.cancel()\n                },\n                CommonItemClick(R.string.toast_show_toast_dialog) {\n                    DialogHelper.showToastDialog()\n                },\n                CommonItemClick(R.string.toast_show_toast_when_start_activity) {\n                    ToastUtils.showLong(R.string.toast_show_toast_when_start_activity)\n                    start(this)\n                }\n        )\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/uiMessage/UiMessageActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.uiMessage\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.common.item.CommonItemTitle\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.UiMessageUtils\n\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2020/04/14\n * desc  : demo about UiMessageUtils\n * ```\n */\nclass UiMessageActivity : CommonActivity(), UiMessageUtils.UiMessageCallback {\n\n    private val titleItem: CommonItemTitle = CommonItemTitle(\"\", true);\n    private var sendContent: String = \"\"\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, UiMessageActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_uiMessage\n    }\n\n    override fun bindItems(): List<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                titleItem,\n                CommonItemClick(R.string.uiMessage_add_listener_id) {\n                    UiMessageUtils.getInstance().addListener(R.id.utilCodeUiMessageAddListenerId, this)\n                },\n                CommonItemClick(R.string.uiMessage_remove_all_id) {\n                    UiMessageUtils.getInstance().removeListeners(R.id.utilCodeUiMessageAddListenerId)\n                },\n                CommonItemClick(R.string.uiMessage_add_listener) {\n                    UiMessageUtils.getInstance().addListener(this)\n                },\n                CommonItemClick(R.string.uiMessage_remove_listener) {\n                    UiMessageUtils.getInstance().removeListener(this)\n                },\n                CommonItemClick(R.string.uiMessage_send) {\n                    sendContent = \"send: UiMessageActivity#${UiMessageActivity.hashCode()}\"\n                    titleItem.title = \"\"\n                    UiMessageUtils.getInstance().send(R.id.utilCodeUiMessageAddListenerId, UiMessageActivity)\n                }\n        )\n    }\n\n    override fun handleMessage(localMessage: UiMessageUtils.UiMessage) {\n        if (localMessage.id == R.id.utilCodeUiMessageAddListenerId) {\n            var content: String = sendContent\n            content += \"\\nreceive: UiMessageActivity#${localMessage.getObject().hashCode()}\"\n            titleItem.title = if (titleItem.title.toString().isEmpty()) {\n                content\n            } else {\n                titleItem.title.toString() + \"\\n\" + content\n            }\n        }\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        UiMessageUtils.getInstance().removeListeners(R.id.utilCodeUiMessageAddListenerId)\n        UiMessageUtils.getInstance().removeListener(this)\n    }\n}"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/vibrate/VibrateActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.vibrate\n\nimport android.content.Context\nimport android.content.Intent\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemClick\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.VibrateUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2018/12/29\n * desc  : demo about VibrateUtils\n * ```\n */\nclass VibrateActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, VibrateActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_vibrate\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                CommonItemClick(R.string.vibrate_1000ms) {\n                    VibrateUtils.vibrate(1000)\n                },\n                CommonItemClick(R.string.vibrate_custom) {\n                    VibrateUtils.vibrate(longArrayOf(0, 1000, 1000, 2000, 2000, 1000), 1)\n                },\n                CommonItemClick(R.string.vibrate_background) {\n                    backHome()\n                    mContentView.postDelayed({\n//                        VibrateUtils.vibrate(1000) -- can not vibrate in background\n                        VibrateUtils.vibrateCompat(longArrayOf(0, 1000, 1000, 2000, 2000, 1000), 1)\n//                        VibrateUtils.vibrateCompat(1000)\n                    }, 1000)\n                },\n                CommonItemClick(R.string.vibrate_cancel) {\n                    VibrateUtils.cancel()\n                }\n        )\n    }\n\n    override fun onDestroy() {\n        super.onDestroy()\n        VibrateUtils.cancel()\n    }\n\n    private fun backHome() {\n        val intent = Intent(Intent.ACTION_MAIN).apply {\n            addCategory(Intent.CATEGORY_HOME)\n        }\n        startActivity(intent)\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/volume/VolumeActivity.kt",
    "content": "package com.blankj.utilcode.pkg.feature.volume\n\nimport android.content.Context\nimport android.content.Intent\nimport android.media.AudioManager\nimport android.widget.SeekBar\nimport com.blankj.common.activity.CommonActivity\nimport com.blankj.common.item.CommonItem\nimport com.blankj.common.item.CommonItemSeekBar\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.CollectionUtils\nimport com.blankj.utilcode.util.VolumeUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2018/12/29\n * desc  : demo about VibrateUtils\n * ```\n */\nclass VolumeActivity : CommonActivity() {\n\n    companion object {\n        fun start(context: Context) {\n            val starter = Intent(context, VolumeActivity::class.java)\n            context.startActivity(starter)\n        }\n    }\n\n    override fun bindTitleRes(): Int {\n        return R.string.demo_volume\n    }\n\n    override fun bindItems(): MutableList<CommonItem<*>> {\n        return CollectionUtils.newArrayList(\n                getItemSeekBar(\"Voice Call\", AudioManager.STREAM_VOICE_CALL),\n                getItemSeekBar(\"System\", AudioManager.STREAM_SYSTEM),\n                getItemSeekBar(\"Music\", AudioManager.STREAM_MUSIC),\n                getItemSeekBar(\"Ring\", AudioManager.STREAM_RING),\n                getItemSeekBar(\"Alarm\", AudioManager.STREAM_ALARM),\n                getItemSeekBar(\"Notification\", AudioManager.STREAM_NOTIFICATION),\n                getItemSeekBar(\"Dtmf\", AudioManager.STREAM_DTMF)\n        )\n    }\n\n    private fun getItemSeekBar(title: CharSequence, streamType: Int): CommonItemSeekBar {\n        return CommonItemSeekBar(title, VolumeUtils.getMaxVolume(streamType), object : CommonItemSeekBar.ProgressListener() {\n            override fun getCurValue(): Int {\n                return VolumeUtils.getVolume(streamType)\n            }\n\n            override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {\n                VolumeUtils.setVolume(streamType, progress,  AudioManager.FLAG_SHOW_UI)\n            }\n        })\n    }\n\n    override fun onResume() {\n        super.onResume()\n        itemsView.updateItems(bindItems())\n    }\n}\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/helper/DialogHelper.kt",
    "content": "package com.blankj.utilcode.pkg.helper\n\nimport android.content.Context\nimport android.content.DialogInterface\nimport android.graphics.Bitmap\nimport android.graphics.drawable.ColorDrawable\nimport android.text.method.ScrollingMovementMethod\nimport android.view.Gravity\nimport android.view.View\nimport android.view.Window\nimport android.widget.Button\nimport android.widget.EditText\nimport android.widget.ImageView\nimport android.widget.TextView\nimport androidx.fragment.app.FragmentActivity\nimport com.blankj.base.dialog.BaseDialogFragment\nimport com.blankj.base.dialog.DialogLayoutCallback\nimport com.blankj.utilcode.pkg.R\nimport com.blankj.utilcode.util.ActivityUtils\nimport com.blankj.utilcode.util.KeyboardUtils\nimport com.blankj.utilcode.util.ScreenUtils\nimport com.blankj.utilcode.util.ToastUtils\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2018/01/10\n * desc  : helper about dialog\n * ```\n */\nobject DialogHelper {\n\n    fun showKeyboardDialog(context: Context) {\n        BaseDialogFragment().init(context, object : DialogLayoutCallback {\n            override fun bindTheme(): Int {\n                return View.NO_ID\n            }\n\n            override fun bindLayout(): Int {\n                return R.layout.keyboard_dialog\n            }\n\n            override fun initView(dialog: BaseDialogFragment, contentView: View) {\n                dialog.dialog.setCanceledOnTouchOutside(false)\n\n                val keyboardDialogEt = contentView.findViewById<EditText>(R.id.keyboardDialogEt)\n                val listener = View.OnClickListener { v ->\n                    when (v.id) {\n                        R.id.keyboardDialogHideSoftInputBtn -> KeyboardUtils.hideSoftInput(keyboardDialogEt)\n                        R.id.keyboardDialogShowSoftInputBtn -> KeyboardUtils.showSoftInput(keyboardDialogEt)\n                        R.id.keyboardDialogToggleSoftInputBtn -> KeyboardUtils.toggleSoftInput()\n                        R.id.keyboardDialogCloseBtn -> {\n                            KeyboardUtils.hideSoftInput(keyboardDialogEt)\n                            dialog.dismiss()\n                        }\n                    }\n                }\n                contentView.findViewById<View>(R.id.keyboardDialogHideSoftInputBtn).setOnClickListener(listener)\n                contentView.findViewById<View>(R.id.keyboardDialogShowSoftInputBtn).setOnClickListener(listener)\n                contentView.findViewById<View>(R.id.keyboardDialogToggleSoftInputBtn).setOnClickListener(listener)\n                contentView.findViewById<View>(R.id.keyboardDialogCloseBtn).setOnClickListener(listener)\n\n                dialog.dialog.setOnShowListener(DialogInterface.OnShowListener {\n                    KeyboardUtils.fixAndroidBug5497(dialog.dialog.window!!)\n                    KeyboardUtils.showSoftInput()\n                })\n            }\n\n            override fun setWindowStyle(window: Window) {\n                window.setBackgroundDrawable(ColorDrawable(0))\n                val attributes = window.attributes\n                attributes.gravity = Gravity.BOTTOM\n                attributes.width = ScreenUtils.getAppScreenWidth()\n                attributes.height = ScreenUtils.getAppScreenHeight() * 2 / 5\n                attributes.windowAnimations = R.style.BottomDialogAnimation\n                window.attributes = attributes\n            }\n\n            override fun onCancel(dialog: BaseDialogFragment) {}\n\n            override fun onDismiss(dialog: BaseDialogFragment) {}\n        }).show()\n    }\n\n    fun showFragmentDialog(info: CharSequence) {\n        val topActivity = ActivityUtils.getTopActivity() ?: return\n        BaseDialogFragment().init(topActivity as FragmentActivity, object : DialogLayoutCallback {\n            override fun bindTheme(): Int {\n                return R.style.CommonContentDialogStyle\n            }\n\n            override fun bindLayout(): Int {\n                return R.layout.fragment_dialog\n            }\n\n            override fun initView(dialog: BaseDialogFragment, contentView: View) {\n                val aboutTv = contentView.findViewById<TextView>(R.id.fragmentDialogAboutTv)\n                aboutTv.movementMethod = ScrollingMovementMethod.getInstance()\n                aboutTv.text = info\n            }\n\n            override fun setWindowStyle(window: Window) {}\n\n            override fun onCancel(dialog: BaseDialogFragment) {}\n\n            override fun onDismiss(dialog: BaseDialogFragment) {}\n        }).show()\n    }\n\n    fun showScreenshotDialog(screenshot: Bitmap) {\n        val topActivity = ActivityUtils.getTopActivity() ?: return\n        BaseDialogFragment().init(topActivity as FragmentActivity, object : DialogLayoutCallback {\n            override fun bindTheme(): Int {\n                return R.style.CommonContentDialogStyle\n            }\n\n            override fun bindLayout(): Int {\n                return R.layout.screen_dialog\n            }\n\n            override fun initView(dialog: BaseDialogFragment, contentView: View) {\n                contentView.findViewById<ImageView>(R.id.screenDialogScreenshotIv)\n                        .setImageBitmap(screenshot)\n            }\n\n            override fun setWindowStyle(window: Window) {}\n\n            override fun onCancel(dialog: BaseDialogFragment) {}\n\n            override fun onDismiss(dialog: BaseDialogFragment) {}\n        }).show()\n    }\n\n    fun showToastDialog() {\n        val topActivity = ActivityUtils.getTopActivity() ?: return\n        BaseDialogFragment().init(topActivity as FragmentActivity, object : DialogLayoutCallback {\n            override fun bindTheme(): Int {\n                return R.style.CommonContentDialogStyle\n            }\n\n            override fun bindLayout(): Int {\n                return R.layout.toast_dialog\n            }\n\n            override fun initView(dialog: BaseDialogFragment, contentView: View) {\n                contentView.findViewById<Button>(R.id.toastDialogShowShortToastBtn)\n                        .setOnClickListener { ToastUtils.showShort(\"Short\") }\n            }\n\n            override fun setWindowStyle(window: Window) {}\n\n            override fun onCancel(dialog: BaseDialogFragment) {}\n\n            override fun onDismiss(dialog: BaseDialogFragment) {}\n        }).show()\n    }\n}"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/anim/fade_in_1000.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<alpha xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"1000\"\n    android:fromAlpha=\"0.0\"\n    android:interpolator=\"@android:interpolator/accelerate_quad\"\n    android:toAlpha=\"1.0\" />"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/anim/fade_out_1000.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<alpha xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"1000\"\n    android:fromAlpha=\"1.0\"\n    android:interpolator=\"@android:interpolator/accelerate_quad\"\n    android:toAlpha=\"0.0\" />"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/anim/slide_bottom_in_200.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<translate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"200\"\n    android:fromYDelta=\"100%\"\n    android:toYDelta=\"0\" />"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/anim/slide_bottom_out_200.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<translate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"200\"\n    android:fromYDelta=\"0\"\n    android:toYDelta=\"100%\" />"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/anim/slide_left_out_1000.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <translate\n        android:duration=\"1000\"\n        android:fromXDelta=\"0\"\n        android:toXDelta=\"-100%\" />\n    <alpha\n        android:duration=\"1000\"\n        android:fromAlpha=\"1.0\"\n        android:toAlpha=\"0.0\" />\n</set>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/anim/slide_right_in_1000.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <translate\n        android:duration=\"1000\"\n        android:fromXDelta=\"100%\"\n        android:toXDelta=\"0\" />\n    <alpha\n        android:duration=\"1000\"\n        android:fromAlpha=\"0.0\"\n        android:toAlpha=\"1.0\" />\n</set>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/animator/fragment_slide_left_enter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <objectAnimator\n        android:duration=\"@android:integer/config_mediumAnimTime\"\n        android:interpolator=\"@android:interpolator/decelerate_quint\"\n        android:propertyName=\"translationX\"\n        android:valueFrom=\"100dp\"\n        android:valueTo=\"0dp\"\n        android:valueType=\"floatType\" />\n    <objectAnimator\n        android:duration=\"@android:integer/config_mediumAnimTime\"\n        android:interpolator=\"@android:interpolator/decelerate_quint\"\n        android:propertyName=\"alpha\"\n        android:valueFrom=\"0.0\"\n        android:valueTo=\"1.0\"\n        android:valueType=\"floatType\" />\n</set>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/animator/fragment_slide_left_exit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <objectAnimator\n        android:duration=\"@android:integer/config_mediumAnimTime\"\n        android:interpolator=\"@android:interpolator/decelerate_quint\"\n        android:propertyName=\"translationX\"\n        android:valueFrom=\"0dp\"\n        android:valueTo=\"-100dp\"\n        android:valueType=\"floatType\" />\n    <objectAnimator\n        android:duration=\"@android:integer/config_mediumAnimTime\"\n        android:interpolator=\"@android:interpolator/decelerate_quint\"\n        android:propertyName=\"alpha\"\n        android:valueFrom=\"1.0\"\n        android:valueTo=\"0.0\"\n        android:valueType=\"floatType\" />\n</set>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/animator/fragment_slide_right_enter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <objectAnimator\n        android:duration=\"@android:integer/config_mediumAnimTime\"\n        android:interpolator=\"@android:interpolator/decelerate_quint\"\n        android:propertyName=\"translationX\"\n        android:valueFrom=\"-100dp\"\n        android:valueTo=\"0dp\"\n        android:valueType=\"floatType\" />\n    <objectAnimator\n        android:duration=\"@android:integer/config_mediumAnimTime\"\n        android:interpolator=\"@android:interpolator/decelerate_quint\"\n        android:propertyName=\"alpha\"\n        android:valueFrom=\"0.0\"\n        android:valueTo=\"1.0\"\n        android:valueType=\"floatType\" />\n</set>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/animator/fragment_slide_right_exit.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <objectAnimator\n        android:duration=\"@android:integer/config_mediumAnimTime\"\n        android:interpolator=\"@android:interpolator/decelerate_quint\"\n        android:propertyName=\"translationX\"\n        android:valueFrom=\"0dp\"\n        android:valueTo=\"100dp\"\n        android:valueType=\"floatType\" />\n    <objectAnimator\n        android:duration=\"@android:integer/config_mediumAnimTime\"\n        android:interpolator=\"@android:interpolator/decelerate_quint\"\n        android:propertyName=\"alpha\"\n        android:valueFrom=\"1.0\"\n        android:valueTo=\"0.0\"\n        android:valueType=\"floatType\" />\n</set>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/drawable/bar_status_custom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <gradient\n        android:angle=\"180\"\n        android:centerColor=\"@android:color/transparent\"\n        android:endColor=\"@color/colorPrimary\"\n        android:startColor=\"@color/colorPrimary\" />\n\n</shape>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/drawable/bar_status_nav_alpha.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"35dp\"\n    android:height=\"24dp\"\n    android:viewportHeight=\"1024\"\n    android:viewportWidth=\"1502\">\n\n    <path\n        android:fillColor=\"#FFFFFFFF\"\n        android:pathData=\"M233.943037 703.44792C234.266942 737.923809 237.735488 769.333798 249.261858\n799.333221 260.328071 828.131582 276.43307 853.749948 303.161054 871.496116\n338.387972 894.888692 377.260237 905.731381 419.439129 897.396596 445.204137\n892.30611 471.237619 885.558213 495.136198 875.167736 530.919182 859.610024\n564.300969 839.24394 594.177963 814.046664 620.816455 791.581329 649.144577\n770.736064 673.742025 746.324979 706.137354 714.174446 735.807718 679.401915\n766.253542 645.419719 778.651349 631.581652 790.733852 617.363978 801.678558\n602.428573 823.199491 573.068059 844.047083 543.234589 864.734942 513.309845\n877.938108 494.211195 888.184013 474.42386 880.931548 449.848898 869.228311\n410.189172 859.067666 370.097981 847.208859 330.485971 832.04984 279.85702\n814.125377 230.339933 783.798807 185.840721 765.716659 159.307558 744.104118\n137.470755 714.74701 122.375623 688.366277 108.81137 660.261919 107.129065\n632.600652 110.750901 580.755334 117.536137 538.227007 144.623154 499.763838\n177.487267 465.742842 206.557373 434.091095 238.131233 408.534903 274.704305\n383.195989 310.9683 357.627031 347.232294 335.025916 385.118434 305.756115\n434.185386 282.117411 485.982204 264.408112 540.305588 246.788304 594.353084\n235.023289 649.236547 233.943037 703.44792M977.186504 747.826812C972.373531\n753.539606 968.998709 758.171656 964.988979 762.20422 940.683421 786.65887\n917.624265 812.515785 891.460878 834.933412 861.502857 860.601569 829.699841\n884.51481 796.889067 906.648253 773.286543 922.566902 746.703412 934.287048\n721.479588 947.932199 662.588504 979.788173 598.780006 998.233396 533.456698\n1011.907592 510.793804 1016.649579 487.351082 1018.051851 464.193901 1020.298386\n445.257381 1022.136267 426.182425 1024.380726 407.228839 1023.94511 365.616661\n1022.98468 324.24736 1019.460343 283.702531 1008.941252 233.178226 995.83336\n185.843274 976.120704 142.820659 947.08793 87.447134 909.716229 45.508934\n861.783954 21.897945 799.660969 10.441886 769.520489 4.295135 738.521228\n1.310227 706.374845-3.321646 656.496814 4.906629 608.421406 16.888991 560.497426\n30.000481 508.053413 50.626562 458.165012 75.501019 410.261776 101.323503\n360.533103 131.360299 312.990804 168.83496 270.443568 181.854842 255.659589\n197.045807 242.518508 208.800173 226.884042 229.404957 199.477578 257.238723\n179.862414 283.372346 158.639618 329.34369 121.305256 380.28774 91.309984\n434.108093 65.477957 495.643319 35.943189 559.750055 14.996288 627.534153\n5.566234 654.67765 1.788822 682.387861-0.445262 709.755053 0.164601 755.04036\n1.17274 798.84062 9.845648 840.90879 28.008756 868.906591 40.09814 894.637535\n54.821959 918.54683 73.047302 953.984611 100.059639 981.179237 133.875883\n1001.867027 172.394712 1015.191632 197.204074 1026.061796 223.264281 1038.670533\n250.129343 1044.949372 241.126612 1051.701132 231.798207 1058.080043 222.231254\n1085.327913 181.366323 1112.420282 140.401831 1139.661735 99.532749 1153.035352\n79.469525 1166.890492 59.705003 1179.94662 39.450938 1184.478353 32.420927\n1190.799719 32.904252 1197.408742 32.902176 1270.195514 32.868987 1342.982217\n32.881432 1415.76892 32.879356 1422.870115 32.879356 1430.018138 32.383587\n1437.05544 33.034933 1441.574407 33.453955 1445.929271 35.551138 1451.879973\n37.357905 1438.864324 56.966851 1426.928858 74.704718 1415.244802 92.598162\n1394.41209 124.501845 1373.866968 156.583925 1352.951113 188.435748 1325.933355\n229.576568 1298.60241 270.518247 1271.533456 311.625871 1244.068307 353.330913\n1216.875797 395.206059 1189.342454 436.869612 1171.162281 464.3798 1152.935211\n491.875463 1133.990158 518.885732 1128.408073 526.845053 1127.355605 533.856401\n1130.376759 542.759558 1152.460105 607.821913 1174.219615 672.987992 1196.145343\n738.100131 1207.269033 771.130197 1219.598646 803.602252 1239.860411 832.686878\n1262.589314 865.314517 1292.592047 888.333712 1330.906131 901.6034 1356.820291\n910.581243 1383.4652 914.406364 1410.623578 914.155363 1438.137808 913.902294\n1465.634903 911.910904 1493.142716 910.767933 1494.819513 910.69948 1496.530372\n911.392315 1501.771554 912.437794 1488.063452 925.122514 1476.364515 936.552252\n1464.007188 947.262177 1436.047683 971.496947 1403.748195 988.983821 1368.747224\n1001.176916 1344.252033 1009.710842 1318.989846 1015.714044 1292.500439\n1016.624691 1244.068307 1018.292476 1197.383212 1011.26039 1152.926678 992.40444\n1116.830575 977.095654 1085.743426 954.821153 1060.583496 925.164004 1033.248251\n892.945018 1014.119981 856.295191 1000.627042 816.822163 993.96689 797.339756\n988.25695 777.552421 982.001593 757.939332 981.174936 755.350524 979.664359\n752.967082 977.186504 747.826812\" />\n</vector>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/drawable/bar_status_nav_color.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportHeight=\"1024\"\n    android:viewportWidth=\"1024\">\n\n    <path\n        android:fillColor=\"#FFFFFFFF\"\n        android:pathData=\"M981.1 215.4c-10.2 0-19.3 5.7-26.6 12.3-8.6 7.7-15.6 17.2-22.1 26.7-5.9 8.5-11.2\n17.4-17.3 25.8-10.3 14.2-20.8 28.2-31.3 42.3-10.6 14.3-21.2 28.7-31.8 43-10.6\n14.3-21.2 28.7-31.8 43-10.3 14-20.7 27.9-31.1 41.8-10.4 13.8-20.9 27.6-31.3\n41.3-10.4 13.7-20.9 27.4-31.2 41.2-10.4 13.8-20.7 27.6-30.8 41.5-10.2 13.9-20.2\n27.8-30 41.9-5.9 8.4-11.6 16.9-17.5 25.3-4.2 6-8.4 12.1-12.5 18.1-5.3 7.8-10.5\n15.7-15.6 23.6-5.5 8.6-10.9 17.3-16 26.2-4.9 8.4-9.5 17-13.6 25.9-3.4 7.3-6.5\n14.8-8.4 22.6-1.3 5.2-2.4 11.2-0.5 16.4 1.5 4 4.8 6.4 9 6.5 5.4 0.2 10.8-2.3\n15.4-4.8 6.9-3.7 13.3-8.3 19.4-13.1 7.3-5.7 14.3-11.8 21.1-18.1 7-6.5 13.8-13.1\n20.5-19.9 5.9-6 11.8-12.1 17.5-18.3 4.1-4.4 8.2-8.9 12.3-13.5 8.1-9.1 16-18.4\n23.8-27.9 10.5-12.8 20.7-25.8 30.6-39 10-13.2 19.8-26.6 29.5-40.1s19.3-27\n28.9-40.6c9.6-13.5 19.1-27 28.8-40.4 10-13.9 20.3-27.7 30.5-41.4 10.6-14.4\n21.2-28.7 31.9-43.1 10.7-14.4 21.3-28.8 32-43.2 10.6-14.4 21.2-28.7 31.9-43.1\n4.4-5.9 8.9-11.8 13.3-17.7 4.7-6.1 9.2-12.3 13.6-18.6 4.9-7.1 9.6-14.4 13.9-22\n4-7.1 7.6-14.6 10-22.4 1.9-6.3 3.2-13.1\n1.8-19.6-1.3-6-5.1-10.8-10.4-13.8-8.1-4-17.4-4.8-25.9-4.8-29.1 0 80.8 0 0\n0zM691.1 283.8c-53.5 0-53.5 83 0 83s53.5-83 0-83zM504.6 229.8c-68 0-68 106.3 0\n107 67.6 0.7 68.7-107 0-107zM311.6 346.8c-68 0-68 106.3 0 107 67.6 0.7 68.7-107\n0-107zM230.1 530.8c-58.7 0-58.7 91 0 91s58.7-91 0-91z\" />\n    <path\n        android:fillColor=\"#FFFFFFFF\"\n        android:pathData=\"M521.9 985.8c-17\n0-34-0.7-50.8-2.6-16.9-1.8-33.6-4.8-50-9.4-15.6-4.4-30.9-10.3-45-18.4-13.8-7.9-26.4-17.9-36.7-30-10.9-12.9-19-27.9-24.2-43.9-2.4-7.4-4.1-14.8-5-22.5-0.9-8-1.4-16.1-2.2-24.2-0.8-8.5-1.7-17.1-3.6-25.4-1.6-7.3-4-14.8-8.3-21-9.5-13.6-27.7-15-42.8-13.9-18.1\n1.4-35.6 6.5-53.5 9.2-7.2 1.1-14.5 1.3-21.8\n1.2-6.8-0.1-13.7-0.6-20.4-1.5-12.1-1.6-24-4.7-35.2-9.6-10.3-4.5-20-10.4-28.6-17.6-8.4-7-15.8-15.3-22.2-24.2-6.6-9.3-12.1-19.5-16.6-30-4.8-11.1-8.5-22.7-11.4-34.4-3.1-12.3-5.2-24.9-6.7-37.5-1.6-13.2-2.4-26.5-2.7-39.8-0.3-13.5\n0-27.1 0.8-40.6 0.8-13.6 2-27.1 3.7-40.6 1.6-13.1 3.6-26.2 6-39.2 2.3-12.2\n4.9-24.4 7.8-36.5 2.7-11.1 5.7-22.1 9.1-33 3.3-10.6 6.9-21.1 11-31.4 6.1-15.5\n13.1-30.6 20.9-45.4 7.8-14.8 16.4-29.3 25.7-43.2 9.3-13.9 19.2-27.4 29.8-40.4\n10.7-13.1 22-25.6 33.8-37.7 11.9-12 24.3-23.5 37.2-34.5 12.9-10.9 26.2-21.2\n40-30.8 13.8-9.7 28.1-18.8 42.7-27.2 14.5-8.4 29.4-16.1 44.6-23.1 15.2-7\n30.8-13.3 46.6-18.9 15.7-5.5 31.7-10.3 47.8-14.3 16-3.9 32.1-7.1 48.4-9.4\n16.3-2.3 32.7-3.7 49.1-4.3 16.3-0.5 32.6-0.2 48.8 1.2 16.3 1.3 32.6 3.6 48.6 6.9\n16.2 3.3 32.1 7.6 47.7 12.9 15.8 5.3 31.2 11.7 46.2 19 15.4 7.6 30.4 16.2 44.7\n25.7 14.3 9.5 27.9 19.9 40.9 31.1-12.2 14.2-24.5 28.3-36.7 42.5-3.4 4-6.9 8-10.3\n12.1-10.9-9.3-22.3-18-34.2-25.9-11.3-7.6-23.1-14.4-35.3-20.5-11.7-5.9-23.7-11-36-15.4-12-4.3-24.3-7.8-36.8-10.6-12.2-2.7-24.6-4.7-37.1-6-12.4-1.3-25-1.8-37.5-1.7-12.5\n0.1-25 0.9-37.5 2.4s-24.8 3.5-37.1 6.2c-12.4 2.7-24.7 6-36.8 9.8-12.2 3.9-24.2\n8.2-36 13.1-12 4.9-23.8 10.4-35.3 16.3-11.6 5.9-23 12.3-34.2 19.1-11.2 6.8-22.1\n14-32.8 21.6-10.7 7.6-21.2 15.7-31.4 24-10.1 8.3-20 17-29.5 26s-18.8 18.4-27.7\n28c-9.3 10.1-18.4 20.4-26.7 31.3-9.2 12-17.5 24.6-25 37.7-7.5 13.1-14.2\n26.7-20.1 40.7-5.9 13.9-11.1 28.2-15.6 42.6-4.6 14.5-8.5 29.3-11.9 44.1-3.5\n15.2-6.5 30.6-8.7 46.1-2.2 15.9-3.5 31.9-3.6 47.9-0.1 15.9 1.1 31.8 3.9 47.4 2.8\n15.6 7.3 30.8 13.5 45.4 4.5 10.5 10.1 20.8 17.7 29.4 5.7 6.4 12.6 11.5 20.6 14.7\n7.7 3 16.1 4.2 24.3 4.4 9.6 0.2 19.1-0.6 28.7-1.5 11.4-1.1 22.7-2.4 34.2-3\n12.5-0.7 25.1-0.5 37.6 1.3 11.7 1.7 23.2 4.7 33.9 9.8 10.7 5.1 20.3 12.2 28.2 21\n8.3 9.2 14.9 20.1 19.1 31.8 4.1 11.2 6.3 22.9 8 34.6 1.8 11.8 3.2 23.7 5.5 35.4\n2.2 11.1 5.3 22.2 10.8 32.2 2.7 4.9 6 9.5 9.9 13.5 3.9 4 8.3 7.4 13 10.4 8.9 5.7\n18.6 10 28.5 13.4 10.9 3.6 22.1 6 33.5 7.7 12.1 1.7 24.2 2.6 36.4 2.7 12.6 0.2\n25.2-0.3 37.8-1.4 12.5-1 25-2.6 37.3-4.7 11.7-1.9 23.4-4.3 34.9-7.1 10.4-2.6\n20.8-5.5 31-8.9 12.4-4.1 24.5-9 36.3-14.5 13.7-6.4 27.1-13.7 40-21.7 13-8.1\n25.5-16.9 37.4-26.3 11.8-9.4 23.2-19.4 33.9-30 10.8-10.7 21-21.9 30.5-33.8\n9.6-12.1 18.5-24.8 26.5-38.1s15.2-27.1 21.4-41.3c4.3-9.7 8.3-19.5 10.6-29.9 2-9\n3.3-18.1 4.6-27.2 1.5-10.4 2.8-20.8 4.5-31.2 1.6-10.3 3.5-20.5 6.1-30.5 2.3-8.9\n5.2-17.7 9.7-25.8 3.6-6.6 8.4-12.8 15-16.6 3.3-1.9 7.1-3.1 10.8-4.1 3.4-0.9\n6.8-1.5 10.3-1.5 5.4-0.1 10.9 1.3 15.3 4.6 4.3 3.2 7.2 7.9 9.2 12.8 2.4 6 3.6\n12.5 4.2 18.9 0.7 7.6 0.7 15.2 0.3 22.7-0.5 8.2-1.4 16.3-2.5 24.4-1.2 7.9-2.6\n15.8-4.2 23.7-1.5 7-3.1 14-4.8 20.9-1.3 5.4-2.8 10.7-4.3 16-2.6 9.1-5.7 18.2-9\n27.1-5.3 14.3-11.3 28.3-18.1 41.9-6.7 13.6-14.2 26.8-22.3 39.6-8.2 12.9-17\n25.3-26.5 37.2-9.3 11.7-19.1 22.9-29.5 33.6-9.9 10.2-20.2 19.9-30.9 29.1-10.5\n9.1-21.4 17.7-32.7 25.7-11.1 7.9-22.6 15.3-34.4 22.2-11.7 6.8-23.8 13.1-36.1\n18.9-12.3 5.7-24.8 10.8-37.5 15.4-12.8 4.6-25.9 8.6-39 12-13.3 3.4-26.8 6.3-40.3\n8.6-13.7 2.3-27.5 4-41.3 5.1-14.2 1.1-28.4 1.7-42.6 1.7h-19.5\n20.4c0-0.3-0.1-0.3-0.2-0.3-86.9 0 5.1 0 0 0z\" />\n    <path\n        android:fillColor=\"#FFFFFFFF\"\n        android:pathData=\"M826 161c0 15.4-9.9 29.5-24.4 34.8-14.6 5.3-31.3\n0.7-41.1-11.2-9.9-12-11.2-29.4-3.1-42.7 8-13.2 23.9-20.2 39.1-17.1 14.9 3 26.7\n15.4 29.1 30.4 0.2 1.9 0.4 3.9 0.4 5.8z\" />\n</vector>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/drawable/bar_status_nav_custom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n\n    <path\n        android:fillColor=\"#FFFFFFFF\"\n        android:pathData=\"M811.008 335.872c-2.048-7.168-11.264-9.216-17.408-4.096L690.176 435.2c-8.192\n8.192-20.48 8.192-28.672 0l-72.704-72.704c-8.192-8.192-8.192-20.48\n0-28.672l104.448-104.448c5.12-5.12\n3.072-14.336-4.096-17.408-17.408-4.096-35.84-7.168-54.272-7.168-108.544\n0-195.584 94.208-183.296 204.8 2.048 17.408 6.144 32.768 12.288 48.128L225.28\n697.344c-27.648 27.648-27.648 73.728 0 101.376 14.336 14.336 32.768 21.504 51.2\n21.504s36.864-7.168 51.2-21.504l238.592-238.592c15.36 6.144 31.744 10.24 48.128\n12.288 111.616 12.288 204.8-74.752 204.8-183.296\n0-18.432-3.072-36.864-8.192-53.248z\" />\n</vector>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/drawable/bar_status_nav_image.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n\n    <path\n        android:fillColor=\"#FFFFFFFF\"\n        android:pathData=\"M876.98432 86.18496 147.01568 86.18496C66.38592 86.18496 1.024 151.54688 1.024\n232.17664l0 559.6416c0 80.62976 65.36192 145.99168 145.99168 145.99168l729.96864\n0c80.62976 0 145.99168-65.36192 145.99168-145.99168L1022.976 232.17664C1022.976\n151.54688 957.61408 86.18496 876.98432 86.18496zM949.98016 791.81824c0\n40.24832-32.74752 72.99584-72.99584 72.99584L147.01568 864.81408c-40.24832\n0-72.99584-32.74752-72.99584-72.99584L74.01984 232.17664c0-40.24832\n32.74752-72.99584 72.99584-72.99584l729.96864 0c40.24832 0 72.99584 32.74752\n72.99584 72.99584L949.98016 791.81824zM259.55328 463.3344c60.37504 0\n109.49632-49.11616 109.49632-109.49632\n0-60.37504-49.11616-109.4912-109.49632-109.4912-60.38016 0-109.49632\n49.11616-109.49632 109.4912C150.05696 414.21824 199.17312 463.3344 259.55328\n463.3344zM259.55328 317.34272c20.12672 0 36.49536 16.37376 36.49536 36.49536 0\n20.12672-16.37376 36.49536-36.49536 36.49536-20.12672\n0-36.50048-16.36864-36.50048-36.49536C223.0528 333.71648 239.42656 317.34272\n259.55328 317.34272zM689.03936\n409.51296c-6.70208-8.00768-16.512-12.78464-26.97216-13.07136-10.42944-0.92672-20.52096\n3.89632-27.65824 11.49952l-157.58848\n168.09472-64.61952-60.30848c-14.208-13.28128-36.31616-13.09184-50.29376\n0.48128L170.2912 701.74208c-14.48448 14.01856-14.848 37.11488-0.82944 51.584\n7.16288 7.41376 16.68096 11.12576 26.21952 11.12576 9.14944 0 18.29888-3.42016\n25.39008-10.28608l166.68672-161.39264 65.88416 61.49632c14.73024 13.73184\n37.79072 12.9536 51.52768-1.71008l154.29632-164.60288 143.89248 172.15488c12.928\n15.47264 35.92704 17.536 51.40992 4.61312 15.47264-12.928 17.52576-35.95264\n4.59776-51.42016L689.03936 409.51296z\" />\n</vector>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/drawable/fragment_nav.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"1024\"\n    android:viewportHeight=\"1024\">\n\n    <path\n        android:fillColor=\"#FFFFFFFF\"\n        android:pathData=\"M450.750464 477.114368l-297.984 0c-14.11584\n0-25.6-11.48416-25.6-25.6l0-297.984c0-14.11584 11.48416-25.6 25.6-25.6l297.984\n0c14.11584 0 25.6 11.48416 25.6 25.6l0 297.984C476.350464 465.630208 464.867328\n477.114368 450.750464 477.114368zM157.886464 446.394368l287.744 0\n0-287.744-287.744 0L157.886464 446.394368z\" />\n    <path\n        android:fillColor=\"#FFFFFFFF\"\n        android:pathData=\"M870.434816 477.114368l-297.984 0c-14.11584\n0-25.6-11.48416-25.6-25.6l0-297.984c0-14.11584 11.48416-25.6 25.6-25.6l297.984\n0c14.11584 0 25.6 11.48416 25.6 25.6l0 297.984C896.034816 465.630208 884.550656\n477.114368 870.434816 477.114368zM577.570816 446.394368l287.744 0\n0-287.744-287.744 0L577.570816 446.394368z\" />\n    <path\n        android:fillColor=\"#FFFFFFFF\"\n        android:pathData=\"M451.661824 894.906368l-297.984 0c-14.11584\n0-25.6-11.48416-25.6-25.6l0-297.984c0-14.11584 11.48416-25.6 25.6-25.6l297.984\n0c14.11584 0 25.6 11.48416 25.6 25.6l0 297.984C477.261824 883.422208 465.777664\n894.906368 451.661824 894.906368zM158.797824 864.186368l287.744 0\n0-287.744-287.744 0L158.797824 864.186368z\" />\n    <path\n        android:fillColor=\"#FFFFFFFF\"\n        android:pathData=\"M871.346176 894.906368l-297.984 0c-14.11584\n0-25.6-11.48416-25.6-25.6l0-297.984c0-14.11584 11.48416-25.6 25.6-25.6l297.984\n0c14.11584 0 25.6 11.48416 25.6 25.6l0 297.984C896.946176 883.422208 885.462016\n894.906368 871.346176 894.906368zM578.482176 864.186368l287.744 0\n0-287.744-287.744 0L578.482176 864.186368z\" />\n</vector>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/drawable/keyboard_dialog_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <solid android:color=\"@color/lightGray\" />\n    <corners\n        android:topLeftRadius=\"@dimen/radius_8\"\n        android:topRightRadius=\"@dimen/radius_8\" />\n    <stroke\n        android:width=\"1dp\"\n        android:color=\"@color/lightGrayDark\" />\n</shape>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/drawable/shadow_circle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"oval\">\n\n    <solid android:color=\"#80FF73A3\" />\n\n</shape>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/drawable/shadow_round_rect.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <solid android:color=\"#8034A48E\" />\n    <corners android:radius=\"@dimen/radius_16\" />\n\n</shape>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/drawable/snackbar_custom_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <solid android:color=\"@color/colorAccent\" />\n    <corners\n        android:topLeftRadius=\"@dimen/radius_16\"\n        android:topRightRadius=\"@dimen/radius_16\" />\n\n</shape>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/drawable/span_block_high.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <solid android:color=\"@color/colorAccent\" />\n    <size\n        android:width=\"@dimen/block_width\"\n        android:height=\"@dimen/block_high_height\" />\n    <stroke\n        android:width=\"5dp\"\n        android:color=\"@color/colorPrimary\" />\n\n</shape>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/drawable/span_block_low.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <solid android:color=\"@color/colorAccent\" />\n    <size\n        android:width=\"@dimen/block_width\"\n        android:height=\"@dimen/block_low_height\" />\n    <stroke\n        android:width=\"1dp\"\n        android:color=\"@color/colorPrimary\" />\n\n</shape>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/drawable/toast_round_rect.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <solid android:color=\"@color/colorAccent\" />\n    <corners android:radius=\"@dimen/radius_16\" />\n    <padding\n        android:bottom=\"@dimen/spacing_16\"\n        android:left=\"@dimen/spacing_16\"\n        android:right=\"@dimen/spacing_16\"\n        android:top=\"@dimen/spacing_16\" />\n\n</shape>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/activity_adaptscreen.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:padding=\"16dp\">\n\n    <Button\n        android:id=\"@+id/adaptScreenAdaptWidthBtn\"\n        style=\"@style/CommonWideBtnStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"Adapt Width\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <Button\n        android:id=\"@+id/adaptScreenAdaptHeightBtn\"\n        style=\"@style/CommonWideBtnStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"12dp\"\n        android:text=\"Adapt Height\"\n        app:layout_constraintTop_toBottomOf=\"@id/adaptScreenAdaptWidthBtn\" />\n\n    <Button\n        android:id=\"@+id/adaptScreenCloseAdaptBtn\"\n        style=\"@style/CommonWideBtnStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"12dp\"\n        android:text=\"Close Adapt pt equals dp\"\n        app:layout_constraintTop_toBottomOf=\"@id/adaptScreenAdaptHeightBtn\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/activity_item_shared_element_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingLeft=\"12dp\"\n    android:paddingTop=\"8dp\"\n    android:paddingRight=\"12dp\"\n    android:paddingBottom=\"8dp\">\n\n    <ImageView\n        android:id=\"@+id/activityViewSharedElement\"\n        android:layout_width=\"@dimen/size_40\"\n        android:layout_height=\"@dimen/size_40\"\n        android:layout_centerInParent=\"true\"\n        android:src=\"@drawable/common_avatar_round\"\n        android:transitionName=\"@string/activity_shared_element\" />\n\n</RelativeLayout>\n\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/activity_sub_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:gravity=\"center_horizontal\"\n    android:padding=\"@dimen/spacing_16\">\n\n    <ImageView\n        android:id=\"@+id/activityViewSharedElement\"\n        android:layout_width=\"@dimen/size_160\"\n        android:layout_height=\"@dimen/size_160\"\n        android:src=\"@drawable/common_avatar_round\"\n        android:transitionName=\"@string/activity_shared_element\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/adaptscreen_close_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fillViewport=\"true\"\n    tools:context=\".feature.adaptScreen.AdaptCloseActivity\">\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <TextView\n            android:id=\"@+id/adaptScreenCloseAdaptTipTv\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:gravity=\"center\"\n            android:text=\"Close Adapt\\npt equals dp\"\n            android:textColor=\"@android:color/black\"\n            android:textSize=\"30sp\"\n            app:layout_constraintTop_toTopOf=\"parent\" />\n\n        <TextView\n            android:id=\"@+id/adaptScreenCloseAdaptDpTv\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"200dp\"\n            android:background=\"@color/colorAccentOrigin\"\n            android:gravity=\"center\"\n            android:text=\"Height: 200dp\\nFont: 30dp\"\n            android:textColor=\"@android:color/black\"\n            android:textSize=\"30dp\"\n            app:layout_constraintTop_toBottomOf=\"@id/adaptScreenCloseAdaptTipTv\" />\n\n        <TextView\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"200pt\"\n            android:background=\"@color/colorPrimaryOrigin\"\n            android:gravity=\"center\"\n            android:text=\"Height: 200pt\\nFont: 30pt\"\n            android:textColor=\"@android:color/black\"\n            android:textSize=\"30pt\"\n            app:layout_constraintTop_toBottomOf=\"@id/adaptScreenCloseAdaptDpTv\" />\n\n    </androidx.constraintlayout.widget.ConstraintLayout>\n\n</ScrollView>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/adaptscreen_height_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<HorizontalScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".feature.adaptScreen.AdaptHeightActivity\">\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"match_parent\">\n\n        <FrameLayout\n            android:id=\"@+id/adaptScreenHeightFl0\"\n            android:layout_width=\"360pt\"\n            android:layout_height=\"match_parent\"\n            android:background=\"@color/colorPrimaryOrigin\"\n            app:layout_constraintLeft_toLeftOf=\"parent\">\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"960pt\"\n                android:layout_gravity=\"top\"\n                android:background=\"@color/colorAccentOrigin\"\n                android:gravity=\"center\"\n                android:text=\"Height: 960pt\\nFont: 30pt\"\n                android:textSize=\"30pt\" />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"960pt\"\n                android:layout_gravity=\"bottom\"\n                android:background=\"@color/colorPrimaryOrigin\"\n                android:gravity=\"center\"\n                android:text=\"Height: 960pt\\nFont: 30sp\"\n                android:textSize=\"30sp\" />\n\n        </FrameLayout>\n\n        <FrameLayout\n            android:id=\"@+id/adaptScreenHeightFl1\"\n            android:layout_width=\"360pt\"\n            android:layout_height=\"1920pt\"\n            app:layout_constraintLeft_toRightOf=\"@id/adaptScreenHeightFl0\"\n            app:layout_constraintTop_toTopOf=\"parent\">\n\n            <WebView\n                android:id=\"@+id/adaptScreenHeightWebView\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\" />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:gravity=\"center\"\n                android:text=\"WebView\\nHeight: 1920pt\"\n                android:textColor=\"@android:color/black\"\n                android:textSize=\"30pt\" />\n\n        </FrameLayout>\n\n        <LinearLayout\n            android:layout_width=\"360pt\"\n            android:layout_height=\"match_parent\"\n            android:orientation=\"vertical\"\n            app:layout_constraintLeft_toRightOf=\"@id/adaptScreenHeightFl1\">\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"320pt\"\n                android:background=\"@color/colorAccentOrigin\"\n                android:gravity=\"center\"\n                android:text=\"1\\nHeight: 320pt\"\n                android:textColor=\"@android:color/black\"\n                android:textSize=\"30pt\" />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"640pt\"\n                android:background=\"@color/colorPrimaryOrigin\"\n                android:gravity=\"center\"\n                android:text=\"2\\nHeight: 640pt\"\n                android:textColor=\"@android:color/black\"\n                android:textSize=\"30pt\" />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"960pt\"\n                android:background=\"@color/colorPrimaryDarkOrigin\"\n                android:gravity=\"center\"\n                android:text=\"3\\nHeight: 960pt\"\n                android:textColor=\"@android:color/black\"\n                android:textSize=\"30pt\" />\n        </LinearLayout>\n\n    </androidx.constraintlayout.widget.ConstraintLayout>\n\n</HorizontalScrollView>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/adaptscreen_width_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fillViewport=\"true\"\n    tools:context=\".feature.adaptScreen.AdaptWidthActivity\">\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <FrameLayout\n            android:id=\"@+id/adaptScreenWidthFl0\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"270pt\"\n            app:layout_constraintLeft_toLeftOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"parent\">\n\n            <TextView\n                android:layout_width=\"540pt\"\n                android:layout_height=\"match_parent\"\n                android:layout_gravity=\"start\"\n                android:background=\"@color/colorAccentOrigin\"\n                android:gravity=\"center\"\n                android:text=\"Width: 540pt\\nFont: 30pt\"\n                android:textColor=\"@android:color/black\"\n                android:textSize=\"30pt\" />\n\n            <TextView\n                android:layout_width=\"540pt\"\n                android:layout_height=\"match_parent\"\n                android:layout_gravity=\"end\"\n                android:background=\"@color/colorPrimaryOrigin\"\n                android:gravity=\"center\"\n                android:text=\"Width: 540pt\\nFont: 30sp\"\n                android:textColor=\"@android:color/black\"\n                android:textSize=\"30sp\" />\n\n        </FrameLayout>\n\n        <FrameLayout\n            android:id=\"@+id/adaptScreenWidthFl1\"\n            android:layout_width=\"1080pt\"\n            android:layout_height=\"270pt\"\n            app:layout_constraintLeft_toLeftOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@id/adaptScreenWidthFl0\">\n\n            <WebView\n                android:id=\"@+id/adaptScreenWidthWebView\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\" />\n\n            <TextView\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:gravity=\"center\"\n                android:text=\"WebView\\nWidth: 1080pt\"\n                android:textColor=\"@android:color/black\"\n                android:textSize=\"30pt\" />\n\n        </FrameLayout>\n\n        <FrameLayout\n            android:id=\"@+id/adaptScreenWidthFl2\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:layout_constraintTop_toBottomOf=\"@id/adaptScreenWidthFl1\">\n\n            <ImageView\n                android:layout_width=\"540pt\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"start\"\n                android:adjustViewBounds=\"true\"\n                android:src=\"@drawable/image_lena\" />\n\n            <TextView\n                android:layout_width=\"540pt\"\n                android:layout_height=\"match_parent\"\n                android:layout_gravity=\"center_vertical|end\"\n                android:background=\"@color/colorAccentOrigin\"\n                android:gravity=\"center\"\n                android:text=\"Left ImageView\\nWidth: 540pt\"\n                android:textColor=\"@android:color/black\"\n                android:textSize=\"30pt\" />\n\n        </FrameLayout>\n\n        <FrameLayout\n            android:id=\"@+id/adaptScreenWidthFl3\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:layout_constraintTop_toBottomOf=\"@id/adaptScreenWidthFl2\">\n\n            <ImageView\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_gravity=\"start\"\n                android:adjustViewBounds=\"true\"\n                android:src=\"@drawable/image_lena\" />\n\n            <TextView\n                android:layout_width=\"540pt\"\n                android:layout_height=\"match_parent\"\n                android:layout_gravity=\"center_vertical|end\"\n                android:background=\"@color/colorAccentOrigin\"\n                android:gravity=\"center\"\n                android:text=\"Left ImageView\\nWidth: wrap_content\"\n                android:textColor=\"@android:color/black\"\n                android:textSize=\"30pt\" />\n\n        </FrameLayout>\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            app:layout_constraintTop_toBottomOf=\"@id/adaptScreenWidthFl3\">\n\n            <TextView\n                android:layout_width=\"180pt\"\n                android:layout_height=\"270pt\"\n                android:background=\"@color/colorAccentOrigin\"\n                android:gravity=\"center\"\n                android:text=\"1\\nWidth: 180pt\"\n                android:textColor=\"@android:color/black\"\n                android:textSize=\"30pt\" />\n\n            <TextView\n                android:id=\"@+id/width2Tv\"\n                android:layout_width=\"360pt\"\n                android:layout_height=\"270pt\"\n                android:background=\"@color/colorPrimaryOrigin\"\n                android:gravity=\"center\"\n                android:text=\"2\\nWidth: 360pt\"\n                android:textColor=\"@android:color/black\"\n                android:textSize=\"30pt\" />\n\n            <TextView\n                android:id=\"@+id/width3Tv\"\n                android:layout_width=\"540pt\"\n                android:layout_height=\"270pt\"\n                android:background=\"@color/colorPrimaryDarkOrigin\"\n                android:gravity=\"center\"\n                android:text=\"3\\nWidth: 540pt\"\n                android:textColor=\"@android:color/black\"\n                android:textSize=\"30pt\" />\n\n        </LinearLayout>\n\n    </androidx.constraintlayout.widget.ConstraintLayout>\n\n</ScrollView>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/bar_status_alpha_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@drawable/image_lena\"\n    android:orientation=\"vertical\">\n\n    <include layout=\"@layout/common_item\" />\n\n</LinearLayout>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/bar_status_alpha_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@drawable/image_lena\"\n    android:orientation=\"vertical\">\n\n    <View\n        android:id=\"@+id/barStatusAlphaFragmentFakeStatusBar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\" />\n\n    <include layout=\"@layout/common_item\" />\n</LinearLayout>\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/bar_status_color_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <View\n        android:id=\"@+id/barStatusColorFragmentFakeStatusBar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\" />\n\n    <include layout=\"@layout/common_item\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/bar_status_custom_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <View\n        android:id=\"@+id/barStatusCustomFragmentFakeStatusBar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:background=\"@drawable/bar_status_custom\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/bar_status_drawer_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/barStatusDrawerRootLl\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <View\n        android:id=\"@+id/barStatusDrawerFakeStatusBar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\" />\n\n    <include layout=\"@layout/common_item\" />\n\n</LinearLayout>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/bar_status_fragment_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <androidx.viewpager.widget.ViewPager\n        android:id=\"@+id/barStatusFragmentVp\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\"\n        android:orientation=\"vertical\"\n        android:overScrollMode=\"never\" />\n\n    <com.google.android.material.bottomnavigation.BottomNavigationView\n        android:id=\"@+id/barStatusFragmentNav\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"bottom\"\n        android:background=\"?android:attr/windowBackground\"\n        app:menu=\"@menu/status_bar_nav\" />\n\n</LinearLayout>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/bar_status_image_view_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <ImageView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/image_lena\" />\n\n    <include layout=\"@layout/common_item\" />\n</LinearLayout>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/bar_status_image_view_fragment.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <ImageView\n            android:id=\"@+id/barStatusImageViewFragmentHeaderIv\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:scaleType=\"centerCrop\"\n            android:src=\"@drawable/image_lena\" />\n\n        <include layout=\"@layout/common_item\" />\n\n    </LinearLayout>\n\n    <View\n        android:id=\"@+id/barStatusImageViewFragmentFakeStatusBar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\" />\n\n</RelativeLayout>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/fragment_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@color/white\"\n    android:orientation=\"vertical\">\n\n    <FrameLayout\n        android:id=\"@+id/fragmentContainer\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"1\" />\n\n    <com.google.android.material.bottomnavigation.BottomNavigationView\n        android:id=\"@+id/fragmentNav\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"bottom\"\n        android:background=\"?android:attr/windowBackground\"\n        app:menu=\"@menu/fragment_nav\" />\n\n</LinearLayout>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/fragment_child.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <include layout=\"@layout/common_item\" />\n\n</FrameLayout>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/fragment_container.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <include layout=\"@layout/common_item\" />\n\n</FrameLayout>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/fragment_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<TextView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/fragmentDialogAboutTv\"\n    style=\"@style/CommonTextStyle\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@drawable/common_content_dialog_bg\"\n    android:padding=\"@dimen/spacing_16\"\n    android:scrollbars=\"vertical\" />"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/fragment_item_shared_element.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingLeft=\"12dp\"\n    android:paddingTop=\"8dp\"\n    android:paddingRight=\"12dp\"\n    android:paddingBottom=\"8dp\">\n\n    <ImageView\n        android:id=\"@+id/fragmentRootSharedElementIv\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\"\n        android:src=\"@drawable/image_lena\"\n        android:transitionName=\"@string/fragment_transition\" />\n\n</RelativeLayout>\n\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/fragment_root.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\">\n\n    <View\n        android:id=\"@+id/rootFragmentFakeStatusBar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\" />\n\n    <ScrollView\n        style=\"@style/CommonScrollStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:fillViewport=\"true\">\n\n        <FrameLayout\n            android:id=\"@+id/rootFragmentContainer\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\" />\n\n    </ScrollView>\n\n</LinearLayout>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/keyboard_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"1000dp\"\n    android:orientation=\"vertical\">\n\n    <include layout=\"@layout/common_item\" />\n\n    <EditText\n        android:id=\"@+id/keyboardEt\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:inputType=\"phone\" />\n\n</RelativeLayout>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/keyboard_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<ScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@drawable/keyboard_dialog_bg\"\n    android:fillViewport=\"true\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1000dp\"\n        android:orientation=\"vertical\"\n        android:padding=\"@dimen/spacing_16\">\n\n        <Button\n            android:id=\"@+id/keyboardDialogHideSoftInputBtn\"\n            style=\"@style/CommonWideBtnStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:text=\"@string/keyboard_hide_soft_input\" />\n\n        <Button\n            android:id=\"@+id/keyboardDialogShowSoftInputBtn\"\n            style=\"@style/CommonWideBtnStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/keyboardDialogHideSoftInputBtn\"\n            android:layout_marginTop=\"@dimen/spacing_6\"\n            android:text=\"@string/keyboard_show_soft_input\" />\n\n        <Button\n            android:id=\"@+id/keyboardDialogToggleSoftInputBtn\"\n            style=\"@style/CommonWideBtnStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/keyboardDialogShowSoftInputBtn\"\n            android:layout_marginTop=\"@dimen/spacing_6\"\n            android:text=\"@string/keyboard_toggle_soft_input\" />\n\n        <Button\n            android:id=\"@+id/keyboardDialogCloseBtn\"\n            style=\"@style/CommonWideBtnStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/keyboardDialogToggleSoftInputBtn\"\n            android:layout_marginTop=\"@dimen/spacing_6\"\n            android:text=\"@string/keyboard_close_dialog\" />\n\n        <EditText\n            android:id=\"@+id/keyboardDialogEt\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentBottom=\"true\"\n            android:inputType=\"text\" />\n\n    </RelativeLayout>\n</ScrollView>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/mvp_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.constraint.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:padding=\"16dp\">\n\n    <TextView\n        android:id=\"@+id/mvpUpdateTv\"\n        style=\"@style/CommonWideBtnStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center\"\n        android:text=\"Get Update Msg\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <TextView\n        android:id=\"@+id/mvpMeasureWidthTv\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"16dp\"\n        android:background=\"@color/white\"\n        android:padding=\"8dp\"\n        android:text=\"测量文本框长度\"\n        android:textColor=\"@color/darkGray\"\n        android:textSize=\"16sp\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/mvpUpdateTv\" />\n\n</android.support.constraint.ConstraintLayout>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/screen_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@drawable/common_content_dialog_bg\"\n    android:orientation=\"vertical\"\n    android:padding=\"@dimen/spacing_16\">\n\n    <ImageView\n        android:id=\"@+id/screenDialogScreenshotIv\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:adjustViewBounds=\"true\" />\n\n</LinearLayout>\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/shadow_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:padding=\"16dp\">\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1px\"\n        android:layout_marginTop=\"32dp\"\n        android:background=\"@drawable/common_item_divider\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center\"\n        android:orientation=\"horizontal\">\n\n        <View\n            android:layout_width=\"80dp\"\n            android:layout_height=\"80dp\"\n            android:layout_gravity=\"center\"\n            android:background=\"#80FF73A3\"\n            android:clickable=\"true\" />\n\n        <View\n            android:id=\"@+id/shadowRectView\"\n            android:layout_width=\"80dp\"\n            android:layout_height=\"80dp\"\n            android:layout_gravity=\"center\"\n            android:layout_marginLeft=\"16dp\"\n            android:background=\"#80FF73A3\"\n            android:clickable=\"true\" />\n\n        <FrameLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"16dp\">\n\n\n            <View\n                android:layout_width=\"80dp\"\n                android:layout_height=\"80dp\"\n                android:layout_gravity=\"center\"\n                android:background=\"#80FF73A3\"\n                android:clickable=\"true\" />\n\n            <View\n                android:id=\"@+id/shadowRectView1\"\n                android:layout_width=\"80dp\"\n                android:layout_height=\"80dp\"\n                android:layout_gravity=\"center\"\n                android:background=\"#80FF73A3\"\n                android:clickable=\"true\" />\n\n        </FrameLayout>\n\n    </LinearLayout>\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1px\"\n        android:background=\"@drawable/common_item_divider\" />\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1px\"\n        android:layout_marginTop=\"32dp\"\n        android:background=\"@drawable/common_item_divider\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center\"\n        android:orientation=\"horizontal\">\n\n        <View\n            android:layout_width=\"80dp\"\n            android:layout_height=\"80dp\"\n            android:layout_gravity=\"center\"\n            android:background=\"@drawable/shadow_round_rect\"\n            android:clickable=\"true\" />\n\n        <View\n            android:id=\"@+id/shadowRoundRectView\"\n            android:layout_width=\"80dp\"\n            android:layout_height=\"80dp\"\n            android:layout_gravity=\"center\"\n            android:layout_marginLeft=\"16dp\"\n            android:background=\"@drawable/shadow_round_rect\"\n            android:clickable=\"true\" />\n\n        <FrameLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"16dp\">\n\n            <View\n                android:layout_width=\"80dp\"\n                android:layout_height=\"80dp\"\n                android:layout_gravity=\"center\"\n                android:background=\"@drawable/shadow_round_rect\"\n                android:clickable=\"true\" />\n\n            <View\n                android:id=\"@+id/shadowRoundRectView1\"\n                android:layout_width=\"80dp\"\n                android:layout_height=\"80dp\"\n                android:layout_gravity=\"center\"\n                android:background=\"@drawable/shadow_round_rect\"\n                android:clickable=\"true\" />\n\n        </FrameLayout>\n\n    </LinearLayout>\n\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1px\"\n        android:background=\"@drawable/common_item_divider\" />\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1px\"\n        android:layout_marginTop=\"32dp\"\n        android:background=\"@drawable/common_item_divider\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"center\"\n        android:orientation=\"horizontal\">\n\n        <View\n            android:layout_width=\"80dp\"\n            android:layout_height=\"80dp\"\n            android:layout_gravity=\"center\"\n            android:background=\"@drawable/shadow_circle\"\n            android:clickable=\"true\" />\n\n        <View\n            android:id=\"@+id/shadowCircleView\"\n            android:layout_width=\"80dp\"\n            android:layout_height=\"80dp\"\n            android:layout_gravity=\"center\"\n            android:layout_marginLeft=\"16dp\"\n            android:background=\"@drawable/shadow_circle\"\n            android:clickable=\"true\" />\n\n        <FrameLayout\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"16dp\">\n\n            <View\n                android:layout_width=\"80dp\"\n                android:layout_height=\"80dp\"\n                android:layout_gravity=\"center\"\n                android:background=\"@drawable/shadow_circle\"\n                android:clickable=\"true\" />\n\n            <View\n                android:id=\"@+id/shadowCircleView1\"\n                android:layout_width=\"80dp\"\n                android:layout_height=\"80dp\"\n                android:layout_gravity=\"center\"\n                android:background=\"@drawable/shadow_circle\"\n                android:clickable=\"true\" />\n\n        </FrameLayout>\n\n    </LinearLayout>\n\n    <View\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"1px\"\n        android:background=\"@drawable/common_item_divider\" />\n\n</LinearLayout>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/snackbar_custom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<TextView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/snackbarCustomTv\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_gravity=\"center\"\n    android:background=\"@drawable/snackbar_custom_bg\"\n    android:drawableLeft=\"@mipmap/ic_launcher\"\n    android:drawablePadding=\"@dimen/spacing_16\"\n    android:gravity=\"center_vertical\"\n    android:padding=\"@dimen/spacing_16\"\n    android:text=\"@string/snackbar_custom_view\"\n    android:textColor=\"@color/white\"\n    android:textSize=\"@dimen/font_24\" />"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/span_activity.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:padding=\"@dimen/spacing_16\">\n\n    <TextView\n        android:id=\"@+id/spanAboutAnimTv\"\n        style=\"@style/CommonTextStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:gravity=\"start\"\n        android:layerType=\"software\" />\n\n    <TextView\n        android:id=\"@+id/spanAboutTv\"\n        style=\"@style/CommonTextStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:clickable=\"true\"\n        android:focusable=\"true\"\n        android:gravity=\"start|center_vertical\" />\n\n</LinearLayout>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/toast_custom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<TextView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:layout_gravity=\"center\"\n    android:background=\"@drawable/toast_round_rect\"\n    android:drawableLeft=\"@mipmap/ic_launcher\"\n    android:drawablePadding=\"@dimen/spacing_16\"\n    android:gravity=\"center\"\n    android:padding=\"@dimen/spacing_16\"\n    android:textColor=\"@color/white\"\n    android:textSize=\"@dimen/font_24\" />"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/layout/toast_dialog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@drawable/common_content_dialog_bg\"\n    android:orientation=\"vertical\"\n    android:padding=\"@dimen/spacing_16\">\n\n    <Button\n        android:id=\"@+id/toastDialogShowShortToastBtn\"\n        style=\"@style/CommonWideBtnStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"@string/toast_show_short\" />\n\n</LinearLayout>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/menu/fragment_nav.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item\n        android:id=\"@+id/fragmentNavigation0\"\n        android:icon=\"@drawable/fragment_nav\"\n        android:title=\"@string/fragment_zero\" />\n\n    <item\n        android:id=\"@+id/fragmentNavigation1\"\n        android:icon=\"@drawable/fragment_nav\"\n        android:title=\"@string/fragment_one\" />\n\n    <item\n        android:id=\"@+id/fragmentNavigation2\"\n        android:icon=\"@drawable/fragment_nav\"\n        android:title=\"@string/fragment_two\" />\n\n</menu>\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/menu/status_bar_nav.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item\n        android:id=\"@+id/barStatusFragmentNavigationColor\"\n        android:icon=\"@drawable/bar_status_nav_color\"\n        android:title=\"@string/bar_status_title_color\" />\n\n    <item\n        android:id=\"@+id/barStatusFragmentNavigationAlpha\"\n        android:icon=\"@drawable/bar_status_nav_alpha\"\n        android:title=\"@string/bar_status_title_alpha\" />\n\n    <item\n        android:id=\"@+id/barStatusFragmentNavigationImageView\"\n        android:icon=\"@drawable/bar_status_nav_image\"\n        android:title=\"@string/bar_status_title_image_view\" />\n\n    <item\n        android:id=\"@+id/barStatusFragmentNavigationCustom\"\n        android:icon=\"@drawable/bar_status_nav_custom\"\n        android:title=\"@string/bar_status_title_custom\" />\n\n</menu>\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/raw/test.txt",
    "content": "1st line\n2nd line"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/transition/explode_1000.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<explode xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"1000\" />"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/transition/fade_1000.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<fade xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"1000\" />\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/transition/slide_1000.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<slide xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:duration=\"1000\" />\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/values/ids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <item name=\"utilCodeUiMessageAddListenerId\" type=\"id\" />\n</resources>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources xmlns:tools=\"http://schemas.android.com/tools\" tools:ignore=\"MissingTranslation\">\n    <string name=\"demo_activity\">ActivityUtils Demo</string>\n    <string name=\"demo_adapt_screen\">AdaptScreenUtils Demo</string>\n    <string name=\"demo_api\">ApiUtils Demo</string>\n    <string name=\"demo_app\">AppUtils Demo</string>\n    <string name=\"demo_bar\">BarUtils Demo</string>\n    <string name=\"demo_brightness\">BrightnessUtils Demo</string>\n    <string name=\"demo_bus\">BusUtils Demo</string>\n    <string name=\"demo_clean\">CleanUtils Demo</string>\n    <string name=\"demo_click\">ClickUtils Demo</string>\n    <string name=\"demo_clipboard\">ClipboardUtils Demo</string>\n    <string name=\"demo_crash\">CrashUtils Demo</string>\n    <string name=\"demo_device\">DeviceUtils Demo</string>\n    <string name=\"demo_file\">FileUtils Demo</string>\n    <string name=\"demo_flashlight\">FlashlightUtils Demo</string>\n    <string name=\"demo_fragment\">FragmentUtils Demo</string>\n    <string name=\"demo_image\">ImageUtils Demo</string>\n    <string name=\"demo_intent\">IntentUtils Demo</string>\n    <string name=\"demo_keyboard\">KeyboardUtils Demo</string>\n    <string name=\"demo_language\">Language Demo</string>\n    <string name=\"demo_log\">LogUtils Demo</string>\n    <string name=\"demo_messenger\">MessengerUtils Demo</string>\n    <string name=\"demo_meta_data\">MetaDataUtils Demo</string>\n    <string name=\"demo_mvp\">Mvp Demo</string>\n    <string name=\"demo_network\">NetworkUtils Demo</string>\n    <string name=\"demo_path\">PathUtils Demo</string>\n    <string name=\"demo_notification\">Notification Demo</string>\n    <string name=\"demo_permission\">PermissionUtils Demo</string>\n    <string name=\"demo_phone\">PhoneUtils Demo</string>\n    <string name=\"demo_process\">ProcessUtils Demo</string>\n    <string name=\"demo_reflect\">ReflectUtils Demo</string>\n    <string name=\"demo_resource\">ResourceUtils Demo</string>\n    <string name=\"demo_screen\">ScreenUtils Demo</string>\n    <string name=\"demo_rom\">RomUtils Demo</string>\n    <string name=\"demo_sdcard\">SDCardUtils Demo</string>\n    <string name=\"demo_shadow\">Shadow Demo</string>\n    <string name=\"demo_snackbar\">SnackbarUtils Demo</string>\n    <string name=\"demo_spStatic\">SPStaticUtils Demo</string>\n    <string name=\"demo_span\">SpanUtils Demo</string>\n    <string name=\"demo_toast\">ToastUtils Demo</string>\n    <string name=\"demo_uiMessage\">UiMessage Demo</string>\n    <string name=\"demo_trans_activity\">TransActivity Demo</string>\n    <string name=\"demo_vibrate\">VibrateUtils Demo</string>\n    <string name=\"demo_volume\">VolumeUtils Demo</string>\n\n    <!--Activity 相关-->\n    <string name=\"activity_shared_element\">Shared Element</string>\n    <string name=\"activity_clz\">Clz</string>\n    <string name=\"activity_clz_opt\">Clz Opt</string>\n    <string name=\"activity_clz_anim\">Clz Anim</string>\n    <string name=\"activity_act_clz\">Act Clz</string>\n    <string name=\"activity_act_clz_opt\">Act Clz Opt</string>\n    <string name=\"activity_act_clz_shared_element\">Act Clz Shared Element</string>\n    <string name=\"activity_act_clz_anim\">Act Clz Anim</string>\n    <string name=\"activity_pkg_cls\">Pkg Cls</string>\n    <string name=\"activity_pkg_cls_opt\">Pkg Cls Opt</string>\n    <string name=\"activity_pkg_cls_anim\">Pkg Cls Anim</string>\n    <string name=\"activity_act_pkg_cls\">Act Pkg Cls</string>\n    <string name=\"activity_act_pkg_cls_opt\">Act Pkg Cls Opt</string>\n    <string name=\"activity_act_pkg_cls_shared_element\">Act Pkg Cls Shared Element</string>\n    <string name=\"activity_act_pkg_cls_anim\">Act Pkg Cls Anim</string>\n    <string name=\"activity_intent\">Intent</string>\n    <string name=\"activity_intent_opt\">Intent Opt</string>\n    <string name=\"activity_intent_shared_element\">Intent Shared Element</string>\n    <string name=\"activity_intent_anim\">Intent Anim</string>\n    <string name=\"activity_intents\">Intents</string>\n    <string name=\"activity_intents_opt\">Intents Opt</string>\n    <string name=\"activity_intents_anim\">Intents Anim</string>\n    <string name=\"activity_act_intents\">Act Intents</string>\n    <string name=\"activity_act_intents_opt\">Act Intents Opt</string>\n    <string name=\"activity_act_intents_anim\">Act Intents Anim</string>\n    <string name=\"activity_start_activities\">Start Activities</string>\n    <string name=\"activity_start_home_activity\">Start Home Activity</string>\n    <string name=\"activity_start_launcher_activity\">Start Launcher Activity</string>\n    <string name=\"activity_finish_activity\">Finish CoreUtilActivity</string>\n    <string name=\"activity_finish_to_activity\">Finish To CoreUtilActivity</string>\n    <string name=\"activity_finish_all_activities_except_newest\">Finish All Activities Except Newest</string>\n    <string name=\"activity_finish_all_activities\">Finish All Activities</string>\n\n    <!--Api 相关-->\n    <string name=\"api_invoke_with_params\">Invoke With Params</string>\n    <string name=\"api_invoke_with_return_value\">Invoke With Return Value</string>\n    <string name=\"api_post_bus\">Post Bus</string>\n\n    <!--App 相关-->\n    <string name=\"app_install\">Install Test App</string>\n    <string name=\"app_install_tips\">Test app have installed</string>\n    <string name=\"install_successfully\">Install successfully</string>\n    <string name=\"install_unsuccessfully\">Install unsuccessfully</string>\n    <string name=\"app_uninstall\">Uninstall Test App</string>\n    <string name=\"app_uninstall_tips\">Please install test app first</string>\n    <string name=\"uninstall_successfully\">Uninstall successfully</string>\n    <string name=\"uninstall_unsuccessfully\">Uninstall unsuccessfully</string>\n    <string name=\"app_launch\">Launch App</string>\n    <string name=\"app_relaunch\">Relaunch App</string>\n    <string name=\"app_launch_details_settings\">Launch App Details Settings</string>\n    <string name=\"app_exit\">Exit App</string>\n\n    <!--AdaptScreen 相关-->\n    <string name=\"adaptScreen_adapt_width\">Adapt Width</string>\n    <string name=\"adaptScreen_adapt_height\">Adapt Height</string>\n    <string name=\"adaptScreen_adapt_close\">Close Adapt pt equals dp</string>\n\n    <!--Bar 相关-->\n    <string name=\"bar_about_status_bar\">About Status Bar</string>\n    <string name=\"bar_about_nav_bar\">About Nav Bar</string>\n    <string name=\"bar_about_notification_bar\">About Notification Bar</string>\n\n    <string name=\"bar_status_about\">About Status</string>\n    <string name=\"bar_status_visibility\">Status Visibility</string>\n    <string name=\"bar_status_light_mode\">Light Mode</string>\n    <string name=\"bar_status_set_color\">Set Color</string>\n    <string name=\"bar_status_set_alpha\">Set Alpha</string>\n    <string name=\"bar_status_set_image_view\">Set Image View</string>\n    <string name=\"bar_status_set_custom\">Set Custom</string>\n    <string name=\"bar_status_set_fragment\">Set Fragment</string>\n    <string name=\"bar_status_set_drawer\">Set Drawer</string>\n    <string name=\"bar_status_random_color\">Random Color</string>\n    <string name=\"bar_status_set_transparent\">Set Transparent</string>\n    <string name=\"bar_status_title_color\">Color</string>\n    <string name=\"bar_status_title_alpha\">Alpha</string>\n    <string name=\"bar_status_is_front\">Front</string>\n    <string name=\"bar_status_title_image_view\">Image</string>\n    <string name=\"bar_status_title_custom\">Custom</string>\n\n    <string name=\"bar_notification_about\">About Notification</string>\n    <string name=\"bar_notification_show\">Show Notification And Hide After 2s</string>\n\n    <string name=\"bar_nav_about\">About Nav</string>\n    <string name=\"bar_nav_visibility\">Nav Visibility</string>\n    <string name=\"bar_nav_light_mode\">Light Mode</string>\n    <string name=\"bar_nav_color\">Set Nav Color Random</string>\n\n    <!-- Brightness 相关 -->\n    <string name=\"brightness_auto_brightness\">Auto Brightness</string>\n\n    <!--Clean 相关-->\n    <string name=\"clean_internal_cache\">Clean Internal Cache</string>\n    <string name=\"clean_internal_files\">Clean Internal Files</string>\n    <string name=\"clean_internal_databases\">Clean Internal Databases</string>\n    <string name=\"clean_internal_sp\">Clean Internal SP</string>\n    <string name=\"clean_external_cache\">Clean External Cache</string>\n    <string name=\"clean_app_user_data\">cleanAppUserData</string>\n\n    <!--Click 相关-->\n    <string name=\"click_view_scale_default\">Click View Scale Default</string>\n    <string name=\"click_view_scale_half\">Click View Scale 50%</string>\n    <string name=\"click_view_alpha_default\">Click View Alpha Default</string>\n    <string name=\"click_bg_alpha_default\">Click Bg Alpha Default</string>\n    <string name=\"click_bg_dark_default\">Click Bg Dark Default</string>\n    <string name=\"click_global_debouncing\">Click Global Debouncing</string>\n    <string name=\"click_single_debouncing\">Click Single Debouncing</string>\n    <string name=\"click_multi\">Click Multi</string>\n    <string name=\"click_single_tip\">The view does not respond to clicks within 5 seconds.</string>\n    <string name=\"click_global_tip\">The views which set global debouncing does not respond to clicks within 5 seconds.</string>\n\n\n    <!--Bus 相关-->\n    <string name=\"bus_register\">Register</string>\n    <string name=\"bus_unregister\">Unregister</string>\n    <string name=\"bus_post\">Post</string>\n    <string name=\"bus_post_basic_type\">Post Basic Type</string>\n    <string name=\"bus_post_sticky\">Post Sticky</string>\n    <string name=\"bus_post_to_io_thread\">Post To IO Thread</string>\n    <string name=\"bus_remove_sticky\">Remove Sticky</string>\n    <string name=\"bus_start_compare\">Start BusUtils Vs EventBus</string>\n    <string name=\"bus_compare_register_10000_times\">Compare Register 10000 Times</string>\n    <string name=\"bus_compare_post_to_1_subscriber_1000000_times\">Compare Post To 1 Subscriber 1000000 Times</string>\n    <string name=\"bus_compare_post_to_100_subscriber_100000_times\">Compare Post To 100 Subscribers 100000 Times</string>\n    <string name=\"bus_compare_unregister_10000_times\">Compare Unregister 10000 Times</string>\n\n    <!-- Flashlight相关 -->\n    <string name=\"flashlight_status\">Flashlight Status</string>\n\n    <!--Fragment 相关-->\n    <string name=\"fragment_zero\">Fragment0</string>\n    <string name=\"fragment_one\">Fragment1</string>\n    <string name=\"fragment_two\">Fragment2</string>\n    <string name=\"fragment_show_stack\">Show Stack</string>\n    <string name=\"fragment_add_child\">Add Child</string>\n    <string name=\"fragment_add_child_stack\">Add Child Stack</string>\n    <string name=\"fragment_add_hide\">Add Hide</string>\n    <string name=\"fragment_add_hide_stack\">Add Hide Stack</string>\n    <string name=\"fragment_add_demo1_show\">Add Demo1 Show</string>\n    <string name=\"fragment_pop\">Pop</string>\n    <string name=\"fragment_remove\">Remove</string>\n    <string name=\"fragment_pop_to_root\">Pop To Root</string>\n    <string name=\"fragment_hide_demo0_show_demo1\">Hide Demo0 Show Demo1</string>\n    <string name=\"fragment_hide_demo1_show_demo0\">Hide Demo1 Show Demo0</string>\n    <string name=\"fragment_replace\">Replace</string>\n    <string name=\"fragment_transition\">Transition</string>\n\n    <!--Image 相关-->\n    <string name=\"image_src\">Src</string>\n    <string name=\"image_add_color\">Add Color</string>\n    <string name=\"image_scale\">Scale</string>\n    <string name=\"image_clip\">Clip</string>\n    <string name=\"image_skew\">Skew</string>\n    <string name=\"image_rotate\">Rotate</string>\n    <string name=\"image_to_round\">To Round</string>\n    <string name=\"image_to_round_border\">To Round Border</string>\n    <string name=\"image_to_round_corner\">To Round Corner</string>\n    <string name=\"image_to_round_corner_border\">To Round Corner Border</string>\n    <string name=\"image_add_corner_border\">Add Corner Border</string>\n    <string name=\"image_add_circle_border\">Add Circle Border</string>\n    <string name=\"image_add_reflection\">Add Reflection</string>\n    <string name=\"image_add_text_watermark\">Add Text Watermark</string>\n    <string name=\"image_add_image_watermark\">Add Image Watermark</string>\n    <string name=\"image_to_gray\">Gray</string>\n    <string name=\"image_fast_blur\">Fast Blur</string>\n    <string name=\"image_render_script_blur\">Render Script Blur</string>\n    <string name=\"image_stack_blur\">Stack Blur</string>\n    <string name=\"image_compress_by_scale\">Compress By Scale</string>\n    <string name=\"image_compress_by_quality_half\">Compress By Quality Half</string>\n    <string name=\"image_compress_by_quality_max_size\">Compress By Quality Max Size</string>\n    <string name=\"image_compress_by_sample_size\">Compress By Sample Size</string>\n\n\n    <!--Network 相关-->\n    <string name=\"network_open_wireless_settings\">Open Wireless Settings</string>\n    <string name=\"network_data_enabled\">Mobile Data Enabled</string>\n    <string name=\"network_wifi_enabled\">Wifi Enabled</string>\n\n    <!--Keyboard 相关-->\n    <string name=\"keyboard_hide_soft_input\">Hide Soft Input</string>\n    <string name=\"keyboard_show_soft_input\">Show Soft Input</string>\n    <string name=\"keyboard_toggle_soft_input\">Toggle Soft Input</string>\n    <string name=\"keyboard_show_dialog\">Show Dialog</string>\n    <string name=\"keyboard_close_dialog\">Close Dialog</string>\n\n    <!--Language 相关-->\n    <string name=\"language_relaunch_app\">Relaunch App</string>\n    <string name=\"language_apply_simple_chinese\">Apply Simple Chinese</string>\n    <string name=\"language_apply_american\">Apply American</string>\n    <string name=\"language_apply_english\">Apply English</string>\n    <string name=\"language_apply_arabic\">Apply Arabic</string>\n    <string name=\"language_apply_system\">Apply System</string>\n\n    <!--Log 相关-->\n    <string name=\"log_switch\">Log Switch</string>\n    <string name=\"log_console_switch\">Console Switch</string>\n    <string name=\"log_console_listener_switch\">Console Listener Switch</string>\n    <string name=\"log_head_switch\">Head Switch</string>\n    <string name=\"log_file_switch\">File Switch</string>\n    <string name=\"log_file_listener_switch\">File Listener Switch</string>\n    <string name=\"log_border_switch\">Border Switch</string>\n    <string name=\"log_single_tag_switch\">Single Tag Switch</string>\n    <string name=\"log_with_no_tag\">Log With No Tag</string>\n    <string name=\"log_with_tag\">Log With Tag</string>\n    <string name=\"log_in_new_thread\">Log In New Thread</string>\n    <string name=\"log_null\">Log Null</string>\n    <string name=\"log_many_params\">Log Many Params</string>\n    <string name=\"log_long_string\">Log Long String</string>\n    <string name=\"log_to_file\">Log To File</string>\n    <string name=\"log_json\">Log Json</string>\n    <string name=\"log_xml\">Log Xml</string>\n    <string name=\"log_array\">Log Array</string>\n    <string name=\"log_throwable\">Log Throwable</string>\n    <string name=\"log_bundle\">Log Bundle</string>\n    <string name=\"log_intent\">Log Intent</string>\n    <string name=\"log_array_list\">Log Array List</string>\n    <string name=\"log_map\">Log Map</string>\n\n    <!--Messenger 相关-->\n    <string name=\"messenger_post_to_main_server\">Post To Main Server</string>\n    <string name=\"messenger_post_to_self_client\">Post To Self Client</string>\n    <string name=\"messenger_start_remote\">Start Remote</string>\n    <string name=\"messenger_register_remote_client\">Register Remote client</string>\n    <string name=\"messenger_unregister_remote_client\">Unregister Remote Client</string>\n\n    <!--Notification 相关-->\n    <string name=\"notification_notify\">Notify</string>\n    <string name=\"notification_cancel\">Cancel</string>\n    <string name=\"notification_cancel_all\">Cancel All</string>\n    <string name=\"notification_show\">Show</string>\n\n    <!--Permission 相关-->\n    <string name=\"permission_open_app_settings\">Open App Settings</string>\n    <string name=\"permission_calendar_status\">Calendar Status</string>\n    <string name=\"permission_record_audio_status\">Record Audio Status</string>\n    <string name=\"permission_calendar_and_record_audio_status\">Calendar And Record Audio Status</string>\n    <string name=\"permission_write_settings_status\">Write Settings Status</string>\n    <string name=\"permission_draw_overlays_status\">Draw Overlays Status</string>\n\n\n    <!--Phone 相关-->\n    <string name=\"phone_dial\">Dial</string>\n    <string name=\"phone_call\">Call</string>\n    <string name=\"phone_send_sms\">Send SMS</string>\n\n    <!--Process 相关-->\n    <string name=\"process_kill_all_background\">Kill All Background Processes</string>\n\n    <!--Resource 相关-->\n    <string name=\"resource_copy_file_from_assets_2_cache\">Copy File From Assets to Cache</string>\n    <string name=\"resource_copy_file_from_raw_2_cache\">Copy File From Raw to Cache</string>\n\n    <!--Screen 相关-->\n    <string name=\"screen_screenshot\">Screenshot</string>\n\n    <!--SnackBar 相关-->\n    <string name=\"snackbar_short\">Short Snackbar</string>\n    <string name=\"snackbar_short_top\">Short Snackbar Top</string>\n    <string name=\"snackbar_short_with_action\">Short Snackbar With Action</string>\n    <string name=\"snackbar_short_with_action_top\">Short Snackbar With Action Top</string>\n    <string name=\"snackbar_long\">Long Snackbar</string>\n    <string name=\"snackbar_long_with_action\">Long Snackbar With Action</string>\n    <string name=\"snackbar_indefinite\">Indefinite Snackbar</string>\n    <string name=\"snackbar_indefinite_with_action\">Indefinite Snackbar With Action</string>\n    <string name=\"snackbar_add_view\">Add View</string>\n    <string name=\"snackbar_add_view_with_action\">Add View With Action</string>\n    <string name=\"snackbar_success\">Success</string>\n    <string name=\"snackbar_warning\">Warning</string>\n    <string name=\"snackbar_error\">Error</string>\n    <string name=\"snackbar_dismiss\">Dismiss Snackbar</string>\n    <string name=\"snackbar_click\">Click</string>\n    <string name=\"snackbar_custom_view\">Custom View</string>\n    <string name=\"snackbar_click_to_dismiss\">Click To Dismiss</string>\n\n    <!--SP 相关-->\n    <string name=\"sp_put_string\">Put String</string>\n    <string name=\"sp_put_int\">Put Int</string>\n    <string name=\"sp_put_long\">Put Long</string>\n    <string name=\"sp_put_float\">Put Float</string>\n    <string name=\"sp_put_boolean\">Put Boolean</string>\n    <string name=\"sp_put_string_set\">Put String Set</string>\n    <string name=\"sp_clear\">Clear</string>\n\n    <!--Toast 相关-->\n    <string name=\"toast_show_short\">Show Short</string>\n    <string name=\"toast_show_long\">Show Long</string>\n    <string name=\"toast_show_null\">Show Null</string>\n    <string name=\"toast_show_empty\">Show Empty</string>\n    <string name=\"toast_show_green_font\">Show Green Font</string>\n    <string name=\"toast_show_bg_color\">Show Bg Color</string>\n    <string name=\"toast_show_bg_resource\">Show Bg Resource</string>\n    <string name=\"toast_show_span\">Show Span</string>\n    <string name=\"toast_show_left_icon\">Show Left Icon</string>\n    <string name=\"toast_show_dark_mode\">Show Dark Mode</string>\n    <string name=\"toast_show_long_string\">Show Long String</string>\n    <string name=\"toast_long_string\">A toast is a view containing a quick little message for the user. The ToastUtils class helps you create and show those.</string>\n    <string name=\"toast_show_custom_view\">Show Custom View</string>\n    <string name=\"toast_custom_view\">Custom View</string>\n    <string name=\"toast_show_middle\">Show Middle</string>\n    <string name=\"toast_show_top\">Show Top</string>\n    <string name=\"toast_cancel\">Cancel</string>\n    <string name=\"toast_show_toast_dialog\">Show Toast Dialog</string>\n    <string name=\"toast_show_toast_when_start_activity\">Show Toast When Start Activity</string>\n    <string name=\"toast_short\">Short</string>\n    <string name=\"toast_long\">Long</string>\n    <string name=\"toast_green_font\">Green Font</string>\n    <string name=\"toast_bg_color\">Bg Color</string>\n    <string name=\"toast_custom_bg\">Custom Bg</string>\n    <string name=\"toast_span\">Spannable String</string>\n    <string name=\"toast_middle\">Middle</string>\n    <string name=\"toast_top\">Top</string>\n\n    <!--UiMessage 相关-->\n    <string name=\"uiMessage_add_listener_id\">Add Listener Id</string>\n    <string name=\"uiMessage_remove_all_id\">Remove All Id</string>\n    <string name=\"uiMessage_add_listener\">Add Listener</string>\n    <string name=\"uiMessage_remove_listener\">Remove Listener</string>\n    <string name=\"uiMessage_send\">Send</string>\n\n    <!--Vibrate 相关-->\n    <string name=\"vibrate_1000ms\">Vibrate 1000ms</string>\n    <string name=\"vibrate_custom\">Vibrate Custom</string>\n    <string name=\"vibrate_background\">Vibrate Background</string>\n    <string name=\"vibrate_cancel\">Cancel</string>\n</resources>\n"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"BottomDialogAnimation\">\n        <item name=\"android:windowEnterAnimation\">@anim/slide_bottom_in_200</item>\n        <item name=\"android:windowExitAnimation\">@anim/slide_bottom_out_200</item>\n    </style>\n</resources>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/values-en-rUS/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"demo_language\">Language Demo</string>\n\n    <string name=\"language_apply_simple_chinese\">Apply Simple Chinese</string>\n    <string name=\"language_apply_american\">Apply American</string>\n    <string name=\"language_apply_english\">Apply English</string>\n    <string name=\"language_apply_arabic\">Apply Arabic</string>\n    <string name=\"language_apply_system\">Apply System</string>\n\n</resources>"
  },
  {
    "path": "feature/utilcode/pkg/src/main/res/values-zh-rCN/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <string name=\"demo_language\">语言例子</string>\n\n    <string name=\"language_apply_simple_chinese\">设置简体中文</string>\n    <string name=\"language_apply_american\">设置美语</string>\n    <string name=\"language_apply_english\">设置英语</string>\n    <string name=\"language_apply_arabic\">设置阿拉伯语</string>\n    <string name=\"language_apply_system\">设置系统语言</string>\n\n</resources>"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Mon May 06 10:07:23 CST 2019\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-6.7.1-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\n# Default value: -Xmx10248m -XX:MaxPermSize=256m\n# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n#org.gradle.jvmargs=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005\n\nandroid.enableJetifier=true\nandroid.useAndroidX=true\n\norg.gradle.jvmargs=-Xmx8192m -XX:MaxPermSize=2048m -XX:+HeapDumpOnOutOfMemoryError -XX:-UseGCOverheadLimit -Dfile.encoding=UTF-8\norg.gradle.daemon=true\n#org.gradle.configureondemand=true\norg.gradle.parallel=true\norg.gradle.caching=true\n\n#-Dorg.gradle.debug=true --no-daemon\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      http://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n\r\n@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windows variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "lib/base/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "lib/base/build.gradle",
    "content": "dependencies {\n    api Config.modules.lib_subutil.dep\n    api Config.modules.lib_utilcode.dep\n\n    api Config.libs.androidx_appcompat.path\n    api Config.libs.androidx_material.path\n    api Config.libs.androidx_multidex.path\n    api Config.libs.androidx_constraint.path\n    api Config.libs.kotlin.path\n    api Config.libs.free_proguard.path\n    api Config.libs.swipe_panel.path\n    api Config.libs.eventbus_lib.path\n    compileOnly Config.modules.lib_utildebug_no_op.dep\n}"
  },
  {
    "path": "lib/base/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "lib/base/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.blankj.base\">\n\n    <uses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\" />\n    <uses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\" />\n</manifest>"
  },
  {
    "path": "lib/base/src/main/java/com/blankj/base/BaseActivity.java",
    "content": "package com.blankj.base;\n\nimport android.app.Activity;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\n\nimport androidx.annotation.LayoutRes;\nimport androidx.appcompat.app.AppCompatActivity;\n\nimport com.blankj.utilcode.util.ClickUtils;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/10/24\n *     desc  : base about activity\n * </pre>\n */\npublic abstract class BaseActivity extends AppCompatActivity\n        implements IBaseView {\n\n    private View.OnClickListener mClickListener = new View.OnClickListener() {\n        @Override\n        public void onClick(View v) {\n            onDebouncingClick(v);\n        }\n    };\n\n    public View     mContentView;\n    public Activity mActivity;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        mActivity = this;\n        super.onCreate(savedInstanceState);\n        initData(getIntent().getExtras());\n        setContentView();\n        initView(savedInstanceState, mContentView);\n        doBusiness();\n    }\n\n    @Override\n    public void setContentView() {\n        if (bindLayout() <= 0) return;\n        mContentView = LayoutInflater.from(this).inflate(bindLayout(), null);\n        setContentView(mContentView);\n    }\n\n    public void applyDebouncingClickListener(View... views) {\n        ClickUtils.applyGlobalDebouncing(views, mClickListener);\n        ClickUtils.applyPressedViewScale(views);\n    }\n}\n\n"
  },
  {
    "path": "lib/base/src/main/java/com/blankj/base/BaseApplication.java",
    "content": "package com.blankj.base;\n\nimport android.app.Application;\nimport android.content.Context;\n\nimport androidx.multidex.MultiDex;\n\nimport com.blankj.utilcode.util.AppUtils;\nimport com.blankj.utilcode.util.CrashUtils;\nimport com.blankj.utilcode.util.LogUtils;\nimport com.blankj.utilcode.util.ProcessUtils;\nimport com.blankj.utildebug.DebugUtils;\nimport com.blankj.utildebug.debug.IDebug;\n\nimport java.util.ArrayList;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/11/16\n *     desc  : base about application\n * </pre>\n */\npublic class BaseApplication extends Application {\n\n    private static BaseApplication sInstance;\n\n    public static BaseApplication getInstance() {\n        return sInstance;\n    }\n\n    private Boolean isDebug;\n    private Boolean isMainProcess;\n\n    @Override\n    protected void attachBaseContext(Context base) {\n        super.attachBaseContext(base);\n        MultiDex.install(this);\n    }\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        sInstance = this;\n        initLog();\n        initCrash();\n        initDebugMenu();\n    }\n\n    // init it in ur application\n    public void initLog() {\n        LogUtils.Config config = LogUtils.getConfig()\n                .setLogSwitch(isDebug())// 设置 log 总开关，包括输出到控制台和文件，默认开\n                .setConsoleSwitch(isDebug())// 设置是否输出到控制台开关，默认开\n                .setGlobalTag(null)// 设置 log 全局标签，默认为空\n                // 当全局标签不为空时，我们输出的 log 全部为该 tag，\n                // 为空时，如果传入的 tag 为空那就显示类名，否则显示 tag\n                .setLogHeadSwitch(true)// 设置 log 头信息开关，默认为开\n                .setLog2FileSwitch(false)// 打印 log 时是否存到文件的开关，默认关\n                .setDir(\"\")// 当自定义路径为空时，写入应用的/cache/log/目录中\n                .setFilePrefix(\"\")// 当文件前缀为空时，默认为\"util\"，即写入文件为\"util-yyyy-MM-dd$fileExtension\"\n                .setFileExtension(\".log\")// 设置日志文件后缀\n                .setBorderSwitch(true)// 输出日志是否带边框开关，默认开\n                .setSingleTagSwitch(true)// 一条日志仅输出一条，默认开，为美化 AS 3.1 的 Logcat\n                .setConsoleFilter(LogUtils.V)// log 的控制台过滤器，和 logcat 过滤器同理，默认 Verbose\n                .setFileFilter(LogUtils.V)// log 文件过滤器，和 logcat 过滤器同理，默认 Verbose\n                .setStackDeep(1)// log 栈深度，默认为 1\n                .setStackOffset(0)// 设置栈偏移，比如二次封装的话就需要设置，默认为 0\n                .setSaveDays(3)// 设置日志可保留天数，默认为 -1 表示无限时长\n                // 新增 ArrayList 格式化器，默认已支持 Array, Throwable, Bundle, Intent 的格式化输出\n                .addFormatter(new LogUtils.IFormatter<ArrayList>() {\n                    @Override\n                    public String format(ArrayList arrayList) {\n                        return \"LogUtils Formatter ArrayList { \" + arrayList.toString() + \" }\";\n                    }\n                })\n                .addFileExtraHead(\"ExtraKey\", \"ExtraValue\");\n        LogUtils.i(config.toString());\n    }\n\n    private void initCrash() {\n        CrashUtils.init(new CrashUtils.OnCrashListener() {\n            @Override\n            public void onCrash(CrashUtils.CrashInfo crashInfo) {\n                crashInfo.addExtraHead(\"extraKey\", \"extraValue\");\n                LogUtils.e(crashInfo.toString());\n                AppUtils.relaunchApp();\n            }\n        });\n    }\n\n    private void initDebugMenu() {\n        DebugUtils.addDebugs(new ArrayList<IDebug>());\n    }\n\n    private boolean isDebug() {\n        if (isDebug == null) isDebug = AppUtils.isAppDebug();\n        return isDebug;\n    }\n\n    public boolean isMainProcess() {\n        if (isMainProcess == null) isMainProcess = ProcessUtils.isMainProcess();\n        return isMainProcess;\n    }\n}\n"
  },
  {
    "path": "lib/base/src/main/java/com/blankj/base/BaseFragment.java",
    "content": "package com.blankj.base;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.blankj.utilcode.util.AppUtils;\nimport com.blankj.utilcode.util.ClickUtils;\n\nimport androidx.annotation.IdRes;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentManager;\nimport androidx.fragment.app.FragmentTransaction;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/03/28\n *     desc  : base about v4-fragment\n * </pre>\n */\npublic abstract class BaseFragment extends Fragment\n        implements IBaseView {\n\n    private static Boolean isDebug;\n\n    private static final String STATE_SAVE_IS_HIDDEN = \"STATE_SAVE_IS_HIDDEN\";\n\n    private View.OnClickListener mClickListener = new View.OnClickListener() {\n        @Override\n        public void onClick(View v) {\n            onDebouncingClick(v);\n        }\n    };\n\n    protected AppCompatActivity mActivity;\n    protected LayoutInflater    mInflater;\n    protected View              mContentView;\n\n    protected boolean mIsVisibleToUser;\n    protected boolean mIsBusinessDone;\n    protected boolean mIsInPager;\n\n    /**\n     * @return true true {@link #doBusiness()} will lazy in view pager, false otherwise\n     */\n    public boolean isLazy() {\n        return false;\n    }\n\n    @Override\n    public void setUserVisibleHint(boolean isVisibleToUser) {\n        log(\"setUserVisibleHint: \" + isVisibleToUser);\n        super.setUserVisibleHint(isVisibleToUser);\n        mIsInPager = true;\n        if (isVisibleToUser) mIsVisibleToUser = true;\n        if (isLazy()) {\n            if (!mIsBusinessDone && isVisibleToUser && mContentView != null) {\n                mIsBusinessDone = true;\n                doBusiness();\n            }\n        }\n    }\n\n    @Override\n    public void onAttach(Context context) {\n        log(\"onAttach\");\n        super.onAttach(context);\n        mActivity = (AppCompatActivity) context;\n    }\n\n    @Override\n    public void onCreate(@Nullable Bundle savedInstanceState) {\n        log(\"onCreate\");\n        super.onCreate(savedInstanceState);\n        FragmentManager fm = getFragmentManager();\n        if (fm == null) return;\n        if (savedInstanceState != null) {\n            boolean isSupportHidden = savedInstanceState.getBoolean(STATE_SAVE_IS_HIDDEN);\n            FragmentTransaction ft = fm.beginTransaction();\n            if (isSupportHidden) {\n                ft.hide(this);\n            } else {\n                ft.show(this);\n            }\n            ft.commitNowAllowingStateLoss();\n        }\n        Bundle bundle = getArguments();\n        initData(bundle);\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n        log(\"onCreateView\");\n        super.onCreateView(inflater, container, savedInstanceState);\n        mInflater = inflater;\n        setContentView();\n        return mContentView;\n    }\n\n    @Override\n    public void setContentView() {\n        if (bindLayout() <= 0) return;\n        mContentView = mInflater.inflate(bindLayout(), null);\n    }\n\n    @Override\n    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n        log(\"onViewCreated\");\n        super.onViewCreated(view, savedInstanceState);\n    }\n\n    @Override\n    public void onActivityCreated(@Nullable Bundle savedInstanceState) {\n        log(\"onActivityCreated\");\n        super.onActivityCreated(savedInstanceState);\n        initView(savedInstanceState, mContentView);\n        if (!mIsInPager || !isLazy() || mIsVisibleToUser) {\n            mIsBusinessDone = true;\n            doBusiness();\n        }\n    }\n\n    @Override\n    public void onHiddenChanged(boolean hidden) {\n        log(\"onHiddenChanged: \" + hidden);\n        super.onHiddenChanged(hidden);\n    }\n\n    @Override\n    public void onDestroyView() {\n        log(\"onDestroyView\");\n        super.onDestroyView();\n        mIsVisibleToUser = false;\n        mIsBusinessDone = false;\n    }\n\n    @Override\n    public void onSaveInstanceState(@NonNull Bundle outState) {\n        log(\"onSaveInstanceState\");\n        super.onSaveInstanceState(outState);\n        outState.putBoolean(STATE_SAVE_IS_HIDDEN, isHidden());\n    }\n\n    @Override\n    public void onDestroy() {\n        log(\"onDestroy\");\n        super.onDestroy();\n    }\n\n    public void applyDebouncingClickListener(View... views) {\n        ClickUtils.applyGlobalDebouncing(views, mClickListener);\n    }\n\n    public <T extends View> T findViewById(@IdRes int id) {\n        if (mContentView == null) throw new NullPointerException(\"ContentView is null.\");\n        return mContentView.findViewById(id);\n    }\n\n    protected void log(String msg) {\n        if (isDebug == null) {\n            isDebug = AppUtils.isAppDebug();\n        }\n        if (isDebug) {\n            Log.d(\"BaseFragment\", getClass().getSimpleName() + \": \" + msg);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/base/src/main/java/com/blankj/base/IBaseView.java",
    "content": "package com.blankj.base;\n\nimport android.os.Bundle;\nimport android.view.View;\n\nimport androidx.annotation.LayoutRes;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/11/16\n *     desc  :\n * </pre>\n */\npublic interface IBaseView {\n\n    void initData(@Nullable Bundle bundle);\n\n    int bindLayout();\n\n    void setContentView();\n\n    void initView(@Nullable Bundle savedInstanceState, @Nullable View contentView);\n\n    void doBusiness();\n\n    void onDebouncingClick(@NonNull View view);\n}\n"
  },
  {
    "path": "lib/base/src/main/java/com/blankj/base/dialog/BaseDialog.java",
    "content": "package com.blankj.base.dialog;\n\nimport android.app.Activity;\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.view.View;\nimport android.view.Window;\n\nimport com.blankj.utilcode.util.ActivityUtils;\nimport com.blankj.utilcode.util.ThreadUtils;\n\nimport androidx.annotation.NonNull;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/11/11\n *     desc  :\n * </pre>\n */\npublic abstract class BaseDialog extends Dialog {\n\n    protected Activity mActivity;\n\n    public abstract int bindLayout();\n\n    public abstract void initView(BaseDialog dialog, View contentView);\n\n    public abstract void setWindowStyle(Window window);\n\n    public BaseDialog(@NonNull Context context) {\n        this(context, 0);\n    }\n\n    public BaseDialog(@NonNull Context context, int themeResId) {\n        super(context, themeResId);\n        Activity activity = ActivityUtils.getActivityByContext(context);\n        if (activity == null) {\n            throw new IllegalArgumentException(\"context is not instance of Activity.\");\n        }\n        mActivity = activity;\n    }\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        View contentView = View.inflate(mActivity, bindLayout(), null);\n        setContentView(contentView);\n        initView(this, contentView);\n        setWindowStyle(getWindow());\n    }\n\n    @Override\n    public void show() {\n        ThreadUtils.runOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                if (ActivityUtils.isActivityAlive(getContext())) {\n                    BaseDialog.super.show();\n                }\n            }\n        });\n    }\n\n    @Override\n    public void dismiss() {\n        ThreadUtils.runOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                if (ActivityUtils.isActivityAlive(getContext())) {\n                    BaseDialog.super.dismiss();\n                }\n            }\n        });\n    }\n}"
  },
  {
    "path": "lib/base/src/main/java/com/blankj/base/dialog/BaseDialogFragment.java",
    "content": "package com.blankj.base.dialog;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.app.Dialog;\nimport android.content.Context;\nimport android.content.DialogInterface;\nimport android.os.Bundle;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.Window;\n\nimport com.blankj.utilcode.util.ActivityUtils;\nimport com.blankj.utilcode.util.LogUtils;\nimport com.blankj.utilcode.util.ThreadUtils;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.DialogFragment;\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentActivity;\nimport androidx.fragment.app.FragmentManager;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/11/11\n *     desc  :\n * </pre>\n */\npublic class BaseDialogFragment extends DialogFragment {\n\n    protected DialogLayoutCallback mDialogLayoutCallback;\n    protected DialogCallback       mDialogCallback;\n\n    protected FragmentActivity mActivity;\n    protected View             mContentView;\n\n    public BaseDialogFragment init(Context context, DialogLayoutCallback listener) {\n        mActivity = getFragmentActivity(context);\n        mDialogLayoutCallback = listener;\n        return this;\n    }\n\n    public BaseDialogFragment init(Context context, DialogCallback dialogCallback) {\n        mActivity = getFragmentActivity(context);\n        mDialogCallback = dialogCallback;\n        return this;\n    }\n\n    private FragmentActivity getFragmentActivity(Context context) {\n        Activity activity = ActivityUtils.getActivityByContext(context);\n        if (activity == null) return null;\n        if (activity instanceof FragmentActivity) {\n            return (FragmentActivity) activity;\n        }\n        LogUtils.w(context + \"not instanceof FragmentActivity\");\n        return null;\n    }\n\n    @Override\n    public int getTheme() {\n        if (mDialogLayoutCallback != null) {\n            int theme = mDialogLayoutCallback.bindTheme();\n            if (theme != View.NO_ID) {\n                return theme;\n            }\n        }\n        return super.getTheme();\n    }\n\n    @NonNull\n    public Dialog onCreateDialog(Bundle savedInstanceState) {\n        Dialog dialog;\n        if (mDialogCallback != null) {\n            dialog = mDialogCallback.bindDialog(mActivity);\n        } else {\n            dialog = super.onCreateDialog(savedInstanceState);\n        }\n        Window window = dialog.getWindow();\n        if (mDialogCallback != null) {\n            mDialogCallback.setWindowStyle(window);\n        } else if (mDialogLayoutCallback != null) {\n            mDialogLayoutCallback.setWindowStyle(window);\n        }\n        return dialog;\n    }\n\n    @Nullable\n    @Override\n    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {\n        if (mDialogLayoutCallback != null) {\n            return inflater.inflate(mDialogLayoutCallback.bindLayout(), container, false);\n        }\n        return super.onCreateView(inflater, container, savedInstanceState);\n    }\n\n    @Override\n    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {\n        if (mDialogLayoutCallback != null) {\n            mDialogLayoutCallback.initView(this, view);\n            return;\n        }\n        super.onViewCreated(view, savedInstanceState);\n    }\n\n    @Override\n    public void onCancel(DialogInterface dialog) {\n        super.onCancel(dialog);\n        if (mDialogLayoutCallback != null) {\n            mDialogLayoutCallback.onCancel(this);\n        }\n    }\n\n    @Override\n    public void onDismiss(DialogInterface dialog) {\n        super.onDismiss(dialog);\n        if (mDialogLayoutCallback != null) {\n            mDialogLayoutCallback.onDismiss(this);\n        }\n    }\n\n    public void show() {\n        show(getClass().getSimpleName());\n    }\n\n    public void show(final String tag) {\n        ThreadUtils.runOnUiThread(new Runnable() {\n            @SuppressLint(\"CommitTransaction\")\n            @Override\n            public void run() {\n                if (ActivityUtils.isActivityAlive(mActivity)) {\n                    FragmentManager fm = mActivity.getSupportFragmentManager();\n                    Fragment prev = fm.findFragmentByTag(tag);\n                    if (prev != null) {\n                        fm.beginTransaction().remove(prev);\n                    }\n                    BaseDialogFragment.super.show(fm, tag);\n                }\n            }\n        });\n    }\n\n    @Override\n    public void dismiss() {\n        ThreadUtils.runOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                if (ActivityUtils.isActivityAlive(mActivity)) {\n                    BaseDialogFragment.super.dismissAllowingStateLoss();\n                }\n            }\n        });\n    }\n}\n\n\n"
  },
  {
    "path": "lib/base/src/main/java/com/blankj/base/dialog/DialogCallback.java",
    "content": "package com.blankj.base.dialog;\n\nimport android.app.Activity;\nimport android.app.Dialog;\nimport android.view.Window;\n\nimport androidx.annotation.NonNull;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/11/12\n *     desc  :\n * </pre>\n */\npublic interface DialogCallback {\n    @NonNull\n    Dialog bindDialog(Activity activity);\n\n    void setWindowStyle(Window window);\n}\n"
  },
  {
    "path": "lib/base/src/main/java/com/blankj/base/dialog/DialogLayoutCallback.java",
    "content": "package com.blankj.base.dialog;\n\nimport android.view.View;\nimport android.view.Window;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/11/12\n *     desc  :\n * </pre>\n */\npublic interface DialogLayoutCallback {\n    int bindTheme();\n\n    int bindLayout();\n\n    void initView(BaseDialogFragment dialog, View contentView);\n\n    void setWindowStyle(Window window);\n\n    void onCancel(BaseDialogFragment dialog);\n\n    void onDismiss(BaseDialogFragment dialog);\n}\n"
  },
  {
    "path": "lib/base/src/main/java/com/blankj/base/mvp/BaseModel.java",
    "content": "package com.blankj.base.mvp;\n\nimport android.util.Log;\n\nimport androidx.annotation.CallSuper;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/02\n *     desc  :\n * </pre>\n */\npublic abstract class BaseModel {\n\n    private static final String TAG = BaseView.TAG;\n\n    public abstract void onCreate();\n\n    @CallSuper\n    public void onDestroy() {\n        Log.i(TAG, \"destroy model: \" + getClass().getSimpleName());\n    }\n}\n"
  },
  {
    "path": "lib/base/src/main/java/com/blankj/base/mvp/BasePresenter.java",
    "content": "package com.blankj.base.mvp;\n\nimport android.util.Log;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport androidx.annotation.CallSuper;\nimport androidx.lifecycle.ViewModel;\nimport androidx.lifecycle.ViewModelProvider;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/02\n *     desc  :\n * </pre>\n */\npublic abstract class BasePresenter<V extends BaseView> {\n\n    private static final String TAG = BaseView.TAG;\n\n    private V                                          mView;\n    private Map<Class<? extends BaseModel>, BaseModel> mModelMap = new HashMap<>();\n    private boolean                                    isAlive   = true;\n\n    public abstract void onBindView();\n\n    void bindView(V view) {\n        this.mView = view;\n        onBindView();\n    }\n\n    public V getView() {\n        return mView;\n    }\n\n    public <M extends BaseModel> M getModel(Class<M> modelClass) {\n        BaseModel baseModel = mModelMap.get(modelClass);\n        if (baseModel != null) {\n            //noinspection unchecked\n            return (M) baseModel;\n        }\n        try {\n            M model = modelClass.newInstance();\n            mModelMap.put(modelClass, model);\n            model.onCreate();\n            return model;\n        } catch (IllegalAccessException e) {\n            Log.e(\"BasePresenter\", \"getModel\", e);\n        } catch (InstantiationException e) {\n            Log.e(\"BasePresenter\", \"getModel\", e);\n        }\n        return null;\n    }\n\n    @CallSuper\n    public void onDestroy() {\n        Log.i(TAG, \"destroy presenter: \" + getClass().getSimpleName());\n        isAlive = false;\n        for (BaseModel model : mModelMap.values()) {\n            if (model != null) {\n                model.onDestroy();\n            }\n        }\n        mModelMap.clear();\n    }\n\n    public boolean isAlive() {\n        return isAlive;\n    }\n}\n"
  },
  {
    "path": "lib/base/src/main/java/com/blankj/base/mvp/BaseView.java",
    "content": "package com.blankj.base.mvp;\n\nimport android.util.Log;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport androidx.annotation.CallSuper;\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentActivity;\nimport androidx.lifecycle.Lifecycle;\nimport androidx.lifecycle.LifecycleObserver;\nimport androidx.lifecycle.OnLifecycleEvent;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/02\n *     desc  :\n * </pre>\n */\npublic class BaseView<V extends BaseView> implements LifecycleObserver {\n\n    public static final String TAG = \"UtilsMVP\";\n\n    private FragmentActivity                mActivity;\n    private Fragment                        mFragment;\n    private Lifecycle                       mLifecycle;\n    private Map<Class<?>, BasePresenter<V>> mPresenterMap = new HashMap<>();\n\n    public BaseView(Fragment fragment) {\n        mFragment = fragment;\n        mActivity = fragment.getActivity();\n        mLifecycle = mFragment.getLifecycle();\n        addLifecycle(this);\n    }\n\n    public BaseView(FragmentActivity activity) {\n        mActivity = activity;\n        mLifecycle = mActivity.getLifecycle();\n        addLifecycle(this);\n    }\n\n    public BaseView(Lifecycle lifecycle) {\n        mLifecycle = lifecycle;\n        addLifecycle(this);\n    }\n\n    public <T extends FragmentActivity> T getActivity() {\n        if (mActivity == null) {\n            return null;\n        }\n        //noinspection unchecked\n        return (T) mActivity;\n    }\n\n    public <T extends Fragment> T getFragment() {\n        if (mFragment == null) {\n            return null;\n        }\n        //noinspection unchecked\n        return (T) mFragment;\n    }\n\n    public V addPresenter(BasePresenter<V> presenter) {\n        if (presenter == null) return (V) this;\n        mPresenterMap.put(presenter.getClass(), presenter);\n        //noinspection unchecked\n        presenter.bindView((V) this);\n        return (V) this;\n    }\n\n    public <P extends BasePresenter<V>> P getPresenter(Class<P> presenterClass) {\n        if (presenterClass == null) {\n            throw new IllegalArgumentException(\"presenterClass is null!\");\n        }\n        BasePresenter<V> basePresenter = mPresenterMap.get(presenterClass);\n        if (basePresenter == null) {\n            throw new IllegalArgumentException(\"presenter of <\" + presenterClass.getSimpleName() + \"> is not added!\");\n        }\n        //noinspection unchecked\n        return (P) basePresenter;\n    }\n\n    @CallSuper\n    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)\n    public void onDestroy() {\n        Log.i(TAG, \"destroy view: \" + getClass().getSimpleName());\n        removeLifecycle(this);\n        for (BasePresenter<V> presenter : mPresenterMap.values()) {\n            if (presenter != null) {\n                presenter.onDestroy();\n            }\n        }\n        mPresenterMap.clear();\n    }\n\n    private void addLifecycle(LifecycleObserver observer) {\n        if (mLifecycle == null) {\n            Log.w(TAG, \"addLifecycle: mLifecycle is null\");\n            return;\n        }\n        mLifecycle.addObserver(observer);\n    }\n\n    private void removeLifecycle(LifecycleObserver observer) {\n        if (mLifecycle == null) {\n            Log.w(TAG, \"removeLifecycle: mLifecycle is null\");\n            return;\n        }\n        mLifecycle.removeObserver(observer);\n    }\n}\n"
  },
  {
    "path": "lib/base/src/main/java/com/blankj/base/rv/BaseItem.java",
    "content": "package com.blankj.base.rv;\n\nimport android.util.SparseArray;\nimport android.util.SparseIntArray;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport java.util.List;\n\nimport androidx.annotation.LayoutRes;\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/03/16\n *     desc  :\n * </pre>\n */\npublic abstract class BaseItem<T extends BaseItem> {\n\n    private static final SparseIntArray    LAYOUT_SPARSE_ARRAY = new SparseIntArray();\n    private static final SparseArray<View> VIEW_SPARSE_ARRAY   = new SparseArray<>();\n    public boolean isBindViewHolder = false;\n\n    static ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n        int layoutByType = LAYOUT_SPARSE_ARRAY.get(viewType, -1);\n        if (layoutByType != -1) {\n            return new ItemViewHolder(LayoutInflater.from(parent.getContext()).inflate(layoutByType, parent, false));\n        }\n        View viewByType = VIEW_SPARSE_ARRAY.get(viewType);\n        if (viewByType != null) {\n            return new ItemViewHolder(viewByType);\n        }\n        throw new RuntimeException(\"onCreateViewHolder: get holder from view type failed.\");\n    }\n\n    public abstract void bind(@NonNull final ItemViewHolder holder, final int position);\n\n    public void partialUpdate(List<Object> payloads) {\n    }\n\n    void bindViewHolder(@NonNull final ItemViewHolder holder, final int position) {\n        isBindViewHolder = true;\n        if (mOnItemClickListener != null) {\n            holder.itemView.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    if (mOnItemClickListener != null) {\n                        //noinspection unchecked\n                        mOnItemClickListener.onItemClick(holder, (T) BaseItem.this, getIndex());\n                    }\n                }\n            });\n        } else {\n            holder.itemView.setOnClickListener(null);\n        }\n        if (mOnItemLongClickListener != null) {\n            holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {\n                @Override\n                public boolean onLongClick(View v) {\n                    if (mOnItemLongClickListener != null) {\n                        //noinspection unchecked\n                        return mOnItemLongClickListener.onItemLongClick(holder, (T) BaseItem.this, getIndex());\n                    }\n                    return false;\n                }\n            });\n        } else {\n            holder.itemView.setOnLongClickListener(null);\n        }\n        bind(holder, position);\n    }\n\n    public void onViewRecycled(@NonNull final ItemViewHolder holder, final int position) {\n        isBindViewHolder = false;\n    }\n\n    public long getItemId() {\n        return RecyclerView.NO_ID;\n    }\n\n    private int viewType;\n    BaseItemAdapter<T> mAdapter;\n    private OnItemClickListener<T>     mOnItemClickListener;\n    private OnItemLongClickListener<T> mOnItemLongClickListener;\n\n    public BaseItem(@LayoutRes int layoutId) {\n        viewType = getViewTypeByLayoutId(layoutId);\n        LAYOUT_SPARSE_ARRAY.put(viewType, layoutId);\n    }\n\n    public BaseItem(@NonNull View view) {\n        viewType = getViewTypeByView(view);\n        VIEW_SPARSE_ARRAY.put(viewType, view);\n    }\n\n    public int getViewType() {\n        return viewType;\n    }\n\n    public BaseItemAdapter<T> getAdapter() {\n        return mAdapter;\n    }\n\n    public boolean isViewType(@LayoutRes int layoutId) {\n        return viewType == getViewTypeByLayoutId(layoutId);\n    }\n\n    public boolean isViewType(@NonNull View view) {\n        return viewType == getViewTypeByView(view);\n    }\n\n    private int getViewTypeByLayoutId(@LayoutRes int layoutId) {\n        return layoutId + getClass().hashCode();\n    }\n\n    private int getViewTypeByView(@NonNull View view) {\n        return view.hashCode() + getClass().hashCode();\n    }\n\n    public void update() {\n        if (getAdapter() == null) return;\n        //noinspection unchecked\n        getAdapter().updateItem((T) this);\n    }\n\n    public List<T> getItems() {\n        return getAdapter().getItems();\n    }\n\n    public int getCount() {\n        return getAdapter().getItemCount();\n    }\n\n    public int getIndex() {\n        //noinspection SuspiciousMethodCalls\n        return getAdapter().getItems().indexOf(this);\n    }\n\n    public OnItemClickListener<T> getOnItemClickListener() {\n        return mOnItemClickListener;\n    }\n\n    public T setOnItemClickListener(OnItemClickListener<T> onItemClickListener) {\n        mOnItemClickListener = onItemClickListener;\n        return (T) this;\n    }\n\n    public OnItemLongClickListener<T> getOnItemLongClickListener() {\n        return mOnItemLongClickListener;\n    }\n\n    public T setOnItemLongClickListener(OnItemLongClickListener<T> onItemLongClickListener) {\n        mOnItemLongClickListener = onItemLongClickListener;\n        return (T) this;\n    }\n\n    public interface OnItemClickListener<T> {\n        void onItemClick(ItemViewHolder holder, T item, int position);\n    }\n\n    public interface OnItemLongClickListener<T> {\n        boolean onItemLongClick(ItemViewHolder holder, T item, int position);\n    }\n}\n"
  },
  {
    "path": "lib/base/src/main/java/com/blankj/base/rv/BaseItemAdapter.java",
    "content": "package com.blankj.base.rv;\n\nimport android.view.ViewGroup;\n\nimport androidx.annotation.IntRange;\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/08/22\n *     desc  :\n * </pre>\n */\npublic class BaseItemAdapter<Item extends BaseItem> extends RecyclerView.Adapter<ItemViewHolder> {\n\n    public  List<Item>   mItems;\n    private RecyclerView mRecyclerView;\n\n    public BaseItemAdapter() {\n        this(false);\n    }\n\n    public BaseItemAdapter(boolean hasStableIds) {\n        setHasStableIds(hasStableIds);\n    }\n\n    @Override\n    public final int getItemViewType(int position) {\n        Item item = mItems.get(position);\n        item.mAdapter = this;\n        return item.getViewType();\n    }\n\n    @Override\n    public long getItemId(int position) {\n        return mItems.get(position).getItemId();\n    }\n\n    @NonNull\n    @Override\n    public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n        return Item.onCreateViewHolder(parent, viewType);\n    }\n\n    @Override\n    public final void onBindViewHolder(@NonNull ItemViewHolder holder, int position) {\n        mItems.get(position).bindViewHolder(holder, position);\n    }\n\n    @Override\n    public void onBindViewHolder(@NonNull ItemViewHolder holder, int position, @NonNull List<Object> payloads) {\n        if (payloads.isEmpty()) {\n            super.onBindViewHolder(holder, position, payloads);\n            return;\n        }\n        mItems.get(position).partialUpdate(payloads);\n    }\n\n    @Override\n    public int getItemCount() {\n        return mItems.size();\n    }\n\n    @Override\n    public void onViewRecycled(@NonNull ItemViewHolder holder) {\n        super.onViewRecycled(holder);\n        int position = holder.getAdapterPosition();\n        if (position < 0 || position >= mItems.size()) {\n            return;\n        }\n        mItems.get(position).onViewRecycled(holder, position);\n    }\n\n    @Override\n    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {\n        super.onAttachedToRecyclerView(recyclerView);\n        mRecyclerView = recyclerView;\n    }\n\n    public RecyclerView getRecyclerView() {\n        return mRecyclerView;\n    }\n\n    public void setItems(@NonNull final List<Item> items) {\n        mItems = items;\n    }\n\n    public List<Item> getItems() {\n        return Collections.unmodifiableList(mItems);\n    }\n\n    public Item getItem(@IntRange(from = 0) final int position) {\n        return mItems.get(position);\n    }\n\n    public boolean isEmpty() {\n        return mItems.isEmpty();\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // id\n    ///////////////////////////////////////////////////////////////////////////\n\n    public Item getItemById(final long id) {\n        int itemIndex = getItemIndexById(id);\n        if (itemIndex != -1) {\n            return mItems.get(itemIndex);\n        } else {\n            return null;\n        }\n    }\n\n    public int getItemIndexById(final long id) {\n        for (int i = 0; i < mItems.size(); i++) {\n            if (getItemId(i) == id) {\n                return i;\n            }\n        }\n        return -1;\n    }\n\n    public boolean hasItemWithId(final long id) {\n        return getItemIndexById(id) != -1;\n    }\n\n    public int replaceItemById(final long id, @NonNull final Item item) {\n        return replaceItemById(id, item, false);\n    }\n\n    public int replaceItemById(final long id, @NonNull final Item item, boolean notifyChanged) {\n        int itemIndex = getItemIndexById(id);\n        if (itemIndex != -1) {\n            replaceItem(itemIndex, item, notifyChanged);\n        }\n        return itemIndex;\n    }\n\n    public int removeItemById(final long id) {\n        return removeItemById(id, false);\n    }\n\n    public int removeItemById(final long id, boolean notifyRemoved) {\n        for (int i = 0; i < mItems.size(); i++) {\n            if (getItemId(i) == id) {\n                removeItem(i, notifyRemoved);\n                return i;\n            }\n        }\n        return -1;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // operate\n    ///////////////////////////////////////////////////////////////////////////\n\n    public void updateItem(@NonNull final Item item) {\n        updateItems(item, 1, null);\n    }\n\n    public void updateItem(@IntRange(from = 0) final int index) {\n        updateItems(index, 1, null);\n    }\n\n    public void updateItem(@NonNull final Item item, Object payload) {\n        updateItems(item, 1, payload);\n    }\n\n    public void updateItem(@IntRange(from = 0) final int index, Object payload) {\n        updateItems(index, 1, payload);\n    }\n\n    public void updateItems(@NonNull final Item item, int itemCount) {\n        int itemIndex = mItems.indexOf(item);\n        if (itemIndex != -1) {\n            updateItems(itemIndex, itemCount);\n        }\n    }\n\n    public void updateItems(@IntRange(from = 0) final int index, int itemCount) {\n        updateItems(index, itemCount, null);\n    }\n\n    public void updateItems(@NonNull final Item item, int itemCount, Object payload) {\n        int itemIndex = mItems.indexOf(item);\n        if (itemIndex != -1) {\n            updateItems(itemIndex, itemCount, payload);\n        }\n    }\n\n    public void updateItems(@IntRange(from = 0) final int index, int itemCount, Object payload) {\n        notifyItemRangeChanged(index, itemCount, payload);\n    }\n\n    public void addItem(@NonNull final Item item) {\n        addItem(item, false);\n    }\n\n    public void addItem(@NonNull final Item item, boolean notifyInserted) {\n        mItems.add(item);\n        if (notifyInserted) notifyItemInserted(mItems.size() - 1);\n    }\n\n    public void addItem(@IntRange(from = 0) final int index, @NonNull final Item item) {\n        addItem(index, item, false);\n    }\n\n    public void addItem(@IntRange(from = 0) final int index, @NonNull final Item item, boolean notifyInserted) {\n        mItems.add(index, item);\n        if (notifyInserted) notifyItemInserted(index);\n    }\n\n    public void addItems(@NonNull final List<Item> items) {\n        addItems(items, false);\n    }\n\n    public void addItems(@NonNull final List<Item> items, boolean notifyInserted) {\n        mItems.addAll(items);\n        if (notifyInserted) notifyItemRangeInserted(mItems.size() - items.size() - 1, items.size());\n    }\n\n    public void addItems(@IntRange(from = 0) final int index, @NonNull final List<Item> items) {\n        addItems(index, items, false);\n    }\n\n    public void addItems(@IntRange(from = 0) final int index, @NonNull final List<Item> items, boolean notifyInserted) {\n        mItems.addAll(index, items);\n        if (notifyInserted) notifyItemRangeInserted(index, items.size());\n    }\n\n    public void swapItem(@IntRange(from = 0) final int firstIndex, @IntRange(from = 0) final int secondIndex) {\n        swapItem(firstIndex, secondIndex, false);\n    }\n\n    public void swapItem(@IntRange(from = 0) final int firstIndex,\n                         @IntRange(from = 0) final int secondIndex, boolean notifyMoved) {\n        Collections.swap(mItems, firstIndex, secondIndex);\n        if (notifyMoved) notifyItemMoved(firstIndex, secondIndex);\n    }\n\n    public Item replaceItem(@IntRange(from = 0) final int index, @NonNull final Item item) {\n        return replaceItem(index, item, false);\n    }\n\n    public Item replaceItem(@IntRange(from = 0) final int index, @NonNull final Item item, boolean notifyChanged) {\n        Item prevItem = mItems.set(index, item);\n        if (notifyChanged) notifyItemChanged(index);\n        return prevItem;\n    }\n\n    public boolean replaceItems(@NonNull final List<Item> items) {\n        return replaceItems(items, false);\n    }\n\n    public boolean replaceItems(@NonNull final List<Item> items, boolean notifyChanged) {\n        mItems.clear();\n        boolean added = mItems.addAll(items);\n        if (notifyChanged) notifyDataSetChanged();\n        return added;\n    }\n\n    public Item removeItem(@IntRange(from = 0) final int index) {\n        return removeItem(index, false);\n    }\n\n    public Item removeItem(@IntRange(from = 0) final int index, boolean notifyRemoved) {\n        Item removedItem = mItems.remove(index);\n        if (notifyRemoved) notifyItemRemoved(index);\n        return removedItem;\n    }\n\n    public int removeItem(@NonNull final Item item) {\n        return removeItem(item, false);\n    }\n\n    public int removeItem(@NonNull final Item item, boolean notifyRemoved) {\n        int itemIndex = mItems.indexOf(item);\n        if (itemIndex != -1) {\n            mItems.remove(itemIndex);\n            if (notifyRemoved) notifyItemRemoved(itemIndex);\n        }\n        return itemIndex;\n    }\n\n    public void clear() {\n        clear(false);\n    }\n\n    public void clear(boolean notifyDataSetChanged) {\n        mItems.clear();\n        if (notifyDataSetChanged) notifyDataSetChanged();\n    }\n\n    public void sortItems(@NonNull final Comparator<Item> comparator) {\n        sortItems(comparator, false);\n    }\n\n    public void sortItems(@NonNull final Comparator<Item> comparator, boolean notifyDataSetChanged) {\n        Collections.sort(mItems, comparator);\n        if (notifyDataSetChanged) notifyDataSetChanged();\n    }\n}\n"
  },
  {
    "path": "lib/base/src/main/java/com/blankj/base/rv/ItemViewHolder.java",
    "content": "package com.blankj.base.rv;\n\nimport androidx.annotation.IdRes;\nimport androidx.recyclerview.widget.RecyclerView;\nimport android.util.SparseArray;\nimport android.view.View;\n\nimport java.util.List;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/08/22\n *     desc  :\n * </pre>\n */\npublic class ItemViewHolder extends RecyclerView.ViewHolder {\n\n    private SparseArray<View> viewArray = new SparseArray<>();\n\n    public ItemViewHolder(View itemView) {\n        super(itemView);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public <T extends View> T findViewById(@IdRes final int viewId) {\n        View view = viewArray.get(viewId);\n        if (view == null) {\n            view = itemView.findViewById(viewId);\n            viewArray.put(viewId, view);\n        }\n        return (T) view;\n    }\n\n    public void setOnClickListener(@IdRes final int viewId, View.OnClickListener listener) {\n        findViewById(viewId).setOnClickListener(listener);\n    }\n\n    public void setOnLongClickListener(@IdRes final int viewId, View.OnLongClickListener listener) {\n        findViewById(viewId).setOnLongClickListener(listener);\n    }\n}\n"
  },
  {
    "path": "lib/base/src/main/java/com/blankj/base/rv/RecycleViewDivider.java",
    "content": "package com.blankj.base.rv;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\nimport androidx.annotation.DrawableRes;\nimport androidx.annotation.NonNull;\nimport androidx.core.content.ContextCompat;\nimport androidx.core.view.ViewCompat;\nimport androidx.recyclerview.widget.RecyclerView;\nimport android.view.View;\nimport android.widget.LinearLayout;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/08/17\n *     desc  :\n * </pre>\n */\npublic class RecycleViewDivider extends RecyclerView.ItemDecoration {\n\n    public static final int HORIZONTAL = LinearLayout.HORIZONTAL;\n    public static final int VERTICAL   = LinearLayout.VERTICAL;\n\n    protected Drawable mDivider;\n\n    protected int     mOrientation;\n    protected boolean mShowFooterDivider;\n\n    protected final Rect mBounds = new Rect();\n\n    public RecycleViewDivider(Context context, int orientation, @DrawableRes int resId) {\n        this(context, orientation, resId, false);\n    }\n\n    public RecycleViewDivider(Context context, int orientation, @NonNull Drawable divider) {\n        this(context, orientation, divider, false);\n    }\n\n    public RecycleViewDivider(Context context, int orientation, @DrawableRes int resId, boolean showFooterDivider) {\n        this(context, orientation, ContextCompat.getDrawable(context, resId), showFooterDivider);\n    }\n\n    public RecycleViewDivider(Context context, int orientation, @NonNull Drawable divider, boolean showFooterDivider) {\n        setOrientation(orientation);\n        mDivider = divider;\n        mShowFooterDivider = showFooterDivider;\n    }\n\n    private void setOrientation(int orientation) {\n        if (orientation != HORIZONTAL && orientation != VERTICAL) {\n            throw new IllegalArgumentException(\n                    \"Invalid orientation. It should be either HORIZONTAL or VERTICAL\");\n        }\n        mOrientation = orientation;\n    }\n\n    @Override\n    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {\n        if (parent.getLayoutManager() == null) {\n            return;\n        }\n        if (mOrientation == VERTICAL) {\n            drawVertical(c, parent);\n        } else {\n            drawHorizontal(c, parent);\n        }\n    }\n\n    @SuppressLint(\"NewApi\")\n    protected void drawVertical(Canvas canvas, RecyclerView parent) {\n        canvas.save();\n        final int left;\n        final int right;\n        if (parent.getClipToPadding()) {\n            left = parent.getPaddingLeft();\n            right = parent.getWidth() - parent.getPaddingRight();\n            canvas.clipRect(left, parent.getPaddingTop(), right,\n                    parent.getHeight() - parent.getPaddingBottom());\n        } else {\n            left = 0;\n            right = parent.getWidth();\n        }\n\n        final int childCount = parent.getChildCount();\n        for (int i = 0; i < childCount; i++) {\n            if (i == childCount - 1 && !mShowFooterDivider) continue;\n            final View child = parent.getChildAt(i);\n            parent.getDecoratedBoundsWithMargins(child, mBounds);\n            final int bottom = mBounds.bottom + Math.round(ViewCompat.getTranslationY(child));\n            final int top = bottom - mDivider.getIntrinsicHeight();\n            mDivider.setBounds(left, top, right, bottom);\n            mDivider.draw(canvas);\n        }\n        canvas.restore();\n    }\n\n    @SuppressLint(\"NewApi\")\n    protected void drawHorizontal(Canvas canvas, RecyclerView parent) {\n        canvas.save();\n        final int top;\n        final int bottom;\n        if (parent.getClipToPadding()) {\n            top = parent.getPaddingTop();\n            bottom = parent.getHeight() - parent.getPaddingBottom();\n            canvas.clipRect(parent.getPaddingLeft(), top,\n                    parent.getWidth() - parent.getPaddingRight(), bottom);\n        } else {\n            top = 0;\n            bottom = parent.getHeight();\n        }\n\n        final int childCount = parent.getChildCount();\n        for (int i = 0; i < childCount; i++) {\n            if (i == childCount - 1 && !mShowFooterDivider) continue;\n            final View child = parent.getChildAt(i);\n            parent.getLayoutManager().getDecoratedBoundsWithMargins(child, mBounds);\n            final int right = mBounds.right + Math.round(ViewCompat.getTranslationX(child));\n            final int left = right - mDivider.getIntrinsicWidth();\n            mDivider.setBounds(left, top, right, bottom);\n            mDivider.draw(canvas);\n        }\n        canvas.restore();\n    }\n\n    @Override\n    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {\n        if (mOrientation == VERTICAL) {\n            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());\n        } else {\n            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/base/src/main/java/com/blankj/base/view/EmptyGoneTextView.java",
    "content": "package com.blankj.base.view;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.TextView;\n\nimport com.blankj.utilcode.util.StringUtils;\n\nimport androidx.annotation.Nullable;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/03\n *     desc  :\n * </pre>\n */\n@SuppressLint(\"AppCompatCustomView\")\npublic class EmptyGoneTextView extends TextView {\n\n    public EmptyGoneTextView(Context context) {\n        this(context, null);\n    }\n\n    public EmptyGoneTextView(Context context, @Nullable AttributeSet attrs) {\n        super(context, attrs);\n        setVisibility(GONE);\n    }\n\n    @Override\n    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {\n        super.onTextChanged(text, start, lengthBefore, lengthAfter);\n        if (StringUtils.isEmpty(text)) {\n            setVisibility(GONE);\n        } else {\n            setVisibility(VISIBLE);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/base/src/main/res/layout/activity_back.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/baseBackRootLayout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/white\">\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:id=\"@+id/baseBackAppBarLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:theme=\"@style/ThemeOverlay.AppCompat.Dark.ActionBar\"\n        app:popupTheme=\"@style/ThemeOverlay.AppCompat.Light\">\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/baseBackToolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            app:layout_scrollFlags=\"scroll|enterAlways\" />\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <androidx.core.widget.NestedScrollView\n        android:id=\"@+id/baseBackNsv\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\">\n\n        <FrameLayout\n            android:id=\"@+id/baseBackContainerView\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"match_parent\" />\n    </androidx.core.widget.NestedScrollView>\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lib/base/src/main/res/layout/fragment_lazy.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/lazyFragmentRootFl\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />\n"
  },
  {
    "path": "lib/common/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "lib/common/build.gradle",
    "content": "dependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    api Config.modules.lib_base.dep\n}"
  },
  {
    "path": "lib/common/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "lib/common/src/main/AndroidManifest.xml",
    "content": "<manifest package=\"com.blankj.common\" />\n"
  },
  {
    "path": "lib/common/src/main/java/com/blankj/common/CommonApplication.java",
    "content": "package com.blankj.common;\n\nimport com.blankj.base.BaseApplication;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/06/05\n *     desc  : app about common\n * </pre>\n */\npublic class CommonApplication extends BaseApplication {\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n    }\n}\n"
  },
  {
    "path": "lib/common/src/main/java/com/blankj/common/activity/CommonActivity.java",
    "content": "package com.blankj.common.activity;\n\nimport android.content.Context;\nimport android.os.Bundle;\nimport android.text.TextUtils;\nimport android.view.LayoutInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport com.blankj.base.BaseActivity;\nimport com.blankj.base.rv.BaseItemAdapter;\nimport com.blankj.base.rv.RecycleViewDivider;\nimport com.blankj.common.R;\nimport com.blankj.common.dialog.CommonDialogLoading;\nimport com.blankj.common.item.CommonItem;\nimport com.blankj.swipepanel.SwipePanel;\nimport com.blankj.utilcode.util.LanguageUtils;\nimport com.blankj.utilcode.util.SizeUtils;\n\nimport java.util.List;\n\nimport androidx.annotation.CallSuper;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.StringRes;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/06/05\n *     desc  :\n * </pre>\n */\npublic abstract class CommonActivity extends BaseActivity {\n\n    private CommonActivityItemsView  mItemsView;\n    private CommonActivityTitleView  mTitleView;\n    private CommonActivityDrawerView mDrawerView;\n\n    private CommonDialogLoading mDialogLoading;\n\n    public View commonContentView;\n\n//    @Override\n//    protected void attachBaseContext(Context newBase) {\n//        super.attachBaseContext(LanguageUtils.attachBaseContext(newBase));\n//    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // title view\n    ///////////////////////////////////////////////////////////////////////////\n    public boolean isSwipeBack() {\n        return true;\n    }\n\n    @StringRes\n    public int bindTitleRes() {\n        return View.NO_ID;\n    }\n\n    public CharSequence bindTitle() {\n        return \"\";\n    }\n\n    public boolean isSupportScroll() {\n        return true;\n    }\n\n    public CommonActivityTitleView bindTitleView() {\n        return null;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // items view\n    ///////////////////////////////////////////////////////////////////////////\n    public CommonActivityItemsView bindItemsView() {\n        return null;\n    }\n\n    public List<CommonItem> bindItems() {\n        return null;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // drawer view\n    ///////////////////////////////////////////////////////////////////////////\n    public CommonActivityDrawerView bindDrawerView() {\n        return null;\n    }\n\n    public boolean bindDrawer() {\n        return false;\n    }\n\n    @CallSuper\n    @Override\n    public void initData(@Nullable Bundle bundle) {\n        mTitleView = bindTitleView();\n        if (mTitleView == null) {\n            int titleRes = bindTitleRes();\n            if (titleRes != View.NO_ID) {\n                mTitleView = new CommonActivityTitleView(this, titleRes, isSupportScroll());\n            } else {\n                CharSequence title = bindTitle();\n                if (!TextUtils.isEmpty(title)) {\n                    mTitleView = new CommonActivityTitleView(this, title, isSupportScroll());\n                }\n            }\n        }\n\n        mItemsView = bindItemsView();\n        if (mItemsView == null) {\n            List<CommonItem> items = bindItems();\n            if (items != null) {\n                mItemsView = new CommonActivityItemsView(this, items);\n            }\n        }\n\n        mDrawerView = bindDrawerView();\n        if (mDrawerView == null) {\n            if (bindDrawer()) {\n                mDrawerView = new CommonActivityDrawerView(this);\n            }\n        }\n\n        if (mTitleView != null && mItemsView != null) {\n            mTitleView.setIsSupportScroll(false);\n        }\n\n        findViewById(android.R.id.content).setBackgroundColor(getResources().getColor(R.color.lightGrayDark));\n        initSwipeBack();\n    }\n\n    @Override\n    public int bindLayout() {\n        return View.NO_ID;\n    }\n\n    @Override\n    public void setContentView() {\n        if (mTitleView != null) {\n            mContentView = LayoutInflater.from(this).inflate(mTitleView.bindLayout(), null);\n            setContentView(mContentView);\n            commonContentView = mTitleView.getContentView();\n        } else if (mDrawerView != null) {\n            mContentView = LayoutInflater.from(this).inflate(mDrawerView.bindLayout(), null);\n            setContentView(mContentView);\n            commonContentView = mDrawerView.getContentView();\n        } else {\n            if (mItemsView != null) {\n                mContentView = LayoutInflater.from(this).inflate(mItemsView.bindLayout(), null);\n                setContentView(mContentView);\n            } else {\n                super.setContentView();\n            }\n            commonContentView = mContentView;\n            return;\n        }\n\n        if (mItemsView != null) {\n            LayoutInflater.from(this).inflate(mItemsView.bindLayout(), (ViewGroup) commonContentView);\n        } else {\n            if (bindLayout() > 0) {\n                LayoutInflater.from(this).inflate(bindLayout(), (ViewGroup) commonContentView);\n            }\n        }\n    }\n\n    private void initSwipeBack() {\n        if (isSwipeBack()) {\n            final SwipePanel swipeLayout = new SwipePanel(this);\n            swipeLayout.setLeftDrawable(R.drawable.common_back);\n            swipeLayout.setLeftEdgeSize(SizeUtils.dp2px(16));\n            swipeLayout.setLeftSwipeColor(getResources().getColor(R.color.colorPrimary));\n            swipeLayout.wrapView(findViewById(android.R.id.content));\n            swipeLayout.setOnFullSwipeListener(new SwipePanel.OnFullSwipeListener() {\n                @Override\n                public void onFullSwipe(int direction) {\n                    swipeLayout.close(direction);\n                    finish();\n                }\n            });\n        }\n    }\n\n    @CallSuper\n    @Override\n    public void initView(@Nullable Bundle savedInstanceState, @Nullable View contentView) {\n        if (mItemsView != null) {\n            mItemsView.initView();\n        }\n    }\n\n    @Override\n    public void doBusiness() {\n    }\n\n    @Override\n    public void onDebouncingClick(@NonNull View view) {\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        if (mTitleView != null) {\n            return mTitleView.onOptionsItemSelected(item);\n        }\n        return super.onOptionsItemSelected(item);\n    }\n\n    public void showLoading() {\n        showLoading(null);\n    }\n\n    public void showLoading(Runnable listener) {\n        if (mDialogLoading != null) {\n            return;\n        }\n        mDialogLoading = new CommonDialogLoading().init(this, listener);\n        mDialogLoading.show();\n    }\n\n    public void dismissLoading() {\n        if (mDialogLoading != null) {\n            mDialogLoading.dismiss();\n            mDialogLoading = null;\n        }\n    }\n\n    public CommonActivityItemsView getItemsView() {\n        return mItemsView;\n    }\n\n    public CommonActivityTitleView getTitleView() {\n        return mTitleView;\n    }\n\n    public CommonActivityDrawerView getDrawerView() {\n        return mDrawerView;\n    }\n\n    private BaseItemAdapter<CommonItem> mCommonItemAdapter;\n\n    public void setCommonItems(RecyclerView rv, List<CommonItem> items) {\n        mCommonItemAdapter = new BaseItemAdapter<>();\n        mCommonItemAdapter.setItems(items);\n        rv.setAdapter(mCommonItemAdapter);\n        rv.setLayoutManager(new LinearLayoutManager(this));\n        rv.addItemDecoration(new RecycleViewDivider(this, RecycleViewDivider.VERTICAL, R.drawable.common_item_divider));\n    }\n\n    public void updateCommonItems(List<CommonItem> data) {\n        mCommonItemAdapter.setItems(data);\n        mCommonItemAdapter.notifyDataSetChanged();\n    }\n\n    public void updateCommonItem(int position) {\n        mCommonItemAdapter.notifyItemChanged(position);\n    }\n\n    public BaseItemAdapter<CommonItem> getCommonItemAdapter() {\n        return mCommonItemAdapter;\n    }\n}"
  },
  {
    "path": "lib/common/src/main/java/com/blankj/common/activity/CommonActivityDrawerView.java",
    "content": "package com.blankj.common.activity;\n\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.widget.FrameLayout;\n\nimport com.blankj.common.R;\nimport com.blankj.utilcode.util.ActivityUtils;\nimport com.blankj.utilcode.util.StringUtils;\nimport com.google.android.material.navigation.NavigationView;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.StringRes;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.drawerlayout.widget.DrawerLayout;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/11/01\n *     desc  :\n * </pre>\n */\npublic class CommonActivityDrawerView {\n\n    public AppCompatActivity mBaseActivity;\n    public DrawerLayout      mBaseDrawerRootLayout;\n    public FrameLayout       mBaseDrawerContainerView;\n\n    private NavigationView.OnNavigationItemSelectedListener mListener = new NavigationView.OnNavigationItemSelectedListener() {\n        @Override\n        public boolean onNavigationItemSelected(@NonNull MenuItem item) {\n            int id = item.getItemId();\n            if (id == R.id.baseDrawerActionGitHub) {\n                return goWeb(R.string.github);\n            } else if (id == R.id.baseDrawerActionBlog) {\n                return goWeb(R.string.blog);\n            }\n            return false;\n        }\n    };\n\n    public CommonActivityDrawerView(@NonNull AppCompatActivity activity) {\n        mBaseActivity = activity;\n    }\n\n    public int bindLayout() {\n        return R.layout.common_activity_drawer;\n    }\n\n    public View getContentView() {\n        mBaseDrawerRootLayout = mBaseActivity.findViewById(R.id.baseDrawerRootLayout);\n        mBaseDrawerContainerView = mBaseActivity.findViewById(R.id.baseDrawerContainerView);\n        NavigationView nav = mBaseActivity.findViewById(R.id.baseDrawerNavView);\n        nav.setNavigationItemSelectedListener(mListener);\n        return mBaseDrawerContainerView;\n    }\n\n    private boolean goWeb(@StringRes int id) {\n        return ActivityUtils.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(StringUtils.getString(id))));\n    }\n}\n"
  },
  {
    "path": "lib/common/src/main/java/com/blankj/common/activity/CommonActivityItemsView.java",
    "content": "package com.blankj.common.activity;\n\n\nimport com.blankj.base.rv.BaseItemAdapter;\nimport com.blankj.base.rv.RecycleViewDivider;\nimport com.blankj.common.R;\nimport com.blankj.common.item.CommonItem;\n\nimport java.util.List;\n\nimport androidx.annotation.NonNull;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/11/01\n *     desc  :\n * </pre>\n */\npublic class CommonActivityItemsView {\n\n    public  AppCompatActivity           mBaseActivity;\n    private List<CommonItem>            mItems;\n    public  BaseItemAdapter<CommonItem> mCommonItemAdapter;\n    public  RecyclerView                mCommonItemRv;\n\n    public CommonActivityItemsView(@NonNull AppCompatActivity activity, @NonNull List<CommonItem> items) {\n        mBaseActivity = activity;\n        mItems = items;\n    }\n\n    public int bindLayout() {\n        return R.layout.common_item;\n    }\n\n    public void initView() {\n        mCommonItemAdapter = new BaseItemAdapter<>();\n        mCommonItemAdapter.setItems(mItems);\n        mCommonItemRv = mBaseActivity.findViewById(R.id.commonItemRv);\n        mCommonItemRv.setAdapter(mCommonItemAdapter);\n        mCommonItemRv.setLayoutManager(new LinearLayoutManager(mBaseActivity));\n        mCommonItemRv.addItemDecoration(new RecycleViewDivider(mBaseActivity, RecycleViewDivider.VERTICAL, R.drawable.common_item_divider));\n    }\n\n    public void updateItems(List<CommonItem> data) {\n        mCommonItemAdapter.setItems(data);\n        mCommonItemAdapter.notifyDataSetChanged();\n    }\n\n    public void updateItem(int position) {\n        mCommonItemAdapter.notifyItemChanged(position);\n    }\n}\n"
  },
  {
    "path": "lib/common/src/main/java/com/blankj/common/activity/CommonActivityTitleView.java",
    "content": "package com.blankj.common.activity;\n\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.view.ViewStub;\nimport android.widget.FrameLayout;\n\nimport com.blankj.common.R;\nimport com.blankj.utilcode.util.BarUtils;\nimport com.blankj.utilcode.util.ColorUtils;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.StringRes;\nimport androidx.appcompat.app.ActionBar;\nimport androidx.appcompat.app.AppCompatActivity;\nimport androidx.appcompat.widget.Toolbar;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/11/01\n *     desc  :\n * </pre>\n */\npublic class CommonActivityTitleView {\n\n    public AppCompatActivity mBaseActivity;\n    public CharSequence      mTitle;\n    public boolean           mIsSupportScroll;\n    public CoordinatorLayout baseTitleRootLayout;\n    public Toolbar           baseTitleToolbar;\n    public FrameLayout       baseTitleContentView;\n    public ViewStub          mViewStub;\n\n    public CommonActivityTitleView(@NonNull AppCompatActivity activity, @StringRes int resId) {\n        this(activity, activity.getString(resId), true);\n    }\n\n    public CommonActivityTitleView(@NonNull AppCompatActivity activity, @NonNull CharSequence title) {\n        this(activity, title, true);\n    }\n\n    public CommonActivityTitleView(@NonNull AppCompatActivity activity, @StringRes int resId, boolean isSupportScroll) {\n        this(activity, activity.getString(resId), isSupportScroll);\n    }\n\n    public CommonActivityTitleView(@NonNull AppCompatActivity activity, @NonNull CharSequence title, boolean isSupportScroll) {\n        mBaseActivity = activity;\n        mTitle = title;\n        mIsSupportScroll = isSupportScroll;\n    }\n\n    public void setIsSupportScroll(boolean isSupportScroll) {\n        mIsSupportScroll = isSupportScroll;\n    }\n\n    public int bindLayout() {\n        return R.layout.common_activity_title;\n    }\n\n    public View getContentView() {\n        baseTitleRootLayout = mBaseActivity.findViewById(R.id.baseTitleRootLayout);\n        baseTitleToolbar = mBaseActivity.findViewById(R.id.baseTitleToolbar);\n        if (mIsSupportScroll) {\n            mViewStub = mBaseActivity.findViewById(R.id.baseTitleStubScroll);\n        } else {\n            mViewStub = mBaseActivity.findViewById(R.id.baseTitleStubNoScroll);\n        }\n        mViewStub.setVisibility(View.VISIBLE);\n        baseTitleContentView = mBaseActivity.findViewById(R.id.commonTitleContentView);\n        setTitleBar();\n        BarUtils.setStatusBarColor(mBaseActivity, ColorUtils.getColor(R.color.colorPrimary));\n        BarUtils.addMarginTopEqualStatusBarHeight(baseTitleRootLayout);\n        return baseTitleContentView;\n    }\n\n    private void setTitleBar() {\n        mBaseActivity.setSupportActionBar(baseTitleToolbar);\n        ActionBar titleBar = mBaseActivity.getSupportActionBar();\n        if (titleBar != null) {\n            titleBar.setDisplayHomeAsUpEnabled(true);\n            titleBar.setTitle(mTitle);\n        }\n    }\n\n    public boolean onOptionsItemSelected(MenuItem item) {\n        if (item.getItemId() == android.R.id.home) {\n            mBaseActivity.finish();\n            return true;\n        }\n        return mBaseActivity.onOptionsItemSelected(item);\n    }\n}\n"
  },
  {
    "path": "lib/common/src/main/java/com/blankj/common/dialog/CommonDialogContent.java",
    "content": "package com.blankj.common.dialog;\n\nimport android.content.Context;\nimport android.text.TextUtils;\nimport android.util.Pair;\nimport android.view.View;\nimport android.view.Window;\nimport android.widget.RelativeLayout;\nimport android.widget.TextView;\n\nimport com.blankj.base.dialog.BaseDialogFragment;\nimport com.blankj.base.dialog.DialogLayoutCallback;\nimport com.blankj.common.R;\nimport com.blankj.utilcode.util.ClickUtils;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/11/18\n *     desc  :\n * </pre>\n */\npublic class CommonDialogContent extends BaseDialogFragment {\n\n    private RelativeLayout cdcTitleRl;\n    private TextView       cdcTitleTv;\n    private RelativeLayout cdcContentRl;\n    private TextView       cdcContentTv;\n    private RelativeLayout cdcBottomRl;\n    private TextView       cdcBottomPositiveTv;\n    private TextView       cdcBottomNegativeTv;\n\n    public CommonDialogContent init(Context context, final CharSequence title, final CharSequence content,\n                                    final Pair<CharSequence, View.OnClickListener> positiveBtnAction,\n                                    final Pair<CharSequence, View.OnClickListener> negativeBtnAction) {\n        super.init(context, new DialogLayoutCallback() {\n            @Override\n            public int bindTheme() {\n                return R.style.CommonContentDialogStyle;\n            }\n\n            @Override\n            public int bindLayout() {\n                return R.layout.common_dialog_content;\n            }\n\n            @Override\n            public void initView(final BaseDialogFragment dialog, View contentView) {\n                cdcTitleRl = contentView.findViewById(R.id.cdcTitleRl);\n                cdcTitleTv = contentView.findViewById(R.id.cdcTitleTv);\n                cdcContentRl = contentView.findViewById(R.id.cdcContentRl);\n                cdcContentTv = contentView.findViewById(R.id.cdcContentTv);\n                cdcBottomRl = contentView.findViewById(R.id.cdcBottomRl);\n                cdcBottomPositiveTv = contentView.findViewById(R.id.cdcBottomPositiveTv);\n                cdcBottomNegativeTv = contentView.findViewById(R.id.cdcBottomNegativeTv);\n\n                if (TextUtils.isEmpty(title)) {\n                    cdcTitleRl.setVisibility(View.GONE);\n                } else {\n                    cdcTitleTv.setText(title);\n                }\n                if (TextUtils.isEmpty(content)) {\n                    cdcContentRl.setVisibility(View.GONE);\n                } else {\n                    cdcContentTv.setText(content);\n                }\n\n                if (positiveBtnAction == null && negativeBtnAction == null) {\n                    cdcBottomRl.setVisibility(View.GONE);\n                } else {\n                    if (positiveBtnAction != null) {\n                        ClickUtils.applyPressedBgDark(cdcBottomPositiveTv);\n                        cdcBottomPositiveTv.setText(positiveBtnAction.first);\n                        cdcBottomPositiveTv.setOnClickListener(new View.OnClickListener() {\n                            @Override\n                            public void onClick(View v) {\n                                dismiss();\n                                positiveBtnAction.second.onClick(v);\n                            }\n                        });\n                    }\n                    if (negativeBtnAction != null) {\n                        ClickUtils.applyPressedBgDark(cdcBottomNegativeTv);\n                        cdcBottomNegativeTv.setText(negativeBtnAction.first);\n                        cdcBottomNegativeTv.setOnClickListener(new View.OnClickListener() {\n                            @Override\n                            public void onClick(View v) {\n                                dismiss();\n                                negativeBtnAction.second.onClick(v);\n                            }\n                        });\n                    }\n                }\n            }\n\n            @Override\n            public void setWindowStyle(Window window) {\n            }\n\n            @Override\n            public void onCancel(BaseDialogFragment dialog) {\n\n            }\n\n            @Override\n            public void onDismiss(BaseDialogFragment dialog) {\n\n            }\n        });\n        return this;\n    }\n}\n"
  },
  {
    "path": "lib/common/src/main/java/com/blankj/common/dialog/CommonDialogLoading.java",
    "content": "package com.blankj.common.dialog;\n\nimport android.content.Context;\nimport android.graphics.Color;\nimport android.view.View;\nimport android.view.Window;\nimport android.view.WindowManager;\n\nimport com.blankj.base.dialog.BaseDialogFragment;\nimport com.blankj.base.dialog.DialogLayoutCallback;\nimport com.blankj.common.R;\nimport com.blankj.utilcode.util.BarUtils;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/10/29\n *     desc  :\n * </pre>\n */\npublic class CommonDialogLoading extends BaseDialogFragment {\n\n    public CommonDialogLoading init(Context context, final Runnable onCancelListener) {\n        super.init(context, new DialogLayoutCallback() {\n            @Override\n            public int bindTheme() {\n                return R.style.CommonLoadingDialogStyle;\n            }\n\n            @Override\n            public int bindLayout() {\n                return R.layout.common_dialog_loading;\n            }\n\n            @Override\n            public void initView(BaseDialogFragment dialog, View contentView) {\n                if (onCancelListener == null) {\n                    setCancelable(false);\n                } else {\n                    setCancelable(true);\n                }\n            }\n\n            @Override\n            public void setWindowStyle(final Window window) {\n                window.setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);\n                BarUtils.setStatusBarColor(window, Color.TRANSPARENT);\n            }\n\n            @Override\n            public void onCancel(BaseDialogFragment dialog) {\n                if (onCancelListener != null) {\n                    onCancelListener.run();\n                }\n            }\n\n            @Override\n            public void onDismiss(BaseDialogFragment dialog) {\n\n            }\n        });\n        return this;\n    }\n}\n"
  },
  {
    "path": "lib/common/src/main/java/com/blankj/common/fragment/CommonFragment.java",
    "content": "package com.blankj.common.fragment;\n\nimport android.os.Bundle;\nimport android.view.View;\n\nimport com.blankj.base.BaseFragment;\nimport com.blankj.base.rv.BaseItemAdapter;\nimport com.blankj.base.rv.RecycleViewDivider;\nimport com.blankj.common.R;\nimport com.blankj.common.activity.CommonActivityItemsView;\nimport com.blankj.common.item.CommonItem;\n\nimport java.util.List;\n\nimport androidx.annotation.CallSuper;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/11/03\n *     desc  :\n * </pre>\n */\npublic class CommonFragment extends BaseFragment {\n\n    private CommonActivityItemsView mItemsView;\n\n    ///////////////////////////////////////////////////////////////////////////\n    // items view\n    ///////////////////////////////////////////////////////////////////////////\n    public CommonActivityItemsView bindItemsView() {\n        return null;\n    }\n\n    public List<CommonItem> bindItems() {\n        return null;\n    }\n\n    @CallSuper\n    @Override\n    public void initData(@Nullable Bundle bundle) {\n        mItemsView = bindItemsView();\n        if (mItemsView == null) {\n            List<CommonItem> items = bindItems();\n            if (items != null) {\n                mItemsView = new CommonActivityItemsView(mActivity, items);\n            }\n        }\n    }\n\n    @Override\n    public int bindLayout() {\n        return View.NO_ID;\n    }\n\n    @Override\n    public void setContentView() {\n        if (mItemsView != null) {\n            mContentView = mInflater.inflate(mItemsView.bindLayout(), null);\n        } else {\n            super.setContentView();\n        }\n    }\n\n    @CallSuper\n    @Override\n    public void initView(@Nullable Bundle savedInstanceState, @Nullable View contentView) {\n        if (mItemsView != null) {\n            mItemsView.initView();\n        }\n    }\n\n    @Override\n    public void doBusiness() {\n        log(\"doBusiness\");\n    }\n\n    @Override\n    public void onDebouncingClick(@NonNull View view) {\n    }\n\n\n    public CommonActivityItemsView getItemsView() {\n        return mItemsView;\n    }\n\n    private BaseItemAdapter<CommonItem> mCommonItemAdapter;\n\n    public void setCommonItems(RecyclerView rv, List<CommonItem> items) {\n        mCommonItemAdapter = new BaseItemAdapter<>();\n        mCommonItemAdapter.setItems(items);\n        rv.setAdapter(mCommonItemAdapter);\n        rv.setLayoutManager(new LinearLayoutManager(mActivity));\n        rv.addItemDecoration(new RecycleViewDivider(mActivity, RecycleViewDivider.VERTICAL, R.drawable.common_item_divider));\n    }\n\n    public void updateCommonItems(List<CommonItem> data) {\n        mCommonItemAdapter.setItems(data);\n        mCommonItemAdapter.notifyDataSetChanged();\n    }\n\n    public void updateCommonItem(int position) {\n        mCommonItemAdapter.notifyItemChanged(position);\n    }\n\n    public BaseItemAdapter<CommonItem> getCommonItemAdapter() {\n        return mCommonItemAdapter;\n    }\n}\n"
  },
  {
    "path": "lib/common/src/main/java/com/blankj/common/helper/PermissionHelper.kt",
    "content": "package com.blankj.common.helper\n\nimport android.content.Context\nimport android.util.Pair\nimport android.view.View\nimport com.blankj.common.R\nimport com.blankj.common.dialog.CommonDialogContent\nimport com.blankj.utilcode.constant.PermissionConstants\nimport com.blankj.utilcode.util.*\n\n/**\n * ```\n * author: Blankj\n * blog  : http://blankj.com\n * time  : 2018/01/06\n * desc  : helper about permission\n * ```\n */\nobject PermissionHelper {\n\n    fun request(context: Context, callback: PermissionUtils.SimpleCallback,\n                @PermissionConstants.PermissionGroup vararg permissions: String) {\n        PermissionUtils.permission(*permissions)\n                .rationale { activity, shouldRequest -> showRationaleDialog(activity, shouldRequest) }\n                .callback(object : PermissionUtils.SingleCallback {\n                    override fun callback(isAllGranted: Boolean, granted: MutableList<String>,\n                                          deniedForever: MutableList<String>, denied: MutableList<String>) {\n                        LogUtils.d(isAllGranted, granted, deniedForever, denied)\n                        if (isAllGranted) {\n                            callback.onGranted()\n                            return\n                        }\n                        if (deniedForever.isNotEmpty()) {\n                            showOpenAppSettingDialog(context)\n                            return\n                        }\n                        val activity = ActivityUtils.getActivityByContext(context)\n                        if (activity != null) {\n                            SnackbarUtils.with(activity.findViewById(android.R.id.content))\n                                    .setMessage(\"Permission denied: ${permissions2String(denied)}\")\n                                    .showError(true)\n                        }\n                        callback.onDenied()\n                    }\n\n                    fun permissions2String(permissions: MutableList<String>): String {\n                        if (permissions.isEmpty()) return \"[]\"\n                        val sb: StringBuilder = StringBuilder()\n                        for (permission in permissions) {\n                            sb.append(\", \" + permission.substring(permission.lastIndexOf('.') + 1))\n                        }\n                        return \"[${sb.substring(2)}]\"\n                    }\n                })\n                .request()\n    }\n\n    fun showRationaleDialog(context: Context, shouldRequest: PermissionUtils.OnRationaleListener.ShouldRequest) {\n        CommonDialogContent().init(context,\n                StringUtils.getString(android.R.string.dialog_alert_title),\n                StringUtils.getString(R.string.permission_rationale_message),\n                Pair(StringUtils.getString(android.R.string.ok), View.OnClickListener {\n                    shouldRequest.again(true)\n                }),\n                Pair(StringUtils.getString(android.R.string.cancel), View.OnClickListener {\n                    shouldRequest.again(false)\n                }))\n                .show()\n    }\n\n    fun showExplainDialog(context: Context, denied: List<String>, shouldRequest: PermissionUtils.OnExplainListener.ShouldRequest) {\n        CommonDialogContent().init(context,\n                StringUtils.getString(android.R.string.dialog_alert_title),\n                \"We needs the permissions of $denied to test the utils of permission.\",\n                Pair(StringUtils.getString(android.R.string.ok), View.OnClickListener {\n                    shouldRequest.start(true)\n                }),\n                Pair(StringUtils.getString(android.R.string.cancel), View.OnClickListener {\n                    ToastUtils.showShort(\"request failed.\")\n                    shouldRequest.start(false)\n                }))\n                .show()\n    }\n\n    fun showOpenAppSettingDialog(context: Context) {\n        CommonDialogContent().init(context,\n                StringUtils.getString(android.R.string.dialog_alert_title),\n                StringUtils.getString(R.string.permission_denied_forever_message),\n                Pair(StringUtils.getString(android.R.string.ok), View.OnClickListener {\n                    PermissionUtils.launchAppDetailsSettings()\n                }),\n                Pair(StringUtils.getString(android.R.string.cancel), View.OnClickListener {\n                }))\n                .show()\n    }\n}"
  },
  {
    "path": "lib/common/src/main/java/com/blankj/common/item/CommonItem.java",
    "content": "package com.blankj.common.item;\n\nimport com.blankj.base.rv.BaseItem;\nimport com.blankj.base.rv.ItemViewHolder;\nimport com.blankj.common.R;\nimport com.blankj.utilcode.util.ColorUtils;\n\nimport androidx.annotation.CallSuper;\nimport androidx.annotation.NonNull;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/10/25\n *     desc  :\n * </pre>\n */\npublic class CommonItem<T extends BaseItem> extends BaseItem<T> {\n\n    private int backgroundColor = ColorUtils.getColor(R.color.lightGray);\n\n    public CommonItem(int layoutId) {\n        super(layoutId);\n    }\n\n    @CallSuper\n    @Override\n    public void bind(@NonNull final ItemViewHolder holder, int position) {\n        holder.itemView.setBackgroundColor(backgroundColor);\n    }\n\n    public CommonItem<T> setBackgroundColor(int backgroundColor) {\n        this.backgroundColor = backgroundColor;\n        return this;\n    }\n\n    public int getBackgroundColor() {\n        return backgroundColor;\n    }\n}\n"
  },
  {
    "path": "lib/common/src/main/java/com/blankj/common/item/CommonItemClick.java",
    "content": "package com.blankj.common.item;\n\nimport android.view.View;\nimport android.widget.TextView;\n\nimport com.blankj.base.rv.ItemViewHolder;\nimport com.blankj.common.R;\nimport com.blankj.utilcode.util.ClickUtils;\nimport com.blankj.utilcode.util.StringUtils;\nimport com.blankj.utilcode.util.Utils;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.StringRes;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/10/31\n *     desc  :\n * </pre>\n */\npublic class CommonItemClick extends CommonItem<CommonItemClick> {\n\n    private CharSequence mTitle;\n    private CharSequence mContent;\n    private boolean      mIsJump;\n\n    public CommonItemClick(@StringRes int title) {\n        this(StringUtils.getString(title), \"\", false, null);\n    }\n\n    public CommonItemClick(@NonNull CharSequence title) {\n        this(title, \"\", false, null);\n    }\n\n    public CommonItemClick(@StringRes int title, boolean isJump) {\n        this(StringUtils.getString(title), \"\", isJump);\n    }\n\n    public CommonItemClick(@NonNull CharSequence title, boolean isJump) {\n        this(title, \"\", isJump, null);\n    }\n\n    public CommonItemClick(@StringRes int title, CharSequence content) {\n        this(StringUtils.getString(title), content, false, null);\n    }\n\n    public CommonItemClick(@NonNull CharSequence title, CharSequence content) {\n        this(title, content, false, null);\n    }\n\n    public CommonItemClick(@StringRes int title, CharSequence content, boolean isJump) {\n        this(StringUtils.getString(title), content, isJump);\n    }\n\n    public CommonItemClick(@NonNull CharSequence title, CharSequence content, boolean isJump) {\n        this(title, content, isJump, null);\n    }\n\n    public CommonItemClick(@StringRes int title, final Runnable simpleClickListener) {\n        this(StringUtils.getString(title), \"\", false, simpleClickListener);\n    }\n\n    public CommonItemClick(@NonNull CharSequence title, final Runnable simpleClickListener) {\n        this(title, \"\", false, simpleClickListener);\n    }\n\n    public CommonItemClick(@StringRes int title, boolean isJump, final Runnable simpleClickListener) {\n        this(StringUtils.getString(title), \"\", isJump, simpleClickListener);\n    }\n\n    public CommonItemClick(@NonNull CharSequence title, boolean isJump, final Runnable simpleClickListener) {\n        this(title, \"\", isJump, simpleClickListener);\n    }\n\n    public CommonItemClick(@StringRes int title, CharSequence content, final Runnable simpleClickListener) {\n        this(StringUtils.getString(title), content, false, simpleClickListener);\n    }\n\n    public CommonItemClick(@NonNull CharSequence title, CharSequence content, final Runnable simpleClickListener) {\n        this(title, content, false, simpleClickListener);\n    }\n\n    public CommonItemClick(@StringRes int title, CharSequence content, boolean isJump, final Runnable simpleClickListener) {\n        this(StringUtils.getString(title), content, isJump, simpleClickListener);\n    }\n\n    public CommonItemClick(@NonNull CharSequence title, CharSequence content, boolean isJump, final Runnable simpleClickListener) {\n        super(R.layout.common_item_title_click);\n        mTitle = title;\n        mContent = content;\n        mIsJump = isJump;\n        if (simpleClickListener == null) return;\n        setOnItemClickListener(new OnItemClickListener<CommonItemClick>() {\n            @Override\n            public void onItemClick(ItemViewHolder holder, CommonItemClick item, int position) {\n                if (simpleClickListener != null) {\n                    simpleClickListener.run();\n                }\n            }\n        });\n    }\n\n    public CommonItemClick setOnClickUpdateContentListener(@NonNull final Utils.Supplier<CharSequence> supplier) {\n        setOnItemClickListener(new OnItemClickListener<CommonItemClick>() {\n            @Override\n            public void onItemClick(ItemViewHolder holder, CommonItemClick item, int position) {\n                item.mContent = supplier.get();\n                update();\n            }\n        });\n        return this;\n    }\n\n    @Override\n    public void bind(@NonNull ItemViewHolder holder, int position) {\n        super.bind(holder, position);\n        final TextView titleTv = holder.findViewById(R.id.commonItemTitleTv);\n        final TextView contentTv = holder.findViewById(R.id.commonItemContentTv);\n\n        titleTv.setText(mTitle);\n        contentTv.setText(mContent);\n\n        ClickUtils.applyPressedBgDark(holder.itemView);\n        holder.findViewById(R.id.commonItemGoIv).setVisibility(mIsJump ? View.VISIBLE : View.GONE);\n    }\n\n    public void setTitle(CharSequence title) {\n        mTitle = title;\n        update();\n    }\n\n    public CharSequence getTitle() {\n        return mTitle;\n    }\n}\n"
  },
  {
    "path": "lib/common/src/main/java/com/blankj/common/item/CommonItemImage.java",
    "content": "package com.blankj.common.item;\n\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport com.blankj.base.rv.ItemViewHolder;\nimport com.blankj.common.R;\nimport com.blankj.utilcode.util.StringUtils;\nimport com.blankj.utilcode.util.Utils;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.StringRes;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/10/31\n *     desc  :\n * </pre>\n */\npublic class CommonItemImage extends CommonItem {\n\n    private CharSequence              mTitle;\n    private Utils.Consumer<ImageView> mSetImageConsumer;\n\n    public CommonItemImage(@StringRes int title, @NonNull Utils.Consumer<ImageView> setImageConsumer) {\n        this(StringUtils.getString(title), setImageConsumer);\n    }\n\n    public CommonItemImage(@NonNull CharSequence title, @NonNull Utils.Consumer<ImageView> setImageConsumer) {\n        super(R.layout.common_item_title_image);\n        mTitle = title;\n        mSetImageConsumer = setImageConsumer;\n    }\n\n    @Override\n    public void bind(@NonNull ItemViewHolder holder, int position) {\n        super.bind(holder, position);\n        final TextView titleTv = holder.findViewById(R.id.commonItemTitleTv);\n\n        titleTv.setText(mTitle);\n        ImageView commonItemIv = holder.findViewById(R.id.commonItemIv);\n        mSetImageConsumer.accept(commonItemIv);\n    }\n}\n"
  },
  {
    "path": "lib/common/src/main/java/com/blankj/common/item/CommonItemSeekBar.java",
    "content": "package com.blankj.common.item;\n\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.widget.SeekBar;\nimport android.widget.TextView;\n\nimport com.blankj.base.rv.ItemViewHolder;\nimport com.blankj.common.R;\nimport com.blankj.utilcode.util.StringUtils;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.StringRes;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/10/31\n *     desc  :\n * </pre>\n */\npublic class CommonItemSeekBar extends CommonItem {\n\n    private CharSequence     mTitle;\n    private CharSequence     mContent;\n    private int              mMaxProgress;\n    private int              mCurProgress;\n    private ProgressListener mProgressListener;\n\n    public CommonItemSeekBar(@StringRes int title, int maxProgress, @NonNull ProgressListener listener) {\n        this(StringUtils.getString(title), maxProgress, listener);\n    }\n\n    public CommonItemSeekBar(@NonNull CharSequence title, int maxProgress, @NonNull ProgressListener listener) {\n        super(R.layout.common_item_title_seekbar);\n        mTitle = title;\n        mMaxProgress = maxProgress;\n        mCurProgress = listener.getCurValue();\n        mProgressListener = listener;\n        mContent = String.valueOf(mCurProgress);\n    }\n\n\n    @Override\n    public void bind(@NonNull ItemViewHolder holder, int position) {\n        super.bind(holder, position);\n        final TextView titleTv = holder.findViewById(R.id.commonItemTitleTv);\n        final TextView contentTv = holder.findViewById(R.id.commonItemContentTv);\n\n        titleTv.setText(mTitle);\n        contentTv.setText(mContent);\n\n        final SeekBar seekBar = holder.findViewById(R.id.commonItemSb);\n        seekBar.setMax(mMaxProgress);\n        seekBar.setProgress(mCurProgress);\n        holder.itemView.setOnTouchListener(new View.OnTouchListener() {\n            @Override\n            public boolean onTouch(View v, MotionEvent event) {\n                return seekBar.dispatchTouchEvent(event);\n            }\n        });\n        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {\n            @Override\n            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {\n                mProgressListener.onProgressChanged(seekBar, progress, fromUser);\n                int curValue = mProgressListener.getCurValue();\n                mCurProgress = curValue;\n                contentTv.setText(String.valueOf(curValue));\n                seekBar.setProgress(curValue);\n            }\n\n            @Override\n            public void onStartTrackingTouch(SeekBar seekBar) {\n                mProgressListener.onStartTrackingTouch(seekBar);\n            }\n\n            @Override\n            public void onStopTrackingTouch(SeekBar seekBar) {\n                mProgressListener.onStopTrackingTouch(seekBar);\n            }\n        });\n    }\n\n    public void setTitle(CharSequence title) {\n        mTitle = title;\n        update();\n    }\n\n    public CharSequence getTitle() {\n        return mTitle;\n    }\n\n    public static abstract class ProgressListener implements SeekBar.OnSeekBarChangeListener {\n\n        public abstract int getCurValue();\n\n        @Override\n        public void onStartTrackingTouch(SeekBar seekBar) {\n        }\n\n        @Override\n        public void onStopTrackingTouch(SeekBar seekBar) {\n        }\n    }\n}\n"
  },
  {
    "path": "lib/common/src/main/java/com/blankj/common/item/CommonItemSwitch.java",
    "content": "package com.blankj.common.item;\n\nimport android.annotation.SuppressLint;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.widget.Switch;\nimport android.widget.TextView;\n\nimport com.blankj.base.rv.ItemViewHolder;\nimport com.blankj.common.R;\nimport com.blankj.utilcode.util.ClickUtils;\nimport com.blankj.utilcode.util.StringUtils;\nimport com.blankj.utilcode.util.Utils;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.StringRes;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/10/31\n *     desc  :\n * </pre>\n */\npublic class CommonItemSwitch extends CommonItem {\n\n    private CharSequence            mTitle;\n    private CharSequence            mContent;\n    private boolean                 mState;\n    private Utils.Supplier<Boolean> mGetStateSupplier;\n    private Utils.Consumer<Boolean> mSetStateConsumer;\n\n\n    public CommonItemSwitch(@StringRes int title, @NonNull Utils.Supplier<Boolean> getStateSupplier, @NonNull Utils.Consumer<Boolean> setStateConsumer) {\n        this(StringUtils.getString(title), getStateSupplier, setStateConsumer);\n    }\n\n    public CommonItemSwitch(@NonNull CharSequence title, @NonNull Utils.Supplier<Boolean> getStateSupplier, @NonNull Utils.Consumer<Boolean> setStateConsumer) {\n        super(R.layout.common_item_title_switch);\n        mTitle = title;\n        mGetStateSupplier = getStateSupplier;\n        mSetStateConsumer = setStateConsumer;\n        mState = getStateSupplier.get();\n        mContent = String.valueOf(mState);\n    }\n\n    @SuppressLint(\"ClickableViewAccessibility\")\n    @Override\n    public void bind(@NonNull final ItemViewHolder holder, int position) {\n        super.bind(holder, position);\n        ClickUtils.applyPressedBgDark(holder.itemView);\n        final TextView titleTv = holder.findViewById(R.id.commonItemTitleTv);\n        final TextView contentTv = holder.findViewById(R.id.commonItemContentTv);\n\n        titleTv.setText(mTitle);\n        contentTv.setText(mContent);\n\n        final Switch switchView = holder.findViewById(R.id.commonItemSwitch);\n        switchView.setChecked(mState);\n        switchView.setOnTouchListener(new View.OnTouchListener() {\n            @Override\n            public boolean onTouch(View v, MotionEvent event) {\n                holder.itemView.onTouchEvent(event);\n                return true;\n            }\n        });\n        holder.itemView.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                mSetStateConsumer.accept(!mState);\n                mState = mGetStateSupplier.get();\n                contentTv.setText(String.valueOf(mState));\n                switchView.setChecked(mState);\n            }\n        });\n    }\n\n    public void setTitle(CharSequence title) {\n        mTitle = title;\n        update();\n    }\n\n    public CharSequence getTitle() {\n        return mTitle;\n    }\n}\n"
  },
  {
    "path": "lib/common/src/main/java/com/blankj/common/item/CommonItemTitle.java",
    "content": "package com.blankj.common.item;\n\nimport android.graphics.Color;\nimport android.view.Gravity;\nimport android.widget.TextView;\n\nimport com.blankj.base.rv.ItemViewHolder;\nimport com.blankj.common.R;\nimport com.blankj.utilcode.util.StringUtils;\nimport com.blankj.utilcode.util.Utils;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.StringRes;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/10/31\n *     desc  :\n * </pre>\n */\npublic class CommonItemTitle extends CommonItem {\n\n    private CharSequence                 mTitle;\n    private Utils.Supplier<CharSequence> mGetTitleSupplier;\n    private boolean                      mIsTitleCenter;\n    private CharSequence                 mContent;\n\n    public CommonItemTitle(@NonNull Utils.Supplier<CharSequence> getTitleSupplier, boolean isTitleCenter) {\n        super(R.layout.common_item_title_content);\n        mTitle = mGetTitleSupplier.get();\n        mGetTitleSupplier = getTitleSupplier;\n        mIsTitleCenter = isTitleCenter;\n    }\n\n    public CommonItemTitle(@StringRes int title, boolean isTitleCenter) {\n        this(StringUtils.getString(title), isTitleCenter);\n    }\n\n    public CommonItemTitle(@NonNull CharSequence title, boolean isTitleCenter) {\n        super(R.layout.common_item_title_content);\n        mTitle = title;\n        mIsTitleCenter = isTitleCenter;\n    }\n\n    public CommonItemTitle(@NonNull CharSequence title, CharSequence content) {\n        super(R.layout.common_item_title_content);\n        mTitle = title;\n        mContent = content;\n    }\n\n    @Override\n    public void bind(@NonNull ItemViewHolder holder, int position) {\n        super.bind(holder, position);\n        if (mGetTitleSupplier != null) {\n            mTitle = mGetTitleSupplier.get();\n        }\n        final TextView titleTv = holder.findViewById(R.id.commonItemTitleTv);\n        final TextView contentTv = holder.findViewById(R.id.commonItemContentTv);\n\n        titleTv.setText(mTitle);\n        contentTv.setText(mContent);\n\n        if (isViewType(R.layout.common_item_title_content)) {\n            if (mIsTitleCenter) {\n                holder.itemView.setBackgroundColor(Color.TRANSPARENT);\n                titleTv.setGravity(Gravity.CENTER_HORIZONTAL);\n                titleTv.getPaint().setFakeBoldText(true);\n            } else {\n                titleTv.setGravity(Gravity.START);\n                titleTv.getPaint().setFakeBoldText(false);\n            }\n        }\n    }\n\n    public void setTitle(CharSequence title) {\n        setTitle(title, true);\n    }\n\n    public void setContent(CharSequence content) {\n        setContent(content, true);\n    }\n\n    public void setTitle(CharSequence title, boolean isUpdate) {\n        mTitle = title;\n        if (isUpdate) {\n            update();\n        }\n    }\n\n    public void setContent(CharSequence content, boolean isUpdate) {\n        mContent = content;\n        if (isUpdate) {\n            update();\n        }\n    }\n\n    public CharSequence getTitle() {\n        return mTitle;\n    }\n}\n"
  },
  {
    "path": "lib/common/src/main/java/com/blankj/common/view/RotateView.java",
    "content": "package com.blankj.common.view;\n\nimport android.animation.ObjectAnimator;\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.animation.LinearInterpolator;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2020/03/28\n *     desc  :\n * </pre>\n */\npublic class RotateView extends View {\n\n    private ObjectAnimator headerAnimator;\n\n    public RotateView(Context context) {\n        this(context, null);\n    }\n\n    public RotateView(Context context, @Nullable AttributeSet attrs) {\n        this(context, attrs, 0);\n    }\n\n    public RotateView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n    }\n\n    @Override\n    protected void onVisibilityChanged(@NonNull View changedView, int visibility) {\n        super.onVisibilityChanged(changedView, visibility);\n        if (headerAnimator == null) {\n            initAnimator();\n        }\n        if (visibility == VISIBLE) {\n            headerAnimator.start();\n        } else {\n            headerAnimator.end();\n        }\n    }\n\n    private void initAnimator() {\n        headerAnimator = ObjectAnimator.ofFloat(this, \"rotation\", 0f, 360f);\n        headerAnimator.setRepeatCount(ObjectAnimator.INFINITE);\n        headerAnimator.setInterpolator(new LinearInterpolator());\n        headerAnimator.setRepeatMode(ObjectAnimator.RESTART);\n        headerAnimator.setDuration(1000);\n        headerAnimator.start();\n    }\n}\n"
  },
  {
    "path": "lib/common/src/main/res/anim/slide_in_left.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <translate\n        android:duration=\"300\"\n        android:fromXDelta=\"100%\"\n        android:toXDelta=\"0\" />\n</set>"
  },
  {
    "path": "lib/common/src/main/res/anim/slide_in_right.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <translate\n        android:duration=\"200\"\n        android:fromXDelta=\"-100%\"\n        android:toXDelta=\"0\" />\n</set>"
  },
  {
    "path": "lib/common/src/main/res/anim/slide_out_left.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <translate\n        android:duration=\"300\"\n        android:fromXDelta=\"0\"\n        android:toXDelta=\"-100%\" />\n</set>"
  },
  {
    "path": "lib/common/src/main/res/anim/slide_out_right.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <translate\n        android:duration=\"300\"\n        android:fromXDelta=\"0\"\n        android:toXDelta=\"100%\" />\n</set>"
  },
  {
    "path": "lib/common/src/main/res/drawable/common_button_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_enabled=\"true\" android:state_pressed=\"true\">\n        <shape>\n            <solid android:color=\"@color/lightGray\" />\n            <corners android:radius=\"@dimen/radius_8\" />\n        </shape>\n    </item>\n\n    <item>\n        <shape>\n            <solid android:color=\"@color/white\" />\n            <corners android:radius=\"@dimen/radius_8\" />\n        </shape>\n    </item>\n</selector>"
  },
  {
    "path": "lib/common/src/main/res/drawable/common_button_txt_color.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:color=\"@color/darkGray\" android:state_pressed=\"true\" />\n    <item android:color=\"@color/darkGrayDark\" />\n</selector>"
  },
  {
    "path": "lib/common/src/main/res/drawable/common_content_dialog_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <solid android:color=\"@color/lightGray\" />\n    <corners android:radius=\"@dimen/radius_8\" />\n    <stroke\n        android:width=\"1dp\"\n        android:color=\"@color/lightGrayDark\" />\n</shape>"
  },
  {
    "path": "lib/common/src/main/res/drawable/common_content_dialog_btn_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <solid android:color=\"@color/lightGray\" />\n    <corners android:radius=\"@dimen/radius_4\" />\n</shape>"
  },
  {
    "path": "lib/common/src/main/res/drawable/common_item_divider.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <size android:height=\"1px\" />\n    <solid android:color=\"@color/mediumGrayDark\" />\n</shape>"
  },
  {
    "path": "lib/common/src/main/res/drawable/common_loading_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <solid android:color=\"#BB000000\" />\n    <corners android:radius=\"50dp\" />\n</shape>"
  },
  {
    "path": "lib/common/src/main/res/drawable/common_rotate_loading.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<rotate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:drawable=\"@drawable/common_loading\"\n    android:fromDegrees=\"0\"\n    android:pivotX=\"50%\"\n    android:pivotY=\"50%\"\n    android:toDegrees=\"1080\" />"
  },
  {
    "path": "lib/common/src/main/res/drawable/common_scrollbar_thumb.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <corners android:radius=\"4dp\" />\n    <solid android:color=\"@color/mediumGray\" />\n</shape>"
  },
  {
    "path": "lib/common/src/main/res/drawable/common_seekbar_progress.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item\n        android:id=\"@android:id/background\"\n        android:gravity=\"center_vertical|fill_horizontal\">\n        <shape>\n            <size android:height=\"2dp\" />\n            <corners android:radius=\"2dp\" />\n            <solid android:color=\"@color/mediumGray\" />\n        </shape>\n    </item>\n    <item\n        android:id=\"@android:id/secondaryProgress\"\n        android:gravity=\"center_vertical|fill_horizontal\">\n        <clip>\n            <shape>\n                <size android:height=\"2dp\" />\n                <corners android:radius=\"2dp\" />\n                <solid android:color=\"@color/mediumGray\" />\n            </shape>\n        </clip>\n    </item>\n    <item\n        android:id=\"@android:id/progress\"\n        android:gravity=\"center_vertical|fill_horizontal\">\n        <clip>\n            <shape>\n                <size android:height=\"2dp\" />\n                <corners android:radius=\"2dp\" />\n                <solid android:color=\"@color/colorPrimary\" />\n            </shape>\n        </clip>\n    </item>\n</layer-list>"
  },
  {
    "path": "lib/common/src/main/res/drawable/common_seekbar_thumb.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_pressed=\"true\">\n        <shape android:shape=\"oval\">\n            <size android:width=\"12dp\" android:height=\"12dp\" />\n            <solid android:color=\"@color/colorPrimary\" />\n        </shape>\n    </item>\n    <item>\n        <shape android:shape=\"oval\">\n            <size android:width=\"12dp\" android:height=\"12dp\" />\n            <solid android:color=\"@color/white\" />\n        </shape>\n    </item>\n</selector>"
  },
  {
    "path": "lib/common/src/main/res/drawable/common_splash.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@color/lightGrayDark\" />\n    <item>\n        <bitmap\n            android:gravity=\"bottom|center_horizontal\"\n            android:src=\"@mipmap/ic_launcher\" />\n    </item>\n</layer-list>"
  },
  {
    "path": "lib/common/src/main/res/drawable/main_menu_blog.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportHeight=\"508.52\"\n    android:viewportWidth=\"508.52\">\n\n    <path\n        android:fillColor=\"#191919\"\n        android:pathData=\"M254.26,0C113.845,0,0,113.845,0,254.26s113.845,254.26,254.26,254.26\ns254.26-113.845,254.26-254.26C508.52,113.813,394.675,0,254.26,0z\nM412.727,310.451c0,56.509-45.989,102.308-102.943,102.308\nH198.831c-56.891,0-103.102-45.735-103.102-102.308V198.132c0.032-56.541,46.18-102.371,103.102-102.371h51.964\nc56.954,0,102.53,42.525,102.53,99.066c0.731,10.584,10.298,19.8,21.167,19.8h17.766c11.378,0,20.5,11.95,20.5,23.233\nL412.727,310.451L412.727,310.451z\" />\n    <path\n        android:fillColor=\"#191919\"\n        android:pathData=\"M313.693,293.893H194.827c-10.901,0-19.8,8.899-19.8,19.801c0,10.87,8.931,19.8,19.8,19.8\nh118.866c10.901,0,19.8-8.931,19.8-19.8C333.494,302.792,324.594,293.893,313.693,293.893z\" />\n    <path\n        android:fillColor=\"#191919\"\n        android:pathData=\"M194.827,214.627h59.433c10.901,0,19.8-8.931,19.8-19.8c0-10.901-8.931-19.8-19.8-19.8h-59.433\nc-10.901,0-19.8,8.931-19.8,19.8C174.994,205.728,183.925,214.627,194.827,214.627z\" />\n</vector>"
  },
  {
    "path": "lib/common/src/main/res/drawable/main_menu_github.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportHeight=\"438.549\"\n    android:viewportWidth=\"438.549\">\n\n    <group>\n        <path\n            android:fillColor=\"#191919\"\n            android:pathData=\"M409.132,114.573c-19.608-33.596-46.205-60.194-79.798-79.8C295.736,15.166,259.057,5.365,219.271,5.365\nc-39.781,0-76.472,9.804-110.063,29.408c-33.596,19.605-60.192,46.204-79.8,79.8C9.803,148.168,0,184.854,0,224.63\nc0,47.78,13.94,90.745,41.827,128.906c27.884,38.164,63.906,64.572,108.063,79.227c5.14,0.954,8.945,0.283,11.419-1.996\nc2.475-2.282,3.711-5.14,3.711-8.562c0-0.571-0.049-5.708-0.144-15.417c-0.098-9.709-0.144-18.179-0.144-25.406l-6.567,1.136\nc-4.187,0.767-9.469,1.092-15.846,1c-6.374-0.089-12.991-0.757-19.842-1.999c-6.854-1.231-13.229-4.086-19.13-8.559\nc-5.898-4.473-10.085-10.328-12.56-17.556l-2.855-6.57c-1.903-4.374-4.899-9.233-8.992-14.559\nc-4.093-5.331-8.232-8.945-12.419-10.848l-1.999-1.431c-1.332-0.951-2.568-2.098-3.711-3.429c-1.142-1.331-1.997-2.663-2.568-3.997\nc-0.572-1.335-0.098-2.43,1.427-3.289c1.525-0.859,4.281-1.276,8.28-1.276l5.708,0.853c3.807,0.763,8.516,3.042,14.133,6.851\nc5.614,3.806,10.229,8.754,13.846,14.842c4.38,7.806,9.657,13.754,15.846,17.847c6.184,4.093,12.419,6.136,18.699,6.136\nc6.28,0,11.704-0.476,16.274-1.423c4.565-0.952,8.848-2.383,12.847-4.285c1.713-12.758,6.377-22.559,13.988-29.41\nc-10.848-1.14-20.601-2.857-29.264-5.14c-8.658-2.286-17.605-5.996-26.835-11.14c-9.235-5.137-16.896-11.516-22.985-19.126\nc-6.09-7.614-11.088-17.61-14.987-29.979c-3.901-12.374-5.852-26.648-5.852-42.826c0-23.035,7.52-42.637,22.557-58.817\nc-7.044-17.318-6.379-36.732,1.997-58.24c5.52-1.715,13.706-0.428,24.554,3.853c10.85,4.283,18.794,7.952,23.84,10.994\nc5.046,3.041,9.089,5.618,12.135,7.708c17.705-4.947,35.976-7.421,54.818-7.421s37.117,2.474,54.823,7.421l10.849-6.849\nc7.419-4.57,16.18-8.758,26.262-12.565c10.088-3.805,17.802-4.853,23.134-3.138c8.562,21.509,9.325,40.922,2.279,58.24\nc15.036,16.18,22.559,35.787,22.559,58.817c0,16.178-1.958,30.497-5.853,42.966c-3.9,12.471-8.941,22.457-15.125,29.979\nc-6.191,7.521-13.901,13.85-23.131,18.986c-9.232,5.14-18.182,8.85-26.84,11.136c-8.662,2.286-18.415,4.004-29.263,5.146\nc9.894,8.562,14.842,22.077,14.842,40.539v60.237c0,3.422,1.19,6.279,3.572,8.562c2.379,2.279,6.136,2.95,11.276,1.995\nc44.163-14.653,80.185-41.062,108.068-79.226c27.88-38.161,41.825-81.126,41.825-128.906\nC438.536,184.851,428.728,148.168,409.132,114.573z\" />\n    </group>\n</vector>\n"
  },
  {
    "path": "lib/common/src/main/res/drawable-xxhdpi/common_switch_thumb.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"oval\">\n    <solid android:color=\"@color/lightGrayDark\" />\n    <stroke\n        android:width=\"2dp\"\n        android:color=\"@android:color/transparent\" />\n    <size\n        android:width=\"20dp\"\n        android:height=\"20dp\" />\n</shape>"
  },
  {
    "path": "lib/common/src/main/res/drawable-xxhdpi/common_switch_track.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:state_checked=\"true\">\n        <shape android:shape=\"rectangle\">\n            <solid android:color=\"@color/loveGreen\" />\n            <corners android:radius=\"20dp\" />\n        </shape>\n    </item>\n\n    <item android:state_checked=\"false\">\n        <shape android:shape=\"rectangle\">\n            <solid android:color=\"@color/mediumGrayDark\" />\n            <corners android:radius=\"20dp\" />\n        </shape>\n    </item>\n</selector>"
  },
  {
    "path": "lib/common/src/main/res/layout/common_activity_drawer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.drawerlayout.widget.DrawerLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:id=\"@+id/baseDrawerRootLayout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fitsSystemWindows=\"true\"\n    android:overScrollMode=\"never\"\n    tools:openDrawer=\"start\">\n\n    <FrameLayout\n        android:id=\"@+id/baseDrawerContainerView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\" />\n\n    <com.google.android.material.navigation.NavigationView\n        android:id=\"@+id/baseDrawerNavView\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"match_parent\"\n        android:layout_gravity=\"start\"\n        android:background=\"@color/white\"\n        app:headerLayout=\"@layout/common_activity_drawer_nav_header\"\n        app:menu=\"@menu/common_drawer\" />\n\n</androidx.drawerlayout.widget.DrawerLayout>\n"
  },
  {
    "path": "lib/common/src/main/res/layout/common_activity_drawer_nav_header.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"192dp\">\n\n    <ImageView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:background=\"@color/colorPrimary\"\n        android:scaleType=\"centerCrop\"\n        android:src=\"@drawable/common_bg_header\" />\n\n    <ImageView\n        android:id=\"@+id/baseDrawerNavHeaderAvatarIv\"\n        android:layout_width=\"80dp\"\n        android:layout_height=\"80dp\"\n        android:layout_above=\"@+id/descTv\"\n        android:layout_marginLeft=\"@dimen/spacing_16\"\n        android:src=\"@drawable/common_avatar_round\" />\n\n    <TextView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"80dp\"\n        android:layout_above=\"@+id/descTv\"\n        android:layout_marginLeft=\"@dimen/spacing_16\"\n        android:layout_toRightOf=\"@id/baseDrawerNavHeaderAvatarIv\"\n        android:gravity=\"center_vertical\"\n        android:text=\"@string/author\"\n        android:textColor=\"@color/white\"\n        android:textSize=\"@dimen/font_40\"\n        android:textStyle=\"bold\" />\n\n    <TextView\n        android:id=\"@+id/descTv\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_marginLeft=\"@dimen/spacing_16\"\n        android:layout_marginTop=\"@dimen/spacing_8\"\n        android:layout_marginBottom=\"@dimen/spacing_16\"\n        android:text=\"@string/demo_of_android_util_code\"\n        android:textColor=\"@color/white\"\n        android:textSize=\"@dimen/font_16\" />\n</RelativeLayout>\n"
  },
  {
    "path": "lib/common/src/main/res/layout/common_activity_title.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/baseTitleRootLayout\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.google.android.material.appbar.AppBarLayout\n        android:id=\"@+id/baseTitleAppBarLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:theme=\"@style/ThemeOverlay.AppCompat.Dark.ActionBar\"\n        app:popupTheme=\"@style/ThemeOverlay.AppCompat.Light\">\n\n        <androidx.appcompat.widget.Toolbar\n            android:id=\"@+id/baseTitleToolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            app:layout_scrollFlags=\"scroll|enterAlways\" />\n    </com.google.android.material.appbar.AppBarLayout>\n\n    <ViewStub\n        android:id=\"@+id/baseTitleStubScroll\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout=\"@layout/common_activity_title_stub_scroll\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\" />\n\n    <ViewStub\n        android:id=\"@+id/baseTitleStubNoScroll\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout=\"@layout/common_activity_title_stub_no_scroll\"\n        app:layout_behavior=\"@string/appbar_scrolling_view_behavior\" />\n\n</androidx.coordinatorlayout.widget.CoordinatorLayout>\n"
  },
  {
    "path": "lib/common/src/main/res/layout/common_activity_title_stub_no_scroll.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/commonTitleContentView\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\" />\n"
  },
  {
    "path": "lib/common/src/main/res/layout/common_activity_title_stub_scroll.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.core.widget.NestedScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/commonTitleNsv\"\n    style=\"@style/CommonScrollStyle\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fillViewport=\"true\">\n\n    <FrameLayout\n        android:id=\"@+id/commonTitleContentView\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n</androidx.core.widget.NestedScrollView>\n\n"
  },
  {
    "path": "lib/common/src/main/res/layout/common_dialog_content.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@drawable/common_content_dialog_bg\"\n    android:padding=\"16dp\">\n\n    <RelativeLayout\n        android:id=\"@+id/cdcTitleRl\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\">\n\n        <TextView\n            android:id=\"@+id/cdcTitleTv\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:textColor=\"@color/darkGrayDark\"\n            android:textSize=\"@dimen/font_20\"\n            tools:text=\"Title\" />\n\n    </RelativeLayout>\n\n    <RelativeLayout\n        android:id=\"@+id/cdcContentRl\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/cdcTitleRl\"\n        android:layout_marginTop=\"8dp\">\n\n        <TextView\n            android:id=\"@+id/cdcContentTv\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:lineSpacingExtra=\"2dp\"\n            android:textColor=\"@color/darkGray\"\n            android:textSize=\"@dimen/font_16\"\n            tools:text=\"ContentContentContentContentContentContentContentContent\" />\n    </RelativeLayout>\n\n    <RelativeLayout\n        android:id=\"@+id/cdcBottomRl\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/cdcContentRl\"\n        android:layout_marginTop=\"4dp\">\n\n        <TextView\n            android:id=\"@+id/cdcBottomPositiveTv\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_marginLeft=\"16dp\"\n            android:background=\"@drawable/common_content_dialog_btn_bg\"\n            android:padding=\"4dp\"\n            android:textColor=\"@color/colorPrimary\"\n            android:textSize=\"@dimen/font_16\"\n            tools:text=\"Yes\" />\n\n        <TextView\n            android:id=\"@+id/cdcBottomNegativeTv\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_toLeftOf=\"@id/cdcBottomPositiveTv\"\n            android:background=\"@drawable/common_content_dialog_btn_bg\"\n            android:padding=\"4dp\"\n            android:textColor=\"@color/darkGrayDark\"\n            android:textSize=\"@dimen/font_16\"\n            tools:text=\"No\" />\n\n    </RelativeLayout>\n\n</RelativeLayout>"
  },
  {
    "path": "lib/common/src/main/res/layout/common_dialog_loading.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <LinearLayout\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"bottom|center_horizontal\"\n        android:layout_marginLeft=\"64dp\"\n        android:layout_marginRight=\"64dp\"\n        android:layout_marginBottom=\"120dp\"\n        android:background=\"@drawable/common_loading_bg\"\n        android:gravity=\"center\"\n        android:minWidth=\"100dp\"\n        android:minHeight=\"40dp\"\n        android:paddingLeft=\"20dp\"\n        android:paddingTop=\"10dp\"\n        android:paddingRight=\"20dp\"\n        android:paddingBottom=\"10dp\">\n\n        <com.blankj.common.view.RotateView\n            android:layout_width=\"22dp\"\n            android:layout_height=\"22dp\"\n            android:background=\"@drawable/common_loading\" />\n\n        <TextView\n            android:id=\"@+id/utilActionLoadingMsgTv\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"4dp\"\n            android:lineSpacingExtra=\"2dp\"\n            android:text=\"Loading...\"\n            android:textColor=\"@color/white\"\n            android:textSize=\"14dp\" />\n\n    </LinearLayout>\n\n</FrameLayout>"
  },
  {
    "path": "lib/common/src/main/res/layout/common_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.recyclerview.widget.RecyclerView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/commonItemRv\"\n    style=\"@style/CommonScrollStyle\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\" />\n"
  },
  {
    "path": "lib/common/src/main/res/layout/common_item_title_click.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingLeft=\"12dp\"\n    android:paddingTop=\"8dp\"\n    android:paddingRight=\"12dp\"\n    android:paddingBottom=\"8dp\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerVertical=\"true\"\n        android:layout_toLeftOf=\"@id/commonItemGoIv\"\n        android:gravity=\"center_vertical\"\n        android:minHeight=\"32dp\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:id=\"@+id/commonItemTitleTv\"\n            style=\"@style/CommonItemTitleStyle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            tools:text=\"title\" />\n\n        <com.blankj.base.view.EmptyGoneTextView\n            android:id=\"@+id/commonItemContentTv\"\n            style=\"@style/CommonItemContentStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"@dimen/spacing_16\"\n            android:gravity=\"right\"\n            tools:text=\"content\" />\n\n    </LinearLayout>\n\n    <ImageView\n        android:id=\"@+id/commonItemGoIv\"\n        android:layout_width=\"16dp\"\n        android:layout_height=\"16dp\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:src=\"@drawable/common_ic_item_go\"\n        android:visibility=\"gone\" />\n\n</RelativeLayout>"
  },
  {
    "path": "lib/common/src/main/res/layout/common_item_title_content.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingLeft=\"12dp\"\n    android:paddingTop=\"8dp\"\n    android:paddingRight=\"12dp\"\n    android:paddingBottom=\"8dp\">\n\n    <TextView\n        android:id=\"@+id/commonItemTitleTv\"\n        style=\"@style/CommonItemTitleStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        tools:text=\"title\" />\n\n    <com.blankj.base.view.EmptyGoneTextView\n        android:id=\"@+id/commonItemContentTv\"\n        style=\"@style/CommonItemContentStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/commonItemTitleTv\"\n        android:layout_marginTop=\"2dp\"\n        tools:text=\"content\" />\n\n</RelativeLayout>"
  },
  {
    "path": "lib/common/src/main/res/layout/common_item_title_image.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\"\n    android:paddingLeft=\"12dp\"\n    android:paddingTop=\"8dp\"\n    android:paddingRight=\"12dp\"\n    android:paddingBottom=\"8dp\">\n\n    <TextView\n        android:id=\"@+id/commonItemTitleTv\"\n        style=\"@style/CommonItemTitleStyle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"2dp\"\n        tools:text=\"title\" />\n\n    <ImageView\n        android:id=\"@+id/commonItemIv\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        tools:src=\"@mipmap/ic_launcher\" />\n\n</LinearLayout>"
  },
  {
    "path": "lib/common/src/main/res/layout/common_item_title_seekbar.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:orientation=\"vertical\"\n    android:paddingLeft=\"12dp\"\n    android:paddingTop=\"8dp\"\n    android:paddingRight=\"12dp\"\n    android:paddingBottom=\"8dp\">\n\n    <TextView\n        android:id=\"@+id/commonItemTitleTv\"\n        style=\"@style/CommonItemTitleStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        tools:text=\"title\" />\n\n    <com.blankj.base.view.EmptyGoneTextView\n        android:id=\"@+id/commonItemContentTv\"\n        style=\"@style/CommonItemContentStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"2dp\"\n        tools:text=\"content\" />\n\n    <SeekBar\n        android:id=\"@+id/commonItemSb\"\n        style=\"@style/CommonSeekBarStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"2dp\" />\n\n</LinearLayout>"
  },
  {
    "path": "lib/common/src/main/res/layout/common_item_title_switch.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingLeft=\"12dp\"\n    android:paddingTop=\"8dp\"\n    android:paddingRight=\"12dp\"\n    android:paddingBottom=\"8dp\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerVertical=\"true\"\n        android:layout_toLeftOf=\"@id/commonItemSwitch\"\n        android:orientation=\"vertical\">\n\n        <TextView\n            android:id=\"@+id/commonItemTitleTv\"\n            style=\"@style/CommonItemTitleStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            tools:text=\"title\" />\n\n        <com.blankj.base.view.EmptyGoneTextView\n            android:id=\"@+id/commonItemContentTv\"\n            style=\"@style/CommonItemContentStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"2dp\"\n            tools:text=\"content\" />\n\n    </LinearLayout>\n\n    <Switch\n        android:id=\"@+id/commonItemSwitch\"\n        style=\"@style/CommonSwitchStyle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:layout_marginLeft=\"8dp\" />\n\n</RelativeLayout>"
  },
  {
    "path": "lib/common/src/main/res/menu/common_drawer.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item\n        android:id=\"@+id/baseDrawerActionGitHub\"\n        android:icon=\"@drawable/main_menu_github\"\n        android:title=\"GitHub\" />\n\n    <item\n        android:id=\"@+id/baseDrawerActionBlog\"\n        android:icon=\"@drawable/main_menu_blog\"\n        android:title=\"Blog\" />\n</menu>\n"
  },
  {
    "path": "lib/common/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimaryOrigin\">#3F51B5</color>\n    <color name=\"colorPrimaryDarkOrigin\">#303F9F</color>\n    <color name=\"colorAccentOrigin\">#FF4081</color>\n\n    <color name=\"colorPrimary\">#34A48E</color>\n    <color name=\"colorPrimaryHalfTrans\">#8034A48E</color>\n    <color name=\"colorPrimaryDark\">#245C50</color>\n    <color name=\"colorAccent\">#FF73A3</color>\n    <color name=\"colorAccentHalfTrans\">#80245C50</color>\n\n    <color name=\"white\">#FFFFFF</color>\n    <color name=\"light_black\">#191919</color>\n\n    <color name=\"rainbow_red\">#bf0c43</color>\n    <color name=\"rainbow_yellow\">#f9ba15</color>\n    <color name=\"rainbow_green\">#8eac00</color>\n    <color name=\"rainbow_blue\">#127a97</color>\n    <color name=\"rainbow_purple\">#452b72</color>\n\n    <integer-array name=\"rainbow\">\n        <item>@color/rainbow_red</item>\n        <item>@color/rainbow_yellow</item>\n        <item>@color/rainbow_green</item>\n        <item>@color/rainbow_blue</item>\n        <item>@color/rainbow_purple</item>\n    </integer-array>\n\n\n    <!--beautiful color-->\n    <color name=\"grapefruit\">#ED5565</color>\n    <color name=\"grapefruitDark\">#DA4453</color>\n\n    <color name=\"bittersweet\">#FC6E51</color>\n    <color name=\"bittersweetDark\">#E9573F</color>\n\n    <color name=\"sunflower\">#FFCE54</color>\n    <color name=\"sunflowerDark\">#F6BB42</color>\n\n    <color name=\"grass\">#A0D468</color>\n    <color name=\"grassDark\">#8CC152</color>\n\n    <color name=\"mint\">#48CFAD</color>\n    <color name=\"mintDark\">#37BC9B</color>\n\n    <color name=\"loveGreen\">#62C554</color>\n    <color name=\"loveGreenDark\">#479841</color>\n\n    <color name=\"aqua\">#4FC1E9</color>\n    <color name=\"aquaDark\">#3BAFDA</color>\n\n    <color name=\"blueJeans\">#5D9CEC</color>\n    <color name=\"blueJeansDark\">#4A89DC</color>\n\n    <color name=\"lavander\">#AC92EC</color>\n    <color name=\"lavanderDark\">#967ADC</color>\n\n    <color name=\"pinkRose\">#EC87C0</color>\n    <color name=\"pinkRoseDark\">#D770AD</color>\n\n    <color name=\"lightGray\">#F5F7FA</color>\n    <color name=\"lightGrayDark\">#E6E9ED</color>\n\n    <color name=\"mediumGray\">#CCD1D9</color>\n    <color name=\"mediumGrayDark\">#AAB2BD</color>\n\n    <color name=\"darkGray\">#656D78</color>\n    <color name=\"darkGrayDark\">#434A54</color>\n</resources>\n"
  },
  {
    "path": "lib/common/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <dimen name=\"radius_4\">4dp</dimen>\n    <dimen name=\"radius_8\">8dp</dimen>\n    <dimen name=\"radius_16\">16dp</dimen>\n\n    <dimen name=\"spacing_6\">6dp</dimen>\n    <dimen name=\"spacing_8\">8dp</dimen>\n    <dimen name=\"spacing_16\">16dp</dimen>\n    <dimen name=\"spacing_24\">24dp</dimen>\n    <dimen name=\"spacing_128\">128dp</dimen>\n\n    <dimen name=\"size_1\">1dp</dimen>\n    <dimen name=\"size_2\">2dp</dimen>\n    <dimen name=\"size_4\">4dp</dimen>\n    <dimen name=\"size_6\">6dp</dimen>\n    <dimen name=\"size_40\">40dp</dimen>\n    <dimen name=\"size_160\">160dp</dimen>\n\n    <dimen name=\"font_14\">14sp</dimen>\n    <dimen name=\"font_16\">16sp</dimen>\n    <dimen name=\"font_18\">18sp</dimen>\n    <dimen name=\"font_20\">20sp</dimen>\n    <dimen name=\"font_24\">24sp</dimen>\n    <dimen name=\"font_40\">40sp</dimen>\n\n    <dimen name=\"block_width\">40dp</dimen>\n    <dimen name=\"block_low_height\">8dp</dimen>\n    <dimen name=\"block_high_height\">196dp</dimen>\n\n    <dimen name=\"offset_64\">64dp</dimen>\n</resources>\n"
  },
  {
    "path": "lib/common/src/main/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"github\">https://github.com/Blankj/AndroidUtilCode</string>\n    <string name=\"blog\">https://blankj.com</string>\n\n    <string name=\"core_util\">Core Util</string>\n    <string name=\"sub_util\">Sub Util</string>\n\n    <string name=\"author\">Blankj</string>\n    <string name=\"demo_of_android_util_code\">Demo of AndroidUtilCode</string>\n\n    <string name=\"navigation_drawer_open\">navigation_drawer_open</string>\n    <string name=\"navigation_drawer_close\">navigation_drawer_close</string>\n\n    <string name=\"permission_rationale_message\">You have rejected us to apply for authorization, please agree to authorization, otherwise the function can\\'t be used normally!</string>\n    <string name=\"permission_denied_forever_message\">We need some of the permissions you rejected or the system failed to apply failed, please manually set to the page authorize, otherwise the function can\\'t be used normally!</string>\n</resources>\n"
  },
  {
    "path": "lib/common/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n        <item name=\"android:windowAnimationStyle\">@style/AnimationActivity</item>\n    </style>\n\n    <style name=\"SplashTheme\" parent=\"AppTheme\">\n        <!--        <item name=\"android:windowDisablePreview\">true</item>-->\n    </style>\n\n\n    <style name=\"CommonItemTitleStyle\">\n        <item name=\"android:textColor\">@color/darkGrayDark</item>\n        <item name=\"android:textSize\">@dimen/font_16</item>\n    </style>\n\n    <style name=\"CommonItemContentStyle\">\n        <item name=\"android:textColor\">@color/darkGray</item>\n        <item name=\"android:textSize\">@dimen/font_14</item>\n    </style>\n\n    <style name=\"CommonTextStyle\">\n        <item name=\"android:textSize\">@dimen/font_16</item>\n        <item name=\"android:textColor\">@color/darkGrayDark</item>\n    </style>\n\n    <style name=\"CommonScrollStyle\">\n        <item name=\"android:overScrollMode\">never</item>\n        <item name=\"android:scrollbarSize\">@dimen/size_4</item>\n        <item name=\"android:fadeScrollbars\">true</item>\n        <item name=\"android:scrollbarStyle\">outsideOverlay</item>\n        <item name=\"android:scrollbarThumbVertical\">@drawable/common_scrollbar_thumb</item>\n        <item name=\"android:scrollbars\">vertical</item>\n        <item name=\"android:descendantFocusability\">afterDescendants</item>\n    </style>\n\n    <style name=\"CommonWideBtnStyle\" parent=\"CommonTextStyle\">\n        <item name=\"android:textAllCaps\">false</item>\n        <item name=\"android:paddingTop\">@dimen/spacing_6</item>\n        <item name=\"android:paddingBottom\">@dimen/spacing_6</item>\n        <item name=\"android:textColor\">@drawable/common_button_txt_color</item>\n        <item name=\"android:background\">@drawable/common_button_bg</item>\n    </style>\n\n    <style name=\"CommonSwitchStyle\">\n        <item name=\"android:thumb\">@drawable/common_switch_thumb</item>\n        <item name=\"android:track\">@drawable/common_switch_track</item>\n        <item name=\"android:background\">@android:color/transparent</item>\n    </style>\n\n    <style name=\"CommonSeekBarStyle\">\n        <item name=\"android:progressDrawable\">@drawable/common_seekbar_progress</item>\n        <item name=\"android:thumb\">@drawable/common_seekbar_thumb</item>\n        <item name=\"android:background\">@null</item>\n    </style>\n\n    <style name=\"CommonLoadingDialogStyle\">\n        <item name=\"android:background\">@android:color/transparent</item>\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"statusBarBackground\">@android:color/transparent</item>\n        <item name=\"android:windowIsFloating\">false</item>\n    </style>\n\n    <style name=\"CommonContentDialogStyle\">\n        <item name=\"android:background\">@android:color/transparent</item>\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:backgroundDimEnabled\">true</item>\n        <item name=\"android:windowIsFloating\">true</item>\n        <item name=\"android:windowMinWidthMajor\">85%</item>\n        <item name=\"android:windowMinWidthMinor\">85%</item>\n    </style>\n\n    <style name=\"AnimationActivity\" parent=\"@android:style/Animation\">\n        <!-- A 打开 B，B 的出现动画 -->\n        <item name=\"android:activityOpenEnterAnimation\">@anim/slide_in_left</item>\n        <!-- A 打开 B，A 的消失动画 -->\n        <item name=\"android:activityOpenExitAnimation\">@anim/slide_out_left</item>\n        <!-- B 关闭到 A，A 的出现动画 *时长要小于 B 的消失动画，否则会闪烁* -->\n        <item name=\"android:activityCloseEnterAnimation\">@anim/slide_in_right</item>\n        <!-- B 关闭到 A，B 的消失动画 *时长要小于 B 的消失动画，否则会闪烁* -->\n        <item name=\"android:activityCloseExitAnimation\">@anim/slide_out_right</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "lib/subutil/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "lib/subutil/README-CN.md",
    "content": "## How to use\n\n从下面选择拷贝你需要用到的类到你项目中即可。\n\n\n## APIs\n\n\n* ### 应用商店相关 -> [AppStoreUtils.java][appStore.java] -> [Demo][appStore.demo]\n```\ngetAppStoreIntent: 获取跳转应用商店意图\n```\n\n* ### 电池相关 -> [BatteryUtils.java][battery.java] -> [Demo][battery.demo]\n```\nregisterBatteryStatusChangedListener    : 注册电池状态改变监听器\nisRegisteredBatteryStatusChangedListener: 判断是否注册电池状态改变监听器\nunregisterBatteryStatusChangedListener  : 注销电池状态改变监听器\n```\n\n* ### 坐标转换相关 -> [CoordinateUtils.java][coordinate.java] -> [Test][coordinate.test]\n```\nbd09ToGcj02 : BD09 坐标转 GCJ02 坐标\ngcj02ToBd09 : GCJ02 坐标转 BD09 坐标\ngcj02ToWGS84: GCJ02 坐标转 WGS84 坐标\nwgs84ToGcj02: WGS84 坐标转 GCJ02 坐标\nbd09ToWGS84 : BD09 坐标转 WGS84 坐标\nwgs84ToBd09 : WGS84 坐标转 BD09 坐标\n```\n\n* ### 国家相关 -> [CountryUtils.java][country.java] -> [Demo][country.demo]\n```\ngetCountryCodeBySim     : 根据 Sim 卡获取国家码\ngetCountryCodeByLanguage: 根据系统语言获取国家码\ngetCountryBySim         : 根据 Sim 卡获取国家\ngetCountryByLanguage    : 根据系统语言获取国家\n```\n\n* ### 危险相关 -> [DangerousUtils.java][dangerous.java] -> [Demo][dangerous.demo]\n```\ninstallAppSilent    : 静默安装 App\nuninstallAppSilent  : 静默卸载 App\nshutdown            : 关机\nreboot              : 重启\nreboot2Recovery     : 重启到 recovery\nreboot2Bootloader   : 重启到 bootloader\nsetMobileDataEnabled: 打开或关闭移动数据\nsendSmsSilent       : 发送短信\n```\n\n* ### 定位相关 -> [LocationUtils.java][location.java] -> [Demo][location.demo]\n```\nisGpsEnabled     : 判断 Gps 是否可用\nisLocationEnabled: 判断定位是否可用\nopenGpsSettings  : 打开 Gps 设置界面\nregister         : 注册\nunregister       : 注销\ngetAddress       : 根据经纬度获取地理位置\ngetCountryName   : 根据经纬度获取所在国家\ngetLocality      : 根据经纬度获取所在地\ngetStreet        : 根据经纬度获取所在街道\nisBetterLocation : 是否更好的位置\nisSameProvider   : 是否相同的提供者\n```\n\n* ### 拼音相关 -> [PinyinUtils.java][pinyin.java] -> [Demo][pinyin.demo]\n```\nccs2Pinyin           : 汉字转拼音\nccs2Pinyin           : 汉字转拼音\ngetPinyinFirstLetter : 获取第一个汉字首字母\ngetPinyinFirstLetters: 获取所有汉字的首字母\ngetSurnamePinyin     : 根据名字获取姓氏的拼音\ngetSurnameFirstLetter: 根据名字获取姓氏的首字母\n```\n\n\n\n[appStore.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/subutil/src/main/java/com/blankj/subutil/util/AppStoreUtils.java\n[appStore.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/feature/appStore/AppStoreActivity.kt\n\n[battery.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/subutil/src/main/java/com/blankj/subutil/util/BatteryUtils.java\n[battery.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/feature/battery/BatteryActivity.kt\n\n[coordinate.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/subutil/src/main/java/com/blankj/subutil/util/CoordinateUtils.java\n[coordinate.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/subutil/src/test/java/com/blankj/subutil/util/CoordinateUtilsTest.java\n\n[country.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/subutil/src/main/java/com/blankj/subutil/util/CountryUtils.java\n[country.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/feature/country/CountryActivity.kt\n\n[dangerous.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/subutil/src/main/java/com/blankj/subutil/util/DangerousUtils.java\n[dangerous.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/feature/dangerous/DangerousActivity.kt\n\n[location.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/subutil/src/main/java/com/blankj/subutil/util/LocationUtils.java\n[location.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/feature/location/LocationActivity.kt\n\n[pinyin.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/subutil/src/main/java/com/blankj/subutil/util/PinyinUtils.java\n[pinyin.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/feature/pinyin/PinyinActivity.kt\n"
  },
  {
    "path": "lib/subutil/README.md",
    "content": "## How to use\n\nYou should copy the following classes which you want to use in your project.\n\n\n## APIs\n\n\n* ### About AppStore -> [AppStoreUtils.java][appStore.java] -> [Demo][appStore.demo]\n```\ngetAppStoreIntent\n```\n\n* ### About Battery -> [BatteryUtils.java][battery.java] -> [Demo][battery.demo]\n```\nregisterBatteryStatusChangedListener\nisRegisteredBatteryStatusChangedListener\nunregisterBatteryStatusChangedListener\n```\n\n* ### About Coordinate -> [CoordinateUtils.java][coordinate.java] -> [Test][coordinate.test]\n```\nbd09ToGcj02\ngcj02ToBd09\ngcj02ToWGS84\nwgs84ToGcj02\nbd09ToWGS84\nwgs84ToBd09\n```\n\n* ### About Country -> [CountryUtils.java][country.java] -> [Demo][country.demo]\n```\ngetCountryCodeBySim\ngetCountryCodeByLanguage\ngetCountryBySim\ngetCountryByLanguage\n```\n\n* ### About Dangerous -> [DangerousUtils.java][dangerous.java] -> [Demo][dangerous.demo]\n```\ninstallAppSilent\nuninstallAppSilent\nshutdown\nreboot\nreboot2Recovery\nreboot2Bootloader\nsetMobileDataEnabled\nsendSmsSilent\n```\n\n* ### About Location -> [LocationUtils.java][location.java] -> [Demo][location.demo]\n```\nisGpsEnabled\nisLocationEnabled\nopenGpsSettings\nregister\nunregister\ngetAddress\ngetCountryName\ngetLocality\ngetStreet\nisBetterLocation\nisSameProvider\n```\n\n* ### About Pinyin -> [PinyinUtils.java][pinyin.java] -> [Demo][pinyin.demo]\n```\nccs2Pinyin\nccs2Pinyin\ngetPinyinFirstLetter\ngetPinyinFirstLetters\ngetSurnamePinyin\ngetSurnameFirstLetter\n```\n\n\n\n[appStore.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/subutil/src/main/java/com/blankj/subutil/util/AppStoreUtils.java\n[appStore.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/feature/appStore/AppStoreActivity.kt\n\n[battery.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/subutil/src/main/java/com/blankj/subutil/util/BatteryUtils.java\n[battery.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/feature/battery/BatteryActivity.kt\n\n[coordinate.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/subutil/src/main/java/com/blankj/subutil/util/CoordinateUtils.java\n[coordinate.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/subutil/src/test/java/com/blankj/subutil/util/CoordinateUtilsTest.java\n\n[country.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/subutil/src/main/java/com/blankj/subutil/util/CountryUtils.java\n[country.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/feature/country/CountryActivity.kt\n\n[dangerous.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/subutil/src/main/java/com/blankj/subutil/util/DangerousUtils.java\n[dangerous.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/feature/dangerous/DangerousActivity.kt\n\n[location.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/subutil/src/main/java/com/blankj/subutil/util/LocationUtils.java\n[location.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/feature/location/LocationActivity.kt\n\n[pinyin.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/subutil/src/main/java/com/blankj/subutil/util/PinyinUtils.java\n[pinyin.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/subutil/pkg/src/main/java/com/blankj/subutil/pkg/feature/pinyin/PinyinActivity.kt\n"
  },
  {
    "path": "lib/subutil/build.gradle",
    "content": "apply {\n    plugin \"readme-sub\"\n}\n\nreadme {\n    readmeFile file('./README.md')\n    readmeCnFile file('./README-CN.md')\n}\n\ndependencies {\n    compileOnly Config.libs.androidx_appcompat.path\n    compileOnly Config.libs.androidx_material.path\n    compileOnly Config.modules.lib_utilcode.dep\n    api(Config.libs.glide.path) {\n        exclude group: \"com.android.support\"\n    }\n    api Config.libs.retrofit.path\n    api Config.libs.gson.path\n\n    testImplementation Config.modules.lib_utilcode.dep\n    testImplementation Config.libs.test_junit.path\n    testImplementation Config.libs.test_robolectric.path\n}"
  },
  {
    "path": "lib/subutil/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/Blankj/Library/Android/android_sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n\n#-keep class com.blankj.subutil.** { *; }\n#-keepclassmembers class com.blankj.subutil.** { *; }\n-dontwarn com.blankj.subutil.**"
  },
  {
    "path": "lib/subutil/src/main/AndroidManifest.xml",
    "content": "<manifest package=\"com.blankj.subutil\" />\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/AppStoreUtils.java",
    "content": "package com.blankj.subutil.util;\n\nimport android.content.Intent;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ResolveInfo;\nimport android.net.Uri;\nimport android.util.Log;\n\nimport com.blankj.utilcode.util.AppUtils;\nimport com.blankj.utilcode.util.RomUtils;\nimport com.blankj.utilcode.util.Utils;\n\nimport java.util.List;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/05/20\n *     desc  : utils about app store\n * </pre>\n */\npublic final class AppStoreUtils {\n\n    private static final String TAG = \"AppStoreUtils\";\n\n    private static final String GOOGLE_PLAY_APP_STORE_PACKAGE_NAME = \"com.android.vending\";\n\n    /**\n     * 获取跳转到应用商店的 Intent\n     *\n     * @return 跳转到应用商店的 Intent\n     */\n    public static Intent getAppStoreIntent() {\n        return getAppStoreIntent(Utils.getApp().getPackageName(), false);\n    }\n\n    /**\n     * 获取跳转到应用商店的 Intent\n     *\n     * @param isIncludeGooglePlayStore 是否包括 Google Play 商店\n     * @return 跳转到应用商店的 Intent\n     */\n    public static Intent getAppStoreIntent(boolean isIncludeGooglePlayStore) {\n        return getAppStoreIntent(Utils.getApp().getPackageName(), isIncludeGooglePlayStore);\n    }\n\n    /**\n     * 获取跳转到应用商店的 Intent\n     *\n     * @param packageName 包名\n     * @return 跳转到应用商店的 Intent\n     */\n    public static Intent getAppStoreIntent(final String packageName) {\n        return getAppStoreIntent(packageName, false);\n    }\n\n    /**\n     * 获取跳转到应用商店的 Intent\n     * <p>优先跳转到手机自带的应用市场</p>\n     *\n     * @param packageName              包名\n     * @param isIncludeGooglePlayStore 是否包括 Google Play 商店\n     * @return 跳转到应用商店的 Intent\n     */\n    public static Intent getAppStoreIntent(final String packageName, boolean isIncludeGooglePlayStore) {\n        if (RomUtils.isSamsung()) {// 三星单独处理跳转三星市场\n            Intent samsungAppStoreIntent = getSamsungAppStoreIntent(packageName);\n            if (samsungAppStoreIntent != null) return samsungAppStoreIntent;\n        }\n        if (RomUtils.isLeeco()) {// 乐视单独处理跳转乐视市场\n            Intent leecoAppStoreIntent = getLeecoAppStoreIntent(packageName);\n            if (leecoAppStoreIntent != null) return leecoAppStoreIntent;\n        }\n\n        Uri uri = Uri.parse(\"market://details?id=\" + packageName);\n        Intent intent = new Intent();\n        intent.setData(uri);\n        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n        List<ResolveInfo> resolveInfos = Utils.getApp().getPackageManager()\n                .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);\n        if (resolveInfos == null || resolveInfos.size() == 0) {\n            Log.e(TAG, \"No app store!\");\n            return null;\n        }\n        Intent googleIntent = null;\n        for (ResolveInfo resolveInfo : resolveInfos) {\n            String pkgName = resolveInfo.activityInfo.packageName;\n            if (!GOOGLE_PLAY_APP_STORE_PACKAGE_NAME.equals(pkgName)) {\n                if (AppUtils.isAppSystem(pkgName)) {\n                    intent.setPackage(pkgName);\n                    return intent;\n                }\n            } else {\n                intent.setPackage(GOOGLE_PLAY_APP_STORE_PACKAGE_NAME);\n                googleIntent = intent;\n            }\n        }\n        if (isIncludeGooglePlayStore && googleIntent != null) {\n            return googleIntent;\n        }\n\n        intent.setPackage(resolveInfos.get(0).activityInfo.packageName);\n        return intent;\n    }\n\n    private static Intent getSamsungAppStoreIntent(final String packageName) {\n        Intent intent = new Intent();\n        intent.setClassName(\"com.sec.android.app.samsungapps\", \"com.sec.android.app.samsungapps.Main\");\n        intent.setData(Uri.parse(\"http://www.samsungapps.com/appquery/appDetail.as?appId=\" + packageName));\n        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n        if (getAvailableIntentSize(intent) > 0) {\n            return intent;\n        }\n        return null;\n    }\n\n    private static Intent getLeecoAppStoreIntent(final String packageName) {\n        Intent intent = new Intent();\n        intent.setClassName(\"com.letv.app.appstore\", \"com.letv.app.appstore.appmodule.details.DetailsActivity\");\n        intent.setAction(\"com.letv.app.appstore.appdetailactivity\");\n        intent.putExtra(\"packageName\", packageName);\n        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n        if (getAvailableIntentSize(intent) > 0) {\n            return intent;\n        }\n        return null;\n    }\n\n    private static int getAvailableIntentSize(final Intent intent) {\n        return Utils.getApp().getPackageManager()\n                .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)\n                .size();\n    }\n}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/BatteryUtils.java",
    "content": "package com.blankj.subutil.util;\n\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.os.BatteryManager;\nimport android.os.Build;\nimport android.os.PowerManager;\n\nimport com.blankj.utilcode.util.ThreadUtils;\nimport com.blankj.utilcode.util.Utils;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.util.HashSet;\nimport java.util.Set;\n\nimport androidx.annotation.IntDef;\nimport androidx.annotation.RequiresApi;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2020/03/31\n *     desc  :\n * </pre>\n */\npublic final class BatteryUtils {\n\n    @IntDef({BatteryStatus.UNKNOWN, BatteryStatus.DISCHARGING, BatteryStatus.CHARGING,\n            BatteryStatus.NOT_CHARGING, BatteryStatus.FULL})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface BatteryStatus {\n        int UNKNOWN      = BatteryManager.BATTERY_STATUS_UNKNOWN;\n        int DISCHARGING  = BatteryManager.BATTERY_STATUS_DISCHARGING;\n        int CHARGING     = BatteryManager.BATTERY_STATUS_CHARGING;\n        int NOT_CHARGING = BatteryManager.BATTERY_STATUS_NOT_CHARGING;\n        int FULL         = BatteryManager.BATTERY_STATUS_FULL;\n    }\n\n    /**\n     * Return whether the app is on the device's power whitelist.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    @RequiresApi(api = Build.VERSION_CODES.M)\n    public static boolean isIgnoringBatteryOptimizations() {\n        return isIgnoringBatteryOptimizations(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return whether the app is on the device's power whitelist.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    @RequiresApi(api = Build.VERSION_CODES.M)\n    public static boolean isIgnoringBatteryOptimizations(String pkgName) {\n        try {\n            PowerManager pm = (PowerManager) Utils.getApp().getSystemService(Context.POWER_SERVICE);\n            //noinspection ConstantConditions\n            return pm.isIgnoringBatteryOptimizations(pkgName);\n        } catch (Exception e) {\n            return true;\n        }\n    }\n\n    /**\n     * Register the status of battery changed listener.\n     *\n     * @param listener The status of battery changed listener.\n     */\n    public static void registerBatteryStatusChangedListener(final OnBatteryStatusChangedListener listener) {\n        BatteryChangedReceiver.getInstance().registerListener(listener);\n    }\n\n    /**\n     * Return whether the status of battery changed listener has been registered.\n     *\n     * @param listener The status of battery changed listener.\n     * @return true to registered, false otherwise.\n     */\n    public static boolean isRegistered(final OnBatteryStatusChangedListener listener) {\n        return BatteryChangedReceiver.getInstance().isRegistered(listener);\n    }\n\n    /**\n     * Unregister the status of battery changed listener.\n     *\n     * @param listener The status of battery changed listener.\n     */\n    public static void unregisterBatteryStatusChangedListener(final OnBatteryStatusChangedListener listener) {\n        BatteryChangedReceiver.getInstance().unregisterListener(listener);\n    }\n\n    public static final class BatteryChangedReceiver extends BroadcastReceiver {\n\n        private static BatteryChangedReceiver getInstance() {\n            return BatteryChangedReceiver.LazyHolder.INSTANCE;\n        }\n\n        private Set<OnBatteryStatusChangedListener> mListeners = new HashSet<>();\n\n        void registerListener(final OnBatteryStatusChangedListener listener) {\n            if (listener == null) return;\n            ThreadUtils.runOnUiThread(new Runnable() {\n                @Override\n                public void run() {\n                    int preSize = mListeners.size();\n                    mListeners.add(listener);\n                    if (preSize == 0 && mListeners.size() == 1) {\n                        IntentFilter intentFilter = new IntentFilter();\n                        intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);\n                        Utils.getApp().registerReceiver(BatteryChangedReceiver.getInstance(), intentFilter);\n                    }\n                }\n            });\n        }\n\n        boolean isRegistered(final OnBatteryStatusChangedListener listener) {\n            if (listener == null) return false;\n            return mListeners.contains(listener);\n        }\n\n        void unregisterListener(final OnBatteryStatusChangedListener listener) {\n            if (listener == null) return;\n            ThreadUtils.runOnUiThread(new Runnable() {\n                @Override\n                public void run() {\n                    int preSize = mListeners.size();\n                    mListeners.remove(listener);\n                    if (preSize == 1 && mListeners.size() == 0) {\n                        Utils.getApp().unregisterReceiver(BatteryChangedReceiver.getInstance());\n                    }\n                }\n            });\n        }\n\n        @Override\n        public void onReceive(Context context, final Intent intent) {\n            if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {\n                ThreadUtils.runOnUiThread(new Runnable() {\n                    @Override\n                    public void run() {\n                        int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);\n                        int status = intent.getIntExtra(BatteryManager.EXTRA_STATUS, BatteryStatus.UNKNOWN);\n                        for (OnBatteryStatusChangedListener listener : mListeners) {\n                            listener.onBatteryStatusChanged(new Status(level, status));\n                        }\n                    }\n                });\n            }\n        }\n\n        private static class LazyHolder {\n            private static final BatteryChangedReceiver INSTANCE = new BatteryChangedReceiver();\n        }\n    }\n\n    public interface OnBatteryStatusChangedListener {\n        void onBatteryStatusChanged(Status status);\n    }\n\n    public static final class Status {\n        private int level;\n        @BatteryStatus\n        private int status;\n\n        Status(int level, int status) {\n            this.level = level;\n            this.status = status;\n        }\n\n        public int getLevel() {\n            return level;\n        }\n\n        public void setLevel(int level) {\n            this.level = level;\n        }\n\n        @BatteryStatus\n        public int getStatus() {\n            return status;\n        }\n\n        public void setStatus(int status) {\n            this.status = status;\n        }\n\n        @Override\n        public String toString() {\n            return batteryStatus2String(status) + \": \" + level + \"%\";\n        }\n\n        public static String batteryStatus2String(@BatteryStatus int status) {\n            if (status == BatteryStatus.DISCHARGING) {\n                return \"discharging\";\n            }\n            if (status == BatteryStatus.CHARGING) {\n                return \"charging\";\n            }\n            if (status == BatteryStatus.NOT_CHARGING) {\n                return \"not_charging\";\n            }\n            if (status == BatteryStatus.FULL) {\n                return \"full\";\n            }\n            return \"unknown\";\n        }\n    }\n}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/BitUtils.java",
    "content": "package com.blankj.subutil.util;\n\nimport android.util.Log;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/03/21\n *     desc  : 位运算工具类\n * </pre>\n */\npublic final class BitUtils {\n\n    private BitUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * 获取运算数指定位置的值<br>\n     * 例如： 0000 1011 获取其第 0 位的值为 1, 第 2 位 的值为 0<br>\n     *\n     * @param source 需要运算的数\n     * @param pos    指定位置 (0...7)\n     * @return 指定位置的值(0 or 1)\n     */\n    public static byte getBitValue(byte source, int pos) {\n        return (byte) ((source >> pos) & 1);\n\n    }\n\n\n    /**\n     * 将运算数指定位置的值置为指定值<br>\n     * 例: 0000 1011 需要更新为 0000 1111, 即第 2 位的值需要置为 1<br>\n     *\n     * @param source 需要运算的数\n     * @param pos    指定位置 (0<=pos<=7)\n     * @param value  只能取值为 0, 或 1, 所有大于0的值作为1处理, 所有小于0的值作为0处理\n     * @return 运算后的结果数\n     */\n    public static byte setBitValue(byte source, int pos, byte value) {\n\n        byte mask = (byte) (1 << pos);\n        if (value > 0) {\n            source |= mask;\n\n        } else {\n            source &= (~mask);\n\n        }\n        return source;\n    }\n\n\n    /**\n     * 将运算数指定位置取反值<br>\n     * 例： 0000 1011 指定第 3 位取反, 结果为 0000 0011; 指定第2位取反, 结果为 0000 1111<br>\n     *\n     * @param source\n     * @param pos    指定位置 (0<=pos<=7)\n     * @return 运算后的结果数\n     */\n    public static byte reverseBitValue(byte source, int pos) {\n        byte mask = (byte) (1 << pos);\n        return (byte) (source ^ mask);\n\n    }\n\n\n    /**\n     * 检查运算数的指定位置是否为1<br>\n     *\n     * @param source 需要运算的数\n     * @param pos    指定位置 (0<=pos<=7)\n     * @return true 表示指定位置值为1, false 表示指定位置值为 0\n     */\n    public static boolean checkBitValue(byte source, int pos) {\n\n        source = (byte) (source >>> pos);\n\n        return (source & 1) == 1;\n\n    }\n\n    /**\n     * 入口函数做测试<br>\n     *\n     * @param args\n     */\n    public static void main(String[] args) {\n        // 取十进制 11 (二级制 0000 1011) 为例子\n        byte source = 11;\n        // 取第2位值并输出, 结果应为 0000 1011\n        for (byte i = 7; i >= 0; i--) {\n            Log.d(\"BitUtils\", getBitValue(source, i) + \"\");\n\n        }\n        // 将第6位置为1并输出 , 结果为 75 (0100 1011)\n        Log.d(\"BitUtils\", setBitValue(source, 6, (byte) 1) + \"\");\n\n        // 将第6位取反并输出, 结果应为75(0100 1011)\n        Log.d(\"BitUtils\", reverseBitValue(source, 6) + \"\");\n\n        // 检查第6位是否为1，结果应为false\n        Log.d(\"BitUtils\", checkBitValue(source, 6) + \"\");\n\n        // 输出为1的位, 结果应为 0 1 3\n        for (byte i = 0; i < 8; i++) {\n            if (checkBitValue(source, i)) {\n                Log.d(\"BitUtils\", i + \"\");\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/CameraUtils.java",
    "content": "package com.blankj.subutil.util;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/09/19\n *     desc  : 相机相关工具类\n * </pre>\n */\npublic final class CameraUtils {\n\n//    private CameraUtils() {\n//        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n//    }\n//\n//    /**\n//     * 获取打开照程序界面的Intent\n//     */\n//    public static Intent getOpenCameraIntent() {\n//        return new Intent(MediaStore.ACTION_IMAGE_CAPTURE);\n//    }\n//\n//    /**\n//     * 获取跳转至相册选择界面的Intent\n//     */\n//    public static Intent getImagePickerIntent() {\n//        Intent intent = new Intent(Intent.ACTION_PICK, null);\n//        return intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, \"image/*\");\n//    }\n//\n//    /**\n//     * 获取[跳转至相册选择界面,并跳转至裁剪界面，默认可缩放裁剪区域]的Intent\n//     */\n//    public static Intent getImagePickerIntent(int outputX, int outputY, Uri fromFileURI,\n//                                              Uri saveFileURI) {\n//        return getImagePickerIntent(1, 1, outputX, outputY, true, fromFileURI, saveFileURI);\n//    }\n//\n//    /**\n//     * 获取[跳转至相册选择界面,并跳转至裁剪界面，默认可缩放裁剪区域]的Intent\n//     */\n//    public static Intent getImagePickerIntent(int aspectX, int aspectY, int outputX, int outputY, Uri fromFileURI,\n//                                              Uri saveFileURI) {\n//        return getImagePickerIntent(aspectX, aspectY, outputX, outputY, true, fromFileURI, saveFileURI);\n//    }\n//\n//    /**\n//     * 获取[跳转至相册选择界面,并跳转至裁剪界面，可以指定是否缩放裁剪区域]的Intent\n//     *\n//     * @param aspectX     裁剪框尺寸比例X\n//     * @param aspectY     裁剪框尺寸比例Y\n//     * @param outputX     输出尺寸宽度\n//     * @param outputY     输出尺寸高度\n//     * @param canScale    是否可缩放\n//     * @param fromFileURI 文件来源路径URI\n//     * @param saveFileURI 输出文件路径URI\n//     */\n//    public static Intent getImagePickerIntent(int aspectX, int aspectY, int outputX, int outputY, boolean canScale,\n//                                              Uri fromFileURI, Uri saveFileURI) {\n//        Intent intent = new Intent(Intent.ACTION_PICK);\n//        intent.setDataAndType(fromFileURI, \"image/*\");\n//        intent.putExtra(\"crop\", \"true\");\n//        intent.putExtra(\"aspectX\", aspectX <= 0 ? 1 : aspectX);\n//        intent.putExtra(\"aspectY\", aspectY <= 0 ? 1 : aspectY);\n//        intent.putExtra(\"outputX\", outputX);\n//        intent.putExtra(\"outputY\", outputY);\n//        intent.putExtra(\"scale\", canScale);\n//        // 图片剪裁不足黑边解决\n//        intent.putExtra(\"scaleUpIfNeeded\", true);\n//        intent.putExtra(\"return-data\", false);\n//        intent.putExtra(MediaStore.EXTRA_OUTPUT, saveFileURI);\n//        intent.putExtra(\"outputFormat\", Bitmap.CompressFormat.JPEG.toString());\n//        // 去除人脸识别\n//        return intent.putExtra(\"noFaceDetection\", true);\n//    }\n//\n//    /**\n//     * 获取[跳转至相册选择界面,并跳转至裁剪界面，默认可缩放裁剪区域]的Intent\n//     */\n//    public static Intent getCameraIntent(final Uri saveFileURI) {\n//        Intent mIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);\n//        return mIntent.putExtra(MediaStore.EXTRA_OUTPUT, saveFileURI);\n//    }\n//\n//    /**\n//     * 获取[跳转至裁剪界面,默认可缩放]的Intent\n//     */\n//    public static Intent getCropImageIntent(int outputX, int outputY, Uri fromFileURI,\n//                                            Uri saveFileURI) {\n//        return getCropImageIntent(1, 1, outputX, outputY, true, fromFileURI, saveFileURI);\n//    }\n//\n//    /**\n//     * 获取[跳转至裁剪界面,默认可缩放]的Intent\n//     */\n//    public static Intent getCropImageIntent(int aspectX, int aspectY, int outputX, int outputY, Uri fromFileURI,\n//                                            Uri saveFileURI) {\n//        return getCropImageIntent(aspectX, aspectY, outputX, outputY, true, fromFileURI, saveFileURI);\n//    }\n//\n//\n//    /**\n//     * 获取[跳转至裁剪界面]的Intent\n//     */\n//    public static Intent getCropImageIntent(int aspectX, int aspectY, int outputX, int outputY, boolean canScale,\n//                                            Uri fromFileURI, Uri saveFileURI) {\n//        Intent intent = new Intent(\"com.android.camera.action.CROP\");\n//        intent.setDataAndType(fromFileURI, \"image/*\");\n//        intent.putExtra(\"crop\", \"true\");\n//        // X方向上的比例\n//        intent.putExtra(\"aspectX\", aspectX <= 0 ? 1 : aspectX);\n//        // Y方向上的比例\n//        intent.putExtra(\"aspectY\", aspectY <= 0 ? 1 : aspectY);\n//        intent.putExtra(\"outputX\", outputX);\n//        intent.putExtra(\"outputY\", outputY);\n//        intent.putExtra(\"scale\", canScale);\n//        // 图片剪裁不足黑边解决\n//        intent.putExtra(\"scaleUpIfNeeded\", true);\n//        intent.putExtra(\"return-data\", false);\n//        // 需要将读取的文件路径和裁剪写入的路径区分，否则会造成文件0byte\n//        intent.putExtra(MediaStore.EXTRA_OUTPUT, saveFileURI);\n//        // true-->返回数据类型可以设置为Bitmap，但是不能传输太大，截大图用URI，小图用Bitmap或者全部使用URI\n//        intent.putExtra(\"outputFormat\", Bitmap.CompressFormat.JPEG.toString());\n//        // 取消人脸识别功能\n//        intent.putExtra(\"noFaceDetection\", true);\n//        return intent;\n//    }\n//\n//    /**\n//     * 获得选中相册的图片\n//     *\n//     * @param context 上下文\n//     * @param data    onActivityResult返回的Intent\n//     * @return bitmap\n//     */\n//    public static Bitmap getChoosedImage(final Activity context, final Intent data) {\n//        if (data == null) return null;\n//        Bitmap bm = null;\n//        ContentResolver cr = context.getContentResolver();\n//        Uri originalUri = data.getData();\n//        try {\n//            bm = MediaStore.Images.Media.getBitmap(cr, originalUri);\n//        } catch (IOException e) {\n//            e.printStackTrace();\n//        }\n//        return bm;\n//    }\n//\n//    /**\n//     * 获得选中相册的图片路径\n//     *\n//     * @param context 上下文\n//     * @param data    onActivityResult返回的Intent\n//     * @return\n//     */\n//    public static String getChoosedImagePath(final Activity context, final Intent data) {\n//        if (data == null) return null;\n//        String path = \"\";\n//        ContentResolver resolver = context.getContentResolver();\n//        Uri originalUri = data.getData();\n//        if (null == originalUri) return null;\n//        String[] projection = {MediaStore.Images.Media.DATA};\n//        Cursor cursor = resolver.query(originalUri, projection, null, null, null);\n//        if (null != cursor) {\n//            try {\n//                int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);\n//                cursor.moveToFirst();\n//                path = cursor.getString(column_index);\n//            } catch (IllegalArgumentException e) {\n//                e.printStackTrace();\n//            } finally {\n//                try {\n//                    if (!cursor.isClosed()) {\n//                        cursor.close();\n//                    }\n//                } catch (Exception e) {\n//                    e.printStackTrace();\n//                }\n//            }\n//        }\n//        return StringUtils.isEmpty(path) ? originalUri.getPath() : null;\n//    }\n//\n//    /**\n//     * 获取拍照之后的照片文件（JPG格式）\n//     *\n//     * @param data     onActivityResult回调返回的数据\n//     * @param filePath The path of file.\n//     * @return 文件\n//     */\n//    public static File getTakePictureFile(final Intent data, final String filePath) {\n//        if (data == null) return null;\n//        Bundle extras = data.getExtras();\n//        if (extras == null) return null;\n//        Bitmap photo = extras.getParcelable(\"data\");\n//        File file = new File(filePath);\n//        if (ImageUtils.save(photo, file, Bitmap.CompressFormat.JPEG)) return file;\n//        return null;\n//    }\n}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/CoordinateUtils.java",
    "content": "package com.blankj.subutil.util;\n\nimport static java.lang.Math.PI;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/03/21\n *     desc  : 坐标相关工具类\n * </pre>\n */\npublic final class CoordinateUtils {\n\n    private final static double X_PI = 3.14159265358979324 * 3000.0 / 180.0;\n    private final static double A    = 6378245.0;\n    private final static double EE   = 0.00669342162296594323;\n\n    /**\n     * BD09 坐标转 GCJ02 坐标\n     *\n     * @param lng BD09 坐标纬度\n     * @param lat BD09 坐标经度\n     * @return GCJ02 坐标：[经度，纬度]\n     */\n    public static double[] bd09ToGcj02(double lng, double lat) {\n        double x = lng - 0.0065;\n        double y = lat - 0.006;\n        double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * X_PI);\n        double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * X_PI);\n        double gg_lng = z * Math.cos(theta);\n        double gg_lat = z * Math.sin(theta);\n        return new double[]{gg_lng, gg_lat};\n    }\n\n    /**\n     * GCJ02 坐标转 BD09 坐标\n     *\n     * @param lng GCJ02 坐标经度\n     * @param lat GCJ02 坐标纬度\n     * @return BD09 坐标：[经度，纬度]\n     */\n    public static double[] gcj02ToBd09(double lng, double lat) {\n        double z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * X_PI);\n        double theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * X_PI);\n        double bd_lng = z * Math.cos(theta) + 0.0065;\n        double bd_lat = z * Math.sin(theta) + 0.006;\n        return new double[]{bd_lng, bd_lat};\n    }\n\n    /**\n     * GCJ02 坐标转 WGS84 坐标\n     *\n     * @param lng GCJ02 坐标经度\n     * @param lat GCJ02 坐标纬度\n     * @return WGS84 坐标：[经度，纬度]\n     */\n    public static double[] gcj02ToWGS84(double lng, double lat) {\n        if (outOfChina(lng, lat)) {\n            return new double[]{lng, lat};\n        }\n        double dlat = transformLat(lng - 105.0, lat - 35.0);\n        double dlng = transformLng(lng - 105.0, lat - 35.0);\n        double radlat = lat / 180.0 * PI;\n        double magic = Math.sin(radlat);\n        magic = 1 - EE * magic * magic;\n        double sqrtmagic = Math.sqrt(magic);\n        dlat = (dlat * 180.0) / ((A * (1 - EE)) / (magic * sqrtmagic) * PI);\n        dlng = (dlng * 180.0) / (A / sqrtmagic * Math.cos(radlat) * PI);\n        double mglat = lat + dlat;\n        double mglng = lng + dlng;\n        return new double[]{lng * 2 - mglng, lat * 2 - mglat};\n    }\n\n    /**\n     * WGS84 坐标转 GCJ02 坐标\n     *\n     * @param lng WGS84 坐标经度\n     * @param lat WGS84 坐标纬度\n     * @return GCJ02 坐标：[经度，纬度]\n     */\n    public static double[] wgs84ToGcj02(double lng, double lat) {\n        if (outOfChina(lng, lat)) {\n            return new double[]{lng, lat};\n        }\n        double dlat = transformLat(lng - 105.0, lat - 35.0);\n        double dlng = transformLng(lng - 105.0, lat - 35.0);\n        double radlat = lat / 180.0 * PI;\n        double magic = Math.sin(radlat);\n        magic = 1 - EE * magic * magic;\n        double sqrtmagic = Math.sqrt(magic);\n        dlat = (dlat * 180.0) / ((A * (1 - EE)) / (magic * sqrtmagic) * PI);\n        dlng = (dlng * 180.0) / (A / sqrtmagic * Math.cos(radlat) * PI);\n        double mglat = lat + dlat;\n        double mglng = lng + dlng;\n        return new double[]{mglng, mglat};\n    }\n\n    /**\n     * BD09 坐标转 WGS84 坐标\n     *\n     * @param lng BD09 坐标经度\n     * @param lat BD09 坐标纬度\n     * @return WGS84 坐标：[经度，纬度]\n     */\n    public static double[] bd09ToWGS84(double lng, double lat) {\n        double[] gcj = bd09ToGcj02(lng, lat);\n        return gcj02ToWGS84(gcj[0], gcj[1]);\n    }\n\n\n    /**\n     * WGS84 坐标转 BD09 坐标\n     *\n     * @param lng WGS84 坐标经度\n     * @param lat WGS84 坐标纬度\n     * @return BD09 坐标：[经度，纬度]\n     */\n    public static double[] wgs84ToBd09(double lng, double lat) {\n        double[] gcj = wgs84ToGcj02(lng, lat);\n        return gcj02ToBd09(gcj[0], gcj[1]);\n    }\n\n    /**\n     * Mercator 坐标转 WGS84 坐标\n     *\n     * @param lng Mercator 坐标经度\n     * @param lat Mercator 坐标纬度\n     * @return WGS84 坐标：[经度，纬度]\n     */\n    public static double[] mercatorToWGS84(double lng, double lat) {\n        double x = lng / 20037508.34d * 180.;\n        double y = lat / 20037508.34d * 180.;\n        y = 180 / PI * (2 * Math.atan(Math.exp(y * PI / 180.0)) - PI / 2);\n        return new double[]{x, y};\n    }\n\n    /**\n     * WGS84 坐标转 Mercator 坐标\n     *\n     * @param lng WGS84 坐标经度\n     * @param lat WGS84 坐标纬度\n     * @return Mercator 坐标：[经度，纬度]\n     */\n    public static double[] wgs84ToMercator(double lng, double lat) {\n        double x = lng * 20037508.34D / 180.0;\n        double y = Math.log(Math.tan((90.0 + lat) * PI / 360.0)) / (PI / 180.);\n        y = y * 20037508.34D / 180.0;\n        return new double[]{x, y};\n    }\n\n    private static double transformLat(double lng, double lat) {\n        double ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));\n        ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;\n        ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0;\n        ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0;\n        return ret;\n    }\n\n    private static double transformLng(double lng, double lat) {\n        double ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));\n        ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;\n        ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0;\n        ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0;\n        return ret;\n    }\n\n    private static boolean outOfChina(double lng, double lat) {\n        return lng < 72.004 || lng > 137.8347 || lat < 0.8293 || lat > 55.8271;\n    }\n}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/CountryUtils.java",
    "content": "package com.blankj.subutil.util;\n\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.telephony.TelephonyManager;\n\nimport com.blankj.utilcode.util.Utils;\n\nimport java.util.HashMap;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/06/11\n *     desc  : utils about country code\n * </pre>\n */\npublic class CountryUtils {\n\n    private static HashMap<String, String> countryCodeMap;\n\n    /**\n     * Return the country code by sim card.\n     *\n     * @param defaultValue The default value.\n     * @return the country code\n     */\n    public static String getCountryCodeBySim(String defaultValue) {\n        String code = getCountryCodeFromMap().get(getCountryBySim());\n        if (code == null) {\n            return defaultValue;\n        }\n        return code;\n    }\n\n    /**\n     * Return the country code by system language.\n     *\n     * @param defaultValue The default value.\n     * @return the country code\n     */\n    public static String getCountryCodeByLanguage(String defaultValue) {\n        String code = getCountryCodeFromMap().get(getCountryByLanguage());\n        if (code == null) {\n            return defaultValue;\n        }\n        return code;\n    }\n\n    /**\n     * Return the country by sim card.\n     *\n     * @return the country\n     */\n    public static String getCountryBySim() {\n        TelephonyManager manager = (TelephonyManager) Utils.getApp().getSystemService(Context.TELEPHONY_SERVICE);\n        if (manager != null) {\n            return manager.getSimCountryIso().toUpperCase();\n        }\n        return \"\";\n    }\n\n    /**\n     * Return the country by system language.\n     *\n     * @return the country\n     */\n    public static String getCountryByLanguage() {\n        return Resources.getSystem().getConfiguration().locale.getCountry();\n    }\n\n    private static HashMap<String, String> getCountryCodeFromMap() {\n        if (countryCodeMap == null) {\n            countryCodeMap = new HashMap<>(256);\n            countryCodeMap.put(\"AL\", \"+355\");\n            countryCodeMap.put(\"DZ\", \"+213\");\n            countryCodeMap.put(\"AF\", \"+93\");\n            countryCodeMap.put(\"AR\", \"+54\");\n            countryCodeMap.put(\"AE\", \"+971\");\n            countryCodeMap.put(\"AW\", \"+297\");\n            countryCodeMap.put(\"OM\", \"+968\");\n            countryCodeMap.put(\"AZ\", \"+994\");\n            countryCodeMap.put(\"AC\", \"+247\");\n            countryCodeMap.put(\"EG\", \"+20\");\n            countryCodeMap.put(\"ET\", \"+251\");\n            countryCodeMap.put(\"IE\", \"+353\");\n            countryCodeMap.put(\"EE\", \"+372\");\n            countryCodeMap.put(\"AD\", \"+376\");\n            countryCodeMap.put(\"AO\", \"+244\");\n            countryCodeMap.put(\"AI\", \"+1\");\n            countryCodeMap.put(\"AG\", \"+1\");\n            countryCodeMap.put(\"AT\", \"+43\");\n            countryCodeMap.put(\"AX\", \"+358\");\n            countryCodeMap.put(\"AU\", \"+61\");\n            countryCodeMap.put(\"BB\", \"+1\");\n            countryCodeMap.put(\"PG\", \"+675\");\n            countryCodeMap.put(\"BS\", \"+1\");\n            countryCodeMap.put(\"PK\", \"+92\");\n            countryCodeMap.put(\"PY\", \"+595\");\n            countryCodeMap.put(\"PS\", \"+970\");\n            countryCodeMap.put(\"BH\", \"+973\");\n            countryCodeMap.put(\"PA\", \"+507\");\n            countryCodeMap.put(\"BR\", \"+55\");\n            countryCodeMap.put(\"BY\", \"+375\");\n            countryCodeMap.put(\"BM\", \"+1\");\n            countryCodeMap.put(\"BG\", \"+359\");\n            countryCodeMap.put(\"MP\", \"+1\");\n            countryCodeMap.put(\"BJ\", \"+229\");\n            countryCodeMap.put(\"BE\", \"+32\");\n            countryCodeMap.put(\"IS\", \"+354\");\n            countryCodeMap.put(\"PR\", \"+1\");\n            countryCodeMap.put(\"PL\", \"+48\");\n            countryCodeMap.put(\"BA\", \"+387\");\n            countryCodeMap.put(\"BO\", \"+591\");\n            countryCodeMap.put(\"BZ\", \"+501\");\n            countryCodeMap.put(\"BW\", \"+267\");\n            countryCodeMap.put(\"BT\", \"+975\");\n            countryCodeMap.put(\"BF\", \"+226\");\n            countryCodeMap.put(\"BI\", \"+257\");\n            countryCodeMap.put(\"KP\", \"+850\");\n            countryCodeMap.put(\"GQ\", \"+240\");\n            countryCodeMap.put(\"DK\", \"+45\");\n            countryCodeMap.put(\"DE\", \"+49\");\n            countryCodeMap.put(\"TL\", \"+670\");\n            countryCodeMap.put(\"TG\", \"+228\");\n            countryCodeMap.put(\"DO\", \"+1\");\n            countryCodeMap.put(\"DM\", \"+1\");\n            countryCodeMap.put(\"RU\", \"+7\");\n            countryCodeMap.put(\"EC\", \"+593\");\n            countryCodeMap.put(\"ER\", \"+291\");\n            countryCodeMap.put(\"FR\", \"+33\");\n            countryCodeMap.put(\"FO\", \"+298\");\n            countryCodeMap.put(\"PF\", \"+689\");\n            countryCodeMap.put(\"GF\", \"+594\");\n            countryCodeMap.put(\"VA\", \"+39\");\n            countryCodeMap.put(\"PH\", \"+63\");\n            countryCodeMap.put(\"FJ\", \"+679\");\n            countryCodeMap.put(\"FI\", \"+358\");\n            countryCodeMap.put(\"CV\", \"+238\");\n            countryCodeMap.put(\"FK\", \"+500\");\n            countryCodeMap.put(\"GM\", \"+220\");\n            countryCodeMap.put(\"CG\", \"+242\");\n            countryCodeMap.put(\"CD\", \"+243\");\n            countryCodeMap.put(\"CO\", \"+57\");\n            countryCodeMap.put(\"CR\", \"+506\");\n            countryCodeMap.put(\"GG\", \"+44\");\n            countryCodeMap.put(\"GD\", \"+1\");\n            countryCodeMap.put(\"GL\", \"+299\");\n            countryCodeMap.put(\"GE\", \"+995\");\n            countryCodeMap.put(\"CU\", \"+53\");\n            countryCodeMap.put(\"GP\", \"+590\");\n            countryCodeMap.put(\"GU\", \"+1\");\n            countryCodeMap.put(\"GY\", \"+592\");\n            countryCodeMap.put(\"KZ\", \"+7\");\n            countryCodeMap.put(\"HT\", \"+509\");\n            countryCodeMap.put(\"KR\", \"+82\");\n            countryCodeMap.put(\"NL\", \"+31\");\n            countryCodeMap.put(\"BQ\", \"+599\");\n            countryCodeMap.put(\"SX\", \"+1\");\n            countryCodeMap.put(\"ME\", \"+382\");\n            countryCodeMap.put(\"HN\", \"+504\");\n            countryCodeMap.put(\"KI\", \"+686\");\n            countryCodeMap.put(\"DJ\", \"+253\");\n            countryCodeMap.put(\"KG\", \"+996\");\n            countryCodeMap.put(\"GN\", \"+224\");\n            countryCodeMap.put(\"GW\", \"+245\");\n            countryCodeMap.put(\"CA\", \"+1\");\n            countryCodeMap.put(\"GH\", \"+233\");\n            countryCodeMap.put(\"GA\", \"+241\");\n            countryCodeMap.put(\"KH\", \"+855\");\n            countryCodeMap.put(\"CZ\", \"+420\");\n            countryCodeMap.put(\"ZW\", \"+263\");\n            countryCodeMap.put(\"CM\", \"+237\");\n            countryCodeMap.put(\"QA\", \"+974\");\n            countryCodeMap.put(\"KY\", \"+1\");\n            countryCodeMap.put(\"CC\", \"+61\");\n            countryCodeMap.put(\"KM\", \"+269\");\n            countryCodeMap.put(\"XK\", \"+383\");\n            countryCodeMap.put(\"CI\", \"+225\");\n            countryCodeMap.put(\"KW\", \"+965\");\n            countryCodeMap.put(\"HR\", \"+385\");\n            countryCodeMap.put(\"KE\", \"+254\");\n            countryCodeMap.put(\"CK\", \"+682\");\n            countryCodeMap.put(\"CW\", \"+599\");\n            countryCodeMap.put(\"LV\", \"+371\");\n            countryCodeMap.put(\"LS\", \"+266\");\n            countryCodeMap.put(\"LA\", \"+856\");\n            countryCodeMap.put(\"LB\", \"+961\");\n            countryCodeMap.put(\"LT\", \"+370\");\n            countryCodeMap.put(\"LR\", \"+231\");\n            countryCodeMap.put(\"LY\", \"+218\");\n            countryCodeMap.put(\"LI\", \"+423\");\n            countryCodeMap.put(\"RE\", \"+262\");\n            countryCodeMap.put(\"LU\", \"+352\");\n            countryCodeMap.put(\"RW\", \"+250\");\n            countryCodeMap.put(\"RO\", \"+40\");\n            countryCodeMap.put(\"MG\", \"+261\");\n            countryCodeMap.put(\"IM\", \"+44\");\n            countryCodeMap.put(\"MV\", \"+960\");\n            countryCodeMap.put(\"MT\", \"+356\");\n            countryCodeMap.put(\"MW\", \"+265\");\n            countryCodeMap.put(\"MY\", \"+60\");\n            countryCodeMap.put(\"ML\", \"+223\");\n            countryCodeMap.put(\"MK\", \"+389\");\n            countryCodeMap.put(\"MH\", \"+692\");\n            countryCodeMap.put(\"MQ\", \"+596\");\n            countryCodeMap.put(\"YT\", \"+262\");\n            countryCodeMap.put(\"MU\", \"+230\");\n            countryCodeMap.put(\"MR\", \"+222\");\n            countryCodeMap.put(\"US\", \"+1\");\n            countryCodeMap.put(\"AS\", \"+1\");\n            countryCodeMap.put(\"VI\", \"+1\");\n            countryCodeMap.put(\"MN\", \"+976\");\n            countryCodeMap.put(\"MS\", \"+1\");\n            countryCodeMap.put(\"BD\", \"+880\");\n            countryCodeMap.put(\"PE\", \"+51\");\n            countryCodeMap.put(\"FM\", \"+691\");\n            countryCodeMap.put(\"MM\", \"+95\");\n            countryCodeMap.put(\"MD\", \"+373\");\n            countryCodeMap.put(\"MA\", \"+212\");\n            countryCodeMap.put(\"MC\", \"+377\");\n            countryCodeMap.put(\"MZ\", \"+258\");\n            countryCodeMap.put(\"MX\", \"+52\");\n            countryCodeMap.put(\"NA\", \"+264\");\n            countryCodeMap.put(\"ZA\", \"+27\");\n            countryCodeMap.put(\"SS\", \"+211\");\n            countryCodeMap.put(\"NR\", \"+674\");\n            countryCodeMap.put(\"NI\", \"+505\");\n            countryCodeMap.put(\"NP\", \"+977\");\n            countryCodeMap.put(\"NE\", \"+227\");\n            countryCodeMap.put(\"NG\", \"+234\");\n            countryCodeMap.put(\"NU\", \"+683\");\n            countryCodeMap.put(\"NO\", \"+47\");\n            countryCodeMap.put(\"NF\", \"+672\");\n            countryCodeMap.put(\"PW\", \"+680\");\n            countryCodeMap.put(\"PT\", \"+351\");\n            countryCodeMap.put(\"JP\", \"+81\");\n            countryCodeMap.put(\"SE\", \"+46\");\n            countryCodeMap.put(\"CH\", \"+41\");\n            countryCodeMap.put(\"SV\", \"+503\");\n            countryCodeMap.put(\"WS\", \"+685\");\n            countryCodeMap.put(\"RS\", \"+381\");\n            countryCodeMap.put(\"SL\", \"+232\");\n            countryCodeMap.put(\"SN\", \"+221\");\n            countryCodeMap.put(\"CY\", \"+357\");\n            countryCodeMap.put(\"SC\", \"+248\");\n            countryCodeMap.put(\"SA\", \"+966\");\n            countryCodeMap.put(\"BL\", \"+590\");\n            countryCodeMap.put(\"CX\", \"+61\");\n            countryCodeMap.put(\"ST\", \"+239\");\n            countryCodeMap.put(\"SH\", \"+290\");\n            countryCodeMap.put(\"PN\", \"+870\");\n            countryCodeMap.put(\"KN\", \"+1\");\n            countryCodeMap.put(\"LC\", \"+1\");\n            countryCodeMap.put(\"MF\", \"+590\");\n            countryCodeMap.put(\"SM\", \"+378\");\n            countryCodeMap.put(\"PM\", \"+508\");\n            countryCodeMap.put(\"VC\", \"+1\");\n            countryCodeMap.put(\"LK\", \"+94\");\n            countryCodeMap.put(\"SK\", \"+421\");\n            countryCodeMap.put(\"SI\", \"+386\");\n            countryCodeMap.put(\"SJ\", \"+47\");\n            countryCodeMap.put(\"SZ\", \"+268\");\n            countryCodeMap.put(\"SD\", \"+249\");\n            countryCodeMap.put(\"SR\", \"+597\");\n            countryCodeMap.put(\"SB\", \"+677\");\n            countryCodeMap.put(\"SO\", \"+252\");\n            countryCodeMap.put(\"TJ\", \"+992\");\n            countryCodeMap.put(\"TH\", \"+66\");\n            countryCodeMap.put(\"TZ\", \"+255\");\n            countryCodeMap.put(\"TO\", \"+676\");\n            countryCodeMap.put(\"TC\", \"+1\");\n            countryCodeMap.put(\"TA\", \"+290\");\n            countryCodeMap.put(\"TT\", \"+1\");\n            countryCodeMap.put(\"TN\", \"+216\");\n            countryCodeMap.put(\"TV\", \"+688\");\n            countryCodeMap.put(\"TR\", \"+90\");\n            countryCodeMap.put(\"TM\", \"+993\");\n            countryCodeMap.put(\"TK\", \"+690\");\n            countryCodeMap.put(\"WF\", \"+681\");\n            countryCodeMap.put(\"VU\", \"+678\");\n            countryCodeMap.put(\"GT\", \"+502\");\n            countryCodeMap.put(\"VE\", \"+58\");\n            countryCodeMap.put(\"BN\", \"+673\");\n            countryCodeMap.put(\"UG\", \"+256\");\n            countryCodeMap.put(\"UA\", \"+380\");\n            countryCodeMap.put(\"UY\", \"+598\");\n            countryCodeMap.put(\"UZ\", \"+998\");\n            countryCodeMap.put(\"GR\", \"+30\");\n            countryCodeMap.put(\"ES\", \"+34\");\n            countryCodeMap.put(\"EH\", \"+212\");\n            countryCodeMap.put(\"SG\", \"+65\");\n            countryCodeMap.put(\"NC\", \"+687\");\n            countryCodeMap.put(\"NZ\", \"+64\");\n            countryCodeMap.put(\"HU\", \"+36\");\n            countryCodeMap.put(\"SY\", \"+963\");\n            countryCodeMap.put(\"JM\", \"+1\");\n            countryCodeMap.put(\"AM\", \"+374\");\n            countryCodeMap.put(\"YE\", \"+967\");\n            countryCodeMap.put(\"IQ\", \"+964\");\n            countryCodeMap.put(\"UM\", \"+1\");\n            countryCodeMap.put(\"IR\", \"+98\");\n            countryCodeMap.put(\"IL\", \"+972\");\n            countryCodeMap.put(\"IT\", \"+39\");\n            countryCodeMap.put(\"IN\", \"+91\");\n            countryCodeMap.put(\"ID\", \"+62\");\n            countryCodeMap.put(\"GB\", \"+44\");\n            countryCodeMap.put(\"VG\", \"+1\");\n            countryCodeMap.put(\"IO\", \"+246\");\n            countryCodeMap.put(\"JO\", \"+962\");\n            countryCodeMap.put(\"VN\", \"+84\");\n            countryCodeMap.put(\"ZM\", \"+260\");\n            countryCodeMap.put(\"JE\", \"+44\");\n            countryCodeMap.put(\"TD\", \"+235\");\n            countryCodeMap.put(\"GI\", \"+350\");\n            countryCodeMap.put(\"CL\", \"+56\");\n            countryCodeMap.put(\"CF\", \"+236\");\n            countryCodeMap.put(\"CN\", \"+86\");\n            countryCodeMap.put(\"MO\", \"+853\");\n            countryCodeMap.put(\"TW\", \"+886\");\n            countryCodeMap.put(\"HK\", \"+852\");\n        }\n        return countryCodeMap;\n    }\n}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/DangerousUtils.java",
    "content": "package com.blankj.subutil.util;\n\nimport android.app.PendingIntent;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.os.Build;\nimport android.os.PowerManager;\nimport android.telephony.SmsManager;\nimport android.telephony.TelephonyManager;\nimport android.text.TextUtils;\nimport android.util.Log;\n\nimport com.blankj.utilcode.util.IntentUtils;\nimport com.blankj.utilcode.util.ShellUtils;\nimport com.blankj.utilcode.util.Utils;\n\nimport java.io.File;\nimport java.lang.reflect.Method;\nimport java.util.List;\n\nimport androidx.annotation.RequiresPermission;\n\nimport static android.Manifest.permission.MODIFY_PHONE_STATE;\nimport static android.Manifest.permission.SEND_SMS;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/06/29\n *     desc  :\n * </pre>\n */\npublic class DangerousUtils {\n\n    private DangerousUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // AppUtils\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Install the app silently.\n     * <p>Without root permission must hold\n     * {@code android:sharedUserId=\"android.uid.shell\"} and\n     * {@code <uses-permission android:name=\"android.permission.INSTALL_PACKAGES\" />}</p>\n     *\n     * @param filePath The path of file.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean installAppSilent(final String filePath) {\n        return installAppSilent(getFileByPath(filePath), null);\n    }\n\n    /**\n     * Install the app silently.\n     * <p>Without root permission must hold\n     * {@code android:sharedUserId=\"android.uid.shell\"} and\n     * {@code <uses-permission android:name=\"android.permission.INSTALL_PACKAGES\" />}</p>\n     *\n     * @param file The file.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean installAppSilent(final File file) {\n        return installAppSilent(file, null);\n    }\n\n\n    /**\n     * Install the app silently.\n     * <p>Without root permission must hold\n     * {@code android:sharedUserId=\"android.uid.shell\"} and\n     * {@code <uses-permission android:name=\"android.permission.INSTALL_PACKAGES\" />}</p>\n     *\n     * @param filePath The path of file.\n     * @param params   The params of installation(e.g.,<code>-r</code>, <code>-s</code>).\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean installAppSilent(final String filePath, final String params) {\n        return installAppSilent(getFileByPath(filePath), params);\n    }\n\n    /**\n     * Install the app silently.\n     * <p>Without root permission must hold\n     * {@code android:sharedUserId=\"android.uid.shell\"} and\n     * {@code <uses-permission android:name=\"android.permission.INSTALL_PACKAGES\" />}</p>\n     *\n     * @param file   The file.\n     * @param params The params of installation(e.g.,<code>-r</code>, <code>-s</code>).\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean installAppSilent(final File file, final String params) {\n        return installAppSilent(file, params, isDeviceRooted());\n    }\n\n    /**\n     * Install the app silently.\n     * <p>Without root permission must hold\n     * {@code android:sharedUserId=\"android.uid.shell\"} and\n     * {@code <uses-permission android:name=\"android.permission.INSTALL_PACKAGES\" />}</p>\n     *\n     * @param file     The file.\n     * @param params   The params of installation(e.g.,<code>-r</code>, <code>-s</code>).\n     * @param isRooted True to use root, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean installAppSilent(final File file,\n                                           final String params,\n                                           final boolean isRooted) {\n        if (!isFileExists(file)) return false;\n        String filePath = '\"' + file.getAbsolutePath() + '\"';\n        String command = \"LD_LIBRARY_PATH=/vendor/lib*:/system/lib* pm install \" +\n                (params == null ? \"\" : params + \" \")\n                + filePath;\n        ShellUtils.CommandResult commandResult = ShellUtils.execCmd(command, isRooted);\n        if (commandResult.successMsg != null\n                && commandResult.successMsg.toLowerCase().contains(\"success\")) {\n            return true;\n        } else {\n            Log.e(\"AppUtils\", \"installAppSilent successMsg: \" + commandResult.successMsg +\n                    \", errorMsg: \" + commandResult.errorMsg);\n            return false;\n        }\n    }\n\n    /**\n     * Uninstall the app silently.\n     * <p>Without root permission must hold\n     * {@code android:sharedUserId=\"android.uid.shell\"} and\n     * {@code <uses-permission android:name=\"android.permission.DELETE_PACKAGES\" />}</p>\n     *\n     * @param packageName The name of the package.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean uninstallAppSilent(final String packageName) {\n        return uninstallAppSilent(packageName, false);\n    }\n\n    /**\n     * Uninstall the app silently.\n     * <p>Without root permission must hold\n     * {@code android:sharedUserId=\"android.uid.shell\"} and\n     * {@code <uses-permission android:name=\"android.permission.DELETE_PACKAGES\" />}</p>\n     *\n     * @param packageName The name of the package.\n     * @param isKeepData  Is keep the data.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean uninstallAppSilent(final String packageName, final boolean isKeepData) {\n        return uninstallAppSilent(packageName, isKeepData, isDeviceRooted());\n    }\n\n    /**\n     * Uninstall the app silently.\n     * <p>Without root permission must hold\n     * {@code android:sharedUserId=\"android.uid.shell\"} and\n     * {@code <uses-permission android:name=\"android.permission.DELETE_PACKAGES\" />}</p>\n     *\n     * @param packageName The name of the package.\n     * @param isKeepData  Is keep the data.\n     * @param isRooted    True to use root, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean uninstallAppSilent(final String packageName,\n                                             final boolean isKeepData,\n                                             final boolean isRooted) {\n        if (isSpace(packageName)) return false;\n        String command = \"LD_LIBRARY_PATH=/vendor/lib*:/system/lib* pm uninstall \"\n                + (isKeepData ? \"-k \" : \"\")\n                + packageName;\n        ShellUtils.CommandResult commandResult = ShellUtils.execCmd(command, isRooted);\n        if (commandResult.successMsg != null\n                && commandResult.successMsg.toLowerCase().contains(\"success\")) {\n            return true;\n        } else {\n            Log.e(\"AppUtils\", \"uninstallAppSilent successMsg: \" + commandResult.successMsg +\n                    \", errorMsg: \" + commandResult.errorMsg);\n            return false;\n        }\n    }\n\n    private static boolean isFileExists(final File file) {\n        return file != null && file.exists();\n    }\n\n    private static File getFileByPath(final String filePath) {\n        return isSpace(filePath) ? null : new File(filePath);\n    }\n\n    private static boolean isSpace(final String s) {\n        if (s == null) return true;\n        for (int i = 0, len = s.length(); i < len; ++i) {\n            if (!Character.isWhitespace(s.charAt(i))) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    private static boolean isDeviceRooted() {\n        String su = \"su\";\n        String[] locations = {\"/system/bin/\", \"/system/xbin/\", \"/sbin/\", \"/system/sd/xbin/\",\n                \"/system/bin/failsafe/\", \"/data/local/xbin/\", \"/data/local/bin/\", \"/data/local/\",\n                \"/system/sbin/\", \"/usr/bin/\", \"/vendor/bin/\"};\n        for (String location : locations) {\n            if (new File(location + su).exists()) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n\n    ///////////////////////////////////////////////////////////////////////////\n    // DeviceUtils\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Shutdown the device\n     * <p>Requires root permission\n     * or hold {@code android:sharedUserId=\"android.uid.system\"},\n     * {@code <uses-permission android:name=\"android.permission.SHUTDOWN\" />}\n     * in manifest.</p>\n     *\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean shutdown() {\n        try {\n            ShellUtils.CommandResult result = ShellUtils.execCmd(\"reboot -p\", true);\n            if (result.result == 0) return true;\n            Utils.getApp().startActivity(IntentUtils.getShutdownIntent());\n            return true;\n        } catch (Exception e) {\n            return false;\n        }\n    }\n\n    /**\n     * Reboot the device.\n     * <p>Requires root permission\n     * or hold {@code android:sharedUserId=\"android.uid.system\"} in manifest.</p>\n     *\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean reboot() {\n        try {\n            ShellUtils.CommandResult result = ShellUtils.execCmd(\"reboot\", true);\n            if (result.result == 0) return true;\n            Intent intent = new Intent(Intent.ACTION_REBOOT);\n            intent.putExtra(\"nowait\", 1);\n            intent.putExtra(\"interval\", 1);\n            intent.putExtra(\"window\", 0);\n            Utils.getApp().sendBroadcast(intent);\n            return true;\n        } catch (Exception e) {\n            return false;\n        }\n    }\n\n    /**\n     * Reboot the device.\n     * <p>Requires root permission\n     * or hold {@code android:sharedUserId=\"android.uid.system\"},\n     * {@code <uses-permission android:name=\"android.permission.REBOOT\" />}</p>\n     *\n     * @param reason code to pass to the kernel (e.g., \"recovery\") to\n     *               request special boot modes, or null.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean reboot(final String reason) {\n        try {\n            PowerManager pm = (PowerManager) Utils.getApp().getSystemService(Context.POWER_SERVICE);\n            pm.reboot(reason);\n            return true;\n        } catch (Exception e) {\n            return false;\n        }\n    }\n\n    /**\n     * Reboot the device to recovery.\n     * <p>Requires root permission.</p>\n     *\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean reboot2Recovery() {\n        ShellUtils.CommandResult result = ShellUtils.execCmd(\"reboot recovery\", true);\n        return result.result == 0;\n    }\n\n    /**\n     * Reboot the device to bootloader.\n     * <p>Requires root permission.</p>\n     *\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean reboot2Bootloader() {\n        ShellUtils.CommandResult result = ShellUtils.execCmd(\"reboot bootloader\", true);\n        return result.result == 0;\n    }\n\n\n    /**\n     * Enable or disable mobile data.\n     * <p>Must hold {@code android:sharedUserId=\"android.uid.system\"},\n     * {@code <uses-permission android:name=\"android.permission.MODIFY_PHONE_STATE\" />}</p>\n     *\n     * @param enabled True to enabled, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    @RequiresPermission(MODIFY_PHONE_STATE)\n    public static boolean setMobileDataEnabled(final boolean enabled) {\n        try {\n            TelephonyManager tm =\n                    (TelephonyManager) Utils.getApp().getSystemService(Context.TELEPHONY_SERVICE);\n            if (tm == null) return false;\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                tm.setDataEnabled(enabled);\n                return true;\n            }\n            Method setDataEnabledMethod =\n                    tm.getClass().getDeclaredMethod(\"setDataEnabled\", boolean.class);\n            setDataEnabledMethod.invoke(tm, enabled);\n            return true;\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return false;\n    }\n\n    /**\n     * Send sms silently.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.SEND_SMS\" />}</p>\n     *\n     * @param phoneNumber The phone number.\n     * @param content     The content.\n     */\n    @RequiresPermission(SEND_SMS)\n    public static void sendSmsSilent(final String phoneNumber, final String content) {\n        if (TextUtils.isEmpty(content)) return;\n        PendingIntent sentIntent = PendingIntent.getBroadcast(Utils.getApp(), 0, new Intent(\"send\"), 0);\n        SmsManager smsManager = SmsManager.getDefault();\n        if (content.length() >= 70) {\n            List<String> ms = smsManager.divideMessage(content);\n            for (String str : ms) {\n                smsManager.sendTextMessage(phoneNumber, null, str, sentIntent, null);\n            }\n        } else {\n            smsManager.sendTextMessage(phoneNumber, null, content, sentIntent, null);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/GlideUtils.java",
    "content": "//package com.blankj.subutil.util;\n//\n//import android.content.Context;\n//import android.graphics.drawable.PictureDrawable;\n//import android.widget.ImageView;\n//\n//import com.blankj.subutil.R;\n//import com.blankj.subutil.util.image.GlideApp;\n//import com.bumptech.glide.Glide;\n//import com.bumptech.glide.load.engine.DiskCacheStrategy;\n//import com.bumptech.glide.request.RequestOptions;\n//\n///**\n// * <pre>\n// *     author: Blankj\n// *     blog  : http://blankj.com\n// *     time  : 2018/05/16\n// *     desc  :\n// * </pre>\n// */\n//public final class GlideUtils {\n//\n//\n//\n//    public static void setCircleImage(Context context, String url, ImageView view) {\n//        RequestOptions requestOptions = new RequestOptions()\n//                .placeholder(R.drawable.def_img_round_holder)\n//                .error(R.drawable.def_img_round_error)\n//                .diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)\n//                .circleCrop().dontAnimate();\n//        Glide.with(context).load(url).apply(requestOptions).into(view);\n//    }\n//\n//    public static void setImage(Context context, String url, ImageView view) {\n//        if (url.endsWith(\".svg\") || url.endsWith(\".SVG\")) {\n//            setSvgImage(context, url, view);\n//            return;\n//        }\n//\n//        RequestOptions requestOptions = new RequestOptions().placeholder(R.drawable.def_img)\n//                .error(R.drawable.def_img).diskCacheStrategy(DiskCacheStrategy.AUTOMATIC).dontAnimate();\n//        Glide.with(context).load(url).apply(requestOptions).into(view);\n//    }\n//\n//    private static void setSvgImage(Context context, String url, ImageView view) {\n//        GlideApp.with(context)\n//                .as(PictureDrawable.class)\n//                .error(R.drawable.def_img).load(url).into(view);\n//    }\n//}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/HttpsUtil.java",
    "content": "package com.blankj.subutil.util;\n\nimport java.io.DataOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.HttpURLConnection;\nimport java.net.URL;\nimport java.util.Scanner;\n\n/**\n * <pre>\n *     author: MilkZS\n *     time  : 2019/01/09\n *     desc  : https 工具类\n * </pre>\n */\npublic final class HttpsUtil {\n\n    private static final int CONNECT_TIMEOUT_TIME = 15000;\n    private static final int READ_TIMEOUT_TIME    = 19000;\n\n    /**\n     * POST + JSON\n     *\n     * @param data send data\n     * @param url  target url\n     * @return data receive from server\n     * @author MilkZS\n     */\n    public static String postJson(String data, String url) {\n        return doHttpAction(data, true, true, url);\n    }\n\n    /**\n     * POST + FORM\n     *\n     * @param data send data\n     * @param url  target url\n     * @return data receive from serv\n     * @author MilkZS\n     */\n    public static String postForm(String data, String url) {\n        return doHttpAction(data, false, true, url);\n    }\n\n    /**\n     * GET + JSON\n     *\n     * @param data send data\n     * @param url  target url\n     * @return data receive from server\n     * @author MilkZS\n     */\n    public static String getJson(String data, String url) {\n        return doHttpAction(data, true, false, url);\n    }\n\n    /**\n     * GET + FORM\n     *\n     * @param data send data\n     * @param url  target url\n     * @return data receive from server\n     * @author MilkZS\n     */\n    public static String getForm(String data, String url) {\n        return doHttpAction(data, false, false, url);\n    }\n\n    private static String doHttpAction(String data, boolean json, boolean post, String url) {\n        HttpURLConnection connection = null;\n        DataOutputStream os = null;\n        InputStream is = null;\n        try {\n            URL sUrl = new URL(url);\n            connection = (HttpURLConnection) sUrl.openConnection();\n            connection.setConnectTimeout(CONNECT_TIMEOUT_TIME);\n            connection.setReadTimeout(READ_TIMEOUT_TIME);\n            if (post) {\n                connection.setRequestMethod(\"POST\");\n            } else {\n                connection.setRequestMethod(\"GET\");\n            }\n            //允许输入输出\n            connection.setDoInput(true);\n            connection.setDoOutput(true);\n            // 是否使用缓冲\n            connection.setUseCaches(false);\n            // 本次连接是否处理重定向，设置成true，系统自动处理重定向；\n            // 设置成false，则需要自己从http reply中分析新的url自己重新连接。\n            connection.setInstanceFollowRedirects(true);\n            // 设置请求头里的属性\n            if (json) {\n                connection.setRequestProperty(\"Content-Type\", \"application/json\");\n            } else {\n                connection.setRequestProperty(\"Content-Type\", \"application/x-www-form-urlencoded\");\n                connection.setRequestProperty(\"Content-Length\", data.length() + \"\");\n            }\n            connection.connect();\n\n            os = new DataOutputStream(connection.getOutputStream());\n            os.write(data.getBytes(), 0, data.getBytes().length);\n            os.flush();\n            os.close();\n\n            is = connection.getInputStream();\n            Scanner scan = new Scanner(is);\n            scan.useDelimiter(\"\\\\A\");\n            if (scan.hasNext()) return scan.next();\n        } catch (Exception e) {\n            e.printStackTrace();\n        } finally {\n            if (connection != null) connection.disconnect();\n            if (os != null) {\n                try {\n                    os.close();\n                } catch (IOException e) {\n                    e.printStackTrace();\n                }\n            }\n            if (is != null) {\n                try {\n                    is.close();\n                } catch (IOException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/LocationUtils.java",
    "content": "package com.blankj.subutil.util;\n\nimport android.content.Context;\nimport android.content.Intent;\nimport android.location.Address;\nimport android.location.Criteria;\nimport android.location.Geocoder;\nimport android.location.Location;\nimport android.location.LocationListener;\nimport android.location.LocationManager;\nimport android.location.LocationProvider;\nimport android.os.Bundle;\nimport android.provider.Settings;\nimport androidx.annotation.RequiresPermission;\nimport android.util.Log;\n\nimport com.blankj.utilcode.util.Utils;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.Locale;\n\nimport static android.Manifest.permission.ACCESS_COARSE_LOCATION;\nimport static android.Manifest.permission.ACCESS_FINE_LOCATION;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 16/11/13\n *     desc  : 定位相关工具类\n * </pre>\n */\npublic final class LocationUtils {\n\n    private static final int TWO_MINUTES = 1000 * 60 * 2;\n\n    private static OnLocationChangeListener mListener;\n    private static MyLocationListener       myLocationListener;\n    private static LocationManager          mLocationManager;\n\n    private LocationUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n\n//    /**\n//     * you have to check for Location Permission before use this method\n//     * add this code <uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" /> to your Manifest file.\n//     * you have also implement LocationListener and passed it to the method.\n//     *\n//     * @param Context\n//     * @param LocationListener\n//     * @return {@code Location}\n//     */\n//\n//    @SuppressLint(\"MissingPermission\")\n//    public static Location getLocation(Context context, LocationListener listener) {\n//        Location location = null;\n//        try {\n//            mLocationManager = (LocationManager) context.getSystemService(LOCATION_SERVICE);\n//            if (!isLocationEnabled()) {\n//                //no Network and GPS providers is enabled\n//                Toast.makeText(context\n//                        , \" you have to open GPS or INTERNET\"\n//                        , Toast.LENGTH_LONG)\n//                        .show();\n//            } else {\n//                if (isLocationEnabled()) {\n//                    mLocationManager.requestLocationUpdates(\n//                            LocationManager.NETWORK_PROVIDER,\n//                            MIN_TIME_BETWEEN_UPDATES,\n//                            MIN_DISTANCE_CHANGE_FOR_UPDATES,\n//                            listener);\n//\n//                    if (mLocationManager != null) {\n//                        location = mLocationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);\n//                        if (location != null) {\n//                            mLocationManager.removeUpdates(listener);\n//                            return location;\n//                        }\n//                    }\n//                }\n//                //when GPS is enabled.\n//                if (isGpsEnabled()) {\n//                    if (location == null) {\n//                        mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,\n//                                MIN_TIME_BETWEEN_UPDATES,\n//                                MIN_DISTANCE_CHANGE_FOR_UPDATES,\n//                                listener);\n//\n//                        if (mLocationManager != null) {\n//                            location =\n//                                    mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);\n//                            if (location != null) {\n//                                mLocationManager.removeUpdates(listener);\n//                                return location;\n//                            }\n//                        }\n//                    }\n//                }\n//\n//            }\n//        } catch (Exception e) {\n//            e.printStackTrace();\n//        }\n//\n//        return location;\n//    }\n\n\n    /**\n     * 判断Gps是否可用\n     *\n     * @return {@code true}: 是<br>{@code false}: 否\n     */\n    public static boolean isGpsEnabled() {\n        LocationManager lm = (LocationManager) Utils.getApp().getSystemService(Context.LOCATION_SERVICE);\n        return lm.isProviderEnabled(LocationManager.GPS_PROVIDER);\n    }\n\n    /**\n     * 判断定位是否可用\n     *\n     * @return {@code true}: 是<br>{@code false}: 否\n     */\n    public static boolean isLocationEnabled() {\n        LocationManager lm = (LocationManager) Utils.getApp().getSystemService(Context.LOCATION_SERVICE);\n        return lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER)\n                || lm.isProviderEnabled(LocationManager.GPS_PROVIDER);\n    }\n\n    /**\n     * 打开Gps设置界面\n     */\n    public static void openGpsSettings() {\n        Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);\n        Utils.getApp().startActivity(intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));\n    }\n\n    /**\n     * 注册\n     * <p>使用完记得调用{@link #unregister()}</p>\n     * <p>需添加权限 {@code <uses-permission android:name=\"android.permission.INTERNET\" />}</p>\n     * <p>需添加权限 {@code <uses-permission android:name=\"android.permission.ACCESS_COARSE_LOCATION\" />}</p>\n     * <p>需添加权限 {@code <uses-permission android:name=\"android.permission.ACCESS_FINE_LOCATION\" />}</p>\n     * <p>如果{@code minDistance}为0，则通过{@code minTime}来定时更新；</p>\n     * <p>{@code minDistance}不为0，则以{@code minDistance}为准；</p>\n     * <p>两者都为0，则随时刷新。</p>\n     *\n     * @param minTime     位置信息更新周期（单位：毫秒）\n     * @param minDistance 位置变化最小距离：当位置距离变化超过此值时，将更新位置信息（单位：米）\n     * @param listener    位置刷新的回调接口\n     * @return {@code true}: 初始化成功<br>{@code false}: 初始化失败\n     */\n    @RequiresPermission(ACCESS_FINE_LOCATION)\n    public static boolean register(long minTime, long minDistance, OnLocationChangeListener listener) {\n        if (listener == null) return false;\n        mLocationManager = (LocationManager) Utils.getApp().getSystemService(Context.LOCATION_SERVICE);\n        if (!mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)\n                && !mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {\n            Log.d(\"LocationUtils\", \"无法定位，请打开定位服务\");\n            return false;\n        }\n        mListener = listener;\n        String provider = mLocationManager.getBestProvider(getCriteria(), true);\n        Location location = mLocationManager.getLastKnownLocation(provider);\n        if (location != null) listener.getLastKnownLocation(location);\n        if (myLocationListener == null) myLocationListener = new MyLocationListener();\n        mLocationManager.requestLocationUpdates(provider, minTime, minDistance, myLocationListener);\n        return true;\n    }\n\n    /**\n     * 注销\n     */\n    @RequiresPermission(ACCESS_COARSE_LOCATION)\n    public static void unregister() {\n        if (mLocationManager != null) {\n            if (myLocationListener != null) {\n                mLocationManager.removeUpdates(myLocationListener);\n                myLocationListener = null;\n            }\n            mLocationManager = null;\n        }\n        if (mListener != null) {\n            mListener = null;\n        }\n    }\n\n    /**\n     * 设置定位参数\n     *\n     * @return {@link Criteria}\n     */\n    private static Criteria getCriteria() {\n        Criteria criteria = new Criteria();\n        // 设置定位精确度 Criteria.ACCURACY_COARSE比较粗略，Criteria.ACCURACY_FINE则比较精细\n        criteria.setAccuracy(Criteria.ACCURACY_FINE);\n        // 设置是否要求速度\n        criteria.setSpeedRequired(false);\n        // 设置是否允许运营商收费\n        criteria.setCostAllowed(false);\n        // 设置是否需要方位信息\n        criteria.setBearingRequired(false);\n        // 设置是否需要海拔信息\n        criteria.setAltitudeRequired(false);\n        // 设置对电源的需求\n        criteria.setPowerRequirement(Criteria.POWER_LOW);\n        return criteria;\n    }\n\n    /**\n     * 根据经纬度获取地理位置\n     *\n     * @param latitude  纬度\n     * @param longitude 经度\n     * @return {@link Address}\n     */\n    public static Address getAddress(double latitude, double longitude) {\n        Geocoder geocoder = new Geocoder(Utils.getApp(), Locale.getDefault());\n        try {\n            List<Address> addresses = geocoder.getFromLocation(latitude, longitude, 1);\n            if (addresses.size() > 0) return addresses.get(0);\n        } catch (IOException e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    /**\n     * 根据经纬度获取所在国家\n     *\n     * @param latitude  纬度\n     * @param longitude 经度\n     * @return 所在国家\n     */\n    public static String getCountryName(double latitude, double longitude) {\n        Address address = getAddress(latitude, longitude);\n        return address == null ? \"unknown\" : address.getCountryName();\n    }\n\n    /**\n     * 根据经纬度获取所在地\n     *\n     * @param latitude  纬度\n     * @param longitude 经度\n     * @return 所在地\n     */\n    public static String getLocality(double latitude, double longitude) {\n        Address address = getAddress(latitude, longitude);\n        return address == null ? \"unknown\" : address.getLocality();\n    }\n\n    /**\n     * 根据经纬度获取所在街道\n     *\n     * @param latitude  纬度\n     * @param longitude 经度\n     * @return 所在街道\n     */\n    public static String getStreet(double latitude, double longitude) {\n        Address address = getAddress(latitude, longitude);\n        return address == null ? \"unknown\" : address.getAddressLine(0);\n    }\n\n    /**\n     * 是否更好的位置\n     *\n     * @param newLocation         The new Location that you want to evaluate\n     * @param currentBestLocation The current Location fix, to which you want to compare the new one\n     * @return {@code true}: 是<br>{@code false}: 否\n     */\n    public static boolean isBetterLocation(Location newLocation, Location currentBestLocation) {\n        if (currentBestLocation == null) {\n            // A new location is always better than no location\n            return true;\n        }\n\n        // Check whether the new location fix is newer or older\n        long timeDelta = newLocation.getTime() - currentBestLocation.getTime();\n        boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;\n        boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;\n        boolean isNewer = timeDelta > 0;\n\n        // If it's been more than two minutes since the current location, use the new location\n        // because the user has likely moved\n        if (isSignificantlyNewer) {\n            return true;\n            // If the new location is more than two minutes older, it must be worse\n        } else if (isSignificantlyOlder) {\n            return false;\n        }\n\n        // Check whether the new location fix is more or less accurate\n        int accuracyDelta = (int) (newLocation.getAccuracy() - currentBestLocation.getAccuracy());\n        boolean isLessAccurate = accuracyDelta > 0;\n        boolean isMoreAccurate = accuracyDelta < 0;\n        boolean isSignificantlyLessAccurate = accuracyDelta > 200;\n\n        // Check if the old and new location are from the same provider\n        boolean isFromSameProvider = isSameProvider(newLocation.getProvider(), currentBestLocation.getProvider());\n\n        // Determine location quality using a combination of timeliness and accuracy\n        if (isMoreAccurate) {\n            return true;\n        } else if (isNewer && !isLessAccurate) {\n            return true;\n        } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * 是否相同的提供者\n     *\n     * @param provider0 提供者1\n     * @param provider1 提供者2\n     * @return {@code true}: 是<br>{@code false}: 否\n     */\n    public static boolean isSameProvider(String provider0, String provider1) {\n        if (provider0 == null) {\n            return provider1 == null;\n        }\n        return provider0.equals(provider1);\n    }\n\n    private static class MyLocationListener\n            implements LocationListener {\n        /**\n         * 当坐标改变时触发此函数，如果Provider传进相同的坐标，它就不会被触发\n         *\n         * @param location 坐标\n         */\n        @Override\n        public void onLocationChanged(Location location) {\n            if (mListener != null) {\n                mListener.onLocationChanged(location);\n            }\n        }\n\n        /**\n         * provider的在可用、暂时不可用和无服务三个状态直接切换时触发此函数\n         *\n         * @param provider 提供者\n         * @param status   状态\n         * @param extras   provider可选包\n         */\n        @Override\n        public void onStatusChanged(String provider, int status, Bundle extras) {\n            if (mListener != null) {\n                mListener.onStatusChanged(provider, status, extras);\n            }\n            switch (status) {\n                case LocationProvider.AVAILABLE:\n                    Log.d(\"LocationUtils\", \"当前GPS状态为可见状态\");\n                    break;\n                case LocationProvider.OUT_OF_SERVICE:\n                    Log.d(\"LocationUtils\", \"当前GPS状态为服务区外状态\");\n                    break;\n                case LocationProvider.TEMPORARILY_UNAVAILABLE:\n                    Log.d(\"LocationUtils\", \"当前GPS状态为暂停服务状态\");\n                    break;\n            }\n        }\n\n        /**\n         * provider被enable时触发此函数，比如GPS被打开\n         */\n        @Override\n        public void onProviderEnabled(String provider) {\n        }\n\n        /**\n         * provider被disable时触发此函数，比如GPS被关闭\n         */\n        @Override\n        public void onProviderDisabled(String provider) {\n        }\n    }\n\n    public interface OnLocationChangeListener {\n\n        /**\n         * 获取最后一次保留的坐标\n         *\n         * @param location 坐标\n         */\n        void getLastKnownLocation(Location location);\n\n        /**\n         * 当坐标改变时触发此函数，如果Provider传进相同的坐标，它就不会被触发\n         *\n         * @param location 坐标\n         */\n        void onLocationChanged(Location location);\n\n        /**\n         * provider的在可用、暂时不可用和无服务三个状态直接切换时触发此函数\n         *\n         * @param provider 提供者\n         * @param status   状态\n         * @param extras   provider可选包\n         */\n        void onStatusChanged(String provider, int status, Bundle extras);//位置状态发生改变\n    }\n}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/LunarUtils.java",
    "content": "package com.blankj.subutil.util;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/12/05\n *     desc  : 日历相关工具类\n * </pre>\n */\npublic final class LunarUtils {\n\n    private LunarUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /*\n     * |----4位闰月|-------------13位1为30天，0为29天|\n     */\n    private static final int[] LUNAR_MONTH_DAYS = {1887, 0x1694, 0x16aa, 0x4ad5, 0xab6, 0xc4b7, 0x4ae, 0xa56, 0xb52a, 0x1d2a,\n            0xd54, 0x75aa, 0x156a, 0x1096d, 0x95c, 0x14ae, 0xaa4d, 0x1a4c, 0x1b2a, 0x8d55, 0xad4, 0x135a, 0x495d, 0x95c,\n            0xd49b, 0x149a, 0x1a4a, 0xbaa5, 0x16a8, 0x1ad4, 0x52da, 0x12b6, 0xe937, 0x92e, 0x1496, 0xb64b, 0xd4a, 0xda8,\n            0x95b5, 0x56c, 0x12ae, 0x492f, 0x92e, 0xcc96, 0x1a94, 0x1d4a, 0xada9, 0xb5a, 0x56c, 0x726e, 0x125c, 0xf92d,\n            0x192a, 0x1a94, 0xdb4a, 0x16aa, 0xad4, 0x955b, 0x4ba, 0x125a, 0x592b, 0x152a, 0xf695, 0xd94, 0x16aa, 0xaab5,\n            0x9b4, 0x14b6, 0x6a57, 0xa56, 0x1152a, 0x1d2a, 0xd54, 0xd5aa, 0x156a, 0x96c, 0x94ae, 0x14ae, 0xa4c, 0x7d26,\n            0x1b2a, 0xeb55, 0xad4, 0x12da, 0xa95d, 0x95a, 0x149a, 0x9a4d, 0x1a4a, 0x11aa5, 0x16a8, 0x16d4, 0xd2da,\n            0x12b6, 0x936, 0x9497, 0x1496, 0x1564b, 0xd4a, 0xda8, 0xd5b4, 0x156c, 0x12ae, 0xa92f, 0x92e, 0xc96, 0x6d4a,\n            0x1d4a, 0x10d65, 0xb58, 0x156c, 0xb26d, 0x125c, 0x192c, 0x9a95, 0x1a94, 0x1b4a, 0x4b55, 0xad4, 0xf55b,\n            0x4ba, 0x125a, 0xb92b, 0x152a, 0x1694, 0x96aa, 0x15aa, 0x12ab5, 0x974, 0x14b6, 0xca57, 0xa56, 0x1526,\n            0x8e95, 0xd54, 0x15aa, 0x49b5, 0x96c, 0xd4ae, 0x149c, 0x1a4c, 0xbd26, 0x1aa6, 0xb54, 0x6d6a, 0x12da,\n            0x1695d, 0x95a, 0x149a, 0xda4b, 0x1a4a, 0x1aa4, 0xbb54, 0x16b4, 0xada, 0x495b, 0x936, 0xf497, 0x1496,\n            0x154a, 0xb6a5, 0xda4, 0x15b4, 0x6ab6, 0x126e, 0x1092f, 0x92e, 0xc96, 0xcd4a, 0x1d4a, 0xd64, 0x956c, 0x155c,\n            0x125c, 0x792e, 0x192c, 0xfa95, 0x1a94, 0x1b4a, 0xab55, 0xad4, 0x14da, 0x8a5d, 0xa5a, 0x1152b, 0x152a,\n            0x1694, 0xd6aa, 0x15aa, 0xab4, 0x94ba, 0x14b6, 0xa56, 0x7527, 0xd26, 0xee53, 0xd54, 0x15aa, 0xa9b5, 0x96c,\n            0x14ae, 0x8a4e, 0x1a4c, 0x11d26, 0x1aa4, 0x1b54, 0xcd6a, 0xada, 0x95c, 0x949d, 0x149a, 0x1a2a, 0x5b25,\n            0x1aa4, 0xfb52, 0x16b4, 0xaba, 0xa95b, 0x936, 0x1496, 0x9a4b, 0x154a, 0x136a5, 0xda4, 0x15ac};\n\n    private static final int[] SOLAR_1_1 = {1887, 0xec04c, 0xec23f, 0xec435, 0xec649, 0xec83e, 0xeca51, 0xecc46, 0xece3a,\n            0xed04d, 0xed242, 0xed436, 0xed64a, 0xed83f, 0xeda53, 0xedc48, 0xede3d, 0xee050, 0xee244, 0xee439, 0xee64d,\n            0xee842, 0xeea36, 0xeec4a, 0xeee3e, 0xef052, 0xef246, 0xef43a, 0xef64e, 0xef843, 0xefa37, 0xefc4b, 0xefe41,\n            0xf0054, 0xf0248, 0xf043c, 0xf0650, 0xf0845, 0xf0a38, 0xf0c4d, 0xf0e42, 0xf1037, 0xf124a, 0xf143e, 0xf1651,\n            0xf1846, 0xf1a3a, 0xf1c4e, 0xf1e44, 0xf2038, 0xf224b, 0xf243f, 0xf2653, 0xf2848, 0xf2a3b, 0xf2c4f, 0xf2e45,\n            0xf3039, 0xf324d, 0xf3442, 0xf3636, 0xf384a, 0xf3a3d, 0xf3c51, 0xf3e46, 0xf403b, 0xf424e, 0xf4443, 0xf4638,\n            0xf484c, 0xf4a3f, 0xf4c52, 0xf4e48, 0xf503c, 0xf524f, 0xf5445, 0xf5639, 0xf584d, 0xf5a42, 0xf5c35, 0xf5e49,\n            0xf603e, 0xf6251, 0xf6446, 0xf663b, 0xf684f, 0xf6a43, 0xf6c37, 0xf6e4b, 0xf703f, 0xf7252, 0xf7447, 0xf763c,\n            0xf7850, 0xf7a45, 0xf7c39, 0xf7e4d, 0xf8042, 0xf8254, 0xf8449, 0xf863d, 0xf8851, 0xf8a46, 0xf8c3b, 0xf8e4f,\n            0xf9044, 0xf9237, 0xf944a, 0xf963f, 0xf9853, 0xf9a47, 0xf9c3c, 0xf9e50, 0xfa045, 0xfa238, 0xfa44c, 0xfa641,\n            0xfa836, 0xfaa49, 0xfac3d, 0xfae52, 0xfb047, 0xfb23a, 0xfb44e, 0xfb643, 0xfb837, 0xfba4a, 0xfbc3f, 0xfbe53,\n            0xfc048, 0xfc23c, 0xfc450, 0xfc645, 0xfc839, 0xfca4c, 0xfcc41, 0xfce36, 0xfd04a, 0xfd23d, 0xfd451, 0xfd646,\n            0xfd83a, 0xfda4d, 0xfdc43, 0xfde37, 0xfe04b, 0xfe23f, 0xfe453, 0xfe648, 0xfe83c, 0xfea4f, 0xfec44, 0xfee38,\n            0xff04c, 0xff241, 0xff436, 0xff64a, 0xff83e, 0xffa51, 0xffc46, 0xffe3a, 0x10004e, 0x100242, 0x100437,\n            0x10064b, 0x100841, 0x100a53, 0x100c48, 0x100e3c, 0x10104f, 0x101244, 0x101438, 0x10164c, 0x101842,\n            0x101a35, 0x101c49, 0x101e3d, 0x102051, 0x102245, 0x10243a, 0x10264e, 0x102843, 0x102a37, 0x102c4b,\n            0x102e3f, 0x103053, 0x103247, 0x10343b, 0x10364f, 0x103845, 0x103a38, 0x103c4c, 0x103e42, 0x104036,\n            0x104249, 0x10443d, 0x104651, 0x104846, 0x104a3a, 0x104c4e, 0x104e43, 0x105038, 0x10524a, 0x10543e,\n            0x105652, 0x105847, 0x105a3b, 0x105c4f, 0x105e45, 0x106039, 0x10624c, 0x106441, 0x106635, 0x106849,\n            0x106a3d, 0x106c51, 0x106e47, 0x10703c, 0x10724f, 0x107444, 0x107638, 0x10784c, 0x107a3f, 0x107c53,\n            0x107e48};\n\n    private static int getBitInt(final int data, final int length, final int shift) {\n        return (data & (((1 << length) - 1) << shift)) >> shift;\n    }\n\n\n    /**\n     * 农历年转干支年\n     *\n     * @param lunarYear 农历年份\n     * @return 干支年\n     */\n    public static String lunarYear2GanZhi(final int lunarYear) {\n        final String[] tianGan = {\"甲\", \"乙\", \"丙\", \"丁\", \"戊\", \"己\", \"庚\", \"辛\", \"壬\", \"癸\"};\n        final String[] diZhi = {\"子\", \"丑\", \"寅\", \"卯\", \"辰\", \"巳\", \"午\", \"未\", \"申\", \"酉\", \"戌\", \"亥\"};\n        return tianGan[(lunarYear - 4) % 10] + diZhi[(lunarYear - 4) % 12] + \"年\";\n    }\n\n    /**\n     * 农历转公历\n     *\n     * @param lunar 农历\n     * @return 公历\n     */\n    public static Solar lunar2Solar(final Lunar lunar) {\n        int days = LUNAR_MONTH_DAYS[lunar.lunarYear - LUNAR_MONTH_DAYS[0]];\n        int leap = getBitInt(days, 4, 13);\n        int offset = 0;\n        int loopend = leap;\n        if (!lunar.isLeap) {\n            if (lunar.lunarMonth <= leap || leap == 0) {\n                loopend = lunar.lunarMonth - 1;\n            } else {\n                loopend = lunar.lunarMonth;\n            }\n        }\n        for (int i = 0; i < loopend; i++) {\n            offset += getBitInt(days, 1, 12 - i) == 1 ? 30 : 29;\n        }\n        offset += lunar.lunarDay;\n\n        int solar11 = SOLAR_1_1[lunar.lunarYear - SOLAR_1_1[0]];\n\n        int y = getBitInt(solar11, 12, 9);\n        int m = getBitInt(solar11, 4, 5);\n        int d = getBitInt(solar11, 5, 0);\n\n        return solarFromInt(solarToInt(y, m, d) + offset - 1);\n    }\n\n    /**\n     * 公历转农历\n     *\n     * @param solar 公历\n     * @return 阴历\n     */\n    public static Lunar solar2Lunar(final Solar solar) {\n        Lunar lunar = new Lunar();\n        int index = solar.solarYear - SOLAR_1_1[0];\n        int data = (solar.solarYear << 9) | (solar.solarMonth << 5) | (solar.solarDay);\n        int solar11 = 0;\n        if (SOLAR_1_1[index] > data) {\n            index--;\n        }\n        solar11 = SOLAR_1_1[index];\n        int y = getBitInt(solar11, 12, 9);\n        int m = getBitInt(solar11, 4, 5);\n        int d = getBitInt(solar11, 5, 0);\n        long offset = solarToInt(solar.solarYear, solar.solarMonth, solar.solarDay) - solarToInt(y, m, d);\n\n        int days = LUNAR_MONTH_DAYS[index];\n        int leap = getBitInt(days, 4, 13);\n\n        int lunarY = index + SOLAR_1_1[0];\n        int lunarM = 1;\n        int lunarD = 1;\n        offset += 1;\n\n        for (int i = 0; i < 13; i++) {\n            int dm = getBitInt(days, 1, 12 - i) == 1 ? 30 : 29;\n            if (offset > dm) {\n                lunarM++;\n                offset -= dm;\n            } else {\n                break;\n            }\n        }\n        lunarD = (int) (offset);\n        lunar.lunarYear = lunarY;\n        lunar.lunarMonth = lunarM;\n        lunar.isLeap = false;\n        if (leap != 0 && lunarM > leap) {\n            lunar.lunarMonth = lunarM - 1;\n            if (lunarM == leap + 1) {\n                lunar.isLeap = true;\n            }\n        }\n        lunar.lunarDay = lunarD;\n        return lunar;\n    }\n\n    private static Solar solarFromInt(final long g) {\n        long y = (10000 * g + 14780) / 3652425;\n        long ddd = g - (365 * y + y / 4 - y / 100 + y / 400);\n        if (ddd < 0) {\n            y--;\n            ddd = g - (365 * y + y / 4 - y / 100 + y / 400);\n        }\n        long mi = (100 * ddd + 52) / 3060;\n        long mm = (mi + 2) % 12 + 1;\n        y = y + (mi + 2) / 12;\n        long dd = ddd - (mi * 306 + 5) / 10 + 1;\n        Solar solar = new Solar();\n        solar.solarYear = (int) y;\n        solar.solarMonth = (int) mm;\n        solar.solarDay = (int) dd;\n        return solar;\n    }\n\n    private static long solarToInt(int y, int m, final int d) {\n        m = (m + 9) % 12;\n        y = y - m / 10;\n        return 365 * y + y / 4 - y / 100 + y / 400 + (m * 306 + 5) / 10 + (d - 1);\n    }\n\n    public static class Lunar {\n\n        public int     lunarYear;\n        public int     lunarMonth;\n        public int     lunarDay;\n        public boolean isLeap;\n\n        Lunar() {\n        }\n\n        public Lunar(int lunarYear, int lunarMonth, int lunarDay, boolean isLeap) {\n            this.lunarYear = lunarYear;\n            this.lunarMonth = lunarMonth;\n            this.lunarDay = lunarDay;\n            this.isLeap = isLeap;\n        }\n\n        @Override\n        public String toString() {\n            return \"\" + lunarYear + \", \" + lunarMonth + \", \" + lunarDay + \", \" + isLeap;\n        }\n    }\n\n    public static class Solar {\n\n        public int solarYear;\n        public int solarMonth;\n        public int solarDay;\n\n        Solar() {\n        }\n\n        public Solar(int solarYear, int solarMonth, int solarDay) {\n            this.solarYear = solarYear;\n            this.solarMonth = solarMonth;\n            this.solarDay = solarDay;\n        }\n\n        @Override\n        public String toString() {\n            return \"\" + solarYear + \", \" + solarMonth + \", \" + solarDay;\n        }\n    }\n}\n\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/PinyinUtils.java",
    "content": "package com.blankj.subutil.util;\n\nimport androidx.collection.SimpleArrayMap;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 16/11/16\n *     desc  : 拼音相关工具类\n * </pre>\n */\npublic final class PinyinUtils {\n\n    private PinyinUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * 汉字转拼音\n     *\n     * @param ccs 汉字字符串(Chinese characters)\n     * @return 拼音\n     */\n    public static String ccs2Pinyin(final CharSequence ccs) {\n        return ccs2Pinyin(ccs, \"\");\n    }\n\n    /**\n     * 汉字转拼音\n     *\n     * @param ccs   汉字字符串(Chinese characters)\n     * @param split 汉字拼音之间的分隔符\n     * @return 拼音\n     */\n    public static String ccs2Pinyin(final CharSequence ccs, final CharSequence split) {\n        if (ccs == null || ccs.length() == 0) return null;\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0, len = ccs.length(); i < len; i++) {\n            char ch = ccs.charAt(i);\n            if (ch >= 0x4E00 && ch <= 0x9FA5) {\n                int sp = (ch - 0x4E00) * 6;\n                sb.append(pinyinTable.substring(sp, sp + 6).trim());\n            } else {\n                sb.append(ch);\n            }\n            sb.append(split);\n        }\n        return sb.toString();\n    }\n\n    /**\n     * 获取第一个汉字首字母\n     *\n     * @param ccs 汉字字符串(Chinese characters)\n     * @return 拼音\n     */\n    public static String getPinyinFirstLetter(final CharSequence ccs) {\n        if (ccs == null || ccs.length() == 0) return null;\n        return ccs2Pinyin(String.valueOf(ccs.charAt(0))).substring(0, 1);\n    }\n\n    /**\n     * 获取所有汉字的首字母\n     *\n     * @param ccs 汉字字符串(Chinese characters)\n     * @return 所有汉字的首字母\n     */\n    public static String getPinyinFirstLetters(final CharSequence ccs) {\n        return getPinyinFirstLetters(ccs, \"\");\n    }\n\n    /**\n     * 获取所有汉字的首字母\n     *\n     * @param ccs   汉字字符串(Chinese characters)\n     * @param split 首字母之间的分隔符\n     * @return 所有汉字的首字母\n     */\n    public static String getPinyinFirstLetters(final CharSequence ccs, final CharSequence split) {\n        if (ccs == null || ccs.length() == 0) return null;\n        int len = ccs.length();\n        StringBuilder sb = new StringBuilder(len);\n        for (int i = 0; i < len; i++) {\n            sb.append(ccs2Pinyin(String.valueOf(ccs.charAt(i))).substring(0, 1)).append(split);\n        }\n        return sb.toString();\n    }\n\n    /**\n     * 根据名字获取姓氏的拼音\n     *\n     * @param name 名字\n     * @return 姓氏的拼音\n     */\n    public static String getSurnamePinyin(final CharSequence name) {\n        if (name == null || name.length() == 0) return null;\n        if (name.length() >= 2) {\n            CharSequence str = name.subSequence(0, 2);\n            if (str.equals(\"澹台\")) return \"tantai\";\n            else if (str.equals(\"尉迟\")) return \"yuchi\";\n            else if (str.equals(\"万俟\")) return \"moqi\";\n            else if (str.equals(\"单于\")) return \"chanyu\";\n        }\n        char ch = name.charAt(0);\n        if (SURNAMES.containsKey(ch)) {\n            return SURNAMES.get(ch);\n        }\n        if (ch >= 0x4E00 && ch <= 0x9FA5) {\n            int sp = (ch - 0x4E00) * 6;\n            return pinyinTable.substring(sp, sp + 6).trim();\n        } else {\n            return String.valueOf(ch);\n        }\n    }\n\n    /**\n     * 根据名字获取姓氏的首字母\n     *\n     * @param name 名字\n     * @return 姓氏的首字母\n     */\n    public static String getSurnameFirstLetter(final CharSequence name) {\n        String surname = getSurnamePinyin(name);\n        if (surname == null || surname.length() == 0) return null;\n        return String.valueOf(surname.charAt(0));\n    }\n\n    // 多音字姓氏映射表\n    private static final SimpleArrayMap<Character, String> SURNAMES;\n\n    /**\n     * 获取拼音对照表，对比过pinyin4j和其他方式，这样查表设计的好处就是读取快\n     * <p>当该类加载后会一直占有123KB的内存</p>\n     * <p>如果你想存进文件，然后读取操作的话也是可以，但速度肯定没有这样空间换时间快，毕竟现在设备内存都很大</p>\n     * <p>如需更多用法可以用pinyin4j开源库</p>\n     */\n    private static final String pinyinTable;\n\n    static {\n        SURNAMES = new SimpleArrayMap<>(35);\n        SURNAMES.put('乐', \"yue\");\n        SURNAMES.put('乘', \"sheng\");\n        SURNAMES.put('乜', \"nie\");\n        SURNAMES.put('仇', \"qiu\");\n        SURNAMES.put('会', \"gui\");\n        SURNAMES.put('便', \"pian\");\n        SURNAMES.put('区', \"ou\");\n        SURNAMES.put('单', \"shan\");\n        SURNAMES.put('参', \"shen\");\n        SURNAMES.put('句', \"gou\");\n        SURNAMES.put('召', \"shao\");\n        SURNAMES.put('员', \"yun\");\n        SURNAMES.put('宓', \"fu\");\n        SURNAMES.put('弗', \"fei\");\n        SURNAMES.put('折', \"she\");\n        SURNAMES.put('曾', \"zeng\");\n        SURNAMES.put('朴', \"piao\");\n        SURNAMES.put('查', \"zha\");\n        SURNAMES.put('洗', \"xian\");\n        SURNAMES.put('盖', \"ge\");\n        SURNAMES.put('祭', \"zhai\");\n        SURNAMES.put('种', \"chong\");\n        SURNAMES.put('秘', \"bi\");\n        SURNAMES.put('繁', \"po\");\n        SURNAMES.put('缪', \"miao\");\n        SURNAMES.put('能', \"nai\");\n        SURNAMES.put('蕃', \"pi\");\n        SURNAMES.put('覃', \"qin\");\n        SURNAMES.put('解', \"xie\");\n        SURNAMES.put('谌', \"shan\");\n        SURNAMES.put('适', \"kuo\");\n        SURNAMES.put('都', \"du\");\n        SURNAMES.put('阿', \"e\");\n        SURNAMES.put('难', \"ning\");\n        SURNAMES.put('黑', \"he\");\n\n        //noinspection StringBufferReplaceableByString\n        pinyinTable = new StringBuilder(125412)\n                .append(\"yi    ding  kao   qi    shang xia   none  wan   zhang san   shang xia   ji    bu    yu    mian  gai   chou  chou  zhuan qie   pi    shi   shi   qiu   bing  ye    cong  dong  si    cheng diu   qiu   liang diu   you   liang yan   bing  sang  shu   jiu   ge    ya    qiang zhong ji    jie   feng  guan  chuan chan  lin   zhuo  zhu   none  wan   dan   wei   zhu   jing  li    ju    pie   fu    yi    yi    nai   none  jiu   jiu   tuo   me    yi    none  zhi   wu    zha   hu    fa    le    zhong ping  pang  qiao  hu    guai  cheng cheng yi    yin   none  mie   jiu   qi    ye    xi    xiang gai   diu   none  none  shu   none  shi   ji    nang  jia   none  shi   none  none  mai   luan  none  ru    xi    yan   fu    sha   na    gan   none  none  none  none  qian  zhi   gui   gan   luan  lin   yi    jue   le    none  yu    zheng shi   shi   er    chu   yu    kui   yu    yun   hu    qi    wu    jing  si    sui   gen   gen   ya    xie   ya    qi    ya    ji    tou   wang  kang  ta    jiao  hai   yi    chan  heng  mu    none  xiang jing  ting  liang heng  jing  ye    qin   bo    you   xie   dan   lian  duo   wei   ren   ren   ji    none  wang  yi    shen  ren   le    ding  ze    jin   pu    chou  ba    zhang jin   jie   bing  reng  cong  fo    san   lun   none  cang  zi    shi   ta    zhang fu    xian  xian  cha   hong  tong  ren   qian  gan   ge    di    dai   ling  yi    chao  chang sa    shang yi    mu    men   ren   jia   chao  yang  qian  zhong pi    wan   wu    jian  jia   yao   feng  cang  ren   wang  fen   di    fang  zhong qi    pei   yu    diao  dun   wen   yi    xin   kang  yi    ji    ai    wu    ji    fu    fa    xiu   jin   bei   chen  fu    tang  zhong you   huo   hui   yu    cui   yun   san   wei   chuan che   ya    xian  shang chang lun   cang  xun   xin   wei   zhu   chi   xuan  nao   bo    gu    ni    ni    xie   ban   xu    ling  zhou  shen  qu    si    beng  si    jia   pi    yi    si    ai    zheng dian  han   mai   dan   zhu   bu    qu    bi    shao  ci    wei   di    zhu   zuo   you   yang  ti    zhan  he    bi    tuo   she   yu    yi    fo    zuo   gou   ning  tong  ni    xuan  ju    yong  wa    qian  none  ka    none  pei   huai  he    lao   xiang ge    yang  bai   fa    ming  jia   nai   bing  ji    heng  huo   gui   quan  tiao  jiao  ci    yi    shi   xing  shen  tuo   kan   zhi   gai   lai   yi    chi   kua   guang li    yin   shi   mi    zhu   xu    you   an    lu    mou   er    lun   dong  cha   chi   xun   gong  zhou  yi    ru    jian  xia   jia   zai   lu:   none  jiao  zhen  ce    qiao  kuai  chai  ning  nong  jin   wu    hou   jiong cheng zhen  cuo   chou  qin   lu:   ju    shu   ting  shen  tuo   bo    nan   hao   bian  tui   yu    xi    cu    e     qiu   xu    kuang ku    wu    jun   yi    fu    lang  zu    qiao  li    yong  hun   jing  xian  san   pai   su    fu    xi    li    mian  ping  bao   yu    si    xia   xin   xiu   yu    ti    che   chou  none  yan   liang li    lai   si    jian  xiu   fu    he    ju    xiao  pai   jian  biao  ti    fei   feng  ya    an    bei   yu    xin   bi    chi   chang zhi   bing  zan   yao   cui   lia   wan   lai   cang  zong  ge    guan  bei   tian  shu   shu   men   dao   tan   jue   chui  xing  peng  tang  hou   yi    qi    ti    gan   jing  jie   xu    chang jie   fang  zhi   kong  juan  zong  ju    qian  ni    lun   zhuo  wo    luo   song  leng  hun   dong  zi    ben   wu    ju    nai   cai   jian  zhai  ye    zhi   sha   qing  none  ying  cheng qian  yan   nuan  zhong chun  jia   jie   wei   yu    bing  ruo   ti    wei   pian  yan   feng  tang  wo    e     xie   che   sheng kan   di    zuo   cha   ting  bei   ye    huang yao   zhan  qiu   yan   you   jian  xu    zha   chai  fu    bi    zhi   zong  mian  ji    yi    xie   xun   si    duan  ce    zhen  ou    tou   tou   bei   za    lou   jie   wei   fen   chang kui   sou   chi   su    xia   fu    yuan  rong  li    ru    yun   gou   ma    bang  dian  tang  hao   jie   xi    shan  qian  jue   cang  chu   san   bei   xiao  yong  yao   ta    suo   wang  fa    bing  jia   dai   zai   tang  none  bin   chu   nuo   zan   lei   cui   yong  zao   zong  peng  song  ao    chuan yu    zhai  zu    shang qian\")\n                .append(\"g qiang chi   sha   han   zhang qing  yan   di    xi    lou   bei   piao  jin   lian  lu    man   qian  xian  qiu   ying  dong  zhuan xiang shan  qiao  jiong tui   zun   pu    xi    lao   chang guang liao  qi    deng  chan  wei   zhang fan   hui   chuan tie   dan   jiao  jiu   seng  fen   xian  jue   e     jiao  jian  tong  lin   bo    gu    xian  su    xian  jiang min   ye    jin   jia   qiao  pi    feng  zhou  ai    sai   yi    jun   nong  shan  yi    dang  jing  xuan  kuai  jian  chu   dan   jiao  sha   zai   none  bin   an    ru    tai   chou  chai  lan   ni    jin   qian  meng  wu    neng  qiong ni    chang lie   lei   lu:   kuang bao   du    biao  zan   zhi   si    you   hao   qin   chen  li    teng  wei   long  chu   chan  rang  shu   hui   li    luo   zan   nuo   tang  yan   lei   nang  er    wu    yun   zan   yuan  xiong chong zhao  xiong xian  guang dui   ke    dui   mian  tu    chang er    dui   er    jin   tu    si    yan   yan   shi   shi   dang  qian  dou   fen   mao   xin   dou   bai   jing  li    kuang ru    wang  nei   quan  liang yu    ba    gong  liu   xi    none  lan   gong  tian  guan  xing  bing  qi    ju    dian  zi    none  yang  jian  shou  ji    yi    ji    chan  jiong mao   ran   nei   yuan  mao   gang  ran   ce    jiong ce    zai   gua   jiong mao   zhou  mao   gou   xu    mian  mi    rong  yin   xie   kan   jun   nong  yi    mi    shi   guan  meng  zhong zui   yuan  ming  kou   none  fu    xie   mi    bing  dong  tai   gang  feng  bing  hu    chong jue   hu    kuang ye    leng  pan   fu    min   dong  xian  lie   xia   jian  jing  shu   mei   shang qi    gu    zhun  song  jing  liang qing  diao  ling  dong  gan   jian  yin   cou   ai    li    cang  ming  zhun  cui   si    duo   jin   lin   lin   ning  xi    du    ji    fan   fan   fan   feng  ju    chu   none  feng  none  none  fu    feng  ping  feng  kai   huang kai   gan   deng  ping  qu    xiong kuai  tu    ao    chu   ji    dang  han   han   zao   dao   diao  dao   ren   ren   chuangfen   qie   yi    ji    kan   qian  cun   chu   wen   ji    dan   xing  hua   wan   jue   li    yue   lie   liu   ze    gang  chuangfu    chu   qu    ju    shan  min   ling  zhong pan   bie   jie   jie   bao   li    shan  bie   chan  jing  gua   gen   dao   chuangkui   ku    duo   er    zhi   shua  quan  cha   ci    ke    jie   gui   ci    gui   kai   duo   ji    ti    jing  lou   luo   ze    yuan  cuo   xue   ke    la    qian  cha   chuan gua   jian  cuo   li    ti    fei   pou   chan  qi    chuangzi    gang  wan   bo    ji    duo   qing  yan   zhuo  jian  ji    bo    yan   ju    huo   sheng jian  duo   duan  wu    gua   fu    sheng jian  ge    zha   kai   chuangjuan  chan  tuan  lu    li    fou   shan  piao  kou   jiao  gua   qiao  jue   hua   zha   zhuo  lian  ju    pi    liu   gui   jiao  gui   jian  jian  tang  huo   ji    jian  yi    jian  zhi   chan  cuan  mo    li    zhu   li    ya    quan  ban   gong  jia   wu    mai   lie   jing  keng  xie   zhi   dong  zhu   nu    jie   qu    shao  yi    zhu   mo    li    jing  lao   lao   juan  kou   yang  wa    xiao  mou   kuang jie   lie   he    shi   ke    jing  hao   bo    min   chi   lang  yong  yong  mian  ke    xun   juan  qing  lu    bu    meng  lai   le    kai   mian  dong  xu    xu    kan   wu    yi    xun   weng  sheng lao   mu    lu    piao  shi   ji    qin   qiang jiao  quan  xiang yi    qiao  fan   juan  tong  ju    dan   xie   mai   xun   xun   lu:   li    che   rang  quan  bao   shao  yun   jiu   bao   gou   wu    yun   none  none  gai   gai   bao   cong  none  xiong peng  ju    tao   ge    pu    an    pao   fu    gong  da    jiu   qiong bi    hua   bei   nao   chi   fang  jiu   yi    za    jiang kang  jiang kuang hu    xia   qu    fan   gui   qie   cang  kuang fei   hu    yu    gui   kui   hui   dan   kui   lian  lian  suan  du    jiu   qu    xi    pi    qu    yi    an    yan   bian  ni    qu    shi   xin   qian  nian  sa    zu    sheng wu    hui   ban   shi   xi    wan   hua   xie   wan   bei   zu    zhuo  xie   dan   mai   nan   dan   ji    bo    shuai bu    kuang bian  bu    zhan  ka    lu    you   lu    xi    gua   wo    xie   jie   jie   wei   ang   qiong zhi   mao   yin   we\")\n                .append(\"i   shao  ji    que   luan  shi   juan  xie   xu    jin   que   wu    ji    e     qing  xi    none  chang han   e     ting  li    zhe   an    li    ya    ya    yan   she   zhi   zha   pang  none  ke    ya    zhi   ce    pang  ti    li    she   hou   ting  zui   cuo   fei   yuan  ce    yuan  xiang yan   li    jue   sha   dian  chu   jiu   qin   ao    gui   yan   si    li    chang lan   li    yan   yan   yuan  si    si    lin   qiu   qu    qu    none  lei   du    xian  zhuan san   can   can   san   can   ai    dai   you   cha   ji    you   shuangfan   shou  guai  ba    fa    ruo   shi   shu   zhui  qu    shou  bian  xu    jia   pan   sou   ji    yu    sou   die   rui   cong  kou   gu    ju    ling  gua   tao   kou   zhi   jiao  zhao  ba    ding  ke    tai   chi   shi   you   qiu   po    ye    hao   si    tan   chi   le    diao  ji    none  hong  mie   yu    mang  chi   ge    xuan  yao   zi    he    ji    diao  cun   tong  ming  hou   li    tu    xiang zha   he    ye    lu:   a     ma    ou    xue   yi    jun   chou  lin   tun   yin   fei   bi    qin   qin   jie   pou   fou   ba    dun   fen   e     han   ting  hang  shun  qi    hu    zhi   yin   wu    wu    chao  na    chuo  xi    chui  dou   wen   hou   ou    wu    gao   ya    jun   lu:   e     ge    mei   dai   qi    cheng wu    gao   fu    jiao  hong  chi   sheng na    tun   m     yi    dai   ou    li    bei   yuan  guo   none  qiang wu    e     shi   quan  pen   wen   ni    mou   ling  ran   you   di    zhou  shi   zhou  zhan  ling  yi    qi    ping  zi    gua   ci    wei   xu    he    nao   xia   pei   yi    xiao  shen  hu    ming  da    qu    ju    gan   za    tuo   duo   pou   pao   bie   fu    bi    he    za    he    hai   jiu   yong  fu    da    zhou  wa    ka    gu    ka    zuo   bu    long  dong  ning  zha   si    xian  huo   qi    er    e     guang zha   xi    yi    lie   zi    mie   mi    zhi   yao   ji    zhou  ge    shuai zan   xiao  ke    hui   kua   huai  tao   xian  e     xuan  xiu   guo   yan   lao   yi    ai    pin   shen  tong  hong  xiong duo   wa    ha    zai   you   di    pai   xiang ai    gen   kuang ya    da    xiao  bi    hui   none  hua   none  kuai  duo   none  ji    nong  mou   yo    hao   yuan  long  pou   mang  ge    e     chi   shao  li    na    zu    he    ku    xiao  xian  lao   bei   zhe   zha   liang ba    mi    le    sui   fou   bu    han   heng  geng  shuo  ge    you   yan   gu    gu    bai   han   suo   chun  yi    ai    jia   tu    xian  guan  li    xi    tang  zuo   miu   che   wu    zao   ya    dou   qi    di    qin   ma    none  gong  dou   none  lao   liang suo   zao   huan  none  gou   ji    zuo   wo    feng  yin   hu    qi    shou  wei   shua  chang er    li    qiang an    jie   yo    nian  yu    tian  lai   sha   xi    tuo   hu    ai    zhou  nou   ken   zhuo  zhuo  shang di    heng  lin   a     xiao  xiang tun   wu    wen   cui   jie   hu    qi    qi    tao   dan   dan   wan   zi    bi    cui   chuo  he    ya    qi    zhe   fei   liang xian  pi    sha   la    ze    qing  gua   pa    zhe   se    zhuan nie   guo   luo   yan   di    quan  tan   bo    ding  lang  xiao  none  tang  chi   ti    an    jiu   dan   ka    yong  wei   nan   shan  yu    zhe   la    jie   hou   han   die   zhou  chai  kuai  re    yu    yin   zan   yao   wo    mian  hu    yun   chuan hui   huan  huan  xi    he    ji    kui   zhong wei   sha   xu    huang du    nie   xuan  liang yu    sang  chi   qiao  yan   dan   pen   shi   li    yo    zha   wei   miao  ying  pen   none  kui   xi    yu    jie   lou   ku    cao   huo   ti    yao   he    a     xiu   qiang se    yong  su    hong  xie   ai    suo   ma    cha   hai   ke    da    sang  chen  ru    sou   gong  ji    pang  wu    qian  shi   ge    zi    jie   luo   weng  wa    si    chi   hao   suo   jia   hai   suo   qin   nie   he    none  sai   ng    ge    na    dia   ai    none  tong  bi    ao    ao    lian  cui   zhe   mo    sou   sou   tan   di    qi    jiao  chong jiao  kai   tan   san   cao   jia   none  xiao  piao  lou   ga    gu    xiao  hu    hui   guo   ou    xian  ze    chang xu    po    de    ma    ma    hu    lei   du    ga    tang  ye    beng  ying  none  jiao  mi    xiao  hua   \")\n                .append(\"mai   ran   zuo   peng  lao   xiao  ji    zhu   chao  kui   zui   xiao  si    hao   fu    liao  qiao  xi    xu    chan  dan   hei   xun   wu    zun   pan   chi   kui   can   zan   cu    dan   yu    tun   cheng jiao  ye    xi    qi    hao   lian  xu    deng  hui   yin   pu    jue   qin   xun   nie   lu    si    yan   ying  da    zhan  o     zhou  jin   nong  hui   hui   qi    e     zao   yi    shi   jiao  yuan  ai    yong  xue   kuai  yu    pen   dao   ga    xin   dun   dang  none  sai   pi    pi    yin   zui   ning  di    han   ta    huo   ru    hao   xia   yan   duo   pi    chou  ji    jin   hao   ti    chang none  none  ca    ti    lu    hui   bao   you   nie   yin   hu    mo    huang zhe   li    liu   none  nang  xiao  mo    yan   li    lu    long  mo    dan   chen  pin   pi    xiang huo   mo    xi    duo   ku    yan   chan  ying  rang  dian  la    ta    xiao  jiao  chuo  huan  huo   zhuan nie   xiao  ca    li    chan  chai  li    yi    luo   nang  zan   su    xi    none  jian  za    zhu   lan   nie   nang  none  none  wei   hui   yin   qiu   si    nin   jian  hui   xin   yin   nan   tuan  tuan  dun   kang  yuan  jiong pian  yun   cong  hu    hui   yuan  e     guo   kun   cong  wei   tu    wei   lun   guo   jun   ri    ling  gu    guo   tai   guo   tu    you   guo   yin   hun   pu    yu    han   yuan  lun   quan  yu    qing  guo   chui  wei   yuan  quan  ku    pu    yuan  yuan  e     tu    tu    tu    tuan  lu:e  hui   yi    yuan  luan  luan  tu    ya    tu    ting  sheng yan   lu    none  ya    zai   wei   ge    yu    wu    gui   pi    yi    di    qian  qian  zhen  zhuo  dang  qia   none  none  kuang chang qi    nie   mo    ji    jia   zhi   zhi   ban   xun   tou   qin   fen   jun   keng  dun   fang  fen   ben   tan   kan   huai  zuo   keng  bi    xing  di    jing  ji    kuai  di    jing  jian  tan   li    ba    wu    fen   zhui  po    pan   tang  kun   qu    tan   zhi   tuo   gan   ping  dian  wa    ni    tai   pi    jiong yang  fo    ao    liu   qiu   mu    ke    gou   xue   ba    chi   che   ling  zhu   fu    hu    zhi   chui  la    long  long  lu    ao    none  pao   none  xing  tong  ji    ke    lu    ci    chi   lei   gai   yin   hou   dui   zhao  fu    guang yao   duo   duo   gui   cha   yang  yin   fa    gou   yuan  die   xie   ken   shang shou  e     none  dian  hong  ya    kua   da    none  dang  kai   none  nao   an    xing  xian  huan  bang  pei   ba    yi    yin   han   xu    chui  cen   geng  ai    peng  fang  que   yong  jun   jia   di    mai   lang  xuan  cheng shan  jin   zhe   lie   lie   pu    cheng none  bu    shi   xun   guo   jiong ye    nian  di    yu    bu    wu    juan  sui   pi    cheng wan   ju    lun   zheng kong  zhong dong  dai   tan   an    cai   shu   beng  kan   zhi   duo   yi    zhi   yi    pei   ji    zhun  qi    sao   ju    ni    ku    ke    tang  kun   ni    jian  dui   jin   gang  yu    e     peng  gu    tu    leng  none  ya    qian  none  an    chen  duo   nao   tu    cheng yin   hun   bi    lian  guo   die   zhuan hou   bao   bao   yu    di    mao   jie   ruan  e     geng  kan   zong  yu    huang e     yao   yan   bao   ji    mei   chang du    tuo   an    feng  zhong jie   zhen  heng  gang  chuan jian  none  lei   gang  huang leng  duan  wan   xuan  ji    ji    kuai  ying  ta    cheng yong  kai   su    su    shi   mi    ta    weng  cheng tu    tang  qiao  zhong li    peng  bang  sai   zang  dui   tian  wu    cheng xun   ge    zhen  ai    gong  yan   kan   tian  yuan  wen   xie   liu   none  lang  chang peng  beng  chen  lu    lu    ou    qian  mei   mo    zhuan shuangshu   lou   chi   man   biao  jing  ce    shu   di    zhang kan   yong  dian  chen  zhi   ji    guo   qiang jin   di    shang mu    cui   yan   ta    zeng  qi    qiang liang none  zhui  qiao  zeng  xu    shan  shan  ba    pu    kuai  dong  fan   que   mo    dun   dun   zun   zui   sheng duo   duo   tan   deng  mu    fen   huang tan   da    ye    chu   none  ao    qiang ji    qiao  ken   yi    pi    bi    dian  jiang ye    yong  xue   tan   lan   ju    huai  dang  rang  qian  xuan  lan   mi    he    kai   ya    dao   hao   ruan  none  lei   kuang lu    yan   tan   wei   huai  long  long  rui   li  \")\n                .append(\"  lin   rang  chan  xun   yan   lei   ba    none  shi   ren   none  zhuangzhuangsheng yi    mai   qiao  zhu   zhuanghu    hu    kun   yi    hu    xu    kun   shou  mang  zun   shou  yi    zhi   gu    chu   xiang feng  bei   none  bian  sui   qun   ling  fu    zuo   xia   xiong none  nao   xia   kui   xi    wai   yuan  mao   su    duo   duo   ye    qing  none  gou   gou   qi    meng  meng  yin   huo   chen  da    ze    tian  tai   fu    guai  yao   yang  hang  gao   shi   ben   tai   tou   yan   bi    yi    kua   jia   duo   none  kuang yun   jia   ba    en    lian  huan  di    yan   pao   juan  qi    nai   feng  xie   fen   dian  none  kui   zou   huan  qi    kai   she   ben   yi    jiang tao   zhuangben   xi    huang fei   diao  sui   beng  dian  ao    she   weng  pan   ao    wu    ao    jiang lian  duo   yun   jiang shi   fen   huo   bei   lian  che   nu:   nu    ding  nai   qian  jian  ta    jiu   nan   cha   hao   xian  fan   ji    shuo  ru    fei   wang  hong  zhuangfu    ma    dan   ren   fu    jing  yan   xie   wen   zhong pa    du    ji    keng  zhong yao   jin   yun   miao  pei   chi   yue   zhuangniu   yan   na    xin   fen   bi    yu    tuo   feng  yuan  fang  wu    yu    gui   du    ba    ni    zhou  zhou  zhao  da    nai   yuan  tou   xuan  zhi   e     mei   mo    qi    bi    shen  qie   e     he    xu    fa    zheng ni    ban   mu    fu    ling  zi    zi    shi   ran   shan  yang  qian  jie   gu    si    xing  wei   zi    ju    shan  pin   ren   yao   tong  jiang shu   ji    gai   shang kuo   juan  jiao  gou   lao   jian  jian  yi    nian  zhi   ji    ji    xian  heng  guang jun   kua   yan   ming  lie   pei   yan   you   yan   cha   xian  yin   chi   gui   quan  zi    song  wei   hong  wa    lou   ya    rao   jiao  luan  ping  xian  shao  li    cheng xie   mang  none  suo   mu    wei   ke    lai   chuo  ding  niang keng  nan   yu    na    pei   sui   juan  shen  zhi   han   di    zhuange     pin   tui   xian  mian  wu    yan   wu    xi    yan   yu    si    yu    wa    li    xian  ju    qu    chui  qi    xian  zhui  dong  chang lu    ai    e     e     lou   mian  cong  pou   ju    po    cai   ling  wan   biao  xiao  shu   qi    hui   fu    wo    rui   tan   fei   none  jie   tian  ni    quan  jing  hun   jing  qian  dian  xing  hu    wan   lai   bi    yin   chou  chuo  fu    jing  lun   yan   lan   kun   yin   ya    none  li    dian  xian  none  hua   ying  chan  shen  ting  yang  yao   wu    nan   chuo  jia   tou   xu    yu    wei   ti    rou   mei   dan   ruan  qin   none  wu    qian  chun  mao   fu    jie   duan  xi    zhong mei   huang mian  an    ying  xuan  none  wei   mei   yuan  zhen  qiu   ti    xie   tuo   lian  mao   ran   si    pian  wei   wa    jiu   hu    ao    none  bao   xu    tou   gui   zou   yao   pi    xi    yuan  ying  rong  ru    chi   liu   mei   pan   ao    ma    gou   kui   qin   jia   sao   zhen  yuan  cha   yong  ming  ying  ji    su    niao  xian  tao   pang  lang  niao  bao   ai    pi    pin   yi    piao  yu    lei   xuan  man   yi    zhang kang  yong  ni    li    di    gui   yan   jin   zhuan chang ce    han   nen   lao   mo    zhe   hu    hu    ao    nen   qiang none  bi    gu    wu    qiao  tuo   zhan  mao   xian  xian  mo    liao  lian  hua   gui   deng  zhi   xu    none  hua   xi    hui   rao   xi    yan   chan  jiao  mei   fan   fan   xian  yi    wei   chan  fan   shi   bi    shan  sui   qiang lian  huan  none  niao  dong  yi    can   ai    niang ning  ma    tiao  chou  jin   ci    yu    pin   none  xu    nai   yan   tai   ying  can   niao  none  ying  mian  none  ma    shen  xing  ni    du    liu   yuan  lan   yan   shuangling  jiao  niang lan   xian  ying  shuangshuai quan  mi    li    luan  yan   zhu   lan   zi    jie   jue   jue   kong  yun   zi    zi    cun   sun   fu    bei   zi    xiao  xin   meng  si    tai   bao   ji    gu    nu    xue   none  chan  hai   luan  sun   nao   mie   cong  jian  shu   chan  ya    zi    ni    fu    zi    li    xue   bo    ru    nai   nie   nie   ying  luan  mian  ning  rong  ta    gui   zhai  qiong yu    shou  an    tu    song  wan   rou   yao   hong  yi    jing  zhun  mi    guai  dang  hong  zong  guan  zhou  ding  wa\")\n                .append(\"n   yi    bao   shi   shi   chong shen  ke    xuan  shi   you   huan  yi    tiao  shi   xian  gong  cheng qun   gong  xiao  zai   zha   bao   hai   yan   xiao  jia   shen  chen  rong  huang mi    kou   kuan  bin   su    cai   zan   ji    yuan  ji    yin   mi    kou   qing  he    zhen  jian  fu    ning  bing  huan  mei   qin   han   yu    shi   ning  jin   ning  zhi   yu    bao   kuan  ning  qin   mo    cha   ju    gua   qin   hu    wu    liao  shi   ning  zhai  shen  wei   xie   kuan  hui   liao  jun   huan  yi    yi    bao   qin   chong bao   feng  cun   dui   si    xun   dao   lu:   dui   shou  po    feng  zhuan fu    she   ke    jiang jiang zhuan wei   zun   xun   shu   dui   dao   xiao  ji    shao  er    er    er    ga    jian  shu   chen  shang shang yuan  ga    chang liao  xian  xian  none  wang  wang  you   liao  liao  yao   mang  wang  wang  wang  ga    yao   duo   kui   zhong jiu   gan   gu    gan   gan   gan   gan   shi   yin   chi   kao   ni    jin   wei   niao  ju    pi    ceng  xi    bi    ju    jie   tian  qu    ti    jie   wu    diao  shi   shi   ping  ji    xie   chen  xi    ni    zhan  xi    none  man   e     lou   ping  ti    fei   shu   xie   tu    lu:   lu:   xi    ceng  lu:   ju    xie   ju    jue   liao  jue   shu   xi    che   tun   ni    shan  wa    xian  li    e     none  none  long  yi    qi    ren   wu    han   shen  yu    chu   sui   qi    none  yue   ban   yao   ang   ya    wu    jie   e     ji    qian  fen   wan   qi    cen   qian  qi    cha   jie   qu    gang  xian  ao    lan   dao   ba    zhai  zuo   yang  ju    gang  ke    gou   xue   bo    li    tiao  qu    yan   fu    xiu   jia   ling  tuo   pei   you   dai   kuang yue   qu    hu    po    min   an    tiao  ling  chi   none  dong  none  kui   xiu   mao   tong  xue   yi    none  he    ke    luo   e     fu    xun   die   lu    lang  er    gai   quan  tong  yi    mu    shi   an    wei   hu    zhi   mi    li    ji    tong  kui   you   none  xia   li    yao   jiao  zheng luan  jiao  e     e     yu    ye    bu    qiao  qun   feng  feng  nao   li    you   xian  hong  dao   shen  cheng tu    geng  jun   hao   xia   yin   wu    lang  kan   lao   lai   xian  que   kong  chong chong ta    none  hua   ju    lai   qi    min   kun   kun   zu    gu    cui   ya    ya    gang  lun   lun   leng  jue   duo   cheng guo   yin   dong  han   zheng wei   yao   pi    yan   song  jie   beng  zu    jue   dong  zhan  gu    yin   zi    ze    huang yu    wei   yang  feng  qiu   dun   ti    yi    zhi   shi   zai   yao   e     zhu   kan   lu:   yan   mei   gan   ji    ji    huan  ting  sheng mei   qian  wu    yu    zong  lan   jie   yan   yan   wei   zong  cha   sui   rong  ke    qin   yu    qi    lou   tu    dui   xi    weng  cang  dang  rong  jie   ai    liu   wu    song  qiao  zi    wei   beng  dian  cuo   qian  yong  nie   cuo   ji    none  none  song  zong  jiang liao  none  chan  di    cen   ding  tu    lou   zhang zhan  zhan  ao    cao   qu    qiang zui   zui   dao   dao   xi    yu    bo    long  xiang ceng  bo    qin   jiao  yan   lao   zhan  lin   liao  liao  jin   deng  duo   zun   jiao  gui   yao   qiao  yao   jue   zhan  yi    xue   nao   ye    ye    yi    e     xian  ji    xie   ke    sui   di    ao    zui   none  yi    rong  dao   ling  za    yu    yue   yin   none  jie   li    sui   long  long  dian  ying  xi    ju    chan  ying  kui   yan   wei   nao   quan  chao  cuan  luan  dian  dian  nie   yan   yan   yan   nao   yan   chuan gui   chuan zhou  huang jing  xun   chao  chao  lie   gong  zuo   qiao  ju    gong  none  wu    none  none  cha   qiu   qiu   ji    yi    si    ba    zhi   zhao  xiang yi    jin   xun   juan  none  xun   jin   fu    za    bi    shi   bu    ding  shuai fan   nie   shi   fen   pa    zhi   xi    hu    dan   wei   zhang tang  dai   ma    pei   pa    tie   fu    lian  zhi   zhou  bo    zhi   di    mo    yi    yi    ping  qia   juan  ru    shuai dai   zhen  shui  qiao  zhen  shi   qun   xi    bang  dai   gui   chou  ping  zhang sha   wan   dai   wei   chang sha   qi    ze    guo   mao   du    hou   zhen  xu    mi    wei   wo    fu    yi    bang  ping  none  gong  pan   huang dao   mi    jia   teng  hui   zhong sen   \")\n                .append(\"man   mu    biao  guo   ze    mu    bang  zhang jiong chan  fu    zhi   hu    fan   chuangbi    bi    none  mi    qiao  dan   fen   meng  bang  chou  mie   chu   jie   xian  lan   gan   ping  nian  jian  bing  bing  xing  gan   yao   huan  you   you   ji    guang pi    ting  ze    guang zhuangmo    qing  bi    qin   dun   chuanggui   ya    bai   jie   xu    lu    wu    none  ku    ying  di    pao   dian  ya    miao  geng  ci    fu    tong  pang  fei   xiang yi    zhi   tiao  zhi   xiu   du    zuo   xiao  tu    gui   ku    pang  ting  you   bu    bing  cheng lai   bi    ji    an    shu   kang  yong  tuo   song  shu   qing  yu    yu    miao  sou   ce    xiang fei   jiu   he    hui   liu   sha   lian  lang  sou   jian  pou   qing  jiu   jiu   qin   ao    kuo   lou   yin   liao  dai   lu    yi    chu   chan  tu    si    xin   miao  chang wu    fei   guang none  guai  bi    qiang xie   lin   lin   liao  lu    none  ying  xian  ting  yong  li    ting  yin   xun   yan   ting  di    po    jian  hui   nai   hui   gong  nian  kai   bian  yi    qi    nong  fen   ju    yan   yi    zang  bi    yi    yi    er    san   shi   er    shi   shi   gong  diao  yin   hu    fu    hong  wu    tui   chi   qiang ba    shen  di    zhang jue   tao   fu    di    mi    xian  hu    chao  nu    jing  zhen  yi    mi    quan  wan   shao  ruo   xuan  jing  diao  zhang jiang qiang beng  dan   qiang bi    bi    she   dan   jian  gou   none  fa    bi    kou   none  bie   xiao  dan   kuang qiang hong  mi    kuo   wan   jue   ji    ji    gui   dang  lu    lu    tuan  hui   zhi   hui   hui   yi    yi    yi    yi    huo   huo   shan  xing  zhang tong  yan   yan   yu    chi   cai   biao  diao  bin   peng  yong  piao  zhang ying  chi   chi   zhuo  tuo   ji    pang  zhong yi    wang  che   bi    di    ling  fu    wang  zheng cu    wang  jing  dai   xi    xun   hen   yang  huai  lu:   hou   wang  cheng zhi   xu    jing  tu    cong  none  lai   cong  de    pai   xi    none  qi    chang zhi   cong  zhou  lai   yu    xie   jie   jian  chi   jia   bian  huang fu    xun   wei   pang  yao   wei   xi    zheng piao  chi   de    zheng zhi   bie   de    chong che   jiao  wei   jiao  hui   mei   long  xiang bao   qu    xin   xin   bi    yi    le    ren   dao   ding  gai   ji    ren   ren   chan  tan   te    te    gan   qi    dai   cun   zhi   wang  mang  xi    fan   ying  tian  min   min   zhong chong wu    ji    wu    xi    ye    you   wan   zong  zhong kuai  yu    bian  zhi   chi   cui   chen  tai   tun   qian  nian  hun   xiong niu   wang  xian  xin   kang  hu    kai   fen   huai  tai   song  wu    ou    chang chuangju    yi    bao   chao  min   pi    zuo   zen   yang  kou   ban   nu    nao   zheng pa    bu    tie   hu    hu    ju    da    lian  si    zhou  di    dai   yi    tu    you   fu    ji    peng  xing  yuan  ni    guai  fu    xi    bi    you   qie   xuan  zong  bing  huang xu    chu   pi    xi    xi    tan   none  zong  dui   none  none  yi    chi   nen   xun   shi   xi    lao   heng  kuang mou   zhi   xie   lian  tiao  huang die   hao   kong  gui   heng  xi    xiao  shu   sai   hu    qiu   yang  hui   hui   chi   jia   yi    xiong guai  lin   hui   zi    xu    chi   xiang nu:   hen   en    ke    dong  tian  gong  quan  xi    qia   yue   peng  ken   de    hui   e     none  tong  yan   kai   ce    nao   yun   mang  yong  yong  juan  mang  kun   qiao  yue   yu    yu    jie   xi    zhe   lin   ti    han   hao   qie   ti    bu    yi    qian  hui   xi    bei   man   yi    heng  song  quan  cheng kui   wu    wu    you   li    liang huan  cong  yi    yue   li    nin   nao   e     que   xuan  qian  wu    min   cong  fei   bei   de    cui   chang men   li    ji    guan  guan  xing  dao   qi    kong  tian  lun   xi    kan   kun   ni    qing  chou  dun   guo   chan  jing  wan   yuan  jin   ji    lin   yu    huo   he    quan  yan   ti    ti    nie   wang  chuo  hu    hun   xi    chang xin   wei   hui   e     rui   zong  jian  yong  dian  ju    can   cheng de    bei   qie   can   dan   guan  duo   nao   yun   xiang zhui  die   huang chun  qiong re    xing  ce    bian  hun   zong  ti    qiao  chou  bei   xuan  wei   ge    qian  wei   yu    yu    bi    xuan  huan\")\n                .append(\"  min   bi    yi    mian  yong  kai   dang  yin   e     chen  mou   qia   ke    yu    ai    qie   yan   nuo   gan   yun   zong  sai   leng  fen   none  kui   kui   que   gong  yun   su    su    qi    yao   song  huang none  gu    ju    chuangta    xie   kai   zheng yong  cao   sun   shen  bo    kai   yuan  xie   hun   yong  yang  li    sao   tao   yin   ci    xu    qian  tai   huang yun   shen  ming  none  she   cong  piao  mo    mu    guo   chi   can   can   can   cui   min   ni    zhang tong  ao    shuangman   guan  que   zao   jiu   hui   kai   lian  ou    song  jin   yin   lu:   shang wei   tuan  man   qian  zhe   yong  qing  kang  di    zhi   lu:   juan  qi    qi    yu    ping  liao  zong  you   chuangzhi   tong  cheng qi    qu    peng  bei   bie   chun  jiao  zeng  chi   lian  ping  kui   hui   qiao  cheng yin   yin   xi    xi    dan   tan   duo   dui   dui   su    jue   ce    xiao  fan   fen   lao   lao   chong han   qi    xian  min   jing  liao  wu    can   jue   chou  xian  tan   sheng pi    yi    chu   xian  nao   dan   tan   jing  song  han   jiao  wei   huan  dong  qin   qin   qu    cao   ken   xie   ying  ao    mao   yi    lin   se    jun   huai  men   lan   ai    lin   yan   gua   xia   chi   yu    yin   dai   meng  ai    meng  dui   qi    mo    lan   men   chou  zhi   nuo   nuo   yan   yang  bo    zhi   xing  kuang you   fu    liu   mie   cheng none  chan  meng  lan   huai  xuan  rang  chan  ji    ju    huan  she   yi    lian  nan   mi    tang  jue   gang  gang  zhuangge    yue   wu    jian  xu    shu   rong  xi    cheng wo    jie   ge    jian  qiang huo   qiang zhan  dong  qi    jia   die   cai   jia   ji    shi   kan   ji    kui   gai   deng  zhan  chuangge    jian  jie   yu    jian  yan   lu    xi    zhan  xi    xi    chuo  dai   qu    hu    hu    hu    e     shi   li    mao   hu    li    fang  suo   bian  dian  jiong shang yi    yi    shan  hu    fei   yan   shou  shou  cai   zha   qiu   le    pu    ba    da    reng  fu    none  zai   tuo   zhang diao  kang  yu    ku    han   shen  cha   chi   gu    kou   wu    tuo   qian  zhi   cha   kuo   men   sao   yang  niu   ban   che   rao   xi    qian  ban   jia   yu    fu    ao    xi    pi    zhi   zi    e     dun   zhao  cheng ji    yan   kuang bian  chao  ju    wen   hu    yue   jue   ba    qin   zhen  zheng yun   wan   na    yi    shu   zhua  pou   tou   dou   kang  zhe   pou   fu    pao   ba    ao    ze    tuan  kou   lun   qiang none  hu    bao   bing  zhi   peng  tan   pu    pi    tai   yao   zhen  zha   yang  bao   he    ni    yi    di    chi   pi    za    mo    mo    chen  ya    chou  qu    min   chu   jia   fu    zha   zhu   dan   chai  mu    nian  la    fu    pao   ban   pai   lin   na    guai  qian  ju    tuo   ba    tuo   tuo   ao    ju    zhuo  pan   zhao  bai   bai   di    ni    ju    kuo   long  jian  qia   yong  lan   ning  bo    ze    qian  hen   kuo   shi   jie   zheng nin   gong  gong  quan  shuan tun   zan   kao   chi   xie   ce    hui   pin   zhuai shi   na    bo    chi   gua   zhi   kuo   duo   duo   zhi   qie   an    nong  zhen  ge    jiao  kua   dong  ru    tiao  lie   zha   lu:   die   wa    jue   none  ju    zhi   luan  ya    wo    ta    xie   nao   dang  jiao  zheng ji    hui   xian  none  ai    tuo   nuo   cuo   bo    geng  ti    zhen  cheng suo   suo   keng  mei   long  ju    peng  jian  yi    ting  shan  nuo   wan   xie   cha   feng  jiao  wu    jun   jiu   tong  kun   huo   tu    zhuo  pou   lu:   ba    han   shao  nie   juan  she   shu   ye    jue   bu    huan  bu    jun   yi    zhai  lu:   sou   tuo   lao   sun   bang  jian  huan  dao   none  wan   qin   peng  she   lie   min   men   fu    bai   ju    dao   wo    ai    juan  yue   zong  chen  chui  jie   tu    ben   na    nian  nuo   zu    wo    xi    xian  cheng dian  sao   lun   qing  gang  duo   shou  diao  pou   di    zhang gun   ji    tao   qia   qi    pai   shu   qian  ling  ye    ya    jue   zheng liang gua   yi    huo   shan  ding  lu:e  cai   tan   che   bing  jie   ti    kong  tui   yan   cuo   zou   ju    tian  qian  ken   bai   shou  jie   lu    guai  none  none  zhi   dan   none  chan  sao   guan  peng  yuan  nuo   jian  zheng jiu   jian  yu    ya\")\n                .append(\"n   kui   nan   hong  rou   pi    wei   sai   zou   xuan  miao  ti    nie   cha   shi   zong  zhen  yi    shun  heng  bian  yang  huan  yan   zan   an    xu    ya    wo    ke    chuai ji    ti    la    la    cheng kai   jiu   jiu   tu    jie   hui   geng  chong shuo  she   xie   yuan  qian  ye    cha   zha   bei   yao   none  none  lan   wen   qin   chan  ge    lou   zong  geng  jiao  gou   qin   yong  que   chou  chuai zhan  sun   sun   bo    chu   rong  bang  cuo   sao   ke    yao   dao   zhi   nu    xie   jian  sou   qiu   gao   xian  shuo  sang  jin   mie   e     chui  nuo   shan  ta    jie   tang  pan   ban   da    li    tao   hu    zhi   wa    xia   qian  wen   qiang chen  zhen  e     xie   nuo   quan  cha   zha   ge    wu    en    she   gong  she   shu   bai   yao   bin   sou   tan   sha   chan  suo   liao  chong chuangguo   bing  feng  shuai di    qi    none  zhai  lian  cheng chi   guan  lu    luo   lou   zong  gai   hu    zha   chuangtang  hua   cui   nai   mo    jiang gui   ying  zhi   ao    zhi   chi   man   shan  kou   shu   suo   tuan  zhao  mo    mo    zhe   chan  keng  biao  jiang yin   gou   qian  liao  ji    ying  jue   pie   pie   lao   dun   xian  ruan  kui   zan   yi    xian  cheng cheng sa    nao   heng  si    han   huang da    zun   nian  lin   zheng hui   zhuangjiao  ji    cao   dan   dan   che   bo    che   jue   xiao  liao  ben   fu    qiao  bo    cuo   zhuo  zhuan tuo   pu    qin   dun   nian  none  xie   lu    jiao  cuan  ta    han   qiao  zhua  jian  gan   yong  lei   kuo   lu    shan  zhuo  ze    pu    chuo  ji    dang  se    cao   qing  jing  huan  jie   qin   kuai  dan   xie   ge    pi    bo    ao    ju    ye    none  none  sou   mi    ji    tai   zhuo  dao   xing  lan   ca    ju    ye    ru    ye    ye    ni    huo   ji    bin   ning  ge    zhi   jie   kuo   mo    jian  xie   lie   tan   bai   sou   lu    lu:e  rao   zhi   pan   yang  lei   sa    shu   zan   nian  xian  jun   huo   lu:e  la    han   ying  lu    long  qian  qian  zan   qian  lan   san   ying  mei   rang  chan  none  cuan  xie   she   luo   jun   mi    li    zan   luan  tan   zuan  li    dian  wa    dang  jiao  jue   lan   li    nang  zhi   gui   gui   qi    xin   po    po    shou  kao   you   gai   gai   gong  gan   ban   fang  zheng bo    dian  kou   min   wu    gu    ge    ce    xiao  mi    chu   ge    di    xu    jiao  min   chen  jiu   shen  duo   yu    chi   ao    bai   xu    jiao  duo   lian  nie   bi    chang dian  duo   yi    gan   san   ke    yan   dun   qi    dou   xiao  duo   jiao  jing  yang  xia   hun   shu   ai    qiao  ai    zheng di    zhen  fu    shu   liao  qu    xiong xi    jiao  none  qiao  zhuo  yi    lian  bi    li    xue   xiao  wen   xue   qi    qi    zhai  bin   jue   zhai  lang  fei   ban   ban   lan   yu    lan   wei   dou   sheng liao  jia   hu    xie   jia   yu    zhen  jiao  wo    tiao  dou   jin   chi   yin   fu    qiang zhan  qu    zhuo  zhan  duan  zhuo  si    xin   zhuo  zhuo  qin   lin   zhuo  chu   duan  zhu   fang  xie   hang  wu    shi   pei   you   none  pang  qi    zhan  mao   lu:   pei   pi    liu   fu    fang  xuan  jing  jing  ni    zu    zhao  yi    liu   shao  jian  none  yi    qi    zhi   fan   piao  fan   zhan  guai  sui   yu    wu    ji    ji    ji    huo   ri    dan   jiu   zhi   zao   xie   tiao  xun   xu    ga    la    gan   han   tai   di    xu    chan  shi   kuang yang  shi   wang  min   min   tun   chun  wu    yun   bei   ang   ze    ban   jie   kun   sheng hu    fang  hao   gui   chang xuan  ming  hun   fen   qin   hu    yi    xi    xin   yan   ze    fang  tan   shen  ju    yang  zan   bing  xing  ying  xuan  pei   zhen  ling  chun  hao   mei   zuo   mo    bian  xu    hun   zhao  zong  shi   shi   yu    fei   die   mao   ni    chang wen   dong  ai    bing  ang   zhou  long  xian  kuang tiao  chao  shi   huang huang xuan  kui   xu    jiao  jin   zhi   jin   shang tong  hong  yan   gai   xiang shai  xiao  ye    yun   hui   han   han   jun   wan   xian  kun   zhou  xi    sheng sheng bu    zhe   zhe   wu    han   hui   hao   chen  wan   tian  zhuo  zui   zhou  pu    jing  xi    shan  yi    xi    qing  qi    jing  gui   zhen  yi    zhi   an    wan   lin   \")\n                .append(\"liang chang wang  xiao  zan   none  xuan  geng  yi    xia   yun   hui   fu    min   kui   he    ying  du    wei   shu   qing  mao   nan   jian  nuan  an    yang  chun  yao   suo   pu    ming  jiao  kai   gao   weng  chang qi    hao   yan   li    ai    ji    gui   men   zan   xie   hao   mu    mo    cong  ni    zhang hui   bao   han   xuan  chuan liao  xian  dan   jing  pie   lin   tun   xi    yi    ji    kuang dai   ye    ye    li    tan   tong  xiao  fei   qin   zhao  hao   yi    xiang xing  sen   jiao  bao   jing  none  ai    ye    ru    shu   meng  xun   yao   pu    li    chen  kuang die   none  yan   huo   lu    xi    rong  long  nang  luo   luan  shai  tang  yan   chu   yue   yue   qu    ye    geng  zhuai hu    he    shu   cao   cao   sheng man   ceng  ceng  ti    zui   can   xu    hui   yin   qie   fen   pi    yue   you   ruan  peng  ban   fu    ling  fei   qu    none  nu:   tiao  shuo  zhen  lang  lang  juan  ming  huang wang  tun   chao  ji    qi    ying  zong  wang  tong  lang  none  meng  long  mu    deng  wei   mo    ben   zha   zhu   shu   none  zhu   ren   ba    po    duo   duo   dao   li    qiu   ji    jiu   bi    xiu   ting  ci    sha   none  za    quan  qian  yu    gan   wu    cha   shan  xun   fan   wu    zi    li    xing  cai   cun   ren   shao  zhe   di    zhang mang  chi   yi    gu    gong  du    yi    qi    shu   gang  tiao  none  none  none  lai   shan  mang  yang  ma    miao  si    yuan  hang  fei   bei   jie   dong  gao   yao   xian  chu   chun  pa    shu   hua   xin   chou  zhu   chou  song  ban   song  ji    yue   yun   gou   ji    mao   pi    bi    wang  ang   fang  fen   yi    fu    nan   xi    hu    ya    dou   xun   zhen  yao   lin   rui   e     mei   zhao  guo   zhi   zong  yun   none  dou   shu   zao   none  li    lu    jian  cheng song  qiang feng  nan   xiao  xian  ku    ping  tai   xi    zhi   guai  xiao  jia   jia   gou   bao   mo    yi    ye    sang  shi   nie   bi    tuo   yi    ling  bing  ni    la    he    ban   fan   zhong dai   ci    yang  fu    bo    mou   gan   qi    ran   rou   mao   zhao  song  zhe   xia   you   shen  ju    tuo   zuo   nan   ning  yong  di    zhi   zha   cha   dan   gu    none  jiu   ao    fu    jian  bo    duo   ke    nai   zhu   bi    liu   chai  zha   si    zhu   pei   shi   guai  cha   yao   cheng jiu   shi   zhi   liu   mei   none  rong  zha   none  biao  zhan  zhi   long  dong  lu    none  li    lan   yong  shu   xun   shuan qi    zhen  qi    li    chi   xiang zhen  li    su    gua   kan   bing  ren   xiao  bo    ren   bing  zi    chou  yi    ci    xu    zhu   jian  zui   er    er    yu    fa    gong  kao   lao   zhan  li    none  yang  he    gen   zhi   chi   ge    zai   luan  fa    jie   heng  gui   tao   guang wei   kuang ru    an    an    juan  yi    zhuo  ku    zhi   qiong tong  sang  sang  huan  jie   jiu   xue   duo   zhui  yu    zan   none  ying  none  none  zhan  ya    rao   zhen  dang  qi    qiao  hua   gui   jiang zhuangxun   suo   suo   zhen  bei   ting  kuo   jing  bo    ben   fu    rui   tong  jue   xi    lang  liu   feng  qi    wen   jun   gan   cu    liang qiu   ting  you   mei   bang  long  peng  zhuangdi    xuan  tu    zao   ao    gu    bi    di    han   zi    zhi   ren   bei   geng  jian  huan  wan   nuo   jia   tiao  ji    xiao  lu:   kuan  shao  cen   fen   song  meng  wu    li    li    dou   cen   ying  suo   ju    ti    xie   kun   zhuo  shu   chan  fan   wei   jing  li    bing  none  none  tao   zhi   lai   lian  jian  zhuo  ling  li    qi    bing  lun   cong  qian  mian  qi    qi    cai   gun   chan  de    fei   pai   bang  pou   hun   zong  cheng zao   ji    li    peng  yu    yu    gu    hun   dong  tang  gang  wang  di    xi    fan   cheng zhan  qi    yuan  yan   yu    quan  yi    sen   ren   chui  leng  qi    zhuo  fu    ke    lai   zou   zou   zhao  guan  fen   fen   chen  qiong nie   wan   guo   lu    hao   jie   yi    chou  ju    ju    cheng zuo   liang qiang zhi   zhui  ya    ju    bei   jiao  zhuo  zi    bin   peng  ding  chu   shan  none  none  jian  gui   xi    du    qian  none  kui   none  luo   zhi   none  none  none  none  peng  shan  none  tuo   sen   duo   ye    fu    wei   wei   duan  jia   zong\")\n                .append(\"  jian  yi    shen  xi    yan   yan   chuan zhan  chun  yu    he    zha   wo    bian  bi    yao   huo   xu    ruo   yang  la    yan   ben   hun   kui   jie   kui   si    feng  xie   tuo   ji    jian  mu    mao   chu   hu    hu    lian  leng  ting  nan   yu    you   mei   song  xuan  xuan  ying  zhen  pian  die   ji    jie   ye    chu   shun  yu    cou   wei   mei   di    ji    jie   kai   qiu   ying  rou   heng  lou   le    none  gui   pin   none  gai   tan   lan   yun   yu    chen  lu:   ju    none  none  none  xie   jia   yi    zhan  fu    nuo   mi    lang  rong  gu    jian  ju    ta    yao   zhen  bang  sha   yuan  zi    ming  su    jia   yao   jie   huang gan   fei   zha   qian  ma    sun   yuan  xie   rong  shi   zhi   cui   yun   ting  liu   rong  tang  que   zhai  si    sheng ta    ke    xi    gu    qi    kao   gao   sun   pan   tao   ge    xun   dian  nou   ji    shuo  gou   chui  qiang cha   qian  huai  mei   xu    gang  gao   zhuo  tuo   qiao  yang  dian  jia   jian  zui   none  long  bin   zhu   none  xi    qi    lian  hui   yong  qian  guo   gai   gai   tuan  hua   qi    sen   cui   beng  you   hu    jiang hu    huan  kui   yi    yi    gao   kang  gui   gui   cao   man   jin   di    zhuangle    lang  chen  cong  li    xiu   qing  shuangfan   tong  guan  ji    suo   lei   lu    liang mi    lou   chao  su    ke    chu   tang  biao  lu    jiu   shu   zha   shu   zhang men   mo    niao  yang  tiao  peng  zhu   sha   xi    quan  heng  jian  cong  none  none  qiang none  ying  er    xin   zhi   qiao  zui   cong  pu    shu   hua   kui   zhen  zun   yue   zhan  xi    xun   dian  fa    gan   mo    wu    qiao  rao   lin   liu   qiao  xian  run   fan   zhan  tuo   lao   yun   shun  tui   cheng tang  meng  ju    cheng su    jue   jue   tan   hui   ji    nuo   xiang tuo   ning  rui   zhu   tong  zeng  fen   qiong ran   heng  cen   gu    liu   lao   gao   chu   none  none  none  none  ji    dou   none  lu    none  none  yuan  ta    shu   jiang tan   lin   nong  yin   xi    sui   shan  zui   xuan  cheng gan   ju    zui   yi    qin   pu    yan   lei   feng  hui   dang  ji    sui   bo    bi    ding  chu   zhua  gui   ji    jia   jia   qing  zhe   jian  qiang dao   yi    biao  song  she   lin   li    cha   meng  yin   tao   tai   mian  qi    none  bin   huo   ji    qian  mi    ning  yi    gao   jian  yin   er    qing  yan   qi    mi    zhao  gui   chun  ji    kui   po    deng  chu   none  mian  you   zhi   guang qian  lei   lei   sa    lu    none  cuan  lu:   mie   hui   ou    lu:   zhi   gao   du    yuan  li    fei   zhu   sou   lian  none  chu   none  zhu   lu    yan   li    zhu   chen  jie   e     su    huai  nie   yu    long  lai   none  xian  none  ju    xiao  ling  ying  jian  yin   you   ying  xiang nong  bo    chan  lan   ju    shuangshe   wei   cong  quan  qu    none  none  yu    luo   li    zan   luan  dang  jue   none  lan   lan   zhu   lei   li    ba    nang  yu    ling  none  qian  ci    huan  xin   yu    yu    qian  ou    xu    chao  chu   qi    kai   yi    jue   xi    xu    xia   yu    kuai  lang  kuan  shuo  xi    e     yi    qi    hu    chi   qin   kuan  kan   kuan  kan   chuan sha   none  yin   xin   xie   yu    qian  xiao  yi    ge    wu    tan   jin   ou    hu    ti    huan  xu    pen   xi    xiao  hu    she   none  lian  chu   yi    kan   yu    chuo  huan  zhi   zheng ci    bu    wu    qi    bu    bu    wai   ju    qian  chi   se    chi   se    zhong sui   sui   li    cuo   yu    li    gui   dai   dai   si    jian  zhe   mo    mo    yao   mo    cu    yang  tian  sheng dai   shang xu    xun   shu   can   jue   piao  qia   qiu   su    qing  yun   lian  yi    fou   zhi   ye    can   hun   dan   ji    ye    none  yun   wen   chou  bin   ti    jin   shang yin   diao  cu    hui   cuan  yi    dan   du    jiang lian  bin   du    jian  jian  shu   ou    duan  zhu   yin   qing  yi    sha   ke    ke    yao   xun   dian  hui   hui   gu    que   ji    yi    ou    hui   duan  yi    xiao  wu    guan  mu    mei   mei   ai    zuo   du    yu    bi    bi    bi    pi    pi    bi    chan  mao   none  none  pi    none  jia   zhan  sai   mu    tuo   xun   er    rong  xian  ju    mu    hao   qiu   dou   none  ta\")\n                .append(\"n   pei   ju    duo   cui   bi    san   none  mao   sui   shu   yu    tuo   he    jian  ta    san   lu:   mu    li    tong  rong  chang pu    lu    zhan  sao   zhan  meng  lu    qu    die   shi   di    min   jue   mang  qi    pie   nai   qi    dao   xian  chuan fen   ri    nei   none  fu    shen  dong  qing  qi    yin   xi    hai   yang  an    ya    ke    qing  ya    dong  dan   lu:   qing  yang  yun   yun   shui  shui  zheng bing  yong  dang  shui  le    ni    tun   fan   gui   ting  zhi   qiu   bin   ze    mian  cuan  hui   diao  han   cha   zhuo  chuan wan   fan   dai   xi    tuo   mang  qiu   qi    shan  pai   han   qian  wu    wu    xun   si    ru    gong  jiang chi   wu    none  none  tang  zhi   chi   qian  mi    gu    wang  qing  jing  rui   jun   hong  tai   quan  ji    bian  bian  gan   wen   zhong fang  xiong jue   hu    none  qi    fen   xu    xu    qin   yi    wo    yun   yuan  hang  yan   shen  chen  dan   you   dun   hu    huo   qi    mu    rou   mei   ta    mian  wu    chong tian  bi    sha   zhi   pei   pan   zhui  za    gou   liu   mei   ze    feng  ou    li    lun   cang  feng  wei   hu    mo    mei   shu   ju    zan   tuo   tuo   duo   he    li    mi    yi    fu    fei   you   tian  zhi   zhao  gu    zhan  yan   si    kuang jiong ju    xie   qiu   yi    jia   zhong quan  bo    hui   mi    ben   zhuo  chu   le    you   gu    hong  gan   fa    mao   si    hu    ping  ci    fan   zhi   su    ning  cheng ling  pao   bo    qi    si    ni    ju    yue   zhu   sheng lei   xuan  xue   fu    pan   min   tai   yang  ji    yong  guan  beng  xue   long  lu    dan   luo   xie   po    ze    jing  yin   zhou  jie   yi    hui   hui   zui   cheng yin   wei   hou   jian  yang  lie   si    ji    er    xing  fu    sa    zi    zhi   yin   wu    xi    kao   zhu   jiang luo   none  an    dong  yi    mou   lei   yi    mi    quan  jin   po    wei   xiao  xie   hong  xu    su    kuang tao   qie   ju    er    zhou  ru    ping  xun   xiong zhi   guang huan  ming  huo   wa    qia   pai   wu    qu    liu   yi    jia   jing  qian  jiang jiao  zhen  shi   zhuo  ce    none  hui   ji    liu   chan  hun   hu    nong  xun   jin   lie   qiu   wei   zhe   jun   han   bang  mang  zhuo  you   xi    bo    dou   huan  hong  yi    pu    ying  lan   hao   lang  han   li    geng  fu    wu    li    chun  feng  yi    yu    tong  lao   hai   jin   jia   chong weng  mei   sui   cheng pei   xian  shen  tu    kun   pin   nie   han   jing  xiao  she   nian  tu    yong  xiao  xian  ting  e     su    tun   juan  cen   ti    li    shui  si    lei   shui  tao   du    lao   lai   lian  wei   wo    yun   huan  di    none  run   jian  zhang se    fu    guan  xing  shou  shuan ya    chuo  zhang ye    kong  wan   han   tuo   dong  he    wo    ju    gan   liang hun   ta    zhuo  dian  qie   de    juan  zi    xi    xiao  qi    gu    guo   han   lin   tang  zhou  peng  hao   chang shu   qi    fang  chi   lu    nao   ju    tao   cong  lei   zhi   peng  fei   song  tian  pi    dan   yu    ni    yu    lu    gan   mi    jing  ling  lun   yin   cui   qu    huai  yu    nian  shen  piao  chun  hu    yuan  lai   hun   qing  yan   qian  tian  miao  zhi   yin   mi    ben   yuan  wen   re    fei   qing  yuan  ke    ji    she   yuan  se    lu    zi    du    none  jian  mian  pi    xi    yu    yuan  shen  shen  rou   huan  zhu   jian  nuan  yu    qiu   ting  qu    du    feng  zha   bo    wo    wo    di    wei   wen   ru    xie   ce    wei   ge    gang  yan   hong  xuan  mi    ke    mao   ying  yan   you   hong  miao  xing  mei   zai   hun   nai   kui   shi   e     pai   mei   lian  qi    qi    mei   tian  cou   wei   can   tuan  mian  xu    mo    xu    ji    pen   jian  jian  hu    feng  xiang yi    yin   zhan  shi   jie   zhen  huang tan   yu    bi    min   shi   tu    sheng yong  ju    zhong none  qiu   jiao  none  yin   tang  long  huo   yuan  nan   ban   you   quan  chui  liang chan  yan   chun  nie   zi    wan   shi   man   ying  la    kui   none  jian  xu    lou   gui   gai   none  none  po    jin   gui   tang  yuan  suo   yuan  lian  yao   meng  zhun  sheng ke    tai   ta    wa    liu   gou   sao   ming  zha   shi   yi    lun   ma    pu    wei   li    \")\n                .append(\"cai   wu    xi    wen   qiang ce    shi   su    yi    zhen  sou   yun   xiu   yin   rong  hun   su    su    ni    ta    shi   ru    wei   pan   chu   chu   pang  weng  cang  mie   he    dian  hao   huang xi    zi    di    zhi   ying  fu    jie   hua   ge    zi    tao   teng  sui   bi    jiao  hui   gun   yin   gao   long  zhi   yan   she   man   ying  chun  lu:   lan   luan  xiao  bin   tan   yu    xiu   hu    bi    biao  zhi   jiang kou   shen  shang di    mi    ao    lu    hu    hu    you   chan  fan   yong  gun   man   qing  yu    piao  ji    ya    jiao  qi    xi    ji    lu    lu:   long  jin   guo   cong  lou   zhi   gai   qiang li    yan   cao   jiao  cong  chun  tuan  ou    teng  ye    xi    mi    tang  mo    shang han   lian  lan   wa    li    qian  feng  xuan  yi    man   zi    mang  kang  luo   peng  shu   zhang zhang chong xu    huan  kuo   jian  yan   chuangliao  cui   ti    yang  jiang cong  ying  hong  xiu   shu   guan  ying  xiao  none  none  xu    lian  zhi   wei   pi    yu    jiao  po    xiang hui   jie   wu    pa    ji    pan   wei   xiao  qian  qian  xi    lu    xi    sun   dun   huang min   run   su    liao  zhen  zhong yi    di    wan   dan   tan   chao  xun   kui   none  shao  tu    zhu   sa    hei   bi    shan  chan  chan  shu   tong  pu    lin   wei   se    se    cheng jiong cheng hua   jiao  lao   che   gan   cun   heng  si    shu   peng  han   yun   liu   hong  fu    hao   he    xian  jian  shan  xi    ao    lu    lan   none  yu    lin   min   zao   dang  huan  ze    xie   yu    li    shi   xue   ling  man   zi    yong  kuai  can   lian  dian  ye    ao    huan  lian  chan  man   dan   dan   yi    sui   pi    ju    ta    qin   ji    zhuo  lian  nong  guo   jin   fen   se    ji    sui   hui   chu   ta    song  ding  se    zhu   lai   bin   lian  mi    shi   shu   mi    ning  ying  ying  meng  jin   qi    bi    ji    hao   ru    zui   wo    tao   yin   yin   dui   ci    huo   jing  lan   jun   ai    pu    zhuo  wei   bin   gu    qian  xing  bin   kuo   fei   none  bin   jian  dui   luo   luo   lu:   li    you   yang  lu    si    jie   ying  du    wang  hui   xie   pan   shen  biao  chan  mie   liu   jian  pu    se    cheng gu    bin   huo   xian  lu    qin   han   ying  rong  li    jing  xiao  ying  sui   wei   xie   huai  hao   zhu   long  lai   dui   fan   hu    lai   none  none  ying  mi    ji    lian  jian  ying  fen   lin   yi    jian  yue   chan  dai   rang  jian  lan   fan   shuangyuan  zhuo  feng  she   lei   lan   cong  qu    yong  qian  fa    guan  que   yan   hao   none  sa    zan   luan  yan   li    mi    dan   tan   dang  jiao  chan  none  hao   ba    zhu   lan   lan   nang  wan   luan  quan  xian  yan   gan   yan   yu    huo   biao  mie   guang deng  hui   xiao  xiao  none  hong  ling  zao   zhuan jiu   zha   xie   chi   zhuo  zai   zai   can   yang  qi    zhong fen   niu   gui   wen   po    yi    lu    chui  pi    kai   pan   yan   kai   pang  mu    chao  liao  gui   kang  dun   guang xin   zhi   guang xin   wei   qiang bian  da    xia   zheng zhu   ke    zhao  fu    ba    duo   duo   ling  zhuo  xuan  ju    tan   pao   jiong pao   tai   tai   bing  yang  tong  han   zhu   zha   dian  wei   shi   lian  chi   ping  none  hu    shuo  lan   ting  jiao  xu    xing  quan  lie   huan  yang  xiao  xiu   xian  yin   wu    zhou  yao   shi   wei   tong  tong  zai   kai   hong  luo   xia   zhu   xuan  zheng po    yan   hui   guang zhe   hui   kao   none  fan   shao  ye    hui   none  tang  jin   re    none  xi    fu    jiong che   pu    jing  zhuo  ting  wan   hai   peng  lang  shan  hu    feng  chi   rong  hu    none  shu   lang  xun   xun   jue   xiao  xi    yan   han   zhuangqu    di    xie   qi    wu    none  none  han   yan   huan  men   ju    dao   bei   fen   lin   kun   hun   chun  xi    cui   wu    hong  ju    fu    yue   jiao  cong  feng  ping  qiong cui   xi    qiong xin   zhuo  yan   yan   yi    jue   yu    gang  ran   pi    yan   none  sheng chang shao  none  none  none  none  chen  he    kui   zhong duan  ya    hui   feng  lian  xuan  xing  huang jiao  jian  bi    ying  zhu   wei   tuan  tian  xi    nuan  nuan  chan  yan   jiong jiong yu    mei   sha   wu    ye  \")\n                .append(\"  xin   qiong rou   mei   huan  xu    zhao  wei   fan   qiu   sui   yang  lie   zhu   none  gao   gua   bao   hu    yun   xia   none  none  bian  wei   tui   tang  chao  shan  yun   bo    huang xie   xi    wu    xi    yun   he    he    xi    yun   xiong nai   kao   none  yao   xun   ming  lian  ying  wen   rong  none  none  qiang liu   xi    bi    biao  cong  lu    jian  shu   yi    lou   feng  sui   yi    teng  jue   zong  yun   hu    yi    zhi   ao    wei   liao  han   ou    re    jiong man   none  shang cuan  zeng  jian  xi    xi    xi    yi    xiao  chi   huang chan  ye    qian  ran   yan   xian  qiao  zun   deng  dun   shen  jiao  fen   si    liao  yu    lin   tong  shao  fen   fan   yan   xun   lan   mei   tang  yi    jing  men   none  none  ying  yu    yi    xue   lan   tai   zao   can   sui   xi    que   cong  lian  hui   zhu   xie   ling  wei   yi    xie   zhao  hui   none  none  lan   ru    xian  kao   xun   jin   chou  dao   yao   he    lan   biao  rong  li    mo    bao   ruo   di    lu:   ao    xun   kuang shuo  none  li    lu    jue   liao  yan   xi    xie   long  yan   none  rang  yue   lan   cong  jue   tong  guan  none  che   mi    tang  lan   zhu   lan   ling  cuan  yu    zhua  lan   pa    zheng pao   zhao  yuan  ai    wei   none  jue   jue   fu    ye    ba    die   ye    yao   zu    shuanger    pan   chuan ke    zang  zang  qiang die   qiang pian  ban   pan   shao  jian  pai   du    yong  tou   tou   bian  die   bang  bo    bang  you   none  du    ya    cheng niu   cheng pin   jiu   mou   ta    mu    lao   ren   mang  fang  mao   mu    ren   wu    yan   fa    bei   si    jian  gu    you   gu    sheng mu    di    qian  quan  quan  zi    te    xi    mang  keng  qian  wu    gu    xi    li    li    pou   ji    gang  zhi   ben   quan  run   du    ju    jia   jian  feng  pian  ke    ju    kao   chu   xi    bei   luo   jie   ma    san   wei   li    dun   tong  se    jiang xi    li    du    lie   pi    piao  bao   xi    chou  wei   kui   chou  quan  quan  ba    fan   qiu   bo    chai  chuo  an    jie   zhuangguang ma    you   kang  bo    hou   ya    han   huan  zhuangyun   kuang niu   di    qing  zhong yun   bei   pi    ju    ni    sheng pao   xia   tuo   hu    ling  fei   pi    ni    sheng you   gou   yue   ju    dan   bo    gu    xian  ning  huan  hen   jiao  he    zhao  ji    huan  shan  ta    rong  shou  tong  lao   du    xia   shi   kuai  zheng yu    sun   yu    bi    mang  xi    juan  li    xia   yin   suan  lang  bei   zhi   yan   sha   li    zhi   xian  jing  han   fei   yao   ba    qi    ni    biao  yin   li    lie   jian  qiang kun   yan   guo   zong  mi    chang yi    zhi   zheng ya    meng  cai   cu    she   lie   none  luo   hu    zong  hu    wei   feng  wo    yuan  xing  zhu   mao   wei   yuan  xian  tuan  ya    nao   xie   jia   hou   bian  you   you   mei   cha   yao   sun   bo    ming  hua   yuan  sou   ma    yuan  dai   yu    shi   hao   none  yi    zhen  chuanghao   man   jing  jiang mo    zhang chan  ao    ao    hao   cui   ben   jue   bi    bi    huang bu    lin   yu    tong  yao   liao  shuo  xiao  shou  none  xi    ge    juan  du    hui   kuai  xian  xie   ta    xian  xun   ning  bian  huo   nou   meng  lie   nao   guang shou  lu    ta    xian  mi    rang  huan  nao   luo   xian  qi    qu    xuan  miao  zi    lu:   lu    yu    su    wang  qiu   ga    ding  le    ba    ji    hong  di    chuan gan   jiu   yu    qi    yu    yang  ma    hong  wu    fu    min   jie   ya    bin   bian  beng  yue   jue   yun   jue   wan   jian  mei   dan   pi    wei   huan  xian  qiang ling  dai   yi    an    ping  dian  fu    xuan  xi    bo    ci    gou   jia   shao  po    ci    ke    ran   sheng shen  yi    zu    jia   min   shan  liu   bi    zhen  zhen  jue   fa    long  jin   jiao  jian  li    guang xian  zhou  gong  yan   xiu   yang  xu    luo   su    zhu   qin   ken   xun   bao   er    xiang yao   xia   heng  gui   chong xu    ban   pei   none  dang  ying  hun   wen   e     cheng ti    wu    wu    cheng jun   mei   bei   ting  xian  chuo  han   xuan  yan   qiu   quan  lang  li    xiu   fu    liu   ya    xi    ling  li    jin   lian  suo   suo   none  wan   dian  bing  zhan  cui   min   yu\")\n                .append(\"    ju    chen  lai   wen   sheng wei   dian  chu   zhuo  pei   cheng hu    qi    e     kun   chang qi    beng  wan   lu    cong  guan  yan   diao  bei   lin   qin   pi    pa    qiang zhuo  qin   fa    none  qiong du    jie   hun   yu    mao   mei   chun  xuan  ti    xing  dai   rou   min   zhen  wei   ruan  huan  xie   chuan jian  zhuan yang  lian  quan  xia   duan  yuan  ye    nao   hu    ying  yu    huang rui   se    liu   none  rong  suo   yao   wen   wu    jin   jin   ying  ma    tao   liu   tang  li    lang  gui   tian  qiang cuo   jue   zhao  yao   ai    bin   tu    chang kun   zhuan cong  jin   yi    cui   cong  qi    li    ying  suo   qiu   xuan  ao    lian  man   zhang yin   none  ying  wei   lu    wu    deng  none  zeng  xun   qu    dang  lin   liao  qiong su    huang gui   pu    jing  fan   jin   liu   ji    none  jing  ai    bi    can   qu    zao   dang  jiao  gun   tan   hui   huan  se    sui   tian  none  yu    jin   fu    bin   shu   wen   zui   lan   xi    ji    xuan  ruan  huo   gai   lei   du    li    zhi   rou   li    zan   qiong zhe   gui   sui   la    long  lu    li    zan   lan   ying  mi    xiang xi    guan  dao   zan   huan  gua   bao   die   pao   hu    zhi   piao  ban   rang  li    wa    none  jiang qian  ban   pen   fang  dan   weng  ou    none  none  none  hu    ling  yi    ping  ci    none  juan  chang chi   none  dang  meng  bu    chui  ping  bian  zhou  zhen  none  ci    ying  qi    xian  lou   di    ou    meng  zhuan beng  lin   zeng  wu    pi    dan   weng  ying  yan   gan   dai   shen  tian  tian  han   chang sheng qing  shen  chan  chan  rui   sheng su    shen  yong  shuai lu    fu    yong  beng  none  ning  tian  you   jia   shen  zha   dian  fu    nan   dian  ping  ding  hua   ting  quan  zai   meng  bi    qi    liu   xun   liu   chang mu    yun   fan   fu    geng  tian  jie   jie   quan  wei   fu    tian  mu    none  pan   jiang wa    da    nan   liu   ben   zhen  chu   mu    mu    ce    none  gai   bi    da    zhi   lu:e  qi    lu:e  pan   none  fan   hua   yu    yu    mu    jun   yi    liu   she   die   chou  hua   dang  chuo  ji    wan   jiang cheng chang tun   lei   ji    cha   liu   die   tuan  lin   jiang jiang chou  bo    die   die   pi    nie   dan   shu   shu   zhi   yi    chuangnai   ding  bi    jie   liao  gong  ge    jiu   zhou  xia   shan  xu    nu:e  li    yang  chen  you   ba    jie   jue   xi    xia   cui   bi    yi    li    zong  chuangfeng  zhu   pao   pi    gan   ke    ci    xie   qi    dan   zhen  fa    zhi   teng  ju    ji    fei   ju    dian  jia   xuan  zha   bing  nie   zheng yong  jing  quan  chong tong  yi    jie   wei   hui   duo   yang  chi   zhi   hen   ya    mei   dou   jing  xiao  tong  tu    mang  pi    xiao  suan  pu    li    zhi   cuo   duo   wu    sha   lao   shou  huan  xian  yi    peng  zhang guan  tan   fei   ma    lin   chi   ji    tian  an    chi   bi    bi    min   gu    dui   e     wei   yu    cui   ya    zhu   xi    dan   shen  zhong ji    yu    hou   feng  la    yang  shen  tu    yu    gua   wen   huan  ku    jia   yin   yi    lou   sao   jue   chi   xi    guan  yi    wen   ji    chuangban   lei   liu   chai  shou  nu:e  dian  da    bie   tan   zhang biao  shen  cu    luo   yi    zong  chou  zhang zhai  sou   suo   que   diao  lou   lou   mo    jin   yin   ying  huang fu    liao  long  qiao  liu   lao   xian  fei   dan   yin   he    ai    ban   xian  guan  guai  nong  yu    wei   yi    yong  pi    lei   li    shu   dan   lin   dian  lin   lai   bie   ji    chi   yang  xuan  jie   zheng none  li    huo   lai   ji    dian  xian  ying  yin   qu    yong  tan   dian  luo   luan  luan  bo    none  gui   po    fa    deng  fa    bai   bai   qie   bi    zao   zao   mao   de    pa    jie   huang gui   ci    ling  gao   mo    ji    jiao  peng  gao   ai    e     hao   han   bi    wan   chou  qian  xi    ai    jiong hao   huang hao   ze    cui   hao   xiao  ye    po    hao   jiao  ai    xing  huang li    piao  he    jiao  pi    gan   pao   zhou  jun   qiu   cun   que   zha   gu    jun   jun   zhou  zha   gu    zhan  du    min   qi    ying  yu    bei   zhao  zhong pen   he    ying  he    yi    bo    wan   he    ang   zhan  yan   jian  \")\n                .append(\"he    yu    kui   fan   gai   dao   pan   fu    qiu   sheng dao   lu    zhan  meng  lu    jin   xu    jian  pan   guan  an    lu    xu    zhou  dang  an    gu    li    mu    ding  gan   xu    mang  mang  zhi   qi    wan   tian  xiang dun   xin   xi    pan   feng  dun   min   ming  sheng shi   yun   mian  pan   fang  miao  dan   mei   mao   kan   xian  kou   shi   yang  zheng yao   shen  huo   da    zhen  kuang ju    shen  yi    sheng mei   mo    zhu   zhen  zhen  mian  di    yuan  die   yi    zi    zi    chao  zha   xuan  bing  mi    long  sui   tong  mi    die   yi    er    ming  xuan  chi   kuang juan  mou   zhen  tiao  yang  yan   mo    zhong mai   zhe   zheng mei   suo   shao  han   huan  di    cheng cuo   juan  e     wan   xian  xi    kun   lai   jian  shan  tian  hun   wan   ling  shi   qiong lie   ya    jing  zheng li    lai   sui   juan  shui  sui   du    pi    pi    mu    hun   ni    lu    gao   jie   cai   zhou  yu    hun   ma    xia   xing  hui   gun   none  chun  jian  mei   du    hou   xuan  ti    kui   gao   rui   mao   xu    fa    wen   miao  chou  kui   mi    weng  kou   dang  chen  ke    sou   xia   qiong mao   ming  man   shui  ze    zhang yi    diao  kou   mo    shun  cong  lou   chi   man   piao  cheng ji    meng  huan  run   pie   xi    qiao  pu    zhu   deng  shen  shun  liao  che   xian  kan   ye    xu    tong  wu    lin   kui   jian  ye    ai    hui   zhan  jian  gu    zhao  ju    wei   chou  ji    ning  xun   yao   huo   meng  mian  bin   mian  li    guang jue   xuan  mian  huo   lu    meng  long  guan  man   xi    chu   tang  kan   zhu   mao   jin   lin   yu    shuo  ce    jue   shi   yi    shen  zhi   hou   shen  ying  ju    zhou  jiao  cuo   duan  ai    jiao  zeng  huo   bai   shi   ding  qi    ji    zi    gan   wu    tuo   ku    qiang xi    fan   kuang dang  ma    sha   dan   jue   li    fu    min   nuo   hua   kang  zhi   qi    kan   jie   fen   e     ya    pi    zhe   yan   sui   zhuan che   dun   pan   yan   none  feng  fa    mo    zha   qu    yu    ke    tuo   tuo   di    zhai  zhen  e     fu    mu    zhu   la    bian  nu    ping  peng  ling  pao   le    po    bo    po    shen  za    ai    li    long  tong  none  li    kuang chu   keng  quan  zhu   kuang gui   e     nao   jia   lu    wei   ai    luo   ken   xing  yan   dong  peng  xi    none  hong  shuo  xia   qiao  none  wei   qiao  none  keng  xiao  que   chan  lang  hong  yu    xiao  xia   mang  long  none  che   che   wo    liu   ying  mang  que   yan   cuo   kun   yu    none  none  lu    chen  jian  none  song  zhuo  keng  peng  yan   zhui  kong  ceng  qi    zong  qing  lin   jun   bo    ding  min   diao  jian  he    liu   ai    sui   que   ling  bei   yin   dui   wu    qi    lun   wan   dian  gang  bei   qi    chen  ruan  yan   die   ding  zhou  tuo   jie   ying  bian  ke    bi    wei   shuo  zhen  duan  xia   dang  ti    nao   peng  jian  di    tan   cha   none  qi    none  feng  xuan  que   que   ma    gong  nian  su    e     ci    liu   si    tang  bang  hua   pi    wei   sang  lei   cuo   tian  xia   xi    lian  pan   wei   yun   dui   zhe   ke    la    none  qing  gun   zhuan chan  qi    ao    peng  lu    lu    kan   qiang chen  yin   lei   biao  qi    mo    qi    cui   zong  qing  chuo  none  ji    shan  lao   qu    zeng  deng  jian  xi    lin   ding  dian  huang pan   za    qiao  di    li    jian  jiao  xi    zhang qiao  dun   jian  yu    zhui  he    huo   zhai  lei   ke    chu   ji    que   dang  wo    jiang pi    pi    yu    pin   qi    ai    ke    jian  yu    ruan  meng  pao   zi    bo    none  mie   ca    xian  kuang lei   lei   zhi   li    li    fan   que   pao   ying  li    long  long  mo    bo    shuangguan  lan   zan   yan   shi   shi   li    reng  she   yue   si    qi    ta    ma    xie   yao   xian  zhi   qi    zhi   beng  shu   chong none  yi    shi   you   zhi   tiao  fu    fu    mi    zu    zhi   suan  mei   zuo   qu    hu    zhu   shen  sui   ci    chai  mi    lu:   yu    xiang wu    tiao  piao  zhu   gui   xia   zhi   ji    gao   zhen  gao   shui  jin   zhen  gai   kun   di    dao   huo   tao   qi    gu    guan  zui   ling  lu    bing  jin   dao   zhi   lu    shan  bei   zhe   hui   you   xi  \")\n                .append(\"  yin   zi    huo   zhen  fu    yuan  wu    xian  yang  ti    yi    mei   si    di    none  zhuo  zhen  yong  ji    gao   tang  chi   ma    ta    none  xuan  qi    yu    xi    ji    si    chan  xuan  hui   sui   li    nong  ni    dao   li    rang  yue   ti    zan   lei   rou   yu    yu    li    xie   qin   he    tu    xiu   si    ren   tu    zi    cha   gan   yi    xian  bing  nian  qiu   qiu   zhong fen   hao   yun   ke    miao  zhi   jing  bi    zhi   yu    mi    ku    ban   pi    ni    li    you   zu    pi    ba    ling  mo    cheng nian  qin   yang  zuo   zhi   zhi   shu   ju    zi    tai   ji    cheng tong  zhi   huo   he    yin   zi    zhi   jie   ren   du    yi    zhu   hui   nong  fu    xi    kao   lang  fu    ze    shui  lu:   kun   gan   jing  ti    cheng tu    shao  shui  ya    lun   lu    gu    zuo   ren   zhun  bang  bai   ji    zhi   zhi   kun   leng  peng  ke    bing  chou  zui   yu    su    none  none  yi    xi    bian  ji    fu    bi    nuo   jie   zhong zong  xu    cheng dao   wen   lian  zi    yu    ji    xu    zhen  zhi   dao   jia   ji    gao   gao   gu    rong  sui   none  ji    kang  mu    shan  men   zhi   ji    lu    su    ji    ying  wen   qiu   se    none  yi    huang qie   ji    sui   xiao  pu    jiao  zhuo  tong  none  lu:   sui   nong  se    hui   rang  nuo   yu    none  ji    tui   wen   cheng huo   gong  lu:   biao  none  rang  jue   li    zan   xue   wa    jiu   qiong xi    qiong kong  yu    sen   jing  yao   chuan zhun  tu    lao   qie   zhai  yao   bian  bao   yao   bing  yu    zhu   jiao  qiao  diao  wu    gui   yao   zhi   chuan yao   tiao  jiao  chuangjiong xiao  cheng kou   cuan  wo    dan   ku    ke    zhui  xu    su    none  kui   dou   none  yin   wo    wa    ya    yu    ju    qiong yao   yao   tiao  liao  yu    tian  diao  ju    liao  xi    wu    kui   chuangju    none  kuan  long  cheng cui   piao  zao   cuan  qiao  qiong dou   zao   zao   qie   li    chu   shi   fu    qian  chu   hong  qi    qian  gong  shi   shu   miao  ju    zhan  zhu   ling  long  bing  jing  jing  zhang yi    si    jun   hong  tong  song  jing  diao  yi    shu   jing  qu    jie   ping  duan  shao  zhuan ceng  deng  cun   huai  jing  kan   jing  zhu   zhu   le    peng  yu    chi   gan   mang  zhu   none  du    ji    xiao  ba    suan  ji    zhen  zhao  sun   ya    zhui  yuan  hu    gang  xiao  cen   pi    bi    jian  yi    dong  shan  sheng xia   di    zhu   na    chi   gu    li    qie   min   bao   tiao  si    fu    ce    ben   fa    da    zi    di    ling  ze    nu    fu    gou   fan   jia   ge    fan   shi   mao   po    none  jian  qiong long  none  bian  luo   gui   qu    chi   yin   yao   xian  bi    qiong gua   deng  jiao  jin   quan  sun   ru    fa    kuang zhu   tong  ji    da    hang  ce    zhong kou   lai   bi    shai  dang  zheng ce    fu    yun   tu    pa    li    lang  ju    guan  jian  han   tong  xia   zhi   cheng suan  shi   zhu   zuo   xiao  shao  ting  jia   yan   gao   kuai  gan   chou  kuang gang  yun   none  qian  xiao  jian  pu    lai   zou   bi    bi    bi    ge    chi   guai  yu    jian  zhao  gu    chi   zheng qing  sha   zhou  lu    bo    ji    lin   suan  jun   fu    zha   gu    kong  qian  qian  jun   chui  guan  yuan  ce    ju    bo    ze    qie   tuo   luo   dan   xiao  ruo   jian  none  bian  sun   xiang xian  ping  zhen  sheng hu    shi   zhu   yue   chun  fu    wu    dong  shuo  ji    jie   huang xing  mei   fan   chuan zhuan pian  feng  zhu   hong  qie   hou   qiu   miao  qian  none  kui   none  lou   yun   he    tang  yue   chou  gao   fei   ruo   zheng gou   nie   qian  xiao  cuan  gong  pang  du    li    bi    zhuo  chu   shai  chi   zhu   qiang long  lan   jian  bu    li    hui   bi    di    cong  yan   peng  sen   cuan  pai   piao  dou   yu    mie   zhuan ze    xi    guo   yi    hu    chan  kou   cu    ping  zao   ji    gui   su    lou   zha   lu    nian  suo   cuan  none  suo   le    duan  liang xiao  bo    mi    shai  dang  liao  dan   dian  fu    jian  min   kui   dai   qiao  deng  huang sun   lao   zan   xiao  lu    shi   zan   none  pai   qi    pai   gan   ju    du    lu    yan   bo    dang  sai   ke    gou   qian  lian  bu    zhou  lai   none  la\")\n                .append(\"n   kui   yu    yue   hao   zhen  tai   ti    mi    chou  ji    none  qi    teng  zhuan zhou  fan   sou   zhou  qian  kuo   teng  lu    lu    jian  tuo   ying  yu    lai   long  none  lian  lan   qian  yue   zhong qu    lian  bian  duan  zuan  li    shai  luo   ying  yue   zhuo  xu    mi    di    fan   shen  zhe   shen  nu:   xie   lei   xian  zi    ni    cun   zhang qian  none  bi    ban   wu    sha   kang  rou   fen   bi    cui   yin   li    chi   tai   none  ba    li    gan   ju    po    mo    cu    zhan  zhou  li    su    tiao  li    xi    su    hong  tong  zi    ce    yue   zhou  lin   zhuangbai   none  fen   mian  qu    none  liang xian  fu    liang can   jing  li    yue   lu    ju    qi    cui   bai   chang lin   zong  jing  guo   none  san   san   tang  bian  rou   mian  hou   xu    zong  hu    jian  zan   ci    li    xie   fu    nuo   bei   gu    xiu   gao   tang  qiu   none  cao   zhuangtang  mi    san   fen   zao   kang  jiang mo    san   san   nuo   chi   liang jiang kuai  bo    huan  shu   zong  jian  nuo   tuan  nie   li    zuo   di    nie   tiao  lan   mi    mi    jiu   xi    gong  zheng jiu   you   ji    cha   zhou  xun   yue   hong  yu    he    wan   ren   wen   wen   qiu   na    zi    tou   niu   fou   jie   shu   chun  pi    yin   sha   hong  zhi   ji    fen   yun   ren   dan   jin   su    fang  suo   cui   jiu   zha   ba    jin   fu    zhi   qi    zi    chou  hong  zha   lei   xi    fu    xie   shen  bei   zhu   qu    ling  zhu   shao  gan   yang  fu    tuo   zhen  dai   chu   shi   zhong xian  zu    jiong ban   ju    pa    shu   zui   kuang jing  ren   heng  xie   jie   zhu   chou  gua   bai   jue   kuang hu    ci    geng  geng  tao   xie   ku    jiao  quan  gai   luo   xuan  beng  xian  fu    gei   tong  rong  tiao  yin   lei   xie   quan  xu    hai   die   tong  si    jiang xiang hui   jue   zhi   jian  juan  chi   mian  zhen  lu:   cheng qiu   shu   bang  tong  xiao  wan   qin   geng  xiu   ti    xiu   xie   hong  xi    fu    ting  sui   dui   kun   fu    jing  hu    zhi   yan   jiong feng  ji    xu    none  zong  lin   duo   li    lu:   liang chou  quan  shao  qi    qi    zhun  qi    wan   qian  xian  shou  wei   qi    tao   wan   gang  wang  beng  zhui  cai   guo   cui   lun   liu   qi    zhan  bei   chuo  ling  mian  qi    jie   tan   zong  gun   zou   yi    zi    xing  liang jin   fei   rui   min   yu    zong  fan   lu:   xu    none  shang none  xu    xiang jian  ke    xian  ruan  mian  ji    duan  zhong di    min   miao  yuan  xie   bao   si    qiu   bian  huan  geng  zong  mian  wei   fu    wei   yu    gou   miao  jie   lian  zong  bian  yun   yin   ti    gua   zhi   yun   cheng chan  dai   jia   yuan  zong  xu    sheng none  geng  none  ying  jin   yi    zhui  ni    bang  gu    pan   zhou  jian  cuo   quan  shuangyun   xia   shuai xi    rong  tao   fu    yun   zhen  gao   ru    hu    zai   teng  xian  su    zhen  zong  tao   huang cai   bi    feng  cu    li    suo   yin   xi    zong  lei   zhuan qian  man   zhi   lu:   mo    piao  lian  mi    xuan  zong  ji    shan  sui   fan   shuai beng  yi    sao   mou   zhou  qiang hun   xian  xi    none  xiu   ran   xuan  hui   qiao  zeng  zuo   zhi   shan  san   lin   yu    fan   liao  chuo  zun   jian  rao   chan  rui   xiu   hui   hua   zuan  xi    qiang none  da    sheng hui   xi    se    jian  jiang huan  qiao  cong  jie   jiao  bo    chan  yi    nao   sui   yi    shai  xu    ji    bin   qian  jian  pu    xun   zuan  qi    peng  li    mo    lei   xie   zuan  kuang you   xu    lei   xian  chan  none  lu    chan  ying  cai   xiang xian  zui   zuan  luo   xi    dao   lan   lei   lian  mi    jiu   yu    hong  zhou  xian  he    yue   ji    wan   kuang ji    ren   wei   yun   hong  chun  pi    sha   gang  na    ren   zong  lun   fen   zhi   wen   fang  zhu   zhen  niu   shu   xian  gan   xie   fu    lian  zu    shen  xi    zhi   zhong zhou  ban   fu    chu   shao  yi    jing  dai   bang  rong  jie   ku    rao   die   hang  hui   ji    xuan  jiang luo   jue   jiao  tong  geng  xiao  juan  xiu   xi    sui   tao   ji    ti    ji    xu    ling  yin   xu    qi    fei   chuo  shang gun   sheng wei   mian  shou  beng  chou  tao   liu   quan  \")\n                .append(\"zong  zhan  wan   lu:   zhui  zi    ke    xiang jian  mian  lan   ti    miao  ji    yun   hui   si    duo   duan  bian  xian  gou   zhui  huan  di    lu:   bian  min   yuan  jin   fu    ru    zhen  feng  cui   gao   chan  li    yi    jian  bin   piao  man   lei   ying  suo   mou   sao   xie   liao  shan  zeng  jiang qian  qiao  huan  jiao  zuan  fou   xie   gang  fou   que   fou   que   bo    ping  hou   none  gang  ying  ying  qing  xia   guan  zun   tan   none  qing  weng  ying  lei   tan   lu    guan  wang  gang  wang  wang  han   none  luo   fu    mi    fa    gu    zhu   ju    mao   gu    min   gang  ba    gua   ti    juan  fu    lin   yan   zhao  zui   gua   zhuo  yu    zhi   an    fa    lan   shu   si    pi    ma    liu   ba    fa    li    chao  wei   bi    ji    zeng  tong  liu   ji    juan  mi    zhao  luo   pi    ji    ji    luan  yang  mie   qiang ta    mei   yang  you   you   fen   ba    gao   yang  gu    qiang zang  gao   ling  yi    zhu   di    xiu   qiang yi    xian  rong  qun   qun   qian  huan  suo   xian  yi    yang  qiang xian  yu    geng  jie   tang  yuan  xi    fan   shan  fen   shan  lian  lei   geng  nou   qiang chan  yu    gong  yi    chong weng  fen   hong  chi   chi   cui   fu    xia   pen   yi    la    yi    pi    ling  liu   zhi   qu    xi    xie   xiang xi    xi    qi    qiao  hui   hui   shu   se    hong  jiang zhai  cui   fei   tao   sha   chi   zhu   jian  xuan  shi   pian  zong  wan   hui   hou   he    he    han   ao    piao  yi    lian  qu    none  lin   pen   qiao  ao    fan   yi    hui   xuan  dao   yao   lao   none  kao   mao   zhe   qi    gou   gou   gou   die   die   er    shua  ruan  er    nai   zhuan lei   ting  zi    geng  chao  hao   yun   pa    pi    chi   si    qu    jia   ju    huo   chu   lao   lun   ji    tang  ou    lou   nou   jiang pang  ze    lou   ji    lao   huo   you   mo    huai  er    zhe   ding  ye    da    song  qin   yun   chi   dan   dan   hong  geng  zhi   none  nie   dan   zhen  che   ling  zheng you   wa    liao  long  zhi   ning  tiao  er    ya    die   guo   none  lian  hao   sheng lie   pin   jing  ju    bi    di    guo   wen   xu    ping  cong  none  none  ting  yu    cong  kui   lian  kui   cong  lian  weng  kui   lian  lian  cong  ao    sheng song  ting  kui   nie   zhi   dan   ning  none  ji    ting  ting  long  yu    yu    zhao  si    su    yi    su    si    zhao  zhao  rou   yi    lei   ji    qiu   ken   cao   ge    di    huan  huang yi    ren   xiao  ru    zhou  yuan  du    gang  rong  gan   cha   wo    chang gu    zhi   qin   fu    fei   ban   pei   pang  jian  fang  zhun  you   na    ang   ken   ran   gong  yu    wen   yao   jin   pi    qian  xi    xi    fei   ken   jing  tai   shen  zhong zhang xie   shen  wei   zhou  die   dan   fei   ba    bo    qu    tian  bei   gua   tai   zi    ku    zhi   ni    ping  zi    fu    pang  zhen  xian  zuo   pei   jia   sheng zhi   bao   mu    qu    hu    ke    yi    yin   xu    yang  long  dong  ka    lu    jing  nu    yan   pang  kua   yi    guang hai   ge    dong  zhi   jiao  xiong xiong er    an    xing  pian  neng  zi    none  cheng tiao  zhi   cui   mei   xie   cui   xie   mo    mai   ji    xie   none  kuai  sa    zang  qi    nao   mi    nong  luan  wan   bo    wen   wan   qiu   jiao  jing  you   heng  cuo   lie   shan  ting  mei   chun  shen  xie   none  juan  cu    xiu   xin   tuo   pao   cheng nei   fu    dou   tuo   niao  nao   pi    gu    luo   li    lian  zhang cui   jie   liang shui  pi    biao  lun   pian  guo   juan  chui  dan   tian  nei   jing  jie   la    ye    a     ren   shen  chuo  fu    fu    ju    fei   qiang wan   dong  pi    guo   zong  ding  wu    mei   ruan  zhuan zhi   cou   gua   ou    di    an    xing  nao   shu   shuan nan   yun   zhong rou   e     sai   tu    yao   jian  wei   jiao  yu    jia   duan  bi    chang fu    xian  ni    mian  wa    teng  tui   bang  qian  lu:   wa    shou  tang  su    zhui  ge    yi    bo    liao  ji    pi    xie   gao   lu:   bin   none  chang lu    guo   pang  chuai biao  jiang fu    tang  mo    xi    zhuan lu:   jiao  ying  lu:   zhi   xue   chun  lin   tong  peng  ni    chuai liao  cui   gui   xiao  teng  fan   zhi   jiao  shan  hu  \")\n                .append(\"  cui   run   xin   sui   fen   ying  shan  gua   dan   kuai  nong  tun   lian  bei   yong  jue   chu   yi    juan  la    lian  sao   tun   gu    qi    cui   bin   xun   nao   huo   zang  xian  biao  xing  kuan  la    yan   lu    hu    za    luo   qu    zang  luan  ni    za    chen  qian  wo    guang zang  lin   guang zi    jiao  nie   chou  ji    gao   chou  mian  nie   zhi   zhi   ge    jian  die   zhi   xiu   tai   zhen  jiu   xian  yu    cha   yao   yu    chong xi    xi    jiu   yu    yu    xing  ju    jiu   xin   she   she   she   jiu   shi   tan   shu   shi   tian  dan   pu    pu    guan  hua   tian  chuan shun  xia   wu    zhou  dao   chuan shan  yi    none  pa    tai   fan   ban   chuan hang  fang  ban   bi    lu    zhong jian  cang  ling  zhu   ze    duo   bo    xian  ge    chuan jia   lu    hong  pang  xi    none  fu    zao   feng  li    shao  yu    lang  ting  none  wei   bo    meng  nian  ju    huang shou  zong  bian  mao   die   none  bang  cha   yi    sou   cang  cao   lou   dai   none  yao   chong none  dang  qiang lu    yi    jie   jian  huo   meng  qi    lu    lu    chan  shuanggen   liang jian  jian  se    yan   fu    ping  yan   yan   cao   cao   yi    le    ting  jiao  ai    nai   tiao  jiao  jie   peng  wan   yi    chai  mian  mi    gan   qian  yu    yu    shao  xiong du    xia   qi    mang  zi    hui   sui   zhi   xiang bi    fu    tun   wei   wu    zhi   qi    shan  wen   qian  ren   fou   kou   jie   lu    zhu   ji    qin   qi    yan   fen   ba    rui   xin   ji    hua   hua   fang  wu    jue   gou   zhi   yun   qin   ao    chu   mao   ya    fei   reng  hang  cong  yin   you   bian  yi    none  wei   li    pi    e     xian  chang cang  zhu   su    yi    yuan  ran   ling  tai   tiao  di    miao  qing  li    rao   ke    mu    pei   bao   gou   min   yi    yi    ju    pie   ruo   ku    zhu   ni    bo    bing  shan  qiu   yao   xian  ben   hong  ying  zha   dong  ju    die   nie   gan   hu    ping  mei   fu    sheng gu    bi    wei   fu    zhuo  mao   fan   qie   mao   mao   ba    zi    mo    zi    di    chi   gou   jing  long  none  niao  none  xue   ying  qiong ge    ming  li    rong  yin   gen   qian  chai  chen  yu    xiu   zi    lie   wu    duo   kui   ce    jian  ci    gou   guang mang  cha   jiao  jiao  fu    yu    zhu   zi    jiang hui   yin   cha   fa    rong  ru    chong mang  tong  zhong none  zhu   xun   huan  kua   quan  gai   da    jing  xing  chuan cao   jing  er    an    shou  chi   ren   jian  ti    huang ping  li    jin   lao   rong  zhuangda    jia   rao   bi    ce    qiao  hui   ji    dang  none  rong  hun   ying  luo   ying  qian  jin   sun   yin   mai   hong  zhou  yao   du    wei   chu   dou   fu    ren   yin   he    bi    bu    yun   di    tu    sui   sui   cheng chen  wu    bie   xi    geng  li    pu    zhu   mo    li    zhuangji    duo   qiu   sha   suo   chen  feng  ju    mei   meng  xing  jing  che   xin   jun   yan   ting  you   cuo   guan  han   you   cuo   jia   wang  you   niu   shao  xian  lang  fu    e     mo    wen   jie   nan   mu    kan   lai   lian  shi   wo    tu    xian  huo   you   ying  ying  none  chun  mang  mang  ci    yu    jing  di    qu    dong  jian  zou   gu    la    lu    ju    wei   jun   nie   kun   he    pu    zai   gao   guo   fu    lun   chang chou  song  chui  zhan  men   cai   ba    li    tu    bo    han   bao   qin   juan  xi    qin   di    jie   pu    dang  jin   zhao  tai   geng  hua   gu    ling  fei   jin   an    wang  beng  zhou  yan   zu    jian  lin   tan   shu   tian  dao   hu    ji    he    cui   tao   chun  bei   chang huan  fei   lai   qi    meng  ping  wei   dan   sha   huan  yan   yi    tiao  qi    wan   ce    nai   none  tuo   jiu   tie   luo   none  none  meng  none  none  ding  ying  ying  ying  xiao  sa    qiu   ke    xiang wan   yu    yu    fu    lian  xuan  xuan  nan   ze    wo    chun  xiao  yu    pian  mao   an    e     luo   ying  huo   gua   jiang wan   zuo   zuo   ju    bao   rou   xi    xie   an    qu    jian  fu    lu:   lu:   pen   feng  hong  hong  hou   yan   tu    zhu   zi    xiang shen  ge    qia   jing  mi    huang shen  pu    ge    dong  zhou  qian  wei   bo    wei   pa    ji    hu    zang  ji\")\n                .append(\"a   duan  yao   jun   cong  quan  wei   xian  kui   ting  hun   xi    shi   qi    lan   zong  yao   yuan  mei   yun   shu   di    zhuan guan  none  qiong chan  kai   kui   none  jiang lou   wei   pai   none  sou   yin   shi   chun  shi   yun   zhen  lang  nu    meng  he    que   suan  yuan  li    ju    xi    bang  chu   xu    tu    liu   huo   zhen  qian  zu    po    cuo   yuan  chu   yu    kuai  pan   pu    pu    na    shuo  xi    fen   yun   zheng jian  ji    ruo   cang  en    mi    hao   sun   zhen  ming  none  xu    liu   xi    gu    lang  rong  weng  gai   cuo   shi   tang  luo   ru    suo   xian  bei   yao   gui   bi    zong  gun   none  xiu   ce    none  lan   none  ji    li    can   lang  yu    none  ying  mo    diao  xiu   wu    tong  zhu   peng  an    lian  cong  xi    ping  qiu   jin   chun  jie   wei   tui   cao   yu    yi    ji    liao  bi    lu    xu    bu    zhang luo   qiang man   yan   leng  ji    biao  gun   han   di    su    lu    she   shang di    mie   xun   man   bo    di    cuo   zhe   sen   xuan  yu    hu    ao    mi    lou   cu    zhong cai   po    jiang mi    cong  niao  hui   jun   yin   jian  nian  shu   yin   kui   chen  hu    sha   kou   qian  ma    cang  ze    qiang dou   lian  lin   kou   ai    bi    li    wei   ji    qian  sheng fan   meng  ou    chan  dian  xun   jiao  rui   rui   lei   yu    qiao  chu   hua   jian  mai   yun   bao   you   qu    lu    rao   hui   e     ti    fei   jue   zui   fa    ru    fen   kui   shun  rui   ya    xu    fu    jue   dang  wu    tong  si    xiao  xi    yong  wen   shao  qi    jian  yun   sun   ling  yu    xia   weng  ji    hong  si    deng  lei   xuan  yun   yu    xi    hao   bo    hao   ai    wei   hui   wei   ji    ci    xiang luan  mie   yi    leng  jiang can   shen  qiang lian  ke    yuan  da    ti    tang  xue   bi    zhan  sun   lian  fan   ding  xiao  gu    xie   shu   jian  kao   hong  sa    xin   xun   yao   bai   sou   shu   xun   dui   pin   wei   neng  chou  mai   ru    piao  tai   qi    zao   chen  zhen  er    ni    ying  gao   cong  xiao  qi    fa    jian  xu    kui   jie   bian  di    mi    lan   jin   zang  miao  qiong qie   xian  none  ou    xian  su    lu:   yi    xu    xie   li    yi    la    lei   jiao  di    zhi   pi    teng  yao   mo    huan  biao  fan   sou   tan   tui   qiong qiao  wei   liu   hui   shu   gao   yun   none  li    zhu   zhu   ai    lin   zao   xuan  chen  lai   huo   tuo   wu    rui   rui   qi    heng  lu    su    tui   mang  yun   pin   yu    xun   ji    jiong xuan  mo    none  su    jiong none  nie   bo    rang  yi    xian  yu    ju    lian  lian  yin   qiang ying  long  tou   wei   yue   ling  qu    yao   fan   mi    lan   kui   lan   ji    dang  none  lei   lei   tong  feng  zhi   wei   kui   zhan  huai  li    ji    mi    lei   huai  luo   ji    nao   lu    jian  none  none  lei   quan  xiao  yi    luan  men   bie   hu    hu    lu    nu:e  lu:   zhi   xiao  qian  chu   hu    xu    cuo   fu    xu    xu    lu    hu    yu    hao   jiao  ju    guo   bao   yan   zhan  zhan  kui   ban   xi    shu   chong qiu   diao  ji    qiu   ding  shi   none  di    zhe   she   yu    gan   zi    hong  hui   meng  ge    sui   xia   chai  shi   yi    ma    xiang fang  e     pa    chi   qian  wen   wen   rui   bang  pi    yue   yue   jun   qi    ran   yin   qi    can   yuan  jue   hui   qian  qi    zhong ya    hao   mu    wang  fen   fen   hang  gong  zao   fu    ran   jie   fu    chi   dou   bao   xian  ni    te    qiu   you   zha   ping  chi   you   he    han   ju    li    fu    ran   zha   gou   pi    bo    xian  zhu   diao  bie   bing  gu    ran   qu    she   tie   ling  gu    dan   gu    ying  li    cheng qu    mou   ge    ci    hui   hui   mang  fu    yang  wa    lie   zhu   yi    xian  kuo   jiao  li    yi    ping  ji    ha    she   yi    wang  mo    qiong qie   gui   gong  zhi   man   none  zhe   jia   nao   si    qi    xing  lie   qiu   shao  yong  jia   tui   che   bai   e     han   shu   xuan  feng  shen  zhen  fu    xian  zhe   wu    fu    li    lang  bi    chu   yuan  you   jie   dan   yan   ting  dian  tui   hui   wo    zhi   song  fei   ju    mi    qi    qi    yu    jun   la    meng  qiang si    xi    \")\n                .append(\"lun   li    die   tiao  tao   kun   gan   han   yu    bang  fei   pi    wei   dun   yi    yuan  su    quan  qian  rui   ni    qing  wei   liang guo   wan   dong  e     ban   zhuo  wang  can   yang  ying  guo   chan  none  la    ke    ji    xie   ting  mai   xu    mian  yu    jie   shi   xuan  huang yan   bian  rou   wei   fu    yuan  mei   wei   fu    ruan  xie   you   you   mao   xia   ying  shi   chong tang  zhu   zong  ti    fu    yuan  kui   meng  la    du    hu    qiu   die   li    gua   yun   ju    nan   lou   chun  rong  ying  jiang tun   lang  pang  si    xi    xi    xi    yuan  weng  lian  sou   ban   rong  rong  ji    wu    xiu   han   qin   yi    bi    hua   tang  yi    du    nai   he    hu    xi    ma    ming  yi    wen   ying  teng  yu    cang  none  none  man   none  shang shi   cao   chi   di    ao    lu    wei   zhi   tang  chen  piao  qu    pi    yu    jian  luo   lou   qin   zhong yin   jiang shuai wen   jiao  wan   zhe   zhe   ma    ma    guo   liao  mao   xi    cong  li    man   xiao  none  zhang mang  xiang mo    zi    si    qiu   te    zhi   peng  peng  jiao  qu    bie   liao  pan   gui   xi    ji    zhuan huang fei   lao   jue   jue   hui   yin   chan  jiao  shan  rao   xiao  wu    chong xun   si    none  cheng dang  li    xie   shan  yi    jing  da    chan  qi    zi    xiang she   luo   qin   ying  chai  li    ze    xuan  lian  zhu   ze    xie   mang  xie   qi    rong  jian  meng  hao   ru    huo   zhuo  jie   bin   he    mie   fan   lei   jie   la    mi    li    chun  li    qiu   nie   lu    du    xiao  zhu   long  li    long  feng  ye    pi    rang  gu    juan  ying  none  xi    can   qu    quan  du    can   man   qu    jie   zhu   zha   xue   huang nu:   pei   nu:   xin   zhong mo    er    mie   mie   shi   xing  yan   kan   yuan  none  ling  xuan  shu   xian  tong  long  jie   xian  ya    hu    wei   dao   chong wei   dao   zhun  heng  qu    yi    yi    bu    gan   yu    biao  cha   yi    shan  chen  fu    gun   fen   shuai jie   na    zhong dan   ri    zhong zhong xie   qi    xie   ran   zhi   ren   qin   jin   jun   yuan  mei   chai  ao    niao  hui   ran   jia   tuo   ling  dai   bao   pao   yao   zuo   bi    shao  tan   ju    he    xue   xiu   zhen  yi    pa    bo    di    wa    fu    gun   zhi   zhi   ran   pan   yi    mao   none  na    kou   xuan  chan  qu    bei   yu    xi    none  bo    none  fu    yi    chi   ku    ren   jiang jia   cun   mo    jie   er    ge    ru    zhu   gui   yin   cai   lie   none  none  zhuangdang  none  kun   ken   niao  shu   jia   kun   cheng li    juan  shen  pou   ge    yi    yu    chen  liu   qiu   qun   ji    yi    bu    zhuangshui  sha   qun   li    lian  lian  ku    jian  fou   tan   bi    gun   tao   yuan  ling  chi   chang chou  duo   biao  liang shang pei   pei   fei   yuan  luo   guo   yan   du    ti    zhi   ju    qi    ji    zhi   gua   ken   none  ti    shi   fu    chong xie   bian  die   kun   duan  xiu   xiu   he    yuan  bao   bao   fu    yu    tuan  yan   hui   bei   chu   lu:   none  none  yun   ta    gou   da    huai  rong  yuan  ru    nai   jiong suo   ban   tun   chi   sang  niao  ying  jie   qian  huai  ku    lian  lan   li    zhe   shi   lu:   yi    die   xie   xian  wei   biao  cao   ji    qiang sen   bao   xiang none  pu    jian  zhuan jian  zui   ji    dan   za    fan   bo    xiang xin   bie   rao   man   lan   ao    duo   hui   cao   sui   nong  chan  lian  bi    jin   dang  shu   tan   bi    lan   pu    ru    zhi   none  shu   wa    shi   bai   xie   bo    chen  lai   long  xi    xian  lan   zhe   dai   none  zan   shi   jian  pan   yi    none  ya    xi    xi    yao   feng  tan   none  none  fu    ba    he    ji    ji    jian  guan  bian  yan   gui   jue   pian  mao   mi    mi    mie   shi   si    zhan  luo   jue   mo    tiao  lian  yao   zhi   jun   xi    shan  wei   xi    tian  yu    lan   e     du    qin   pang  ji    ming  ping  gou   qu    zhan  jin   guan  deng  jian  luo   qu    jian  wei   jue   qu    luo   lan   shen  di    guan  jian  guan  yan   gui   mi    shi   chan  lan   jue   ji    xi    di    tian  yu    gou   jin   qu    jiao  jiu   jin   cu    jue   zhi   chao  ji    gu    dan   zui   di    shan\")\n                .append(\"g hua   quan  ge    zhi   jie   gui   gong  chu   jie   huan  qiu   xing  su    ni    ji    lu    zhi   zhu   bi    xing  hu    shang gong  zhi   xue   chu   xi    yi    li    jue   xi    yan   xi    yan   yan   ding  fu    qiu   qiu   jiao  hong  ji    fan   xun   diao  hong  cha   tao   xu    jie   yi    ren   xun   yin   shan  qi    tuo   ji    xun   yin   e     fen   ya    yao   song  shen  yin   xin   jue   xiao  ne    chen  you   zhi   xiong fang  xin   chao  she   xian  sa    zhun  xu    yi    yi    su    chi   he    shen  he    xu    zhen  zhu   zheng gou   zi    zi    zhan  gu    fu    jian  die   ling  di    yang  li    nao   pan   zhou  gan   shi   ju    ao    zha   tuo   yi    qu    zhao  ping  bi    xiong chu   ba    da    zu    tao   zhu   ci    zhe   yong  xu    xun   yi    huang he    shi   cha   jiao  shi   hen   cha   gou   gui   quan  hui   jie   hua   gai   xiang hui   shen  chou  tong  mi    zhan  ming  e     hui   yan   xiong gua   er    beng  tiao  chi   lei   zhu   kuang kua   wu    yu    teng  ji    zhi   ren   su    lang  e     kuang e^    shi   ting  dan   bei   chan  you   heng  qiao  qin   shua  an    yu    xiao  cheng jie   xian  wu    wu    gao   song  pu    hui   jing  shuo  zhen  shuo  du    none  chang shui  jie   ke    qu    cong  xiao  sui   wang  xuan  fei   chi   ta    yi    na    yin   diao  pi    chuo  chan  chen  zhun  ji    qi    tan   chui  wei   ju    qing  jian  zheng ze    zou   qian  zhuo  liang jian  zhu   hao   lun   shen  biao  huai  pian  yu    die   xu    pian  shi   xuan  shi   hun   hua   e     zhong di    xie   fu    pu    ting  jian  qi    yu    zi    chuan xi    hui   yin   an    xian  nan   chen  feng  zhu   yang  yan   heng  xuan  ge    nuo   qi    mou   ye    wei   none  teng  zou   shan  jian  bo    none  huang huo   ge    ying  mi    xiao  mi    xi    qiang chen  nu:e  si    su    bang  chi   qian  shi   jiang yuan  xie   xue   tao   yao   yao   hu    yu    biao  cong  qing  li    mo    mo    shang zhe   miu   jian  ze    zha   lian  lou   can   ou    guan  xi    zhuo  ao    ao    jin   zhe   yi    hu    jiang man   chao  han   hua   chan  xu    zeng  se    xi    she   dui   zheng nao   lan   e     ying  jue   ji    zun   jiao  bo    hui   zhuan wu    jian  zha   shi   qiao  tan   zen   pu    sheng xuan  zao   zhan  dang  sui   qian  ji    jiao  jing  lian  nou   yi    ai    zhan  pi    hui   hua   yi    yi    shan  rang  nou   qian  zhui  ta    hu    zhou  hao   ni    ying  jian  yu    jian  hui   du    zhe   xuan  zan   lei   shen  wei   chan  li    yi    bian  zhe   yan   e     chou  wei   chou  yao   chan  rang  yin   lan   chen  huo   zhe   huan  zan   yi    dang  zhan  yan   du    yan   ji    ding  fu    ren   ji    jie   hong  tao   rang  shan  qi    tuo   xun   yi    xun   ji    ren   jiang hui   ou    ju    ya    ne    xu    e     lun   xiong song  feng  she   fang  jue   zheng gu    he    ping  zu    shi   xiong zha   su    zhen  di    zhou  ci    qu    zhao  bi    yi    yi    kuang lei   shi   gua   shi   jie   hui   cheng zhu   shen  hua   dan   gou   quan  gui   xun   yi    zheng gai   xiang cha   hun   xu    zhou  jie   wu    yu    qiao  wu    gao   you   hui   kuang shuo  song  ei    qing  zhu   zou   nuo   du    zhuo  fei   ke    wei   yu    shei  shen  diao  chan  liang zhun  sui   tan   shen  yi    mou   chen  die   huang jian  xie   xue   ye    wei   e     yu    xuan  chan  zi    an    yan   di    mi    pian  xu    mo    dang  su    xie   yao   bang  shi   qian  mi    jin   man   zhe   jian  miu   tan   jian  qiao  lan   pu    jue   yan   qian  zhan  chen  gu    qian  hong  ya    jue   hong  han   hong  qi    xi    huo   liao  han   du    long  dou   jiang qi    chi   feng  deng  wan   bi    shu   xian  feng  zhi   zhi   yan   yan   shi   chu   hui   tun   yi    tun   yi    jian  ba    hou   e     cu    xiang huan  jian  ken   gai   qu    fu    xi    bin   hao   yu    zhu   jia   fen   xi    hu    wen   huan  bin   di    zong  fen   yi    zhi   bao   chai  han   pi    na    pi    gou   duo   you   diao  mo    si    xiu   huan  kun   he    he    mo    an    mao   li    ni    bi    yu    jia   tuan  mao   pi    xi    e     ju\")\n                .append(\"    mo    chu   tan   huan  qu    bei   zhen  yuan  fu    cai   gong  te    yi    hang  wan   pin   huo   fan   tan   guan  ze    zhi   er    zhu   shi   bi    zi    er    gui   pian  bian  mai   dai   sheng kuang fei   tie   yi    chi   mao   he    bi    lu    lin   hui   gai   pian  zi    jia   xu    zei   jiao  gai   zang  jian  ying  xun   zhen  she   bin   bin   qiu   she   chuan zang  zhou  lai   zan   si    chen  shang tian  pei   geng  xian  mai   jian  sui   fu    dan   cong  cong  zhi   ji    zhang du    jin   xiong shun  yun   bao   zai   lai   feng  cang  ji    sheng ai    zhuan fu    gou   sai   ze    liao  wei   bai   chen  zhuan zhi   zhui  biao  yun   zeng  tan   zan   yan   none  shan  wan   ying  jin   gan   xian  zang  bi    du    shu   yan   none  xuan  long  gan   zang  bei   zhen  fu    yuan  gong  cai   ze    xian  bai   zhang huo   zhi   fan   tan   pin   bian  gou   zhu   guan  er    jian  bi    shi   tie   gui   kuang dai   mao   fei   he    yi    zei   zhi   jia   hui   zi    lin   lu    zang  zi    gai   jin   qiu   zhen  lai   she   fu    du    ji    shu   shang ci    bi    zhou  geng  pei   dan   lai   feng  zhui  fu    zhuan sai   ze    yan   zan   yun   zeng  shan  ying  gan   chi   xi    she   nan   xiong xi    cheng he    cheng zhe   xia   tang  zou   zou   li    jiu   fu    zhao  gan   qi    shan  qiong qin   xian  ci    jue   qin   chi   ci    chen  chen  die   ju    chao  di    se    zhan  zhu   yue   qu    jie   chi   chu   gua   xue   zi    tiao  duo   lie   gan   suo   cu    xi    zhao  su    yin   ju    jian  que   tang  chuo  cui   lu    qu    dang  qiu   zi    ti    qu    chi   huang qiao  qiao  yao   zao   yue   none  zan   zan   zu    pa    bao   ku    he    dun   jue   fu    chen  jian  fang  zhi   ta    yue   pa    qi    yue   qiang tuo   tai   yi    nian  ling  mei   ba    die   ku    tuo   jia   ci    pao   qia   zhu   ju    die   zhi   fu    pan   ju    shan  bo    ni    ju    li    gen   yi    ji    dai   xian  jiao  duo   chu   quan  kua   zhuai gui   qiong kui   xiang chi   lu    beng  zhi   jia   tiao  cai   jian  da    qiao  bi    xian  duo   ji    ju    ji    shu   tu    chu   xing  nie   xiao  bo    xue   qun   mou   shu   liang yong  jiao  chou  xiao  none  ta    jian  qi    wo    wei   chuo  jie   ji    nie   ju    ju    lun   lu    leng  huai  ju    chi   wan   quan  ti    bo    zu    qie   qi    cu    zong  cai   zong  pan   zhi   zheng dian  zhi   yu    duo   dun   chun  yong  zhong di    zha   chen  chuai jian  gua   tang  ju    fu    zu    die   pian  rou   nuo   ti    cha   tui   jian  dao   cuo   xi    ta    qiang zhan  dian  ti    ji    nie   pan   liu   zhan  bi    chong lu    liao  cu    tang  dai   su    xi    kui   ji    zhi   qiang di    man   zong  lian  beng  zao   nian  bie   tui   ju    deng  ceng  xian  fan   chu   zhong dun   bo    cu    zu    jue   jue   lin   ta    qiao  qiao  pu    liao  dun   cuan  kuang zao   ta    bi    bi    zhu   ju    chu   qiao  dun   chou  ji    wu    yue   nian  lin   lie   zhi   li    zhi   chan  chu   duan  wei   long  lin   xian  wei   zuan  lan   xie   rang  xie   nie   ta    qu    jie   cuan  zuan  xi    kui   jue   lin   shen  gong  dan   none  qu    ti    duo   duo   gong  lang  none  luo   ai    ji    ju    tang  none  none  yan   none  kang  qu    lou   lao   duo   zhi   none  ti    dao   none  yu    che   ya    gui   jun   wei   yue   xin   di    xuan  fan   ren   shan  qiang shu   tun   chen  dai   e     na    qi    mao   ruan  ren   qian  zhuan hong  hu    qu    huang di    ling  dai   ao    zhen  fan   kuang ang   peng  bei   gu    gu    pao   zhu   rong  e     ba    zhou  zhi   yao   ke    yi    qing  shi   ping  er    qiong ju    jiao  guang lu    kai   quan  zhou  zai   zhi   ju    liang yu    shao  you   huan  yun   zhe   wan   fu    qing  zhou  ni    ling  zhe   zhan  liang zi    hui   wang  chuo  guo   kan   yi    peng  qian  gun   nian  ping  guan  bei   lun   pai   liang ruan  rou   ji    yang  xian  chuan cou   chun  ge    you   hong  shu   fu    zi    fu    wen   ben   zhan  yu    wen   tao   gu    zhen  xia   yuan  lu    jiu   chao  zhuan wei   hun   none  che   jiao  zhan  \")\n                .append(\"pu    lao   fen   fan   lin   ge    se    kan   huan  yi    ji    dui   er    yu    xian  hong  lei   pei   li    li    lu    lin   che   ya    gui   xuan  dai   ren   zhuan e     lun   ruan  hong  gu    ke    lu    zhou  zhi   yi    hu    zhen  li    yao   qing  shi   zai   zhi   jiao  zhou  quan  lu    jiao  zhe   fu    liang nian  bei   hui   gun   wang  liang chuo  zi    cou   fu    ji    wen   shu   pei   yuan  xia   zhan  lu    zhe   lin   xin   gu    ci    ci    pi    zui   bian  la    la    ci    xue   ban   bian  bian  bian  none  bian  ban   ci    bian  bian  chen  ru    nong  nong  zhen  chuo  chuo  none  reng  bian  bian  none  none  liao  da    chan  gan   qian  yu    yu    qi    xun   yi    guo   mai   qi    za    wang  none  zhun  ying  ti    yun   jin   hang  ya    fan   wu    ta    e     hai   zhei  none  jin   yuan  wei   lian  chi   che   ni    tiao  zhi   yi    jiong jia   chen  dai   er    di    po    wang  die   ze    tao   shu   tuo   none  jing  hui   tong  you   mi    beng  ji    nai   yi    jie   zhui  lie   xun   tui   song  shi   tao   pang  hou   ni    dun   jiong xuan  xun   bu    you   xiao  qiu   tou   zhu   qiu   di    di    tu    jing  ti    dou   yi    zhe   tong  guang wu    shi   cheng su    zao   qun   feng  lian  suo   hui   li    none  zui   ben   cuo   jue   beng  huan  dai   lu    you   zhou  jin   yu    chuo  kui   wei   ti    yi    da    yuan  luo   bi    nuo   yu    dang  sui   dun   sui   yan   chuan chi   ti    yu    shi   zhen  you   yun   e     bian  guo   e     xia   huang qiu   dao   da    wei   none  yi    gou   yao   chu   liu   xun   ta    di    chi   yuan  su    ta    qian  none  yao   guan  zhang ao    shi   ce    su    su    zao   zhe   dun   zhi   lou   chi   cuo   lin   zun   rao   qian  xuan  yu    yi    wu    liao  ju    shi   bi    yao   mai   xie   sui   huan  zhan  deng  er    miao  bian  bian  la    li    yuan  you   luo   li    yi    ting  deng  qi    yong  shan  han   yu    mang  ru    qiong none  kuang fu    kang  bin   fang  xing  nei   none  shen  bang  yuan  cun   huo   xie   bang  wu    ju    you   han   tai   qiu   bi    pi    bing  shao  bei   wa    di    zou   ye    lin   kuang gui   zhu   shi   ku    yu    gai   he    qie   zhi   ji    xun   hou   xing  jiao  xi    gui   nuo   lang  jia   kuai  zheng lang  yun   yan   cheng dou   xi    lu:   fu    wu    fu    gao   hao   lang  jia   geng  jun   ying  bo    xi    bei   li    yun   bu    xiao  qi    pi    qing  guo   none  tan   zou   ping  lai   ni    chen  you   bu    xiang dan   ju    yong  qiao  yi    dou   yan   mei   ruo   bei   e     yu    juan  yu    yun   hou   kui   xiang xiang sou   tang  ming  xi    ru    chu   zi    zou   ju    wu    xiang yun   hao   yong  bi    mao   chao  fu    liao  yin   zhuan hu    qiao  yan   zhang fan   wu    xu    deng  bi    xin   bi    ceng  wei   zheng mao   shan  lin   po    dan   meng  ye    cao   kuai  feng  meng  zou   kuang lian  zan   chan  you   qi    yan   chan  cuo   ling  huan  xi    feng  zan   li    you   ding  qiu   zhuo  pei   zhou  yi    gan   yu    jiu   yan   zui   mao   dan   xu    tou   zhen  fen   none  none  yun   tai   tian  qia   tuo   zuo   han   gu    su    fa    chou  dai   ming  lao   chuo  chou  you   tong  zhi   xian  jiang cheng yin   tu    jiao  mei   ku    suan  lei   pu    zui   hai   yan   shi   niang wei   lu    lan   yan   tao   pei   zhan  chun  tan   zui   chuo  cu    kun   ti    xian  du    hu    xu    xing  tan   qiu   chun  yun   fa    ke    sou   mi    quan  chou  cuo   yun   yong  ang   zha   hai   tang  jiang piao  lao   yu    li    zao   lao   yi    jiang bu    jiao  xi    tan   fa    nong  yi    li    ju    yan   yi    niang ru    xun   chou  yan   ling  mi    mi    niang xin   jiao  shi   mi    yan   bian  cai   shi   you   shi   shi   li    zhong ye    liang li    jin   jin   ga    yi    liao  dao   zhao  ding  li    qiu   he    fu    zhen  zhi   ba    luan  fu    nai   diao  shan  qiao  kou   chuan zi    fan   yu    hua   han   gong  qi    mang  jian  di    si    xi    yi    chai  ta    tu    xi    nu:   qian  none  jian  pi    ye    yin   ba    fang  chen  jian  tou   yue   yan   fu    bu  \")\n                .append(\"  na    xin   e     jue   dun   gou   yin   qian  ban   ji    ren   chao  niu   fen   yun   yi    qin   pi    guo   hong  yin   jun   shi   yi    zhong nie   gai   ri    huo   tai   kang  none  lu    none  none  duo   zi    ni    tu    shi   min   gu    ke    ling  bing  yi    gu    ba    pi    yu    si    zuo   bu    you   dian  jia   zhen  shi   shi   tie   ju    zhan  ta    she   xuan  zhao  bao   he    bi    sheng chu   shi   bo    zhu   chi   za    po    tong  qian  fu    zhai  liu   qian  fu    li    yue   pi    yang  ban   bo    jie   gou   shu   zheng mu    ni    xi    di    jia   mu    tan   shen  yi    si    kuang ka    bei   jian  tong  xing  hong  jiao  chi   er    ge    bing  shi   mou   jia   yin   jun   zhou  chong shang tong  mo    lei   ji    yu    xu    ren   cun   zhi   qiong shan  chi   xian  xing  quan  pi    yi    zhu   hou   ming  kua   yao   xian  xian  xiu   jun   cha   lao   ji    yong  ru    mi    yi    yin   guang an    diu   you   se    kao   qian  luan  none  ai    diao  han   rui   shi   keng  qiu   xiao  zhe   xiu   zang  ti    cuo   gua   gong  zhong dou   lu:   mei   lang  wan   xin   yun   bei   wu    su    yu    chan  ting  bo    han   jia   hong  cuan  feng  chan  wan   zhi   si    xuan  wu    wu    tiao  gong  zhuo  lu:e  xing  qin   shen  han   none  ye    chu   zeng  ju    xian  e     mang  pu    li    shi   rui   cheng gao   li    te    none  zhu   none  tu    liu   zui   ju    chang yuan  jian  gang  diao  tao   chang lun   guo   ling  bei   lu    li    qing  pei   juan  min   zui   peng  an    pi    xian  ya    zhui  lei   a     kong  ta    kun   du    wei   chui  zi    zheng ben   nie   cong  chun  tan   ding  qi    qian  zhuo  qi    yu    jin   guan  mao   chang dian  xi    lian  tao   gu    cuo   shu   zhen  lu    meng  lu    hua   biao  ga    lai   ken   zhui  none  nai   wan   zan   none  de    xian  none  huo   liang none  men   kai   ying  di    lian  guo   xian  du    tu    wei   cong  fu    rou   ji    e     rou   chen  ti    zha   hong  yang  duan  xia   yu    keng  xing  huang wei   fu    zhao  cha   qie   she   hong  kui   nuo   mou   qiao  qiao  hou   zhen  huo   huan  ye    min   jian  duan  jian  si    kui   hu    xuan  zang  jie   zhen  bian  zhong zi    xiu   ye    mei   pai   ai    jie   none  mei   cha   ta    bang  xia   lian  suo   xi    liu   zu    ye    nou   weng  rong  tang  suo   qiang ge    shuo  chui  bo    pan   ta    bi    sang  gang  zi    wu    ying  huang tiao  liu   kai   sun   sha   sou   wan   hao   zhen  zhen  luo   yi    yuan  tang  nie   xi    jia   ge    ma    juan  rong  none  suo   none  none  none  na    lu    suo   kou   zu    tuan  xiu   guan  xuan  lian  shou  ao    man   mo    luo   bi    wei   liu   di    qiao  huo   yin   lu    ao    keng  qiang cui   qi    chang tang  man   yong  chan  feng  jing  biao  shu   lou   xiu   cong  long  zan   jian  cao   li    xia   xi    kang  none  beng  none  none  zheng lu    hua   ji    pu    hui   qiang po    lin   suo   xiu   san   cheng kui   san   liu   nao   huang pie   sui   fan   qiao  chuan yang  tang  xiang jue   jiao  zun   liao  jie   lao   dui   tan   zan   ji    jian  zhong deng  lou   ying  dui   jue   nou   ti    pu    tie   none  none  ding  shan  kai   jian  fei   sui   lu    juan  hui   yu    lian  zhuo  qiao  qian  zhuo  lei   bi    tie   huan  ye    duo   guo   dang  ju    fen   da    bei   yi    ai    dang  xun   diao  zhu   heng  zhui  ji    nie   ta    huo   qing  bin   ying  kui   ning  xu    jian  jian  qiang cha   zhi   mie   li    lei   ji    zuan  kuang shang peng  la    du    shuo  chuo  lu:   biao  bao   lu    none  none  long  e     lu    xin   jian  lan   bo    jian  yao   chan  xiang jian  xi    guan  cang  nie   lei   cuan  qu    pan   luo   zuan  luan  zao   nie   jue   tang  shu   lan   jin   ga    yi    zhen  ding  zhao  po    liao  tu    qian  chuan shan  sa    fan   diao  men   nu:   yang  chai  xing  gai   bu    tai   ju    dun   chao  zhong na    bei   gang  ban   qian  yao   qin   jun   wu    gou   kang  fang  huo   tou   niu   ba    yu    qian  zheng qian  gu    bo    ke    po    bu    bo    yue   zuan  mu    tan   jia   dian  you   ti\")\n                .append(\"e   bo    ling  shuo  qian  mao   bao   shi   xuan  tuo   bi    ni    pi    duo   xing  kao   lao   er    mang  ya    you   cheng jia   ye    nao   zhi   dang  tong  lu:   diao  yin   kai   zha   zhu   xian  ting  diu   xian  hua   quan  sha   ha    yao   ge    ming  zheng se    jiao  yi    chan  chong tang  an    yin   ru    zhu   lao   pu    wu    lai   te    lian  keng  xiao  suo   li    zeng  chu   guo   gao   e     xiu   cuo   lu:e  feng  xin   liu   kai   jian  rui   ti    lang  qin   ju    a     qing  zhe   nuo   cuo   mao   ben   qi    de    ke    kun   chang xi    gu    luo   chui  zhui  jin   zhi   xian  juan  huo   pei   tan   ding  jian  ju    meng  zi    qie   ying  kai   qiang si    e     cha   qiao  zhong duan  sou   huang huan  ai    du    mei   lou   zi    fei   mei   mo    zhen  bo    ge    nie   tang  juan  nie   na    liu   hao   bang  yi    jia   bin   rong  biao  tang  man   luo   beng  yong  jing  di    zu    xuan  liu   chan  jue   liao  pu    lu    dun   lan   pu    cuan  qiang deng  huo   lei   huan  zhuo  lian  yi    cha   biao  la    chan  xiang chang chang jiu   ao    die   qu    liao  mi    zhang men   ma    shuan shan  huo   men   yan   bi    han   bi    none  kai   kang  beng  hong  run   san   xian  xian  jian  min   xia   min   dou   zha   nao   none  peng  ke    ling  bian  bi    run   he    guan  ge    he    fa    chu   hong  gui   min   none  kun   lang  lu:   ting  sha   yan   yue   yue   chan  qu    lin   chang shai  kun   yan   min   yan   e     hun   yu    wen   xiang none  xiang qu    yao   wen   ban   an    wei   yin   kuo   que   lan   du    none  none  tian  nie   da    kai   he    que   chuangguan  dou   qi    kui   tang  guan  piao  kan   xi    hui   chan  pi    dang  huan  ta    wen   none  men   shuan shan  yan   han   bi    wen   chuangrun   wei   xian  hong  jian  min   kang  men   zha   nao   gui   wen   ta    min   lu:   kai   fa    ge    he    kun   jiu   yue   lang  du    yu    yan   chang xi    wen   hun   yan   yan   chan  lan   qu    hui   kuo   que   he    tian  da    que   kan   huan  fu    fu    le    dui   xin   qian  wu    yi    tuo   yin   yang  dou   e     sheng ban   pei   keng  yun   ruan  zhi   pi    jing  fang  yang  yin   zhen  jie   cheng e     qu    di    zu    zuo   dian  ling  a     tuo   tuo   po    bing  fu    ji    lu    long  chen  xing  duo   lou   mo    jiang shu   duo   xian  er    gui   wu    gai   shan  jun   qiao  xing  chun  fu    bi    shan  shan  sheng zhi   pu    dou   yuan  zhen  chu   xian  zhi   nie   yun   xian  pei   pei   zou   yi    dui   lun   yin   ju    chui  chen  pi    ling  tao   xian  lu    none  xian  yin   zhu   yang  reng  shan  chong yan   yin   yu    ti    yu    long  wei   wei   nie   dui   sui   an    huang jie   sui   yin   gai   yan   hui   ge    yun   wu    wei   ai    xi    tang  ji    zhang dao   ao    xi    yin   sa    rao   lin   tui   deng  pi    sui   sui   yu    xian  fen   ni    er    ji    dao   xi    yin   zhi   hui   long  xi    li    li    li    zhui  he    zhi   sun   juan  nan   yi    que   yan   qin   ya    xiong ya    ji    gu    huan  zhi   gou   jun   ci    yong  ju    chu   hu    za    luo   yu    chou  diao  sui   han   huo   shuangguan  chu   za    yong  ji    sui   chou  liu   li    nan   xue   za    ji    ji    yu    yu    xue   na    fou   se    mu    wen   fen   pang  yun   li    li    yang  ling  lei   an    bao   meng  dian  dang  hang  wu    zhao  xu    ji    mu    chen  xiao  zha   ting  zhen  pei   mei   ling  qi    chou  huo   sha   fei   weng  zhan  ying  ni    chou  tun   lin   none  dong  ying  wu    ling  shuangling  xia   hong  yin   mai   mo    yun   liu   meng  bin   wu    wei   kuo   yin   xi    yi    ai    dan   deng  xian  yu    lu    long  dai   ji    pang  yang  ba    pi    wei   none  xi    ji    mai   meng  meng  lei   li    huo   ai    fei   dai   long  ling  ai    feng  li    bao   none  he    he    bing  qing  qing  jing  qi    zhen  jing  cheng qing  jing  jing  dian  jing  tian  fei   fei   kao   mi    mian  mian  pao   ye    tian  hui   ye    ge    ding  ren   jian  ren   di    du    wu    ren   qin   jin   xue   niu   ba    yin   sa    ren   \")\n                .append(\"mo    zu    da    ban   yi    yao   tao   bei   jia   hong  pao   yang  mo    yin   jia   tao   ji    xie   an    an    hen   gong  gong  da    qiao  ting  man   ying  sui   tiao  qiao  xuan  kong  beng  ta    zhang bing  kuo   ju    la    xie   rou   bang  yi    qiu   qiu   he    xiao  mu    ju    jian  bian  di    jian  none  tao   gou   ta    bei   xie   pan   ge    bi    kuo   tang  lou   gui   qiao  xue   ji    jian  jiang chan  da    huo   xian  qian  du    wa    jian  lan   wei   ren   fu    mei   juan  ge    wei   qiao  han   chang none  rou   xun   she   wei   ge    bei   tao   gou   yun   gao   bi    wei   hui   shu   wa    du    wei   ren   fu    han   wei   yun   tao   jiu   jiu   xian  xie   xian  ji    yin   za    yun   shao  luo   peng  huang ying  yun   peng  yin   yin   xiang hu    ye    ding  qing  pan   xiang shun  han   xu    yi    xu    gu    song  kui   qi    hang  yu    wan   ban   dun   di    dan   pan   po    ling  cheng jing  lei   he    qiao  e     e     wei   jie   gua   shen  yi    yi    ke    dui   pian  ping  lei   fu    jia   tou   hui   kui   jia   le    ting  cheng ying  jun   hu    han   jing  tui   tui   pin   lai   tui   zi    zi    chui  ding  lai   yan   han   qian  ke    cui   jiong qin   yi    sai   ti    e     e     yan   hun   kan   yong  zhuan yan   xian  xin   yi    yuan  sang  dian  dian  jiang ku    lei   liao  piao  yi    man   qi    yao   hao   qiao  gu    xun   qian  hui   zhan  ru    hong  bin   xian  pin   lu    lan   nie   quan  ye    ding  qing  han   xiang shun  xu    xu    wan   gu    dun   qi    ban   song  hang  yu    lu    ling  po    jing  jie   jia   ting  he    ying  jiong ke    yi    pin   hui   tui   han   ying  ying  ke    ti    yong  e     zhuan yan   e     nie   man   dian  sang  hao   lei   zhan  ru    pin   quan  feng  biao  none  fu    xia   zhan  biao  sa    fa    tai   lie   gua   xuan  shao  ju    biao  si    wei   yang  yao   sou   kai   sao   fan   liu   xi    liao  piao  piao  liu   biao  biao  biao  liao  none  se    feng  biao  feng  yang  zhan  biao  sa    ju    si    sou   yao   liu   piao  biao  biao  fei   fan   fei   fei   shi   shi   can   ji    ding  si    tuo   jian  sun   xiang tun   ren   yu    juan  chi   yin   fan   fan   sun   yin   zhu   yi    zhai  bi    jie   tao   liu   ci    tie   si    bao   shi   duo   hai   ren   tian  jiao  jia   bing  yao   tong  ci    xiang yang  yang  er    yan   le    yi    can   bo    nei   e     bu    jun   dou   su    yu    shi   yao   hun   guo   shi   jian  zhui  bing  xian  bu    ye    tan   fei   zhang wei   guan  e     nuan  hun   hu    huang tie   hui   jian  hou   he    xing  fen   wei   gu    cha   song  tang  bo    gao   xi    kui   liu   sou   tao   ye    yun   mo    tang  man   bi    yu    xiu   jin   san   kui   zhuan shan  chi   dan   yi    ji    rao   cheng yong  tao   hui   xiang zhan  fen   hai   meng  yan   mo    chan  xiang luo   zuan  nang  shi   ding  ji    tuo   xing  tun   xi    ren   yu    chi   fan   yin   jian  shi   bao   si    duo   yi    er    rao   xiang he    le    jiao  xi    bing  bo    dou   e     yu    nei   jun   guo   hun   xian  guan  cha   kui   gu    sou   chan  ye    mo    bo    liu   xiu   jin   man   san   zhuan nang  shou  kui   guo   xiang fen   ba    ni    bi    bo    tu    han   fei   jian  yan   ai    fu    xian  wen   xin   fen   bin   xing  ma    yu    feng  han   di    tuo   tuo   chi   xun   zhu   zhi   pei   xin   ri    sa    yin   wen   zhi   dan   lu:   you   bo    bao   kuai  tuo   yi    qu    wen   qu    jiong bo    zhao  yuan  peng  zhou  ju    zhu   nu    ju    pi    zang  jia   ling  zhen  tai   fu    yang  shi   bi    tuo   tuo   si    liu   ma    pian  tao   zhi   rong  teng  dong  xun   quan  shen  jiong er    hai   bo    none  yin   luo   none  dan   xie   liu   ju    song  qin   mang  liang han   tu    xuan  tui   jun   e     cheng xing  ai    lu    zhui  zhou  she   pian  kun   tao   lai   zong  ke    qi    qi    yan   fei   sao   yan   jie   yao   wu    pian  cong  pian  qian  fei   huang jian  huo   yu    ti    quan  xia   zong  kui   rou   si    gua   tuo   kui   sou   qian  cheng zhi   liu   pang  teng  xi    cao \")\n                .append(\"  du    yan   yuan  zou   sao   shan  li    zhi   shuanglu    xi    luo   zhang mo    ao    can   piao  cong  qu    bi    zhi   yu    xu    hua   bo    su    xiao  lin   zhan  dun   liu   tuo   zeng  tan   jiao  tie   yan   luo   zhan  jing  yi    ye    tuo   bin   zou   yan   peng  lu:   teng  xiang ji    shuangju    xi    huan  li    biao  ma    yu    tuo   xun   chi   qu    ri    bo    lu:   zang  shi   si    fu    ju    zou   zhu   tuo   nu    jia   yi    tai   xiao  ma    yin   jiao  hua   luo   hai   pian  biao  li    cheng yan   xing  qin   jun   qi    qi    ke    zhui  zong  su    can   pian  zhi   kui   sao   wu    ao    liu   qian  shan  piao  luo   cong  zhan  zhou  ji    shuangxiang gu    wei   wei   wei   yu    gan   yi    ang   tou   jie   bo    bi    ci    ti    di    ku    hai   qiao  hou   kua   ge    tui   geng  pian  bi    ke    qia   yu    sui   lou   bo    xiao  bang  bo    cuo   kuan  bin   mo    liao  lou   nao   du    zang  sui   ti    bin   kuan  lu    gao   gao   qiao  kao   qiao  lao   zao   biao  kun   kun   ti    fang  xiu   ran   mao   dan   kun   bin   fa    tiao  pi    zi    fa    ran   ti    pao   pi    mao   fu    er    rong  qu    none  xiu   gua   ji    peng  zhua  shao  sha   ti    li    bin   zong  ti    peng  song  zheng quan  zong  shun  jian  duo   hu    la    jiu   qi    lian  zhen  bin   peng  mo    san   man   man   seng  xu    lie   qian  qian  nong  huan  kuai  ning  bin   lie   rang  dou   dou   nao   hong  xi    dou   kan   dou   dou   jiu   chang yu    yu    li    juan  fu    qian  gui   zong  liu   gui   shang yu    gui   mei   ji    qi    jie   kui   hun   ba    po    mei   xu    yan   xiao  liang yu    tui   qi    wang  liang wei   jian  chi   piao  bi    mo    ji    xu    chou  yan   zhan  yu    dao   ren   ji    ba    hong  tuo   diao  ji    yu    e     que   sha   hang  tun   mo    gai   shen  fan   yuan  pi    lu    wen   hu    lu    za    fang  fen   na    you   none  none  he    xia   qu    han   pi    ling  tuo   ba    qiu   ping  fu    bi    ji    wei   ju    diao  ba    you   gun   pi    nian  xing  tai   bao   fu    zha   ju    gu    none  none  none  ta    jie   shua  hou   xiang er    an    wei   tiao  zhu   yin   lie   luo   tong  yi    qi    bing  wei   jiao  pu    gui   xian  ge    hui   none  none  kao   none  duo   jun   ti    mian  shao  za    suo   qin   yu    nei   zhe   gun   geng  none  wu    qiu   ting  fu    huan  chou  li    sha   sha   gao   meng  none  none  none  none  yong  ni    zi    qi    qing  xiang nei   chun  ji    diao  qie   gu    zhou  dong  lai   fei   ni    yi    kun   lu    jiu   chang jing  lun   ling  zou   li    meng  zong  zhi   nian  none  none  none  shi   sao   hun   ti    hou   xing  ju    la    zong  ji    bian  bian  huan  quan  ji    wei   wei   yu    chun  rou   die   huang lian  yan   qiu   qiu   jian  bi    e     yang  fu    sai   jian  ha    tuo   hu    none  ruo   none  wen   jian  hao   wu    pang  sao   liu   ma    shi   shi   guan  zi    teng  ta    yao   ge    rong  qian  qi    wen   ruo   none  lian  ao    le    hui   min   ji    tiao  qu    jian  sao   man   xi    qiu   biao  ji    ji    zhu   jiang qiu   zhuan yong  zhang kang  xue   bie   jue   qu    xiang bo    jiao  xun   su    huang zun   shan  shan  fan   gui   lin   xun   miao  xi    none  xiang fen   guan  hou   kuai  zei   sao   zhan  gan   gui   sheng li    chang none  none  ai    ru    ji    xu    huo   none  li    lie   li    mie   zhen  xiang e     lu    guan  li    xian  yu    dao   ji    you   tun   lu    fang  ba    ke    ba    ping  nian  lu    you   zha   fu    ba    bao   hou   pi    tai   gui   jie   kao   wei   er    tong  zei   hou   kuai  ji    jiao  xian  zha   xiang xun   geng  li    lian  jian  li    shi   tiao  gun   sha   huan  jun   ji    yong  qing  ling  qi    zou   fei   kun   chang gu    ni    nian  diao  jing  shen  shi   zi    fen   die   bi    chang ti    wen   wei   sai   e     qiu   fu    huang quan  jiang bian  sao   ao    qi    ta    guan  yao   pang  jian  le    biao  xue   bie   man   min   yong  wei   xi    gui   shan  lin   zun   hu    gan   li    shan  guan  niao  yi    fu    li    jiu   bu    ya\")\n                .append(\"n   fu    diao  ji    feng  none  gan   shi   feng  ming  bao   yuan  zhi   hu    qian  fu    fen   wen   jian  shi   yu    fou   yiao  ju    jue   pi    huan  zhen  bao   yan   ya    zheng fang  feng  wen   ou    te    jia   nu    ling  mie   fu    tuo   wen   li    bian  zhi   ge    yuan  zi    qu    xiao  chi   dan   ju    you   gu    zhong yu    yang  rong  ya    zhi   yu    none  ying  zhui  wu    er    gua   ai    zhi   yan   heng  jiao  ji    lie   zhu   ren   ti    hong  luo   ru    mou   ge    ren   jiao  xiu   zhou  chi   luo   none  none  none  luan  jia   ji    yu    huan  tuo   bu    wu    juan  yu    bo    xun   xun   bi    xi    jun   ju    tu    jing  ti    e     e     kuang hu    wu    shen  la    none  none  lu    bing  shu   fu    an    zhao  peng  qin   qian  bei   diao  lu    que   jian  ju    tu    ya    yuan  qi    li    ye    zhui  kong  duo   kun   sheng qi    jing  ni    e     jing  zi    lai   dong  qi    chun  geng  ju    qu    none  none  ji    shu   none  chi   miao  rou   fu    qiu   ti    hu    ti    e     jie   mao   fu    chun  tu    yan   he    yuan  pian  yun   mei   hu    ying  dun   mu    ju    none  cang  fang  ge    ying  yuan  xuan  weng  shi   he    chu   tang  xia   ruo   liu   ji    gu    jian  zhun  han   zi    ci    yi    yao   yan   ji    li    tian  kou   ti    ti    ni    tu    ma    jiao  liu   zhen  chen  li    zhuan zhe   ao    yao   yi    ou    chi   zhi   liao  rong  lou   bi    shuangzhuo  yu    wu    jue   yin   tan   si    jiao  yi    hua   bi    ying  su    huang fan   jiao  liao  yan   kao   jiu   xian  xian  tu    mai   zun   yu    ying  lu    tuan  xian  xue   yi    pi    shu   luo   qi    yi    ji    zhe   yu    zhan  ye    yang  pi    ning  hu    mi    ying  meng  di    yue   yu    lei   bo    lu    he    long  shuangyue   ying  guan  qu    li    luan  niao  jiu   ji    yuan  ming  shi   ou    ya    cang  bao   zhen  gu    dong  lu    ya    xiao  yang  ling  chi   qu    yuan  xue   tuo   si    zhi   er    gua   xiu   heng  zhou  ge    luan  hong  wu    bo    li    juan  hu    e     yu    xian  ti    wu    que   miao  an    kun   bei   peng  qian  chun  geng  yuan  su    hu    he    e     gu    qiu   ci    mei   wu    yi    yao   weng  liu   ji    yi    jian  he    yi    ying  zhe   liu   liao  jiao  jiu   yu    lu    huan  zhan  ying  hu    meng  guan  shuanglu    jin   ling  jian  xian  cuo   jian  jian  yan   cuo   lu    you   cu    ji    biao  cu    pao   zhu   jun   zhu   jian  mi    mi    wu    liu   chen  jun   lin   ni    qi    lu    jiu   jun   jing  li    xiang yan   jia   mi    li    she   zhang lin   jing  qi    ling  yan   cu    mai   mai   ge    chao  fu    mian  mian  fu    pao   qu    qu    mou   fu    xian  lai   qu    mian  chi   feng  fu    qu    mian  ma    ma    mo    hui   none  zou   nen   fen   huang huang jin   guang tian  tou   hong  xi    kuang hong  shu   li    nian  chi   hei   hei   yi    qian  zhen  xi    tuan  mo    mo    qian  dai   chu   you   dian  yi    xia   yan   qu    mei   yan   qing  yu    li    dang  du    can   yin   an    yan   tan   an    zhen  dai   can   yi    mei   dan   yan   du    lu    zhi   fen   fu    fu    min   min   yuan  cu    qu    chao  wa    zhu   zhi   mang  ao    bie   tuo   bi    yuan  chao  tuo   ding  mi    nai   ding  zi    gu    gu    dong  fen   tao   yuan  pi    chang gao   qi    yuan  tang  teng  shu   shu   fen   fei   wen   ba    diao  tuo   tong  qu    sheng shi   you   shi   ting  wu    nian  jing  hun   ju    yan   tu    si    xi    xian  yan   lei   bi    yao   yan   han   hui   wu    hou   xi    ge    zha   xiu   weng  zha   nong  nang  qi    zhai  ji    zi    ji    ji    qi    ji    chi   chen  chen  he    ya    ken   xie   bao   ze    shi   zi    chi   nian  ju    tiao  ling  ling  chu   quan  xie   yin   nie   jiu   nie   chuo  kun   yu    chu   yi    ni    cuo   chuo  qu    nian  xian  yu    e     wo    yi    chi   zou   dian  chu   jin   ya    chi   chen  he    yin   ju    ling  bao   tiao  zi    yin   yu    chuo  qu    wo    long  pang  gong  pang  yan   long  long  gong  kan   ta    ling  ta    long  gong  kan   gui   qiu   bie   gui   yue   chui  he    jue   \")\n                .append(\"xie   yue   \").toString();\n    }\n}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/RetrofitUtils.java",
    "content": "package com.blankj.subutil.util;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/08/25\n *     desc  : utils about retrofit\n * </pre>\n */\npublic final class RetrofitUtils {\n\n}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/TemperatureUtils.java",
    "content": "package com.blankj.subutil.util;\n\n/**\n * <pre>\n *     author: Faramarz Afzali\n *     time  : 2020/09/05\n *     desc  : This class is intended for converting temperatures into different units.\n *             C refers to the Celsius unit\n *             F refers to the Fahrenheit unit\n *             K refers to the Kelvin unit\n * </pre>\n */\npublic final class TemperatureUtils {\n\n    public static float cToF(float temp) {\n        return (temp * 9) / 5 + 32;\n    }\n\n    public static float cToK(float temp) {\n        return temp + 273.15f;\n    }\n\n\n    public static float fToC(float temp) {\n        return (temp - 32) * 5 / 9;\n    }\n\n    public static float fToK(float temp) {\n        return temp + 255.3722222222f;\n    }\n\n\n    public static float kToC(float temp) {\n        return temp - 273.15f;\n    }\n\n    public static float kToF(float temp) {\n        return temp - 459.67f;\n    }\n}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/Utils.java",
    "content": "package com.blankj.subutil.util;\n\nimport android.annotation.SuppressLint;\nimport android.app.Application;\nimport android.content.ContentProvider;\nimport android.content.ContentValues;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport java.lang.reflect.InvocationTargetException;\n\n/**\n * <pre>\n *     author:\n *                                      ___           ___           ___         ___\n *         _____                       /  /\\         /__/\\         /__/|       /  /\\\n *        /  /::\\                     /  /::\\        \\  \\:\\       |  |:|      /  /:/\n *       /  /:/\\:\\    ___     ___    /  /:/\\:\\        \\  \\:\\      |  |:|     /__/::\\\n *      /  /:/~/::\\  /__/\\   /  /\\  /  /:/~/::\\   _____\\__\\:\\   __|  |:|     \\__\\/\\:\\\n *     /__/:/ /:/\\:| \\  \\:\\ /  /:/ /__/:/ /:/\\:\\ /__/::::::::\\ /__/\\_|:|____    \\  \\:\\\n *     \\  \\:\\/:/~/:/  \\  \\:\\  /:/  \\  \\:\\/:/__\\/ \\  \\:\\~~\\~~\\/ \\  \\:\\/:::::/     \\__\\:\\\n *      \\  \\::/ /:/    \\  \\:\\/:/    \\  \\::/       \\  \\:\\  ~~~   \\  \\::/~~~~      /  /:/\n *       \\  \\:\\/:/      \\  \\::/      \\  \\:\\        \\  \\:\\        \\  \\:\\         /__/:/\n *        \\  \\::/        \\__\\/        \\  \\:\\        \\  \\:\\        \\  \\:\\        \\__\\/\n *         \\__\\/                       \\__\\/         \\__\\/         \\__\\/\n *     blog  : http://blankj.com\n *     time  : 16/12/08\n *     desc  : utils about initialization\n * </pre>\n */\npublic final class Utils {\n\n    @SuppressLint(\"StaticFieldLeak\")\n    private static Application sApplication;\n\n    private Utils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Init utils.\n     * <p>Init it in the class of Application.</p>\n     *\n     * @param context context\n     */\n    public static void init(final Context context) {\n        if (context == null) {\n            init(getApplicationByReflect());\n            return;\n        }\n        init((Application) context.getApplicationContext());\n    }\n\n    /**\n     * Init utils.\n     * <p>Init it in the class of Application.</p>\n     *\n     * @param app application\n     */\n    public static void init(final Application app) {\n        if (sApplication == null) {\n            if (app == null) {\n                Utils.sApplication = getApplicationByReflect();\n            } else {\n                Utils.sApplication = app;\n            }\n        }\n    }\n\n    /**\n     * Return the context of Application object.\n     *\n     * @return the context of Application object\n     */\n    public static Application getApp() {\n        if (sApplication != null) return sApplication;\n        return getApplicationByReflect();\n    }\n\n    private static Application getApplicationByReflect() {\n        try {\n            @SuppressLint(\"PrivateApi\")\n            Class<?> activityThread = Class.forName(\"android.app.ActivityThread\");\n            Object at = activityThread.getMethod(\"currentActivityThread\").invoke(null);\n            Object app = activityThread.getMethod(\"getApplication\").invoke(at);\n            if (app == null) {\n                throw new NullPointerException(\"u should init first\");\n            }\n            init((Application) app);\n            return sApplication;\n        } catch (NoSuchMethodException e) {\n            e.printStackTrace();\n        } catch (IllegalAccessException e) {\n            e.printStackTrace();\n        } catch (InvocationTargetException e) {\n            e.printStackTrace();\n        } catch (ClassNotFoundException e) {\n            e.printStackTrace();\n        }\n        throw new NullPointerException(\"u should init first\");\n    }\n\n\n    public static final class ContentProvider4SubUtil extends ContentProvider {\n\n        @Override\n        public boolean onCreate() {\n            Utils.init(getContext());\n            return true;\n        }\n\n        @Nullable\n        @Override\n        public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {\n            return null;\n        }\n\n        @Nullable\n        @Override\n        public String getType(@NonNull Uri uri) {\n            return null;\n        }\n\n        @Nullable\n        @Override\n        public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {\n            return null;\n        }\n\n        @Override\n        public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {\n            return 0;\n        }\n\n        @Override\n        public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {\n            return 0;\n        }\n    }\n}"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/http/Chain.java",
    "content": "package com.blankj.subutil.util.http;\n\npublic interface Chain {\n}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/http/ExecutorFactory.java",
    "content": "package com.blankj.subutil.util.http;\n\nimport android.os.Handler;\nimport android.os.Looper;\n\nimport androidx.annotation.NonNull;\n\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\n\npublic final class ExecutorFactory {\n\n    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();\n\n    private static final Executor DEFAULT_WORK_EXECUTOR = new ThreadPoolExecutor(2 * CPU_COUNT + 1,\n            2 * CPU_COUNT + 1,\n            30, TimeUnit.SECONDS,\n            new LinkedBlockingQueue<Runnable>(128),\n            new ThreadFactory() {\n                private final AtomicInteger mCount = new AtomicInteger(1);\n\n                public Thread newThread(@NonNull Runnable r) {\n                    return new Thread(r, \"http-pool-\" + mCount.getAndIncrement());\n                }\n            }\n    );\n\n    private static final Executor DEFAULT_MAIN_EXECUTOR = new Executor() {\n        private final Handler mHandler = new Handler(Looper.getMainLooper());\n\n        @Override\n        public void execute(@NonNull Runnable command) {\n            mHandler.post(command);\n        }\n    };\n\n    public static Executor getDefaultWorkExecutor() {\n        return DEFAULT_WORK_EXECUTOR;\n    }\n\n    public static Executor getDefaultMainExecutor() {\n        return DEFAULT_MAIN_EXECUTOR;\n    }\n}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/http/Headers.java",
    "content": "package com.blankj.subutil.util.http;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\npublic class Headers {\n\n    private Map<String, List<String>> header = new HashMap<>();\n\n\n}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/http/HttpUtils.java",
    "content": "package com.blankj.subutil.util.http;\n\nimport android.accounts.NetworkErrorException;\n\nimport androidx.annotation.NonNull;\n\nimport java.io.BufferedOutputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.net.HttpURLConnection;\nimport java.net.Proxy;\nimport java.nio.charset.Charset;\nimport java.util.Map;\nimport java.util.concurrent.Executor;\n\nimport javax.net.ssl.HttpsURLConnection;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/02/08\n *     desc  : utils about http\n * </pre>\n */\npublic final class HttpUtils {\n\n    private static final String BOUNDARY    = java.util.UUID.randomUUID().toString();\n    private static final String TWO_HYPHENS = \"--\";\n\n    private static final int CONNECT_TIMEOUT_TIME = 15000;\n    private static final int READ_TIMEOUT_TIME    = 20000;\n    private static final int BUFFER_SIZE          = 8192;\n\n    private static final Config CONFIG = new Config();\n\n\n    private final  Config    mConfig;\n    private static HttpUtils sHttpUtils;\n\n    private HttpUtils(@NonNull Config config) {\n        mConfig = config;\n    }\n\n    public static HttpUtils getInstance(@NonNull Config config) {\n        if (sHttpUtils == null) {\n            synchronized (HttpUtils.class) {\n                sHttpUtils = new HttpUtils(config);\n            }\n        }\n        return sHttpUtils;\n    }\n\n    public static void call(@NonNull final Request request, @NonNull final ResponseCallback callback) {\n        new Call(request, callback).run();\n    }\n\n    private static HttpURLConnection getConnection(final Request request) throws IOException {\n        HttpURLConnection conn = (HttpURLConnection) request.mURL.openConnection();\n        if (conn instanceof HttpsURLConnection) {\n            HttpsURLConnection httpsConn = (HttpsURLConnection) conn;\n            httpsConn.setSSLSocketFactory(CONFIG.sslConfig.mSSLSocketFactory);\n            httpsConn.setHostnameVerifier(CONFIG.sslConfig.mHostnameVerifier);\n        }\n        System.out.println(conn.getHeaderField(\"USE\"));\n        addHeader(conn, request.mHeader);\n        addBody(conn, request.mBody);\n        conn.setConnectTimeout(CONFIG.connectTimeout);\n        conn.setReadTimeout(CONFIG.readTimeout);\n        return conn;\n    }\n\n    private static void addBody(HttpURLConnection conn, Request.Body body) throws IOException {\n        if (body == null) {\n            conn.setRequestMethod(\"GET\");\n        } else {\n            conn.setRequestMethod(\"POST\");\n            conn.setUseCaches(false);\n            conn.setDoOutput(true);\n            conn.setRequestProperty(\"content-type\", body.mediaType);\n            if (body.length > 0) {\n                conn.setRequestProperty(\"content-length\", String.valueOf(body.length));\n            }\n            BufferedOutputStream bos = new BufferedOutputStream(conn.getOutputStream(), 10240);\n            if (body.bis != null) {\n                byte[] buffer = new byte[10240];\n                for (int len; (len = body.bis.read(buffer)) != -1; ) {\n                    bos.write(buffer, 0, len);\n                }\n                bos.close();\n                body.bis.close();\n            }\n        }\n    }\n\n    private static void addHeader(final HttpURLConnection conn, final Map<String, String> headerMap) {\n        if (headerMap != null) {\n            for (String key : headerMap.keySet()) {\n                conn.setRequestProperty(key, headerMap.get(key));\n            }\n        }\n    }\n\n    private static boolean isSpace(final String s) {\n        if (s == null) return true;\n        for (int i = 0, len = s.length(); i < len; ++i) {\n            if (!Character.isWhitespace(s.charAt(i))) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    static String is2String(final InputStream is, final String charset) {\n        ByteArrayOutputStream result = new ByteArrayOutputStream();\n        byte[] buffer = new byte[8192];\n        try {\n            for (int len; (len = is.read(buffer)) != -1; ) {\n                result.write(buffer, 0, len);\n            }\n            return result.toString(charset);\n        } catch (Exception e) {\n            e.printStackTrace();\n            return \"\";\n        } finally {\n            try {\n                is.close();\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    static boolean writeFileFromIS(final File file,\n                                   final InputStream is) {\n        if (!createOrExistsFile(file) || is == null) return false;\n        OutputStream os = null;\n        try {\n            os = new BufferedOutputStream(new FileOutputStream(file));\n            byte[] data = new byte[8192];\n            for (int len; (len = is.read(data)) != -1; ) {\n                os.write(data, 0, len);\n            }\n            return true;\n        } catch (IOException e) {\n            e.printStackTrace();\n            return false;\n        } finally {\n            try {\n                is.close();\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n            try {\n                if (os != null) {\n                    os.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    private static boolean createOrExistsFile(final File file) {\n        if (file == null) return false;\n        if (file.exists()) return file.isFile();\n        if (!createOrExistsDir(file.getParentFile())) return false;\n        try {\n            return file.createNewFile();\n        } catch (IOException e) {\n            e.printStackTrace();\n            return false;\n        }\n    }\n\n    private static boolean createOrExistsDir(final File file) {\n        return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());\n    }\n\n    public static class Config {\n\n        private Executor workExecutor = ExecutorFactory.getDefaultWorkExecutor();\n        private Executor mainExecutor = ExecutorFactory.getDefaultMainExecutor();\n\n        private SSLConfig sslConfig      = SSLConfig.DEFAULT_SSL_CONFIG;\n        private int       connectTimeout = CONNECT_TIMEOUT_TIME;\n        private int       readTimeout    = READ_TIMEOUT_TIME;\n        private Charset   charset        = Charset.defaultCharset();\n\n        private Proxy proxy = null;\n    }\n\n    static class Call implements Runnable {\n\n        private Request          request;\n        private ResponseCallback callback;\n\n        public Call(Request request, ResponseCallback callback) {\n            this.request = request;\n            this.callback = callback;\n        }\n\n        @Override\n        public void run() {\n            HttpURLConnection conn = null;\n            try {\n                conn = getConnection(request);\n                int responseCode = conn.getResponseCode();\n                if (responseCode == 200) {\n                    InputStream is = conn.getInputStream();\n                    callback.onResponse(new Response(conn.getHeaderFields(), is));\n                    is.close();\n                } else if (responseCode == 301 || responseCode == 302) {\n                    String location = conn.getHeaderField(\"Location\");\n                    call(request, callback);\n                } else {\n                    String errorMsg = null;\n                    InputStream es = conn.getErrorStream();\n                    if (es != null) {\n                        errorMsg = is2String(es, \"utf-8\");\n                    }\n                    callback.onFailed(new NetworkErrorException(\"error code: \" + responseCode +\n                            (isSpace(errorMsg) ? \"\" : (\"\\n\" + \"error message: \" + errorMsg))));\n                }\n            } catch (IOException e) {\n                callback.onFailed(e);\n            } finally {\n                if (conn != null) {\n                    conn.disconnect();\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/http/Interceptor.java",
    "content": "package com.blankj.subutil.util.http;\n\nimport java.io.IOException;\n\npublic interface Interceptor {\n\n    Response intercept(Chain chain) throws IOException;\n}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/http/Request.java",
    "content": "package com.blankj.subutil.util.http;\n\n\nimport androidx.annotation.NonNull;\n\nimport java.io.BufferedInputStream;\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.io.UnsupportedEncodingException;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.nio.charset.Charset;\nimport java.nio.charset.IllegalCharsetNameException;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/02/17\n * </pre>\n */\npublic final class Request {\n\n    URL                 mURL;\n    Map<String, String> mHeader;\n    Body                mBody;\n\n    public static Request withUrl(@NonNull final String url) {\n        return new Request(url);\n    }\n\n    private Request(final String url) {\n        try {\n            mURL = new URL(url);\n        } catch (MalformedURLException e) {\n            e.printStackTrace();\n        }\n    }\n\n    public Request addHeader(@NonNull final String name, @NonNull final String value) {\n        if (mHeader == null) {\n            mHeader = new HashMap<>();\n        }\n        mHeader.put(name, value);\n        return this;\n    }\n\n    public Request addHeader(@NonNull final Map<String, String> header) {\n        if (this.mHeader == null) {\n            this.mHeader = new HashMap<>();\n        }\n        this.mHeader.putAll(header);\n        return this;\n    }\n\n    public Request post(@NonNull final Body body) {\n        this.mBody = body;\n        return this;\n    }\n\n    public static class Body {\n        String              mediaType;\n        BufferedInputStream bis;\n        long                length;\n\n        private Body(final String mediaType, final byte[] body) {\n            this.mediaType = mediaType;\n            bis = new BufferedInputStream(new ByteArrayInputStream(body));\n            length = body.length;\n        }\n\n        private Body(final String mediaType, final InputStream body) {\n            this.mediaType = mediaType;\n            if (body instanceof BufferedInputStream) {\n                bis = (BufferedInputStream) body;\n            } else {\n                bis = new BufferedInputStream(body);\n            }\n            length = -1;\n        }\n\n        private static String getCharsetFromMediaType(String mediaType) {\n            mediaType = mediaType.toLowerCase().replace(\" \", \"\");\n            int index = mediaType.indexOf(\"charset=\");\n            if (index == -1) return \"utf-8\";\n            int st = index + 8;\n            int end = mediaType.length();\n            if (st >= end) {\n                throw new IllegalArgumentException(\"MediaType is not correct: \\\"\" + mediaType + \"\\\"\");\n            }\n            for (int i = st; i < end; i++) {\n                char c = mediaType.charAt(i);\n                if (c >= 'A' && c <= 'Z') continue;\n                if (c >= 'a' && c <= 'z') continue;\n                if (c >= '0' && c <= '9') continue;\n                if (c == '-' && i != 0) continue;\n                if (c == '+' && i != 0) continue;\n                if (c == ':' && i != 0) continue;\n                if (c == '_' && i != 0) continue;\n                if (c == '.' && i != 0) continue;\n                end = i;\n                break;\n            }\n            String charset = mediaType.substring(st, end);\n            return checkCharset(charset);\n        }\n\n        public static Body create(@NonNull String mediaType, @NonNull byte[] content) {\n            return new Body(mediaType, content);\n        }\n\n        public static Body form(@NonNull final Map<String, String> form) {\n            return form(form, \"utf-8\");\n        }\n\n        public static Body form(@NonNull final Map<String, String> form, String charset) {\n            String mediaType = \"application/x-www-form-urlencoded;charset=\" + checkCharset(charset);\n            final StringBuilder sb = new StringBuilder();\n            for (String key : form.keySet()) {\n                if (sb.length() > 0) sb.append(\"&\");\n                sb.append(key).append(\"=\").append(form.get(key));\n            }\n            try {\n                return new Body(mediaType, sb.toString().getBytes(charset));\n            } catch (UnsupportedEncodingException e) {\n                e.printStackTrace();\n                return null;\n            }\n        }\n\n        public static Body json(@NonNull final String json) {\n            return json(json, \"utf-8\");\n        }\n\n        public static Body json(@NonNull final String json, String charset) {\n            String mediaType = \"application/json;charset=\" + checkCharset(charset);\n            try {\n                return new Body(mediaType, json.getBytes(charset));\n            } catch (UnsupportedEncodingException e) {\n                e.printStackTrace();\n                return null;\n            }\n        }\n\n//        public static RequestBody file(String mediaType, final File file) {\n//\n//            return new RequestBody(mediaType, );\n//        }\n\n    }\n\n    private static String checkCharset(final String charset) {\n        if (Charset.isSupported(charset)) return charset;\n        throw new IllegalCharsetNameException(charset);\n    }\n}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/http/Response.java",
    "content": "package com.blankj.subutil.util.http;\n\n\nimport com.google.gson.Gson;\n\nimport java.io.File;\nimport java.io.InputStream;\nimport java.lang.reflect.Type;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/02/17\n * </pre>\n */\npublic class Response {\n    private Map<String, List<String>> mHeaders;\n    private InputStream               mBody;\n\n    public Response(Map<String, List<String>> headers, InputStream body) {\n        mHeaders = headers;\n        mBody = body;\n    }\n\n    public Map<String, List<String>> getHeaders() {\n        return mHeaders;\n    }\n\n    public InputStream getBody() {\n        return mBody;\n    }\n\n    public String getString() {\n        return getString(\"utf-8\");\n    }\n\n    public String getString(final String charset) {\n        return HttpUtils.is2String(mBody, charset);\n    }\n\n    public <T> T getJson(final Type type) {\n        return getJson(type, \"utf-8\");\n    }\n\n    public <T> T getJson(final Type type, final String charset) {\n        return new Gson().fromJson(getString(charset), type);\n    }\n\n    public boolean downloadFile(final File file) {\n        return HttpUtils.writeFileFromIS(file, mBody);\n    }\n}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/http/ResponseCallback.java",
    "content": "package com.blankj.subutil.util.http;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/02/17\n * </pre>\n */\npublic abstract class ResponseCallback {\n    public abstract void onResponse(Response response);\n\n    public abstract void onFailed(Exception e);\n}\n"
  },
  {
    "path": "lib/subutil/src/main/java/com/blankj/subutil/util/http/SSLConfig.java",
    "content": "package com.blankj.subutil.util.http;\n\nimport android.annotation.SuppressLint;\nimport android.os.Build;\n\nimport androidx.annotation.NonNull;\n\nimport java.io.IOException;\nimport java.net.InetAddress;\nimport java.net.Socket;\nimport java.security.GeneralSecurityException;\nimport java.security.SecureRandom;\nimport java.security.cert.X509Certificate;\n\nimport javax.net.ssl.HostnameVerifier;\nimport javax.net.ssl.SSLContext;\nimport javax.net.ssl.SSLEngine;\nimport javax.net.ssl.SSLSession;\nimport javax.net.ssl.SSLSocket;\nimport javax.net.ssl.SSLSocketFactory;\nimport javax.net.ssl.TrustManager;\nimport javax.net.ssl.X509ExtendedTrustManager;\nimport javax.net.ssl.X509TrustManager;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/02/20\n * </pre>\n */\npublic final class SSLConfig {\n\n    SSLSocketFactory mSSLSocketFactory;\n    HostnameVerifier mHostnameVerifier;\n\n    public SSLConfig(@NonNull SSLSocketFactory factory, @NonNull HostnameVerifier verifier) {\n        mSSLSocketFactory = factory;\n        mHostnameVerifier = verifier;\n    }\n\n    public static final HostnameVerifier DEFAULT_VERIFIER           = new HostnameVerifier() {\n        public boolean verify(String hostname, SSLSession session) {\n            return true;\n        }\n    };\n    public static final SSLSocketFactory DEFAULT_SSL_SOCKET_FACTORY = new DefaultSSLSocketFactory();\n\n    public static final SSLConfig DEFAULT_SSL_CONFIG = new SSLConfig(DEFAULT_SSL_SOCKET_FACTORY, DEFAULT_VERIFIER);\n\n    private static class DefaultSSLSocketFactory extends SSLSocketFactory {\n\n        private static final String[]       PROTOCOL_ARRAY;\n        private static final TrustManager[] DEFAULT_TRUST_MANAGERS;\n\n        static {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                PROTOCOL_ARRAY = new String[]{\"TLSv1\", \"TLSv1.1\", \"TLSv1.2\"};\n            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n                PROTOCOL_ARRAY = new String[]{\"SSLv3\", \"TLSv1\", \"TLSv1.1\", \"TLSv1.2\"};\n            } else {\n                PROTOCOL_ARRAY = new String[]{\"SSLv3\", \"TLSv1\"};\n            }\n\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n                DEFAULT_TRUST_MANAGERS = new TrustManager[]{\n                        new X509ExtendedTrustManager() {\n                            @SuppressLint(\"TrustAllX509TrustManager\")\n                            @Override\n                            public void checkClientTrusted(X509Certificate[] chain, String authType) {/**/}\n\n                            @SuppressLint(\"TrustAllX509TrustManager\")\n                            @Override\n                            public void checkServerTrusted(X509Certificate[] chain, String authType) {/**/}\n\n                            @Override\n                            public X509Certificate[] getAcceptedIssuers() {\n                                return new X509Certificate[0];\n                            }\n\n                            @SuppressLint(\"TrustAllX509TrustManager\")\n                            @Override\n                            public void checkClientTrusted(X509Certificate[] chain, String authType, Socket socket) {/**/}\n\n                            @SuppressLint(\"TrustAllX509TrustManager\")\n                            @Override\n                            public void checkServerTrusted(X509Certificate[] chain, String authType, Socket socket) {/**/}\n\n                            @SuppressLint(\"TrustAllX509TrustManager\")\n                            @Override\n                            public void checkClientTrusted(X509Certificate[] chain, String authType, SSLEngine engine) {/**/}\n\n                            @SuppressLint(\"TrustAllX509TrustManager\")\n                            @Override\n                            public void checkServerTrusted(X509Certificate[] chain, String authType, SSLEngine engine) {/**/}\n                        }\n                };\n            } else {\n                DEFAULT_TRUST_MANAGERS = new TrustManager[]{\n                        new X509TrustManager() {\n                            @SuppressLint(\"TrustAllX509TrustManager\")\n                            @Override\n                            public void checkClientTrusted(X509Certificate[] chain, String authType) {/**/}\n\n                            @SuppressLint(\"TrustAllX509TrustManager\")\n                            @Override\n                            public void checkServerTrusted(X509Certificate[] chain, String authType) {/**/}\n\n                            @Override\n                            public X509Certificate[] getAcceptedIssuers() {\n                                return new X509Certificate[0];\n                            }\n                        }\n                };\n            }\n\n        }\n\n        private SSLSocketFactory mFactory;\n\n        DefaultSSLSocketFactory() {\n            try {\n                SSLContext sslContext = SSLContext.getInstance(\"TLS\");\n                sslContext.init(null, DEFAULT_TRUST_MANAGERS, new SecureRandom());\n                mFactory = sslContext.getSocketFactory();\n            } catch (GeneralSecurityException e) {\n                throw new AssertionError();\n            }\n        }\n\n        @Override\n        public String[] getDefaultCipherSuites() {\n            return mFactory.getDefaultCipherSuites();\n        }\n\n        @Override\n        public String[] getSupportedCipherSuites() {\n            return mFactory.getSupportedCipherSuites();\n        }\n\n        @Override\n        public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {\n            Socket ssl = mFactory.createSocket(s, host, port, autoClose);\n            setSupportProtocolAndCipherSuites(ssl);\n            return ssl;\n        }\n\n        @Override\n        public Socket createSocket(String host, int port) throws IOException {\n            Socket ssl = mFactory.createSocket(host, port);\n            setSupportProtocolAndCipherSuites(ssl);\n            return ssl;\n        }\n\n        @Override\n        public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {\n            Socket ssl = mFactory.createSocket(host, port, localHost, localPort);\n            setSupportProtocolAndCipherSuites(ssl);\n            return ssl;\n        }\n\n        @Override\n        public Socket createSocket(InetAddress host, int port) throws IOException {\n            Socket ssl = mFactory.createSocket(host, port);\n            setSupportProtocolAndCipherSuites(ssl);\n            return ssl;\n        }\n\n        @Override\n        public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {\n            Socket ssl = mFactory.createSocket(address, port, localAddress, localPort);\n            setSupportProtocolAndCipherSuites(ssl);\n            return ssl;\n        }\n\n        @Override\n        public Socket createSocket() throws IOException {\n            Socket ssl = mFactory.createSocket();\n            setSupportProtocolAndCipherSuites(ssl);\n            return ssl;\n        }\n\n        private void setSupportProtocolAndCipherSuites(Socket socket) {\n            if (socket instanceof SSLSocket) {\n                ((SSLSocket) socket).setEnabledProtocols(PROTOCOL_ARRAY);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lib/subutil/src/test/java/com/blankj/subutil/util/BaseTest.java",
    "content": "package com.blankj.subutil.util;\n\n\nimport com.blankj.utilcode.util.Utils;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.robolectric.RobolectricTestRunner;\nimport org.robolectric.RuntimeEnvironment;\nimport org.robolectric.annotation.Config;\nimport org.robolectric.shadows.ShadowLog;\n\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/08/03\n *     desc  :\n * </pre>\n */\n@RunWith(RobolectricTestRunner.class)\n@Config(manifest = Config.NONE, shadows = {ShadowLog.class})\npublic class BaseTest {\n\n    public BaseTest() {\n        ShadowLog.stream = System.out;\n        Utils.init(RuntimeEnvironment.application);\n    }\n\n    @Test\n    public void test() throws Exception {\n    }\n}\n"
  },
  {
    "path": "lib/subutil/src/test/java/com/blankj/subutil/util/CoordinateUtilsTest.java",
    "content": "package com.blankj.subutil.util;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport static java.lang.Math.PI;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/03/22\n *     desc  : CoordinateUtils 单元测试\n * </pre>\n */\npublic class CoordinateUtilsTest {\n\n    // 以下为各个坐标系的 天安门坐标\n    private static final double[] locationWGS84 = new double[]{116.3912022800, 39.9075017400};\n    private static final double[] locationGCJ02 = new double[]{116.3973900000, 39.9088600000};\n    private static final double[] locationBD09  = new double[]{116.4038206839, 39.9152478931};\n\n    // 以下为美国纽约坐标\n    private static final double[] newyorkWGS84 = new double[]{-74.0059413000, 40.7127837000};\n\n    @Test\n    public void gcj2BD09() throws Exception {\n        double[] BD09 = CoordinateUtils.gcj02ToBd09(locationGCJ02[0], locationGCJ02[1]);\n        double distance = distance(locationBD09[0], locationBD09[1], BD09[0], BD09[1]);\n        System.out.println(\"distance: \" + distance);\n        Assert.assertTrue(distance < 10);\n    }\n\n    @Test\n    public void bd092GCJ() {\n        double[] GCJ02 = CoordinateUtils.bd09ToGcj02(locationBD09[0], locationBD09[1]);\n        double distance = distance(locationGCJ02[0], locationGCJ02[1], GCJ02[0], GCJ02[1]);\n        System.out.println(\"distance: \" + distance);\n        Assert.assertTrue(distance < 10);\n    }\n\n    @Test\n    public void bd092WGS() {\n        double[] WGS84 = CoordinateUtils.bd09ToWGS84(locationBD09[0], locationBD09[1]);\n        double distance = distance(locationWGS84[0], locationWGS84[1], WGS84[0], WGS84[1]);\n        System.out.println(\"distance: \" + distance);\n        Assert.assertTrue(distance < 10);\n    }\n\n    @Test\n    public void wgs2BD09() {\n        double[] BD09 = CoordinateUtils.wgs84ToBd09(locationWGS84[0], locationWGS84[1]);\n        double distance = distance(locationBD09[0], locationBD09[1], BD09[0], BD09[1]);\n        System.out.println(\"distance: \" + distance);\n        Assert.assertTrue(distance < 10);\n    }\n\n    @Test\n    public void wgs2GCJ() {\n        double[] GCJ02 = CoordinateUtils.wgs84ToGcj02(locationWGS84[0], locationWGS84[1]);\n        double distance = distance(locationGCJ02[0], locationGCJ02[1], GCJ02[0], GCJ02[1]);\n        System.out.println(\"distance: \" + distance);\n        Assert.assertTrue(distance < 10);\n    }\n\n    @Test\n    public void gcj2WGS() {\n        double[] WGS84 = CoordinateUtils.gcj02ToWGS84(locationGCJ02[0], locationGCJ02[1]);\n        double distance = distance(locationWGS84[0], locationWGS84[1], WGS84[0], WGS84[1]);\n        System.out.println(\"distance: \" + distance);\n        Assert.assertTrue(distance < 10);\n    }\n\n    @Test\n    public void gcj2WGSExactly() {\n        double[] WGS84 = CoordinateUtils.gcj02ToWGS84(locationGCJ02[0], locationGCJ02[1]);\n        double distance = distance(locationWGS84[0], locationWGS84[1], WGS84[0], WGS84[1]);\n        System.out.println(\"distance: \" + distance);\n        Assert.assertTrue(distance < 10);\n    }\n\n    public static double distance(double lngA, double latA, double lngB, double latB) {\n        int earthR = 6371000;\n        double x = Math.cos(latA * PI / 180) * Math.cos(latB * PI / 180) * Math.cos((lngA - lngB) * PI / 180);\n        double y = Math.sin(latA * PI / 180) * Math.sin(latB * PI / 180);\n        double s = x + y;\n        if (s > 1)\n            s = 1;\n        if (s < -1)\n            s = -1;\n        double alpha = Math.acos(s);\n        return alpha * earthR;\n    }\n}"
  },
  {
    "path": "lib/subutil/src/test/java/com/blankj/subutil/util/LunarUtilsTest.java",
    "content": "package com.blankj.subutil.util;\n\nimport org.junit.Test;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/04/08\n *     desc  : LunarUtils 单元测试\n * </pre>\n */\npublic class LunarUtilsTest {\n\n    @Test\n    public void lunarYear2GanZhi() throws Exception {\n        System.out.println(LunarUtils.lunarYear2GanZhi(2018));\n    }\n\n    @Test\n    public void lunar2Solar() throws Exception {\n        System.out.println(LunarUtils.lunar2Solar(new LunarUtils.Lunar(2018, 2, 23, false)));\n    }\n\n    @Test\n    public void solar2Lunar() throws Exception {\n        System.out.println(LunarUtils.solar2Lunar(new LunarUtils.Solar(2018, 4, 8)));\n    }\n\n}"
  },
  {
    "path": "lib/subutil/src/test/java/com/blankj/subutil/util/TemperatureUtilsTest.java",
    "content": "package com.blankj.subutil.util;\n\n\nimport org.junit.Assert;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.junit.runners.JUnit4;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/03/22\n *     desc  : TemperatureUtils 单元测试\n * </pre>\n */\n@RunWith(JUnit4.class)\npublic class TemperatureUtilsTest {\n\n    private float delta = 1e-15f;\n\n    @Test\n    public void cToF() {\n        Assert.assertEquals(32f, TemperatureUtils.cToF(0f), delta);\n    }\n\n    @Test\n    public void cToK() {\n        Assert.assertEquals(273.15f, TemperatureUtils.cToK(0f), delta);\n    }\n\n\n    @Test\n    public void fToC() {\n        Assert.assertEquals(-17.777779f, TemperatureUtils.fToC(0f), delta);\n    }\n\n    @Test\n    public void fToK() {\n        Assert.assertEquals(255.3722222222f, TemperatureUtils.fToK(0f), delta);\n    }\n\n\n    @Test\n    public void kToC() {\n        Assert.assertEquals(-273.15f, TemperatureUtils.kToC(0f), delta);\n    }\n\n    @Test\n    public void kToF() {\n        Assert.assertEquals(-459.67f, TemperatureUtils.kToF(0f), delta);\n    }\n\n}\n"
  },
  {
    "path": "lib/subutil/src/test/java/com/blankj/subutil/util/TestConfig.java",
    "content": "package com.blankj.subutil.util;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/09/10\n *     desc  : config of test\n * </pre>\n */\npublic class TestConfig {\n\n    static final String FILE_SEP = System.getProperty(\"file.separator\");\n\n    static final String LINE_SEP = System.getProperty(\"line.separator\");\n\n    static final String TEST_PATH;\n\n    static {\n        String projectPath = System.getProperty(\"user.dir\");\n        if (!projectPath.contains(\"subutil\")) {\n            projectPath += FILE_SEP + \"subutil\";\n        }\n        TEST_PATH = projectPath + FILE_SEP + \"src\" + FILE_SEP + \"test\" + FILE_SEP + \"res\";\n    }\n\n\n    public static final String PATH_HTTP = TEST_PATH + \"http\" + FILE_SEP;\n}\n"
  },
  {
    "path": "lib/subutil/src/test/java/com/blankj/subutil/util/http/HttpUtilsTest.java",
    "content": "package com.blankj.subutil.util.http;\n\nimport com.blankj.subutil.util.BaseTest;\nimport com.blankj.subutil.util.TestConfig;\nimport com.blankj.utilcode.util.FileIOUtils;\nimport com.blankj.utilcode.util.GsonUtils;\nimport com.blankj.utilcode.util.TimeUtils;\n\nimport org.apache.tools.ant.util.FileUtils;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.util.List;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/02/10\n *     desc  :\n * </pre>\n */\npublic class HttpUtilsTest extends BaseTest {\n\n    private static final String BASE_URL = \"http://127.0.0.1:8081\";\n\n//    @Test\n//    public void getString() {\n//        HttpUtils.call(Request.withUrl(BASE_URL + \"/listUsers\"), new ResponseCallback() {\n//            @Override\n//            public void onResponse(Response response) {\n//                System.out.println(response.getHeaders());\n//                System.out.println(response.getString());\n//            }\n//\n//            @Override\n//            public void onFailed(Exception e) {\n//                e.printStackTrace();\n//            }\n//        });\n//    }\n//\n//    @Test\n//    public void getJson() {\n//        HttpUtils.call(Request.withUrl(BASE_URL + \"/listUsers\"), new ResponseCallback() {\n//            @Override\n//            public void onResponse(Response response) {\n//                System.out.println(response.getHeaders());\n//                List<UserBean> users = response.getJson(GsonUtils.getListType(UserBean.class));\n//                System.out.println(GsonUtils.toJson(users));\n//            }\n//\n//            @Override\n//            public void onFailed(Exception e) {\n//                e.printStackTrace();\n//            }\n//        });\n//    }\n//\n//    @Test\n//    public void downloadFile() {\n//        HttpUtils.call(Request.withUrl(BASE_URL + \"/listUsers\"), new ResponseCallback() {\n//            @Override\n//            public void onResponse(Response response) {\n//                System.out.println(response.getHeaders());\n//                File file = new File(TestConfig.PATH_HTTP + TimeUtils.getNowMills());\n//                response.downloadFile(file);\n//                System.out.println(FileIOUtils.readFile2String(file));\n//                FileUtils.delete(file);\n//            }\n//\n//            @Override\n//            public void onFailed(Exception e) {\n//                e.printStackTrace();\n//            }\n//        });\n//    }\n\n}"
  },
  {
    "path": "lib/subutil/src/test/java/com/blankj/subutil/util/http/UserBean.java",
    "content": "package com.blankj.subutil.util.http;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/02/17\n *     desc  :\n * </pre>\n */\nclass UserBean {\n\n    private String name;\n    private String password;\n    private String profession;\n    private int    id;\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n\n    public String getPassword() {\n        return password;\n    }\n\n    public void setPassword(String password) {\n        this.password = password;\n    }\n\n    public String getProfession() {\n        return profession;\n    }\n\n    public void setProfession(String profession) {\n        this.profession = profession;\n    }\n\n    public int getId() {\n        return id;\n    }\n\n    public void setId(int id) {\n        this.id = id;\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/.gitignore",
    "content": "/build"
  },
  {
    "path": "lib/utilcode/README-CN.md",
    "content": "## Download\n\nGradle:\n```groovy\n// if u use AndroidX, use the following\nimplementation 'com.blankj:utilcodex:1.31.1'\n\n// Not in maintenance\nimplementation 'com.blankj:utilcode:1.30.7'\n```\n\n\n## APIs\n\n* ### Activity 相关 -> [ActivityUtils.java][activity.java] -> [Demo][activity.demo]\n```\naddActivityLifecycleCallbacks   : 增加 Activity 生命周期监听\nremoveActivityLifecycleCallbacks: 移除 Activity 生命周期监听\ngetAliveActivityByContext       : 根据上下文获取存活的 Activity\ngetActivityByContext            : 根据上下文获取 Activity\nisActivityExists                : 判断 Activity 是否存在\nstartActivity                   : 启动 Activity\nstartActivityForResult          : 启动 Activity 为返回结果\nstartActivities                 : 启动多个 Activity\nstartHomeActivity               : 回到桌面\ngetActivityList                 : 获取 Activity 栈链表\ngetLauncherActivity             : 获取启动项 Activity\ngetMainActivities               : 获取主的 Activity 们\ngetTopActivity                  : 获取栈顶 Activity\nisActivityAlive                 : 判断 Activity 是否存活\nisActivityExistsInStack         : 判断 Activity 是否存在栈中\nfinishActivity                  : 结束 Activity\nfinishToActivity                : 结束到指定 Activity\nfinishOtherActivities           : 结束所有其他类型的 Activity\nfinishAllActivities             : 结束所有 Activity\nfinishAllActivitiesExceptNewest : 结束除最新之外的所有 Activity\n```\n\n* ### AdaptScreen 相关 -> [AdaptScreenUtils.java][adaptScreen.java] -> [Demo][adaptScreen.demo]\n```\nadaptWidth : 适配宽度\nadaptHeight: 适配高度\ncloseAdapt : 关闭适配（pt 等同于 dp）\npt2Px      : pt 转 px\npx2Pt      : px 转 pt\n```\n\n* ### Api 相关 -> [ApiUtils.java][api.java] -> [README][api.readme]\n```\ngetApi: 获取 api 的实例\n```\n\n* ### App 相关 -> [AppUtils.java][app.java] -> [Demo][app.demo]\n```\nregisterAppStatusChangedListener  : 注册 App 前后台切换监听器\nunregisterAppStatusChangedListener: 注销 App 前后台切换监听器\ninstallApp                        : 安装 App（支持 8.0）\nuninstallApp                      : 卸载 App\nisAppInstalled                    : 判断 App 是否安装\nisAppRoot                         : 判断 App 是否有 root 权限\nisAppDebug                        : 判断 App 是否是 Debug 版本\nisAppSystem                       : 判断 App 是否是系统应用\nisAppForeground                   : 判断 App 是否处于前台\nisAppRunning                      : 判断 App 是否运行\nlaunchApp                         : 打开 App\nrelaunchApp                       : 重启 App\nlaunchAppDetailsSettings          : 打开 App 具体设置\nexitApp                           : 关闭应用\ngetAppIcon                        : 获取 App 图标\ngetAppPackageName                 : 获取 App 包名\ngetAppName                        : 获取 App 名称\ngetAppPath                        : 获取 App 路径\ngetAppVersionName                 : 获取 App 版本号\ngetAppVersionCode                 : 获取 App 版本码\ngetAppMinSdkVersion               : 获取 App 支持最低系统版本号\ngetAppTargetSdkVersion            : 获取 App 目标系统版本号\ngetAppSignatures                  : 获取 App 签名\ngetAppSignaturesSHA1              : 获取应用签名的的 SHA1 值\ngetAppSignaturesSHA256            : 获取应用签名的的 SHA256 值\ngetAppSignaturesMD5               : 获取应用签名的的 MD5 值\ngetAppInfo                        : 获取 App 信息\ngetAppsInfo                       : 获取所有已安装 App 信息\ngetApkInfo                        : 获取 Apk 信息\nisFirstTimeInstalled              : 判断应用是否首次安装\n```\n\n* ### 数组相关 -> [ArrayUtils.java][array.java] -> [Test][array.test]\n```\nnewArray          : 新建数组\nnewLongArray      : 新建长整型数组\nnewIntArray       : 新建整型数组\nnewShortArray     : 新建 short 数组\nnewCharArray      : 新建字符数组\nnewByteArray      : 新建字节数组\nnewDoubleArray    : 新建双精度数组\nnewFloatArray     : 新建浮点数数组\nnewBooleanArray   : 新建 boolean 数组\nisEmpty           : 判断数组是否为空\ngetLength         : 获取数组长度\nisSameLength      : 判断两数组长度是否相等\nget               : 获取数组的索引值\nset               : 设置数组的索引值\nequals            : 判断数组是否相等\nreverse           : 逆序数组\ncopy              : 拷贝数组\nsubArray          : 截取数组\nadd               : 增加数组\nremove            : 移除指定的索引\nremoveElement     : 移除指定的元素\nindexOf           : 查找第一个元素的索引\nlastIndexOf       : 查找最后一个元素的索引\ncontains          : 判断是否包含该元素\ntoPrimitive       : 装箱数组转基本类型数组\ntoObject          : 基本类型数组转装箱数组\nasList            : 转为链表\nasUnmodifiableList: 转为不可变链表\nasArrayList       : 转为数组链表\nasLinkedList      : 转为双向链表\nsort              : 排序\nforAllDo          : 对所有元素做操作\ntoString          : 数组转为字符串\n```\n\n* ### 栏相关 -> [BarUtils.java][bar.java] -> [Demo][bar.demo]\n```\ngetStatusBarHeight                   : 获取状态栏高度（px）\nsetStatusBarVisibility               : 设置状态栏是否可见\nisStatusBarVisible                   : 判断状态栏是否可见\nsetStatusBarLightMode                : 设置状态栏是否为浅色模式\nisStatusBarLightMode                 : 判断状态栏是否为浅色模式\naddMarginTopEqualStatusBarHeight     : 为 view 增加 MarginTop 为状态栏高度\nsubtractMarginTopEqualStatusBarHeight: 为 view 减少 MarginTop 为状态栏高度\nsetStatusBarColor                    : 设置状态栏颜色\nsetStatusBarColor4Drawer             : 为 DrawerLayout 设置状态栏颜色\ntransparentStatusBar                 : 透明状态栏\ngetActionBarHeight                   : 获取 ActionBar 高度\nsetNotificationBarVisibility         : 设置通知栏是否可见\ngetNavBarHeight                      : 获取导航栏高度\nsetNavBarVisibility                  : 设置导航栏是否可见\nisNavBarVisible                      : 判断导航栏是否可见\nsetNavBarColor                       : 设置导航栏颜色\ngetNavBarColor                       : 获取导航栏颜色\nisSupportNavBar                      : 判断是否支持导航栏\nsetNavBarLightMode                   : 设置导航栏是否为浅色模式\nisNavBarLightMode                    : 判断导航栏是否为浅色模式\ntransparentNavBar                    : 透明导航栏\n```\n\n* ### 亮度相关 -> [BrightnessUtils.java][brightness.java] -> [Demo][brightness.demo]\n```\nisAutoBrightnessEnabled : 判断是否开启自动调节亮度\nsetAutoBrightnessEnabled: 设置是否开启自动调节亮度\ngetBrightness           : 获取屏幕亮度\nsetBrightness           : 设置屏幕亮度\nsetWindowBrightness     : 设置窗口亮度\ngetWindowBrightness     : 获取窗口亮度\n```\n\n* ### Bus 相关 -> [BusUtils.java][bus.java] -> [README][bus.readme]\n```\nregister    : 注册\nunregister  : 注销\npost        : 发送\npostSticky  : 发送粘性\nremoveSticky: 移除粘性\ntoString_   : 查看插入的信息\n```\n\n* ### 磁盘缓存相关 -> [CacheDiskStaticUtils.java][cacheDiskStatic.java] -> [Test][cacheDiskStatic.test]\n```\nsetDefaultCacheDiskUtils: 设置默认磁盘缓存实例\nput                     : 缓存中写入数据\ngetBytes                : 缓存中读取字节数组\ngetString               : 缓存中读取 String\ngetJSONObject           : 缓存中读取 JSONObject\ngetJSONArray            : 缓存中读取 JSONArray\ngetBitmap               : 缓存中读取 Bitmap\ngetDrawable             : 缓存中读取 Drawable\ngetParcelable           : 缓存中读取 Parcelable\ngetSerializable         : 缓存中读取 Serializable\ngetCacheSize            : 获取缓存大小\ngetCacheCount           : 获取缓存个数\nremove                  : 根据键值移除缓存\nclear                   : 清除所有缓存\n```\n\n* ### 磁盘缓存相关 -> [CacheDiskUtils.java][cacheDisk.java] -> [Test][cacheDisk.test]\n```\ngetInstance             : 获取缓存实例\nInstance.put            : 缓存中写入数据\nInstance.getBytes       : 缓存中读取字节数组\nInstance.getString      : 缓存中读取 String\nInstance.getJSONObject  : 缓存中读取 JSONObject\nInstance.getJSONArray   : 缓存中读取 JSONArray\nInstance.getBitmap      : 缓存中读取 Bitmap\nInstance.getDrawable    : 缓存中读取 Drawable\nInstance.getParcelable  : 缓存中读取 Parcelable\nInstance.getSerializable: 缓存中读取 Serializable\nInstance.getCacheSize   : 获取缓存大小\nInstance.getCacheCount  : 获取缓存个数\nInstance.remove         : 根据键值移除缓存\nInstance.clear          : 清除所有缓存\n```\n\n* ### 二级缓存相关 -> [CacheDoubleStaticUtils.java][cacheDoubleStatic.java] -> [Test][cacheDoubleStatic.test]\n```\nsetDefaultCacheDoubleUtils: 设置默认二级缓存实例\nput                       : 缓存中写入数据\ngetBytes                  : 缓存中读取字节数组\ngetString                 : 缓存中读取 String\ngetJSONObject             : 缓存中读取 JSONObject\ngetJSONArray              : 缓存中读取 JSONArray\ngetBitmap                 : 缓存中读取 Bitmap\ngetDrawable               : 缓存中读取 Drawable\ngetParcelable             : 缓存中读取 Parcelable\ngetSerializable           : 缓存中读取 Serializable\ngetCacheDiskSize          : 获取磁盘缓存大小\ngetCacheDiskCount         : 获取磁盘缓存个数\ngetCacheMemoryCount       : 获取内存缓存个数\nremove                    : 根据键值移除缓存\nclear                     : 清除所有缓存\n```\n\n* ### 二级缓存相关 -> [CacheDoubleUtils.java][cacheDouble.java] -> [Test][cacheDouble.test]\n```\ngetInstance                 : 获取缓存实例\nInstance.put                : 缓存中写入数据\nInstance.getBytes           : 缓存中读取字节数组\nInstance.getString          : 缓存中读取 String\nInstance.getJSONObject      : 缓存中读取 JSONObject\nInstance.getJSONArray       : 缓存中读取 JSONArray\nInstance.getBitmap          : 缓存中读取 Bitmap\nInstance.getDrawable        : 缓存中读取 Drawable\nInstance.getParcelable      : 缓存中读取 Parcelable\nInstance.getSerializable    : 缓存中读取 Serializable\nInstance.getCacheDiskSize   : 获取磁盘缓存大小\nInstance.getCacheDiskCount  : 获取磁盘缓存个数\nInstance.getCacheMemoryCount: 获取内存缓存个数\nInstance.remove             : 根据键值移除缓存\nInstance.clear              : 清除所有缓存\n```\n\n* ### 内存缓存相关 -> [CacheMemoryStaticUtils.java][cacheMemoryStatic.java] -> [Test][cacheMemoryStatic.test]\n```\nsetDefaultCacheMemoryUtils: 设置默认内存缓存实例\nput                       : 缓存中写入数据\nget                       : 缓存中读取字节数组\ngetCacheCount             : 获取缓存个数\nremove                    : 根据键值移除缓存\nclear                     : 清除所有缓存\n```\n\n* ### 内存缓存相关 -> [CacheMemoryUtils.java][cacheMemory.java] -> [Test][cacheMemory.test]\n```\ngetInstance           : 获取缓存实例\nInstance.put          : 缓存中写入数据\nInstance.get          : 缓存中读取字节数组\nInstance.getCacheCount: 获取缓存个数\nInstance.remove       : 根据键值移除缓存\nInstance.clear        : 清除所有缓存\n```\n\n* ### 清除相关 -> [CleanUtils.java][clean.java] -> [Demo][clean.demo]\n```\ncleanInternalCache   : 清除内部缓存\ncleanInternalFiles   : 清除内部文件\ncleanInternalDbs     : 清除内部数据库\ncleanInternalDbByName: 根据名称清除数据库\ncleanInternalSp      : 清除内部 SP\ncleanExternalCache   : 清除外部缓存\ncleanCustomDir       : 清除自定义目录下的文件\n```\n\n* ### 点击相关 -> [ClickUtils.java][click.java] -> [Demo][click.demo]\n```\napplyPressedViewScale               : 应用点击后对视图缩放\napplyPressedViewAlpha               : 应用点击后对视图改变透明度\napplyPressedBgAlpha                 : 应用点击后对背景改变透明度\napplyPressedBgDark                  : 应用点击后对背景加深\napplySingleDebouncing               : 对单视图应用防抖点击\napplyGlobalDebouncing               : 对所有设置 GlobalDebouncing 的视图应用防抖点击\nexpandClickArea                     : 扩大点击区域\nback2HomeFriendly                   : 友好地返回桌面\nClickUtils#OnDebouncingClickListener: 防抖点击监听器\nClickUtils#OnMultiClickListener     : 连续点击监听器\n```\n\n* ### 剪贴板相关 -> [ClipboardUtils.java][clipboard.java] -> [Demo][clipboard.demo]\n```\ncopyText             : 复制文本到剪贴板\ngetText              : 获取剪贴板的文本\ncopyUri              : 复制 uri 到剪贴板\ngetUri               : 获取剪贴板的 uri\ncopyIntent           : 复制意图到剪贴板\ngetIntent            : 获取剪贴板的意图\naddChangedListener   : 增加剪贴板监听器\nremoveChangedListener: 移除剪贴板监听器\n```\n\n* ### 克隆相关 -> [CloneUtils.java][clone.java] -> [Test][clone.test]\n```\ndeepClone: 深度克隆\n```\n\n* ### 关闭相关 -> [CloseUtils.java][close.java]\n```\ncloseIO       : 关闭 IO\ncloseIOQuietly: 安静关闭 IO\n```\n\n* ### 集合相关 -> [CollectionUtils.java][collection.java] -> [Test][collection.test]\n```\nnewUnmodifiableList[NotNull]: 新建只读[非空]链表\nnewArrayList[NotNull]       : 新建数组型[非空]链表\nnewLinkedList[NotNull]      : 新建指针型[非空]链表\nnewHashSet[NotNull]         : 新建哈希[非空]集合\nnewTreeSet[NotNull]         : 新建有序[非空]集合\nnewSynchronizedCollection   : 新建同步集合\nnewUnmodifiableCollection   : 新建只读集合\nunion                       : 获取并集\nintersection                : 获取交集\ndisjunction                 : 获取并集减交集\nsubtract                    : 获取差集\ncontainsAny                 : 判断是否有交集\ngetCardinalityMap           : 获取集合中所有元素的基数\nisSubCollection             : 是否子集\nisProperSubCollection       : 是否真子集\nisEqualCollection           : 判断集合是否相等\ncardinality                 : 获取集合中元素的基数\nfind                        : 查找第一个符合条件的元素\nforAllDo                    : 对所有元素做操作\nfilter                      : 删除原集合中不符合条件的元素\nselect                      : 查找出所有符合条件的元素并返回新集合\nselectRejected              : 查找出所有不符合条件的元素并返回新集合\ntransform                   : 对原集合进行转变\ncollect                     : 转变为新的集合\ncountMatches                : 查找到匹配的元素个数\nexists                      : 判断集合是否存在符合条件的元素\naddIgnoreNull               : 增加元素如果不为空\naddAll                      : 增加多个元素\nget                         : 获取集合元素\nsize                        : 获取集合个数\nsizeIsEmpty                 : 判断个数是否为零\nisEmpty                     : 判断是否为空\nisNotEmpty                  : 判断是否非空\nretainAll                   : 保留元素\nremoveAll                   : 删除下来\ntoString                    : 集合转为字符串\n```\n\n* ### 颜色相关 -> [ColorUtils.java][color.java]\n```\ngetColor         : 获取颜色\nsetAlphaComponent: 设置颜色透明度值\nsetRedComponent  : 设置颜色红色值\nsetGreenComponent: 设置颜色绿色值\nsetBlueComponent : 设置颜色蓝色值\nstring2Int       : 颜色串转颜色值\nint2RgbString    : 颜色值转 RGB 串\nint2ArgbString   : 颜色值转 ARGB 串\ngetRandomColor   : 获取随机色\nisLightColor     : 判断是否亮色\n```\n\n* ### 转换相关 -> [ConvertUtils.java][convert.java] -> [Test][convert.test]\n```\nint2HexString, hexString2Int            : int 与 hexString 互转\nbytes2Bits, bits2Bytes                  : bytes 与 bits 互转\nbytes2Chars, chars2Bytes                : bytes 与 chars 互转\nbytes2HexString, hexString2Bytes        : bytes 与 hexString 互转\nbytes2String, string2Bytes              : bytes 与 string 互转\nbytes2JSONObject, jsonObject2Bytes      : bytes 与 JSONObject 互转\nbytes2JSONArray, jsonArray2Bytes        : bytes 与 JSONArray 互转\nbytes2Parcelable, parcelable2Bytes      : bytes 与 Parcelable 互转\nbytes2Object, serializable2Bytes        : bytes 与 Object 互转\nbytes2Bitmap, bitmap2Bytes              : bytes 与 Bitmap 互转\nmemorySize2Byte, byte2MemorySize        : 以 unit 为单位的内存大小与字节数互转\nbyte2FitMemorySize                      : 字节数转合适内存大小\ntimeSpan2Millis, millis2TimeSpan        : 以 unit 为单位的时间长度与毫秒时间戳互转\nmillis2FitTimeSpan                      : 毫秒时间戳转合适时间长度\ninput2OutputStream, output2InputStream  : inputStream 与 outputStream 互转\ninputStream2Bytes, bytes2InputStream    : inputStream 与 bytes 互转\noutputStream2Bytes, bytes2OutputStream  : outputStream 与 bytes 互转\ninputStream2String, string2InputStream  : inputStream 与 string 按编码互转\noutputStream2String, string2OutputStream: outputStream 与 string 按编码互转\ninputStream2Lines                       : inputStream 转 文本行\ndrawable2Bitmap, bitmap2Drawable        : drawable 与 bitmap 互转\ndrawable2Bytes, bytes2Drawable          : drawable 与 bytes 互转\nview2Bitmap                             : view 转 Bitmap\ndp2px, px2dp                            : dp 与 px 互转\nsp2px, px2sp                            : sp 与 px 互转\n```\n\n* ### 崩溃相关 -> [CrashUtils.java][crash.java]\n```\ninit                  : 初始化\nCrashInfo.addExtraHead: 增加额外头部\nCrashInfo.getThrowable: 获取崩溃异常\nCrashInfo.toString    : 获取崩溃信息\n```\n\n* ### 防抖相关 -> [DebouncingUtils.java][debouncing.java]\n```\nisValid: 是否有效\n```\n\n* ### 设备相关 -> [DeviceUtils.java][device.java] -> [Demo][device.demo]\n```\nisDeviceRooted              : 判断设备是否 rooted\nisAdbEnabled                : 判断设备 ADB 是否可用\ngetSDKVersionName           : 获取设备系统版本号\ngetSDKVersionCode           : 获取设备系统版本码\ngetAndroidID                : 获取设备 AndroidID\ngetMacAddress               : 获取设备 MAC 地址\ngetManufacturer             : 获取设备厂商\ngetModel                    : 获取设备型号\ngetABIs                     : 获取设备 ABIs\nisTablet                    : 判断是否是平板\nisEmulator                  : 判断是否是模拟器\nisDevelopmentSettingsEnabled: 开发者选项是否打开\ngetUniqueDeviceId           : 获取唯一设备 ID\nisSameDevice                : 判断是否同一设备\n```\n\n* ### 闪光灯相关 -> [FlashlightUtils.java][flashlight.java] -> [Demo][flashlight.demo]\n```\nisFlashlightEnable : 判断设备是否支持闪光灯\nisFlashlightOn     : 判断闪光灯是否打开\nsetFlashlightStatus: 设置闪光灯状态\ndestroy            : 销毁\n```\n\n* ### 编码解码相关 -> [EncodeUtils.java][encode.java] -> [Test][encode.test]\n```\nurlEncode          : URL 编码\nurlDecode          : URL 解码\nbase64Encode       : Base64 编码\nbase64Encode2String: Base64 编码\nbase64Decode       : Base64 解码\nhtmlEncode         : Html 编码\nhtmlDecode         : Html 解码\nbinaryEncode       : 二进制编码\nbinaryDecode       : 二进制解码\n```\n\n* ### 加密解密相关 -> [EncryptUtils.java][encrypt.java] -> [Test][encrypt.test]\n```\nencryptMD2, encryptMD2ToString                        : MD2 加密\nencryptMD5, encryptMD5ToString                        : MD5 加密\nencryptMD5File, encryptMD5File2String                 : MD5 加密文件\nencryptSHA1, encryptSHA1ToString                      : SHA1 加密\nencryptSHA224, encryptSHA224ToString                  : SHA224 加密\nencryptSHA256, encryptSHA256ToString                  : SHA256 加密\nencryptSHA384, encryptSHA384ToString                  : SHA384 加密\nencryptSHA512, encryptSHA512ToString                  : SHA512 加密\nencryptHmacMD5, encryptHmacMD5ToString                : HmacMD5 加密\nencryptHmacSHA1, encryptHmacSHA1ToString              : HmacSHA1 加密\nencryptHmacSHA224, encryptHmacSHA224ToString          : HmacSHA224 加密\nencryptHmacSHA256, encryptHmacSHA256ToString          : HmacSHA256 加密\nencryptHmacSHA384, encryptHmacSHA384ToString          : HmacSHA384 加密\nencryptHmacSHA512, encryptHmacSHA512ToString          : HmacSHA512 加密\nencryptDES, encryptDES2HexString, encryptDES2Base64   : DES 加密\ndecryptDES, decryptHexStringDES, decryptBase64DES     : DES 解密\nencrypt3DES, encrypt3DES2HexString, encrypt3DES2Base64: 3DES 加密\ndecrypt3DES, decryptHexString3DES, decryptBase64_3DES : 3DES 解密\nencryptAES, encryptAES2HexString, encryptAES2Base64   : AES 加密\ndecryptAES, decryptHexStringAES, decryptBase64AES     : AES 解密\nencryptRSA, encryptRSA2HexString, encryptRSA2Base64   : RSA 加密\ndecryptRSA, decryptHexStringRSA, decryptBase64RSA     : RSA 解密\nrc4                                                   : rc4 加解密\n```\n\n* ### 文件相关 -> [FileIOUtils.java][fileIo.java] -> [Test][fileIo.test]\n```\nwriteFileFromIS            : 将输入流写入文件\nwriteFileFromBytesByStream : 将字节数组写入文件\nwriteFileFromBytesByChannel: 将字节数组写入文件\nwriteFileFromBytesByMap    : 将字节数组写入文件\nwriteFileFromString        : 将字符串写入文件\nreadFile2List              : 读取文件到字符串链表中\nreadFile2String            : 读取文件到字符串中\nreadFile2BytesByStream     : 读取文件到字节数组中\nreadFile2BytesByChannel    : 读取文件到字节数组中\nreadFile2BytesByMap        : 读取文件到字节数组中\nsetBufferSize              : 设置缓冲区尺寸\n```\n\n* ### 文件相关 -> [FileUtils.java][file.java] -> [Test][file.test]\n```\ngetFileByPath             : 根据文件路径获取文件\nisFileExists              : 判断文件是否存在\nrename                    : 重命名文件\nisDir                     : 判断是否是目录\nisFile                    : 判断是否是文件\ncreateOrExistsDir         : 判断目录是否存在，不存在则判断是否创建成功\ncreateOrExistsFile        : 判断文件是否存在，不存在则判断是否创建成功\ncreateFileByDeleteOldFile : 判断文件是否存在，存在则在创建之前删除\ncopy                      : 复制文件或目录\nmove                      : 移动文件或目录\ndelete                    : 删除文件或目录\ndeleteAllInDir            : 删除目录下所有内容\ndeleteFilesInDir          : 删除目录下所有文件\ndeleteFilesInDirWithFilter: 删除目录下所有过滤的文件\nlistFilesInDir            : 获取目录下所有文件\nlistFilesInDirWithFilter  : 获取目录下所有过滤的文件\ngetFileLastModified       : 获取文件最后修改的毫秒时间戳\ngetFileCharsetSimple      : 简单获取文件编码格式\ngetFileLines              : 获取文件行数\ngetSize                   : 获取文件或目录大小\ngetLength                 : 获取文件或目录长度\ngetFileMD5                : 获取文件的 MD5 校验码\ngetFileMD5ToString        : 获取文件的 MD5 校验码\ngetDirName                : 根据全路径获取最长目录\ngetFileName               : 根据全路径获取文件名\ngetFileNameNoExtension    : 根据全路径获取文件名不带拓展名\ngetFileExtension          : 根据全路径获取文件拓展名\nnotifySystemToScan        : 通知系统扫描文件\ngetFsTotalSize            : 获取文件系统总大小\ngetFsAvailableSize        : 获取文件系统可用大小\n```\n\n* ### Fragment 相关 -> [FragmentUtils.java][fragment.java] -> [Demo][fragment.demo]\n```\nadd                   : 增加 fragment\nshow                  : 显示 fragment\nhide                  : 隐藏 fragment\nshowHide              : 先显示后隐藏 fragment\nreplace               : 替换 fragment\npop                   : 出栈 fragment\npopTo                 : 出栈到指定 fragment\npopAll                : 出栈所有 fragment\nremove                : 移除 fragment\nremoveTo              : 移除到指定 fragment\nremoveAll             : 移除所有 fragment\ngetTop                : 获取顶部 fragment\ngetTopInStack         : 获取栈中顶部 fragment\ngetTopShow            : 获取顶部可见 fragment\ngetTopShowInStack     : 获取栈中顶部可见 fragment\ngetFragments          : 获取同级别的 fragment\ngetFragmentsInStack   : 获取同级别栈中的 fragment\ngetAllFragments       : 获取所有 fragment\ngetAllFragmentsInStack: 获取栈中所有 fragment\nfindFragment          : 查找 fragment\ndispatchBackPress     : 处理 fragment 回退键\nsetBackgroundColor    : 设置背景色\nsetBackgroundResource : 设置背景资源\nsetBackground         : 设置背景\n```\n\n* ### Gson 相关 -> [GsonUtils.java][gson.java] -> [Test][gson.test]\n```\nsetGsonDelegate: 设置默认的 Gson 代理对象\nsetGson        : 设置 Gson 对象\ngetGson        : 获取 Gson 对象\ntoJson         : 对象转 Json 串\nfromJson       : Json 串转对象\ngetListType    : 获取链表类型\ngetSetType     : 获取集合类型\ngetMapType     : 获取字典类型\ngetArrayType   : 获取数组类型\ngetType        : 获取类型\n```\n\n* ### 图片相关 -> [ImageUtils.java][image.java] -> [Demo][image.demo]\n```\nbitmap2Bytes, bytes2Bitmap      : bitmap 与 bytes 互转\ndrawable2Bitmap, bitmap2Drawable: drawable 与 bitmap 互转\ndrawable2Bytes, bytes2Drawable  : drawable 与 bytes 互转\nview2Bitmap                     : view 转 bitmap\ngetBitmap                       : 获取 bitmap\nscale                           : 缩放图片\nclip                            : 裁剪图片\nskew                            : 倾斜图片\nrotate                          : 旋转图片\ngetRotateDegree                 : 获取图片旋转角度\ntoRound                         : 转为圆形图片\ntoRoundCorner                   : 转为圆角图片\naddCornerBorder                 : 添加圆角边框\naddCircleBorder                 : 添加圆形边框\naddReflection                   : 添加倒影\naddTextWatermark                : 添加文字水印\naddImageWatermark               : 添加图片水印\ntoAlpha                         : 转为 alpha 位图\ntoGray                          : 转为灰度图片\nfastBlur                        : 快速模糊\nrenderScriptBlur                : renderScript 模糊图片\nstackBlur                       : stack 模糊图片\nsave                            : 保存图片\nsave2Album                      : 保存图片到相册\nisImage                         : 根据文件名判断文件是否为图片\ngetImageType                    : 获取图片类型\ncompressByScale                 : 按缩放压缩\ncompressByQuality               : 按质量压缩\ncompressBySampleSize            : 按采样大小压缩\ngetSize                         : 获取图片尺寸\n```\n\n* ### 意图相关 -> [IntentUtils.java][intent.java]\n```\nisIntentAvailable                : 判断意图是否可用\ngetInstallAppIntent              : 获取安装 App（支持 6.0）的意图\ngetUninstallAppIntent            : 获取卸载 App 的意图\ngetLaunchAppIntent               : 获取打开 App 的意图\ngetLaunchAppDetailsSettingsIntent: 获取 App 具体设置的意图\ngetShareTextIntent               : 获取分享文本的意图\ngetShareImageIntent              : 获取分享图片的意图\ngetShareTextImageIntent          : 获取分享图文的意图\ngetComponentIntent               : 获取其他应用组件的意图\ngetShutdownIntent                : 获取关机的意图\ngetCaptureIntent                 : 获取拍照的意图\n```\n\n* ### 键盘相关 -> [KeyboardUtils.java][keyboard.java] -> [Demo][keyboard.demo]\n```\nshowSoftInput                     : 显示软键盘\nhideSoftInput                     : 隐藏软键盘\ntoggleSoftInput                   : 切换键盘显示与否状态\nisSoftInputVisible                : 判断软键盘是否可见\nregisterSoftInputChangedListener  : 注册软键盘改变监听器\nunregisterSoftInputChangedListener: 注销软键盘改变监听器\nfixAndroidBug5497                 : 修复安卓 5497 BUG\nfixSoftInputLeaks                 : 修复软键盘内存泄漏\nclickBlankArea2HideSoftInput      : 点击屏幕空白区域隐藏软键盘\n```\n\n* ### 语言相关 -> [LanguageUtils.java][language.java] -> [Demo][language.demo]\n```\napplySystemLanguage     : 设置系统语言\napplyLanguage           : 设置语言\nisAppliedLanguage       : 是否设置了语言\ngetAppliedLanguage      : 获取设置的语言\ngetContextLanguage      : 获取上下文的语言\ngetAppContextLanguage   : 获取应用上下文的语言\ngetSystemLanguage       : 获取系统的语言\nupdateAppContextLanguage: 更新应用上下文语言\nattachBaseContext       : 如果设置语言无效则在 Activity#attachBaseContext 调用它\n```\n\n* ### 日志相关 -> [LogUtils.java][log.java] -> [Demo][log.demo]\n```\ngetConfig                        : 获取 log 配置\nConfig.setLogSwitch              : 设置 log 总开关\nConfig.setConsoleSwitch          : 设置 log 控制台开关\nConfig.setGlobalTag              : 设置 log 全局 tag\nConfig.setLogHeadSwitch          : 设置 log 头部信息开关\nConfig.setLog2FileSwitch         : 设置 log 文件开关\nConfig.setDir                    : 设置 log 文件存储目录\nConfig.setFilePrefix             : 设置 log 文件前缀\nConfig.setBorderSwitch           : 设置 log 边框开关\nConfig.setSingleTagSwitch        : 设置 log 单一 tag 开关（为美化 AS 3.1 的 Logcat）\nConfig.setConsoleFilter          : 设置 log 控制台过滤器\nConfig.setFileFilter             : 设置 log 文件过滤器\nConfig.setStackDeep              : 设置 log 栈深度\nConfig.setStackOffset            : 设置 log 栈偏移\nConfig.setSaveDays               : 设置 log 可保留天数\nConfig.addFormatter              : 增加 log 格式化器\nConfig.setFileWriter             : 设置文件写入器\nConfig.setOnConsoleOutputListener: 设置控制台输出监听器\nConfig.setOnFileOutputListener   : 设置文件输出监听器\nConfig.addFileExtraHead          : 增加 log 文件头部\nlog                              : 自定义 tag 的 type 日志\nv                                : tag 为类名的 Verbose 日志\nvTag                             : 自定义 tag 的 Verbose 日志\nd                                : tag 为类名的 Debug 日志\ndTag                             : 自定义 tag 的 Debug 日志\ni                                : tag 为类名的 Info 日志\niTag                             : 自定义 tag 的 Info 日志\nw                                : tag 为类名的 Warn 日志\nwTag                             : 自定义 tag 的 Warn 日志\ne                                : tag 为类名的 Error 日志\neTag                             : 自定义 tag 的 Error 日志\na                                : tag 为类名的 Assert 日志\naTag                             : 自定义 tag 的 Assert 日志\nfile                             : log 到文件\njson                             : log 字符串之 json\nxml                              : log 字符串之 xml\ngetCurrentLogFilePath            : 获取当前日志文件路径\ngetLogFiles                      : 获取所有日志\n```\n\n* ### Map 相关 -> [MapUtils.java][map.java] -> [Test][map.test]\n```\nnewUnmodifiableMap: 创建 UnmodifiableMap\nnewHashMap        : 创建 HashMap\nnewLinkedHashMap  : 创建 LinkedHashMap\nnewTreeMap        : 创建 TreeMap\nnewHashTable      : 创建 HashTable\nisEmpty           : 判断 Map 是否为空\nisNotEmpty        : 判断 Map 是否非空\nsize              : 获取 Map 元素个数\nforAllDo          : 对所有元素做操作\ntransform         : 对 Map 做转变\ntoString          : Map 转为字符串\n```\n\n* ### MetaData 相关 -> [MetaDataUtils.java][metaData.java] -> [Demo][metaData.demo]\n```\ngetMetaDataInApp     : 获取 application 的 meta-data 值\ngetMetaDataInActivity: 获取 activity 的 meta-data 值\ngetMetaDataInService : 获取 service 的 meta-data 值\ngetMetaDataInReceiver: 获取 receiver 的 meta-data 值\n```\n\n* ### 网络相关 -> [NetworkUtils.java][network.java] -> [Demo][network.demo]\n```\nopenWirelessSettings                    : 打开网络设置界面\nisConnected                             : 判断网络是否连接\nisAvailable[Async]                      : 判断网络是否可用\nisAvailableByPing[Async]                : 用 ping 判断网络是否可用\nisAvailableByDns[Async]                 : 用 DNS 判断网络是否可用\ngetMobileDataEnabled                    : 判断移动数据是否打开\nisMobileData                            : 判断网络是否是移动数据\nis4G                                    : 判断网络是否是 4G\ngetWifiEnabled                          : 判断 wifi 是否打开\nsetWifiEnabled                          : 打开或关闭 wifi\nisWifiConnected                         : 判断 wifi 是否连接状态\nisWifiAvailable[Async]                  : 判断 wifi 数据是否可用\ngetNetworkOperatorName                  : 获取移动网络运营商名称\ngetNetworkType                          : 获取当前网络类型\ngetIPAddress[Async]                     : 获取 IP 地址\ngetDomainAddress[Async]                 : 获取域名 IP 地址\ngetIpAddressByWifi                      : 根据 WiFi 获取网络 IP 地址\ngetGatewayByWifi                        : 根据 WiFi 获取网关 IP 地址\ngetNetMaskByWifi                        : 根据 WiFi 获取子网掩码 IP 地址\ngetServerAddressByWifi                  : 根据 WiFi 获取服务端 IP 地址\nregisterNetworkStatusChangedListener    : 注册网络状态改变监听器\nisRegisteredNetworkStatusChangedListener: 判断是否注册网络状态改变监听器\nunregisterNetworkStatusChangedListener  : 注销网络状态改变监听器\ngetWifiScanResult                       : 获取 WIFI 列表\naddOnWifiChangedConsumer                : 增加 WIFI 改变监听\nremoveOnWifiChangedConsumer             : 移除 WIFI 改变监听\n```\n\n* ### 通知相关 -> [NotificationUtils.java][notification.java] -> [Demo][notification.demo]\n```\nareNotificationsEnabled     : 判断通知是否可用\nnotify                      : 发送通知\ncancel                      : 取消通知\ncancelAll                   : 取消所有通知\nsetNotificationBarVisibility: 设置通知栏是否可见\n```\n\n* ### 数字相关 -> [NumberUtils.java][number.java] -> [Test][number.test]\n```\nformat      : 格式化\nfloat2Double: 浮点转双精度\n```\n\n* ### 对象相关 -> [ObjectUtils.java][object.java] -> [Test][object.test]\n```\nisEmpty          : 判断对象是否为空\nisNotEmpty       : 判断对象是否非空\nequals           : 判断对象是否相等\ncompare          : 比较对象大小\nrequireNonNull(s): 要求对象非空\ngetOrDefault     : 获取非空或默认对象\ntoString         : 转字符串\nhashCode(s)      : 获取对象哈希值\n```\n\n* ### 路径相关 -> [PathUtils.java][path.java] -> [Demo][path.demo]\n```\njoin                           : 连接路径\ngetRootPath                    : 获取根路径\ngetDataPath                    : 获取数据路径\ngetDownloadCachePath           : 获取下载缓存路径\ngetInternalAppDataPath         : 获取内存应用数据路径\ngetInternalAppCodeCacheDir     : 获取内存应用代码缓存路径\ngetInternalAppCachePath        : 获取内存应用缓存路径\ngetInternalAppDbsPath          : 获取内存应用数据库路径\ngetInternalAppDbPath           : 获取内存应用数据库路径\ngetInternalAppFilesPath        : 获取内存应用文件路径\ngetInternalAppSpPath           : 获取内存应用 SP 路径\ngetInternalAppNoBackupFilesPath: 获取内存应用未备份文件路径\ngetExternalStoragePath         : 获取外存路径\ngetExternalMusicPath           : 获取外存音乐路径\ngetExternalPodcastsPath        : 获取外存播客路径\ngetExternalRingtonesPath       : 获取外存铃声路径\ngetExternalAlarmsPath          : 获取外存闹铃路径\ngetExternalNotificationsPath   : 获取外存通知路径\ngetExternalPicturesPath        : 获取外存图片路径\ngetExternalMoviesPath          : 获取外存影片路径\ngetExternalDownloadsPath       : 获取外存下载路径\ngetExternalDcimPath            : 获取外存数码相机图片路径\ngetExternalDocumentsPath       : 获取外存文档路径\ngetExternalAppDataPath         : 获取外存应用数据路径\ngetExternalAppCachePath        : 获取外存应用缓存路径\ngetExternalAppFilesPath        : 获取外存应用文件路径\ngetExternalAppMusicPath        : 获取外存应用音乐路径\ngetExternalAppPodcastsPath     : 获取外存应用播客路径\ngetExternalAppRingtonesPath    : 获取外存应用铃声路径\ngetExternalAppAlarmsPath       : 获取外存应用闹铃路径\ngetExternalAppNotificationsPath: 获取外存应用通知路径\ngetExternalAppPicturesPath     : 获取外存应用图片路径\ngetExternalAppMoviesPath       : 获取外存应用影片路径\ngetExternalAppDownloadPath     : 获取外存应用下载路径\ngetExternalAppDcimPath         : 获取外存应用数码相机图片路径\ngetExternalAppDocumentsPath    : 获取外存应用文档路径\ngetExternalAppObbPath          : 获取外存应用 OBB 路径\ngetRootPathExternalFirst       : 优先获取外部根路径\ngetAppDataPathExternalFirst    : 优先获取外部数据路径\ngetFilesPathExternalFirst      : 优先获取外部文件路径\ngetCachePathExternalFirst      : 优先获取外部缓存路径\n```\n\n* ### 权限相关 -> [PermissionUtils.java][permission.java] -> [Demo][permission.demo]\n```\npermission              : 设置请求权限\npermissionGroup         : 设置请求权限组\npermission.explain      : 设置权限请求前的解释\npermission.rationale    : 设置拒绝权限后再次请求的回调接口\npermission.callback     : 设置回调\npermission.theme        : 设置主题\npermission.request      : 开始请求\ngetPermissions          : 获取应用权限\nisGranted               : 判断权限是否被授予\nisGrantedWriteSettings  : 判断修改系统权限是否被授予\nrequestWriteSettings    : 申请修改系统权限\nisGrantedDrawOverlays   : 判断悬浮窗权限是否被授予\nrequestDrawOverlays     : 申请悬浮窗权限\nlaunchAppDetailsSettings: 打开应用具体设置\n```\n\n* ### 手机相关 -> [PhoneUtils.java][phone.java] -> [Demo][phone.demo]\n```\nisPhone            : 判断设备是否是手机\ngetDeviceId        : 获取设备码\ngetSerial          : 获取序列号\ngetIMEI            : 获取 IMEI 码\ngetMEID            : 获取 MEID 码\ngetIMSI            : 获取 IMSI 码\ngetPhoneType       : 获取移动终端类型\nisSimCardReady     : 判断 sim 卡是否准备好\ngetSimOperatorName : 获取 Sim 卡运营商名称\ngetSimOperatorByMnc: 获取 Sim 卡运营商名称\ndial               : 跳至拨号界面\ncall               : 拨打 phoneNumber\nsendSms            : 跳至发送短信界面\n```\n\n* ### 进程相关 -> [ProcessUtils.java][process.java] -> [Demo][process.demo]\n```\ngetForegroundProcessName  : 获取前台线程包名\nkillAllBackgroundProcesses: 杀死所有的后台服务进程\nkillBackgroundProcesses   : 杀死后台服务进程\nisMainProcess             : 判断是否运行在主进程\ngetCurrentProcessName     : 获取当前进程名称\n```\n\n* ### 反射相关 -> [ReflectUtils.java][reflect.java] -> [Test][reflect.test]\n```\nreflect    : 设置要反射的类\nnewInstance: 实例化反射对象\nfield      : 设置反射的字段\nmethod     : 设置反射的方法\nget        : 获取反射想要获取的\n```\n\n* ### 正则相关 -> [RegexUtils.java][regex.java] -> [Test][regex.test]\n```\nisMobileSimple                           : 简单验证手机号\nisMobileExact                            : 精确验证手机号\nisTel                                    : 验证电话号码\nisIDCard15                               : 验证身份证号码 15 位\nisIDCard18                               : 简单验证身份证号码 18 位\nisIDCard18Exact                          : 精确验证身份证号码 18 位\nisEmail                                  : 验证邮箱\nisURL                                    : 验证 URL\nisZh                                     : 验证汉字\nisUsername                               : 验证用户名\nisDate                                   : 验证 yyyy-MM-dd 格式的日期校验，已考虑平闰年\nisIP                                     : 验证 IP 地址\nisMatch                                  : 判断是否匹配正则\ngetMatches                               : 获取正则匹配的部分\ngetSplits                                : 获取正则匹配分组\ngetReplaceFirst                          : 替换正则匹配的第一部分\ngetReplaceAll                            : 替换所有正则匹配的部分\nRegexConstants.REGEX_DOUBLE_BYTE_CHAR    : 双字节\nRegexConstants.REGEX_BLANK_LINE          : 空行\nRegexConstants.REGEX_QQ_NUM              : QQ 号\nRegexConstants.REGEX_CHINA_POSTAL_CODE   : 邮编\nRegexConstants.REGEX_INTEGER             : 整数\nRegexConstants.REGEX_POSITIVE_INTEGER    : 正整数\nRegexConstants.REGEX_NEGATIVE_INTEGER    : 负整数\nRegexConstants.REGEX_NOT_NEGATIVE_INTEGER: 非负整数\nRegexConstants.REGEX_NOT_POSITIVE_INTEGER: 非正整数\nRegexConstants.REGEX_FLOAT               : 浮点数\nRegexConstants.REGEX_POSITIVE_FLOAT      : 正浮点数\nRegexConstants.REGEX_NEGATIVE_FLOAT      : 负浮点数\nRegexConstants.REGEX_NOT_NEGATIVE_FLOAT  : 非负浮点数\nRegexConstants.REGEX_NOT_POSITIVE_FLOAT  : 非正浮点数\n```\n\n* ### 资源相关 -> [ResourceUtils.java][resource.java] -> [Demo][resource.demo]\n```\ngetDrawable        : 获取 Drawable\ngetIdByName        : 根据名字获取 ID\ngetStringIdByName  : 根据名字获取 string ID\ngetColorIdByName   : 根据名字获取 color ID\ngetDimenIdByName   : 根据名字获取 dimen ID\ngetDrawableIdByName: 根据名字获取 dimen ID\ngetMipmapIdByName  : 根据名字获取 mipmap ID\ngetLayoutIdByName  : 根据名字获取 layout ID\ngetStyleIdByName   : 根据名字获取 style ID\ngetAnimIdByName    : 根据名字获取 anim ID\ngetMenuIdByName    : 根据名字获取 menu ID\ncopyFileFromAssets : 从 assets 中拷贝文件\nreadAssets2String  : 从 assets 中读取字符串\nreadAssets2List    : 从 assets 中按行读取字符串\ncopyFileFromRaw    : 从 raw 中拷贝文件\nreadRaw2String     : 从 raw 中读取字符串\nreadRaw2List       : 从 raw 中按行读取字符串\n```\n\n* ### Rom 相关 -> [RomUtils.java][rom.java] -> [Demo][rom.demo]\n```\nisHuawei   : 是否华为\nisVivo     : 是否 VIVO\nisXiaomi   : 是否小米\nisOppo     : 是否 OPPO\nisLeeco    : 是否乐视\nis360      : 是否 360\nisZte      : 是否中兴\nisOneplus  : 是否一加\nisNubia    : 是否努比亚\nisCoolpad  : 是否酷派\nisLg       : 是否 LG\nisGoogle   : 是否谷歌\nisSamsung  : 是否三星\nisMeizu    : 是否魅族\nisLenovo   : 是否联想\nisSmartisan: 是否锤子\nisHtc      : 是否 HTC\nisSony     : 是否索尼\nisGionee   : 是否金立\nisMotorola : 是否摩托罗拉\ngetRomInfo : 获取 ROM 信息\n```\n\n* ### 屏幕相关 -> [ScreenUtils.java][screen.java] -> [Demo][screen.demo]\n```\ngetScreenWidth     : 获取屏幕的宽度（单位：px）\ngetScreenHeight    : 获取屏幕的高度（单位：px）\ngetAppScreenWidth  : 获取应用屏幕的宽度（单位：px）\ngetAppScreenHeight : 获取应用屏幕的高度（单位：px）\ngetScreenDensity   : 获取屏幕密度\ngetScreenDensityDpi: 获取屏幕密度 DPI\nsetFullScreen      : 设置屏幕为全屏\nsetNonFullScreen   : 设置屏幕为非全屏\ntoggleFullScreen   : 切换屏幕为全屏与否状态\nisFullScreen       : 判断屏幕是否为全屏\nsetLandscape       : 设置屏幕为横屏\nsetPortrait        : 设置屏幕为竖屏\nisLandscape        : 判断是否横屏\nisPortrait         : 判断是否竖屏\ngetScreenRotation  : 获取屏幕旋转角度\nscreenShot         : 截屏\nisScreenLock       : 判断是否锁屏\nsetSleepDuration   : 设置进入休眠时长\ngetSleepDuration   : 获取进入休眠时长\n```\n\n* ### SD 卡相关 -> [SDCardUtils.java][sdcard.java] -> [Demo][sdcard.demo]\n```\nisSDCardEnableByEnvironment: 根据 Environment 判断 SD 卡是否可用\ngetSDCardPathByEnvironment : 根据 Environment 获取 SD 卡路径\ngetSDCardInfo              : 获取 SD 卡信息\ngetMountedSDCardPath       : 获取已挂载的 SD 卡路径\ngetExternalTotalSize       : 获取外置 SD 卡总大小\ngetExternalAvailableSize   : 获取外置 SD 卡可用大小\ngetInternalTotalSize       : 获取内置 SD 卡总大小\ngetInternalAvailableSize   : 获取内置 SD 卡可用大小\n```\n\n* ### 服务相关 -> [ServiceUtils.java][service.java]\n```\ngetAllRunningServices: 获取所有运行的服务\nstartService         : 启动服务\nstopService          : 停止服务\nbindService          : 绑定服务\nunbindService        : 解绑服务\nisServiceRunning     : 判断服务是否运行\n```\n\n* ### 阴影相关 -> [ShadowUtils.java][shadow.java] -> [Demo][shadow.demo]\n```\napply: 应用阴影\n```\n\n* ### Shell 相关 -> [ShellUtils.java][shell.java]\n```\nexecCmd[Async]: 执行命令\n```\n\n* ### 尺寸相关 -> [SizeUtils.java][size.java]\n```\ndp2px, px2dp     : dp 与 px 转换\nsp2px, px2sp     : sp 与 px 转换\napplyDimension   : 各种单位转换\nforceGetViewSize : 在 onCreate 中获取视图的尺寸\nmeasureView      : 测量视图尺寸\ngetMeasuredWidth : 获取测量视图宽度\ngetMeasuredHeight: 获取测量视图高度\n```\n\n* ### Snackbar 相关 -> [SnackbarUtils.java][snackbar.java] -> [Demo][snackbar.demo]\n```\nwith           : 设置 snackbar 依赖 view\nsetMessage     : 设置消息\nsetMessageColor: 设置消息颜色\nsetBgColor     : 设置背景色\nsetBgResource  : 设置背景资源\nsetDuration    : 设置显示时长\nsetAction      : 设置行为\nsetBottomMargin: 设置底边距\nshow           : 显示 snackbar\nshowSuccess    : 显示预设成功的 snackbar\nshowWarning    : 显示预设警告的 snackbar\nshowError      : 显示预设错误的 snackbar\ndismiss        : 消失 snackbar\ngetView        : 获取 snackbar 视图\naddView        : 添加 snackbar 视图\n```\n\n* ### SpannableString 相关 -> [SpanUtils.java][span.java] -> [Demo][span.demo]\n```\nwith              : 设置控件\nsetFlag           : 设置标识\nsetForegroundColor: 设置前景色\nsetBackgroundColor: 设置背景色\nsetLineHeight     : 设置行高\nsetQuoteColor     : 设置引用线的颜色\nsetLeadingMargin  : 设置缩进\nsetBullet         : 设置列表标记\nsetFontSize       : 设置字体尺寸\nsetFontProportion : 设置字体比例\nsetFontXProportion: 设置字体横向比例\nsetStrikethrough  : 设置删除线\nsetUnderline      : 设置下划线\nsetSuperscript    : 设置上标\nsetSubscript      : 设置下标\nsetBold           : 设置粗体\nsetItalic         : 设置斜体\nsetBoldItalic     : 设置粗斜体\nsetFontFamily     : 设置字体系列\nsetTypeface       : 设置字体\nsetAlign          : 设置对齐\nsetClickSpan      : 设置点击事件\nsetUrl            : 设置超链接\nsetBlur           : 设置模糊\nsetShader         : 设置着色器\nsetShadow         : 设置阴影\nsetSpans          : 设置样式\nappend            : 追加样式字符串\nappendLine        : 追加一行样式字符串\nappendImage       : 追加图片\nappendSpace       : 追加空白\ncreate            : 创建样式字符串\n```\n\n* ### SP 相关 -> [SPStaticUtils.java][spStatic.java] -> [Demo][spStatic.demo]\n```\nsetDefaultSPUtils: 设置默认 SP 实例\nput              : SP 中写入数据\ngetString        : SP 中读取 String\ngetInt           : SP 中读取 int\ngetLong          : SP 中读取 long\ngetFloat         : SP 中读取 float\ngetBoolean       : SP 中读取 boolean\ngetAll           : SP 中获取所有键值对\ncontains         : SP 中是否存在该 key\nremove           : SP 中移除该 key\nclear            : SP 中清除所有数据\n```\n\n* ### SP 相关 -> [SPUtils.java][sp.java]\n```\ngetInstance        : 获取 SP 实例\nInstance.put       : SP 中写入数据\nInstance.getString : SP 中读取 String\nInstance.getInt    : SP 中读取 int\nInstance.getLong   : SP 中读取 long\nInstance.getFloat  : SP 中读取 float\nInstance.getBoolean: SP 中读取 boolean\nInstance.getAll    : SP 中获取所有键值对\nInstance.contains  : SP 中是否存在该 key\nInstance.remove    : SP 中移除该 key\nInstance.clear     : SP 中清除所有数据\n```\n\n* ### 字符串相关 -> [StringUtils.java][string.java] -> [Test][string.test]\n```\nisEmpty         : 判断字符串是否为 null 或长度为 0\nisTrimEmpty     : 判断字符串是否为 null 或全为空格\nisSpace         : 判断字符串是否为 null 或全为空白字符\nequals          : 判断两字符串是否相等\nequalsIgnoreCase: 判断两字符串忽略大小写是否相等\nnull2Length0    : null 转为长度为 0 的字符串\nlength          : 返回字符串长度\nupperFirstLetter: 首字母大写\nlowerFirstLetter: 首字母小写\nreverse         : 反转字符串\ntoDBC           : 转化为半角字符\ntoSBC           : 转化为全角字符\ngetString       : 获取字符资源\ngetStringArray  : 获取字符数组资源\nformat          : 格式化字符串\n```\n\n* ### 线程相关 -> [ThreadUtils.java][thread.java] -> [Test][thread.test]\n```\nisMainThread            : 判断当前是否主线程\ngetMainHandler          : 获取主线程 Handler\nrunOnUiThread           : 运行在主线程\nrunOnUiThreadDelayed    : 延时运行在主线程\ngetFixedPool            : 获取固定线程池\ngetSinglePool           : 获取单线程池\ngetCachedPool           : 获取缓冲线程池\ngetIoPool               : 获取 IO 线程池\ngetCpuPool              : 获取 CPU 线程池\nexecuteByFixed          : 在固定线程池执行任务\nexecuteByFixedWithDelay : 在固定线程池延时执行任务\nexecuteByFixedAtFixRate : 在固定线程池按固定频率执行任务\nexecuteBySingle         : 在单线程池执行任务\nexecuteBySingleWithDelay: 在单线程池延时执行任务\nexecuteBySingleAtFixRate: 在单线程池按固定频率执行任务\nexecuteByCached         : 在缓冲线程池执行任务\nexecuteByCachedWithDelay: 在缓冲线程池延时执行任务\nexecuteByCachedAtFixRate: 在缓冲线程池按固定频率执行任务\nexecuteByIo             : 在 IO 线程池执行任务\nexecuteByIoWithDelay    : 在 IO 线程池延时执行任务\nexecuteByIoAtFixRate    : 在 IO 线程池按固定频率执行任务\nexecuteByCpu            : 在 CPU 线程池执行任务\nexecuteByCpuWithDelay   : 在 CPU 线程池延时执行任务\nexecuteByCpuAtFixRate   : 在 CPU 线程池按固定频率执行任务\nexecuteByCustom         : 在自定义线程池执行任务\nexecuteByCustomWithDelay: 在自定义线程池延时执行任务\nexecuteByCustomAtFixRate: 在自定义线程池按固定频率执行任务\ncancel                  : 取消任务的执行\nsetDeliver              : 设置任务结束后交付的线程\n```\n\n* ### 时间相关 -> [TimeUtils.java][time.java] -> [Test][time.test]\n```\ngetSafeDateFormat       : 获取安全的日期格式\nmillis2String           : 将时间戳转为时间字符串\nstring2Millis           : 将时间字符串转为时间戳\nstring2Date             : 将时间字符串转为 Date 类型\ndate2String             : 将 Date 类型转为时间字符串\ndate2Millis             : 将 Date 类型转为时间戳\nmillis2Date             : 将时间戳转为 Date 类型\ngetTimeSpan             : 获取两个时间差（单位：unit）\ngetFitTimeSpan          : 获取合适型两个时间差\ngetNowMills             : 获取当前毫秒时间戳\ngetNowString            : 获取当前时间字符串\ngetNowDate              : 获取当前 Date\ngetTimeSpanByNow        : 获取与当前时间的差（单位：unit）\ngetFitTimeSpanByNow     : 获取合适型与当前时间的差\ngetFriendlyTimeSpanByNow: 获取友好型与当前时间的差\ngetMillis               : 获取与给定时间等于时间差的时间戳\ngetString               : 获取与给定时间等于时间差的时间字符串\ngetDate                 : 获取与给定时间等于时间差的 Date\ngetMillisByNow          : 获取与当前时间等于时间差的时间戳\ngetStringByNow          : 获取与当前时间等于时间差的时间字符串\ngetDateByNow            : 获取与当前时间等于时间差的 Date\nisToday                 : 判断是否今天\nisLeapYear              : 判断是否闰年\ngetChineseWeek          : 获取中式星期\ngetUSWeek               : 获取美式式星期\nisAm                    : 判断是否上午\nisPm                    : 判断是否下午\ngetValueByCalendarField : 根据日历字段获取值\ngetChineseZodiac        : 获取生肖\ngetZodiac               : 获取星座\n```\n\n* ### 吐司相关 -> [ToastUtils.java][toast.java] -> [Demo][toast.demo]\n```\nmake                     : 制作吐司\nmake.setMode             : 设置模式\nmake.setGravity          : 设置位置\nmake.setBgColor          : 设置背景颜色\nmake.setBgResource       : 设置背景资源\nmake.setTextColor        : 设置字体颜色\nmake.setTextSize         : 设置字体大小\nmake.setDurationIsLong   : 设置是否长时间显示\nmake.setLeftIcon         : 设置左侧图标\nmake.setTopIcon          : 设置顶部图标\nmake.setRightIcon        : 设置右侧图标\nmake.setBottomIcon       : 设置底部图标\nmake.setNotUseSystemToast: 设置不使用系统吐司\nmake.show                : 显示吐司\ngetDefaultMaker          : 获取默认制作实例（控制 showShort、showLong 样式）\nshowShort                : 显示短时吐司\nshowLong                 : 显示长时吐司\ncancel                   : 取消吐司显示\n```\n\n* ### 触摸相关 -> [TouchUtils.java][touch.java]\n```\nsetOnTouchListener: 设置触摸事件\n```\n\n* ### UI 消息相关 -> [UiMessageUtils.java][uiMessage.java] -> [Demo][uiMessage.demo]\n```\nsend          : 发送消息\naddListener   : 增加消息监听器\nremoveListener: 移除消息监听器\n```\n\n* ### URI 相关 -> [UriUtils.java][uri.java]\n```\nres2Uri            : res 转 uri\nfile2Uri           : file 转 uri\nuri2File           : uri 转 file\nuri2FileNoCacheCopy: uri 转 file 不拷贝缓存\nuri2Bytes          : uri 转 bytes\n```\n\n* ### UtilsTransActivity -> [UtilsTransActivity.java][trans.java]\n```\nstart: 启动随当前线程的透明 Activity\n```\n\n* ### UtilsTransActivity4MainProcess -> [UtilsTransActivity4MainProcess.java][trans4Main.java]\n```\nstart: 启动主线程的透明 Activity\n```\n\n* ### 震动相关 -> [VibrateUtils.java][vibrate.java] -> [Demo][vibrate.demo]\n```\nvibrate: 震动\ncancel : 取消\n```\n\n* ### 视图相关 -> [ViewUtils.java][view.java]\n```\nsetViewEnabled      : 设置视图是否可用\nrunOnUiThread       : 在 UI 线程运行\nrunOnUiThreadDelayed: 在 UI 线程延迟运行\nisLayoutRtl         : 布局是否从右到左\nfixScrollViewTopping: 修复 ScrollView 置顶问题\nlayoutId2View       : layoutId 转为 view\n```\n\n* ### 音量相关 -> [VolumeUtils.java][volume.java]\n```\ngetVolume   : 获取音量\nsetVolume   : 设置音量\ngetMaxVolume: 获取最大音量\ngetMinVolume: 获取最小音量\n```\n\n* ### 压缩相关 -> [ZipUtils.java][zip.java] -> [Test][zip.test]\n```\nzipFiles          : 批量压缩文件\nzipFile           : 压缩文件\nunzipFile         : 解压文件\nunzipFileByKeyword: 解压带有关键字的文件\ngetFilesPath      : 获取压缩文件中的文件路径链表\ngetComments       : 获取压缩文件中的注释链表\n```\n\n\n## 打个小广告\n\n欢迎加入我的小专栏「**[基你太美](https://xiaozhuanlan.com/Blankj)**」一起学习。\n\n\n\n[activity.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ActivityUtils.java\n[activity.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/activity/ActivityActivity.kt\n\n[adaptScreen.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/AdaptScreenUtils.java\n[adaptScreen.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/adaptScreen/AdaptScreenActivity.kt\n\n[api.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ApiUtils.java\n[api.readme]: https://github.com/Blankj/AndroidUtilCode/blob/master/plugin/api-gradle-plugin\n\n[app.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/AppUtils.java\n[app.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/app/AppActivity.kt\n\n[array.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ArrayUtils.java\n[array.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/ArrayUtilsTest.java\n\n[bar.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/BarUtils.java\n[bar.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/bar/BarActivity.kt\n\n[brightness.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/BrightnessUtils.java\n[brightness.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/brightness/BrightnessActivity.kt\n\n[bus.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/BusUtils.java\n[bus.readme]: https://github.com/Blankj/AndroidUtilCode/blob/master/plugin/bus-gradle-plugin\n\n[cacheDiskStatic.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDiskStaticUtils.java\n[cacheDiskStatic.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/CacheDiskStaticUtilsTest.java\n\n[cacheDisk.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDiskUtils.java\n[cacheDisk.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/CacheDiskUtilsTest.java\n\n[cacheDoubleStatic.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDoubleStaticUtils.java\n[cacheDoubleStatic.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/CacheDoubleStaticUtilsTest.java\n\n[cacheDouble.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDoubleUtils.java\n[cacheDouble.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/CacheDoubleUtilsTest.java\n\n[cacheMemoryStatic.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheMemoryStaticUtils.java\n[cacheMemoryStatic.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/CacheMemoryStaticUtilsTest.java\n\n[cacheMemory.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheMemoryUtils.java\n[cacheMemory.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/CacheMemoryUtilsTest.java\n\n[clean.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CleanUtils.java\n[clean.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/clean/CleanActivity.kt\n\n[click.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ClickUtils.java\n[click.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/click/ClickActivity.kt\n\n[clipboard.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ClipboardUtils.java\n[clipboard.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/clipboard/ClipboardActivity.kt\n\n[clone.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CloneUtils.java\n[clone.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/CloneUtilsTest.java\n\n[close.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CloseUtils.java\n\n[collection.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CollectionUtils.java\n[collection.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/CollectionUtilsTest.java\n\n[color.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ColorUtils.java\n[color.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/ColorUtilsTest.java\n\n[convert.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ConvertUtils.java\n[convert.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/ConvertUtilsTest.java\n\n[crash.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CrashUtils.java\n\n[debouncing.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/DebouncingUtils.java\n\n[device.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/DeviceUtils.java\n[device.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/device/DeviceActivity.kt\n\n[empty.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/EmptyUtils.java\n[empty.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/EmptyUtilsTest.java\n\n[encode.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/EncodeUtils.java\n[encode.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/EncodeUtilsTest.java\n\n[encrypt.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/EncryptUtils.java\n[encrypt.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/EncryptUtilsTest.java\n\n[fileIo.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/FileIOUtils.java\n[fileIo.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/FileIOUtilsTest.java\n\n[file.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/FileUtils.java\n[file.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/FileUtilsTest.java\n\n[flashlight.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/FlashlightUtils.java\n[flashlight.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/flashlight/FlashlightActivity.kt\n\n[fragment.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/FragmentUtils.java\n[fragment.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/fragment/FragmentActivity.kt\n\n[gson.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/GsonUtils.java\n[gson.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/GsonUtilsTest.java\n\n[image.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ImageUtils.java\n[image.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/image/ImageActivity.kt\n\n[intent.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/IntentUtils.java\n\n[keyboard.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/KeyboardUtils.java\n[keyboard.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/keyboard/KeyboardActivity.kt\n\n[language.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/LanguageUtils.java\n[language.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/language/LanguageActivity.kt\n\n[log.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/LogUtils.java\n[log.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/log/LogActivity.kt\n\n[map.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/MapUtils.java\n[map.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/MapUtilsTest.java\n\n[metaData.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/MetaDataUtils.java\n[metaData.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/metaData/MetaDataActivity.kt\n\n[network.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/NetworkUtils.java\n[network.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/network/NetworkActivity.kt\n\n[notification.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/NotificationUtils.java\n[notification.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/notification/NotificationActivity.kt\n\n[number.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/NumberUtils.java\n[number.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/NumberUtilsTest.java\n\n[object.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ObjectUtils.java\n[object.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/ObjectUtilsTest.java\n\n[path.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/PathUtils.java\n[path.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/path/PathActivity.kt\n\n[permission.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/PermissionUtils.java\n[permission.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/permission/PermissionActivity.kt\n\n[phone.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/PhoneUtils.java\n[phone.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/phone/PhoneActivity.kt\n\n[process.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ProcessUtils.java\n[process.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/process/ProcessActivity.kt\n\n[reflect.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ReflectUtils.java\n[reflect.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/reflect/ReflectUtilsTest.java\n\n[regex.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/RegexUtils.java\n[regex.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/RegexUtilsTest.java\n\n[resource.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ResourceUtils.java\n[resource.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/resource/ResourceActivity.kt\n\n[rom.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/RomUtils.java\n[rom.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/rom/RomActivity.kt\n\n[screen.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ScreenUtils.java\n[screen.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/screen/ScreenActivity.kt\n\n[sdcard.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/SDCardUtils.java\n[sdcard.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/sdcard/SDCardActivity.kt\n\n[service.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ServiceUtils.java\n\n[shadow.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ShadowUtils.java\n[shadow.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/shadow/ShadowActivity.kt\n\n[shell.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ShellUtils.java\n\n[size.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/SizeUtils.java\n\n[snackbar.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/SnackbarUtils.java\n[snackbar.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/snackbar/SnackbarActivity.kt\n\n[span.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/SpanUtils.java\n[span.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/span/SpanActivity.kt\n\n[spStatic.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/SPStaticUtils.java\n[spStatic.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/spStatic/SPStaticActivity.kt\n\n[sp.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/SPUtils.java\n\n[string.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/StringUtils.java\n[string.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/StringUtilsTest.java\n\n[thread.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ThreadUtils.java\n[thread.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/ThreadUtilsTest.java\n\n[time.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/TimeUtils.java\n[time.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/TimeUtilsTest.java\n\n[toast.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ToastUtils.java\n[toast.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/toast/ToastActivity.kt\n\n[touch.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/TouchUtils.java\n\n[uiMessage.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/UiMessageUtils.java\n[uiMessage.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/uiMessage/UiMessageActivity.kt\n\n[uri.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/UriUtils.java\n\n[trans.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/UtilsTransActivity.java\n\n[trans4Main.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/UtilsTransActivity4MainProcess.java\n\n[vibrate.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/VibrateUtils.java\n[vibrate.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/vibrate/VibrateActivity.kt\n\n[view.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ViewUtils.java\n\n[volume.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/VolumeUtils.java\n[volume.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/volume/VolumeActivity.kt\n\n[zip.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ZipUtils.java\n[zip.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/ZipUtilsTest.java\n"
  },
  {
    "path": "lib/utilcode/README.md",
    "content": "## Download\n\nGradle:\n```groovy\n// if u use AndroidX, use the following\nimplementation 'com.blankj:utilcodex:1.31.1'\n\n// Not in maintenance\nimplementation 'com.blankj:utilcode:1.30.7'\n```\n\n\n## APIs\n\n* ### About Activity -> [ActivityUtils.java][activity.java] -> [Demo][activity.demo]\n```\naddActivityLifecycleCallbacks\nremoveActivityLifecycleCallbacks\ngetAliveActivityByContext\ngetActivityByContext\nisActivityExists\nstartActivity\nstartActivityForResult\nstartActivities\nstartHomeActivity\ngetActivityList\ngetLauncherActivity\ngetMainActivities\ngetTopActivity\nisActivityAlive\nisActivityExistsInStack\nfinishActivity\nfinishToActivity\nfinishOtherActivities\nfinishAllActivities\nfinishAllActivitiesExceptNewest\n```\n\n* ### About AdaptScreen -> [AdaptScreenUtils.java][adaptScreen.java] -> [Demo][adaptScreen.demo]\n```\nadaptWidth\nadaptHeight\ncloseAdapt\npt2Px\npx2Pt\n```\n\n* ### About Api -> [ApiUtils.java][api.java] -> [README][api.readme]\n```\ngetApi\n```\n\n* ### About App -> [AppUtils.java][app.java] -> [Demo][app.demo]\n```\nregisterAppStatusChangedListener\nunregisterAppStatusChangedListener\ninstallApp\nuninstallApp\nisAppInstalled\nisAppRoot\nisAppDebug\nisAppSystem\nisAppForeground\nisAppRunning\nlaunchApp\nrelaunchApp\nlaunchAppDetailsSettings\nexitApp\ngetAppIcon\ngetAppPackageName\ngetAppName\ngetAppPath\ngetAppVersionName\ngetAppVersionCode\ngetAppMinSdkVersion\ngetAppTargetSdkVersion\ngetAppSignatures\ngetAppSignaturesSHA1\ngetAppSignaturesSHA256\ngetAppSignaturesMD5\ngetAppInfo\ngetAppsInfo\ngetApkInfo\n```\n\n* ### About Array -> [ArrayUtils.java][array.java] -> [Test][array.test]\n```\nnewArray\nnewLongArray\nnewIntArray\nnewShortArray\nnewCharArray\nnewByteArray\nnewDoubleArray\nnewFloatArray\nnewBooleanArray\nisEmpty\ngetLength\nisSameLength\nget\nset\nequals\nreverse\ncopy\nsubArray\nadd\nremove\nremoveElement\nindexOf\nlastIndexOf\ncontains\ntoPrimitive\ntoObject\nasList\nasUnmodifiableList\nasArrayList\nasLinkedList\nsort\nforAllDo\ntoString\n```\n\n* ### About Bar -> [BarUtils.java][bar.java] -> [Demo][bar.demo]\n```\ngetStatusBarHeight\nsetStatusBarVisibility\nisStatusBarVisible\nsetStatusBarLightMode\nisStatusBarLightMode\naddMarginTopEqualStatusBarHeight\nsubtractMarginTopEqualStatusBarHeight\nsetStatusBarColor\nsetStatusBarColor4Drawer\ntransparentStatusBar\ngetActionBarHeight\nsetNotificationBarVisibility\ngetNavBarHeight\nsetNavBarVisibility\nisNavBarVisible\nsetNavBarColor\ngetNavBarColor\nisSupportNavBar\nsetNavBarLightMode\nisNavBarLightMode\ntransparentNavBar\n```\n\n* ### About Brightness -> [BrightnessUtils.java][brightness.java] -> [Demo][brightness.demo]\n```\nisAutoBrightnessEnabled\nsetAutoBrightnessEnabled\ngetBrightness\nsetBrightness\nsetWindowBrightness\ngetWindowBrightness\n```\n\n* ### About Bus -> [BusUtils.java][bus.java] -> [README][bus.readme]\n```\nregister\nunregister\npost\npostSticky\nremoveSticky\ntoString_\n```\n\n* ### About CacheDiskStatic -> [CacheDiskStaticUtils.java][cacheDiskStatic.java] -> [Test][cacheDiskStatic.test]\n```\nsetDefaultCacheDiskUtils\nput\ngetBytes\ngetString\ngetJSONObject\ngetJSONArray\ngetBitmap\ngetDrawable\ngetParcelable\ngetSerializable\ngetCacheSize\ngetCacheCount\nremove\nclear\n```\n\n* ### About CacheDisk -> [CacheDiskUtils.java][cacheDisk.java] -> [Test][cacheDisk.test]\n```\ngetInstance\nInstance.put\nInstance.getBytes\nInstance.getString\nInstance.getJSONObject\nInstance.getJSONArray\nInstance.getBitmap\nInstance.getDrawable\nInstance.getParcelable\nInstance.getSerializable\nInstance.getCacheSize\nInstance.getCacheCount\nInstance.remove\nInstance.clear\n```\n\n* ### About CacheDoubleStatic -> [CacheDoubleStaticUtils.java][cacheDoubleStatic.java] -> [Test][cacheDoubleStatic.test]\n```\nsetDefaultCacheDoubleUtils\nput\ngetBytes\ngetString\ngetJSONObject\ngetJSONArray\ngetBitmap\ngetDrawable\ngetParcelable\ngetSerializable\ngetCacheDiskSize\ngetCacheDiskCount\ngetCacheMemoryCount\nremove\nclear\n```\n\n* ### About CacheDouble -> [CacheDoubleUtils.java][cacheDouble.java] -> [Test][cacheDouble.test]\n```\ngetInstance\nInstance.put\nInstance.getBytes\nInstance.getString\nInstance.getJSONObject\nInstance.getJSONArray\nInstance.getBitmap\nInstance.getDrawable\nInstance.getParcelable\nInstance.getSerializable\nInstance.getCacheDiskSize\nInstance.getCacheDiskCount\nInstance.getCacheMemoryCount\nInstance.remove\nInstance.clear\n```\n\n* ### About CacheMemoryStatic -> [CacheMemoryStaticUtils.java][cacheMemoryStatic.java] -> [Test][cacheMemoryStatic.test]\n```\nsetDefaultCacheMemoryUtils\nput\nget\ngetCacheCount\nremove\nclear\n```\n\n* ### About CacheMemory -> [CacheMemoryUtils.java][cacheMemory.java] -> [Test][cacheMemory.test]\n```\ngetInstance\nInstance.put\nInstance.get\nInstance.getCacheCount\nInstance.remove\nInstance.clear\n```\n\n* ### About Clean -> [CleanUtils.java][clean.java] -> [Demo][clean.demo]\n```\ncleanInternalCache\ncleanInternalFiles\ncleanInternalDbs\ncleanInternalDbByName\ncleanInternalSp\ncleanExternalCache\ncleanCustomDir\n```\n\n* ### About Click -> [ClickUtils.java][click.java] -> [Demo][click.demo]\n```\napplyPressedViewScale\napplyPressedViewAlpha\napplyPressedBgAlpha\napplyPressedBgDark\napplySingleDebouncing\napplyGlobalDebouncing\nexpandClickArea\nback2HomeFriendly\nClickUtils#OnDebouncingClickListener\nClickUtils#OnMultiClickListener\n```\n\n* ### About Clipboard -> [ClipboardUtils.java][clipboard.java] -> [Demo][clipboard.demo]\n```\ncopyText\ngetText\ncopyUri\ngetUri\ncopyIntent\ngetIntent\naddChangedListener\nremoveChangedListener\n```\n\n* ### About Clone -> [CloneUtils.java][clone.java] -> [Test][clone.test]\n```\ndeepClone\n```\n\n* ### About Close -> [CloseUtils.java][close.java]\n```\ncloseIO\ncloseIOQuietly\n```\n\n* ### About Collection -> [CollectionUtils.java][collection.java] -> [Test][collection.test]\n```\nnewUnmodifiableList[NotNull]: 新建只读[非空]链表\nnewArrayList[NotNull]       : 新建数组型[非空]链表\nnewLinkedList[NotNull]      : 新建指针型[非空]链表\nnewHashSet[NotNull]         : 新建哈希[非空]集合\nnewTreeSet[NotNull]         : 新建有序[非空]集合\nnewSynchronizedCollection\nnewUnmodifiableCollection\nunion\nintersection\ndisjunction\nsubtract\ncontainsAny\ngetCardinalityMap\nisSubCollection\nisProperSubCollection\nisEqualCollection\ncardinality\nfind\nforAllDo\nfilter\nselect\nselectRejected\ntransform\ncollect\ncountMatches\nexists\naddIgnoreNull\naddAll\nget\nsize\nsizeIsEmpty\nisEmpty\nisNotEmpty\nretainAll\nremoveAll\ntoString\n```\n\n* ### About Color -> [ColorUtils.java][color.java]\n```\ngetColor\nsetAlphaComponent\nsetRedComponent\nsetGreenComponent\nsetBlueComponent\nstring2Int\nint2RgbString\nint2ArgbString\ngetRandomColor\nisLightColor\n```\n\n* ### About Convert -> [ConvertUtils.java][convert.java] -> [Test][convert.test]\n```\nint2HexString, hexString2Int\nbytes2Bits, bits2Bytes\nbytes2Chars, chars2Bytes\nbytes2HexString, hexString2Bytes\nbytes2String, string2Bytes\nbytes2JSONObject, jsonObject2Bytes\nbytes2JSONArray, jsonArray2Bytes\nbytes2Parcelable, parcelable2Bytes\nbytes2Object, serializable2Bytes\nbytes2Bitmap, bitmap2Bytes\nmemorySize2Byte, byte2MemorySize\nbyte2FitMemorySize\ntimeSpan2Millis, millis2TimeSpan\nmillis2FitTimeSpan\ninput2OutputStream, output2InputStream\ninputStream2Bytes, bytes2InputStream\noutputStream2Bytes, bytes2OutputStream\ninputStream2String, string2InputStream\noutputStream2String, string2OutputStream\ninputStream2Lines\ndrawable2Bitmap, bitmap2Drawable\ndrawable2Bytes, bytes2Drawable\nview2Bitmap\ndp2px, px2dp\nsp2px, px2sp\n```\n\n* ### About Crash -> [CrashUtils.java][crash.java]\n```\ninit\nCrashInfo.addExtraHead\nCrashInfo.getThrowable\nCrashInfo.toString\n```\n\n* ### About Debouncing -> [DebouncingUtils.java][debouncing.java]\n```\nisValid\n```\n\n* ### About Device -> [DeviceUtils.java][device.java] -> [Demo][device.demo]\n```\nisDeviceRooted\nisAdbEnabled\ngetSDKVersionName\ngetSDKVersionCode\ngetAndroidID\ngetMacAddress\ngetManufacturer\ngetModel\ngetABIs\nisTablet\nisEmulator\nisDevelopmentSettingsEnabled\ngetUniqueDeviceId\nisSameDevice\n```\n\n* ### About Flashlight -> [FlashlightUtils.java][flashlight.java] -> [Demo][flashlight.demo]\n```\nisFlashlightEnable\nisFlashlightOn\nsetFlashlightStatus\ndestroy\n```\n\n* ### About Encode -> [EncodeUtils.java][encode.java] -> [Test][encode.test]\n```\nurlEncode\nurlDecode\nbase64Encode\nbase64Encode2String\nbase64Decode\nhtmlEncode\nhtmlDecode\nbinaryEncode\nbinaryDecode\n```\n\n* ### About Encrypt -> [EncryptUtils.java][encrypt.java] -> [Test][encrypt.test]\n```\nencryptMD2, encryptMD2ToString\nencryptMD5, encryptMD5ToString\nencryptMD5File, encryptMD5File2String\nencryptSHA1, encryptSHA1ToString\nencryptSHA224, encryptSHA224ToString\nencryptSHA256, encryptSHA256ToString\nencryptSHA384, encryptSHA384ToString\nencryptSHA512, encryptSHA512ToString\nencryptHmacMD5, encryptHmacMD5ToString\nencryptHmacSHA1, encryptHmacSHA1ToString\nencryptHmacSHA224, encryptHmacSHA224ToString\nencryptHmacSHA256, encryptHmacSHA256ToString\nencryptHmacSHA384, encryptHmacSHA384ToString\nencryptHmacSHA512, encryptHmacSHA512ToString\nencryptDES, encryptDES2HexString, encryptDES2Base64\ndecryptDES, decryptHexStringDES, decryptBase64DES\nencrypt3DES, encrypt3DES2HexString, encrypt3DES2Base64\ndecrypt3DES, decryptHexString3DES, decryptBase64_3DES\nencryptAES, encryptAES2HexString, encryptAES2Base64\ndecryptAES, decryptHexStringAES, decryptBase64AES\nencryptRSA, encryptRSA2HexString, encryptRSA2Base64\ndecryptRSA, decryptHexStringRSA, decryptBase64RSA\nrc4\n```\n\n* ### About FileIO -> [FileIOUtils.java][fileIo.java] -> [Test][fileIo.test]\n```\nwriteFileFromIS\nwriteFileFromBytesByStream\nwriteFileFromBytesByChannel\nwriteFileFromBytesByMap\nwriteFileFromString\nreadFile2List\nreadFile2String\nreadFile2BytesByStream\nreadFile2BytesByChannel\nreadFile2BytesByMap\nsetBufferSize\n```\n\n* ### About File -> [FileUtils.java][file.java] -> [Test][file.test]\n```\ngetFileByPath\nisFileExists\nrename\nisDir\nisFile\ncreateOrExistsDir\ncreateOrExistsFile\ncreateFileByDeleteOldFile\ncopy\nmove\ndelete\ndeleteAllInDir\ndeleteFilesInDir\ndeleteFilesInDirWithFilter\nlistFilesInDir\nlistFilesInDirWithFilter\ngetFileLastModified\ngetFileCharsetSimple\ngetFileLines\ngetSize\ngetLength\ngetFileMD5\ngetFileMD5ToString\ngetDirName\ngetFileName\ngetFileNameNoExtension\ngetFileExtension\nnotifySystemToScan\ngetFsTotalSize\ngetFsAvailableSize\n```\n\n* ### About Fragment -> [FragmentUtils.java][fragment.java] -> [Demo][fragment.demo]\n```\nadd\nshow\nhide\nshowHide\nreplace\npop\npopTo\npopAll\nremove\nremoveTo\nremoveAll\ngetTop\ngetTopInStack\ngetTopShow\ngetTopShowInStack\ngetFragments\ngetFragmentsInStack\ngetAllFragments\ngetAllFragmentsInStack\nfindFragment\ndispatchBackPress\nsetBackgroundColor\nsetBackgroundResource\nsetBackground\n```\n\n* ### About Gson -> [GsonUtils.java][gson.java] -> [Test][gson.test]\n```\nsetGsonDelegate\nsetGson\ngetGson\ntoJson\nfromJson\ngetListType\ngetSetType\ngetMapType\ngetArrayType\ngetType\n```\n\n* ### About Image -> [ImageUtils.java][image.java] -> [Demo][image.demo]\n```\nbitmap2Bytes, bytes2Bitmap\ndrawable2Bitmap, bitmap2Drawable\ndrawable2Bytes, bytes2Drawable\nview2Bitmap\ngetBitmap\nscale\nclip\nskew\nrotate\ngetRotateDegree\ntoRound\ntoRoundCorner\naddCornerBorder\naddCircleBorder\naddReflection\naddTextWatermark\naddImageWatermark\ntoAlpha\ntoGray\nfastBlur\nrenderScriptBlur\nstackBlur\nsave\nsave2Album\nisImage\ngetImageType\ncompressByScale\ncompressByQuality\ncompressBySampleSize\ngetSize\n```\n\n* ### About Intent -> [IntentUtils.java][intent.java]\n```\nisIntentAvailable\ngetInstallAppIntent\ngetUninstallAppIntent\ngetLaunchAppIntent\ngetLaunchAppDetailsSettingsIntent\ngetShareTextIntent\ngetShareImageIntent\ngetShareTextImageIntent\ngetComponentIntent\ngetShutdownIntent\ngetCaptureIntent\n```\n\n* ### About Keyboard -> [KeyboardUtils.java][keyboard.java] -> [Demo][keyboard.demo]\n```\nshowSoftInput\nhideSoftInput\ntoggleSoftInput\nisSoftInputVisible\nregisterSoftInputChangedListener\nunregisterSoftInputChangedListener\nfixAndroidBug5497\nfixSoftInputLeaks\nclickBlankArea2HideSoftInput\n```\n\n* ### About Language -> [LanguageUtils.java][language.java] -> [Demo][language.demo]\n```\napplySystemLanguage\napplyLanguage\nisAppliedLanguage\ngetAppliedLanguage\ngetContextLanguage\ngetAppContextLanguage\ngetSystemLanguage\nupdateAppContextLanguage\nattachBaseContext\n```\n\n* ### About Log -> [LogUtils.java][log.java] -> [Demo][log.demo]\n```\ngetConfig\nConfig.setLogSwitch\nConfig.setConsoleSwitch\nConfig.setGlobalTag\nConfig.setLogHeadSwitch\nConfig.setLog2FileSwitch\nConfig.setDir\nConfig.setFilePrefix\nConfig.setBorderSwitch\nConfig.setSingleTagSwitch\nConfig.setConsoleFilter\nConfig.setFileFilter\nConfig.setStackDeep\nConfig.setStackOffset\nConfig.setSaveDays\nConfig.addFormatter\nConfig.setFileWriter\nConfig.setOnConsoleOutputListener\nConfig.setOnFileOutputListener\nConfig.addFileExtraHead\nlog\nv\nvTag\nd\ndTag\ni\niTag\nw\nwTag\ne\neTag\na\naTag\nfile\njson\nxml\ngetCurrentLogFilePath\ngetLogFiles\n```\n\n* ### About Map -> [MapUtils.java][map.java] -> [Test][map.test]\n```\nnewUnmodifiableMap\nnewHashMap\nnewLinkedHashMap\nnewTreeMap\nnewHashTable\nisEmpty\nisNotEmpty\nsize\nforAllDo\ntransform\ntoString\n```\n\n* ### About MetaData -> [MetaDataUtils.java][metaData.java] -> [Demo][metaData.demo]\n```\ngetMetaDataInApp\ngetMetaDataInActivity\ngetMetaDataInService\ngetMetaDataInReceiver\n```\n\n* ### About Network -> [NetworkUtils.java][network.java] -> [Demo][network.demo]\n```\nopenWirelessSettings\nisConnected\nisAvailable[Async]                      : 判断网络是否可用\nisAvailableByPing[Async]                : 用 ping 判断网络是否可用\nisAvailableByDns[Async]                 : 用 DNS 判断网络是否可用\ngetMobileDataEnabled\nisMobileData\nis4G\ngetWifiEnabled\nsetWifiEnabled\nisWifiConnected\nisWifiAvailable[Async]                  : 判断 wifi 数据是否可用\ngetNetworkOperatorName\ngetNetworkType\ngetIPAddress[Async]                     : 获取 IP 地址\ngetDomainAddress[Async]                 : 获取域名 IP 地址\ngetIpAddressByWifi\ngetGatewayByWifi\ngetNetMaskByWifi\ngetServerAddressByWifi\nregisterNetworkStatusChangedListener\nisRegisteredNetworkStatusChangedListener\nunregisterNetworkStatusChangedListener\ngetWifiScanResult\naddOnWifiChangedConsumer\nremoveOnWifiChangedConsumer\n```\n\n* ### About Notification -> [NotificationUtils.java][notification.java] -> [Demo][notification.demo]\n```\nareNotificationsEnabled\nnotify\ncancel\ncancelAll\nsetNotificationBarVisibility\n```\n\n* ### About Number -> [NumberUtils.java][number.java] -> [Test][number.test]\n```\nformat\nfloat2Double\n```\n\n* ### About Object -> [ObjectUtils.java][object.java] -> [Test][object.test]\n```\nisEmpty\nisNotEmpty\nequals\ncompare\nrequireNonNull(s)\ngetOrDefault\ntoString\nhashCode(s)\n```\n\n* ### About Path -> [PathUtils.java][path.java] -> [Demo][path.demo]\n```\njoin\ngetRootPath\ngetDataPath\ngetDownloadCachePath\ngetInternalAppDataPath\ngetInternalAppCodeCacheDir\ngetInternalAppCachePath\ngetInternalAppDbsPath\ngetInternalAppDbPath\ngetInternalAppFilesPath\ngetInternalAppSpPath\ngetInternalAppNoBackupFilesPath\ngetExternalStoragePath\ngetExternalMusicPath\ngetExternalPodcastsPath\ngetExternalRingtonesPath\ngetExternalAlarmsPath\ngetExternalNotificationsPath\ngetExternalPicturesPath\ngetExternalMoviesPath\ngetExternalDownloadsPath\ngetExternalDcimPath\ngetExternalDocumentsPath\ngetExternalAppDataPath\ngetExternalAppCachePath\ngetExternalAppFilesPath\ngetExternalAppMusicPath\ngetExternalAppPodcastsPath\ngetExternalAppRingtonesPath\ngetExternalAppAlarmsPath\ngetExternalAppNotificationsPath\ngetExternalAppPicturesPath\ngetExternalAppMoviesPath\ngetExternalAppDownloadPath\ngetExternalAppDcimPath\ngetExternalAppDocumentsPath\ngetExternalAppObbPath\ngetRootPathExternalFirst\ngetAppDataPathExternalFirst\ngetFilesPathExternalFirst\ngetCachePathExternalFirst\n```\n\n* ### About Permission -> [PermissionUtils.java][permission.java] -> [Demo][permission.demo]\n```\npermission\npermissionGroup\npermission.explain\npermission.rationale\npermission.callback\npermission.theme\npermission.request\ngetPermissions\nisGranted\nisGrantedWriteSettings\nrequestWriteSettings\nisGrantedDrawOverlays\nrequestDrawOverlays\nlaunchAppDetailsSettings\n```\n\n* ### About Phone -> [PhoneUtils.java][phone.java] -> [Demo][phone.demo]\n```\nisPhone\ngetDeviceId\ngetSerial\ngetIMEI\ngetMEID\ngetIMSI\ngetPhoneType\nisSimCardReady\ngetSimOperatorName\ngetSimOperatorByMnc\ndial\ncall\nsendSms\n```\n\n* ### About Process -> [ProcessUtils.java][process.java] -> [Demo][process.demo]\n```\ngetForegroundProcessName\nkillAllBackgroundProcesses\nkillBackgroundProcesses\nisMainProcess\ngetCurrentProcessName\n```\n\n* ### About Reflect -> [ReflectUtils.java][reflect.java] -> [Test][reflect.test]\n```\nreflect\nnewInstance\nfield\nmethod\nget\n```\n\n* ### About Regex -> [RegexUtils.java][regex.java] -> [Test][regex.test]\n```\nisMobileSimple\nisMobileExact\nisTel\nisIDCard15\nisIDCard18\nisIDCard18Exact\nisEmail\nisURL\nisZh\nisUsername\nisDate\nisIP\nisMatch\ngetMatches\ngetSplits\ngetReplaceFirst\ngetReplaceAll\nRegexConstants.REGEX_DOUBLE_BYTE_CHAR\nRegexConstants.REGEX_BLANK_LINE\nRegexConstants.REGEX_QQ_NUM\nRegexConstants.REGEX_CHINA_POSTAL_CODE\nRegexConstants.REGEX_INTEGER\nRegexConstants.REGEX_POSITIVE_INTEGER\nRegexConstants.REGEX_NEGATIVE_INTEGER\nRegexConstants.REGEX_NOT_NEGATIVE_INTEGER\nRegexConstants.REGEX_NOT_POSITIVE_INTEGER\nRegexConstants.REGEX_FLOAT\nRegexConstants.REGEX_POSITIVE_FLOAT\nRegexConstants.REGEX_NEGATIVE_FLOAT\nRegexConstants.REGEX_NOT_NEGATIVE_FLOAT\nRegexConstants.REGEX_NOT_POSITIVE_FLOAT\n```\n\n* ### About Resource -> [ResourceUtils.java][resource.java] -> [Demo][resource.demo]\n```\ngetDrawable\ngetIdByName\ngetStringIdByName\ngetColorIdByName\ngetDimenIdByName\ngetDrawableIdByName\ngetMipmapIdByName\ngetLayoutIdByName\ngetStyleIdByName\ngetAnimIdByName\ngetMenuIdByName\ncopyFileFromAssets\nreadAssets2String\nreadAssets2List\ncopyFileFromRaw\nreadRaw2String\nreadRaw2List\n```\n\n* ### About Rom -> [RomUtils.java][rom.java] -> [Demo][rom.demo]\n```\nisHuawei\nisVivo\nisXiaomi\nisOppo\nisLeeco\nis360\nisZte\nisOneplus\nisNubia\nisCoolpad\nisLg\nisGoogle\nisSamsung\nisMeizu\nisLenovo\nisSmartisan\nisHtc\nisSony\nisGionee\nisMotorola\ngetRomInfo\n```\n\n* ### About Screen -> [ScreenUtils.java][screen.java] -> [Demo][screen.demo]\n```\ngetScreenWidth\ngetScreenHeight\ngetAppScreenWidth\ngetAppScreenHeight\ngetScreenDensity\ngetScreenDensityDpi\nsetFullScreen\nsetNonFullScreen\ntoggleFullScreen\nisFullScreen\nsetLandscape\nsetPortrait\nisLandscape\nisPortrait\ngetScreenRotation\nscreenShot\nisScreenLock\nsetSleepDuration\ngetSleepDuration\n```\n\n* ### About SDCard -> [SDCardUtils.java][sdcard.java] -> [Demo][sdcard.demo]\n```\nisSDCardEnableByEnvironment\ngetSDCardPathByEnvironment\ngetSDCardInfo\ngetMountedSDCardPath\ngetExternalTotalSize\ngetExternalAvailableSize\ngetInternalTotalSize\ngetInternalAvailableSize\n```\n\n* ### About Service -> [ServiceUtils.java][service.java]\n```\ngetAllRunningServices\nstartService\nstopService\nbindService\nunbindService\nisServiceRunning\n```\n\n* ### About Shadow -> [ShadowUtils.java][shadow.java] -> [Demo][shadow.demo]\n```\napply\n```\n\n* ### About Shell -> [ShellUtils.java][shell.java]\n```\nexecCmd[Async]: 执行命令\n```\n\n* ### About Size -> [SizeUtils.java][size.java]\n```\ndp2px, px2dp\nsp2px, px2sp\napplyDimension\nforceGetViewSize\nmeasureView\ngetMeasuredWidth\ngetMeasuredHeight\n```\n\n* ### About Snackbar -> [SnackbarUtils.java][snackbar.java] -> [Demo][snackbar.demo]\n```\nwith\nsetMessage\nsetMessageColor\nsetBgColor\nsetBgResource\nsetDuration\nsetAction\nsetBottomMargin\nshow\nshowSuccess\nshowWarning\nshowError\ndismiss\ngetView\naddView\n```\n\n* ### About Span -> [SpanUtils.java][span.java] -> [Demo][span.demo]\n```\nwith\nsetFlag\nsetForegroundColor\nsetBackgroundColor\nsetLineHeight\nsetQuoteColor\nsetLeadingMargin\nsetBullet\nsetFontSize\nsetFontProportion\nsetFontXProportion\nsetStrikethrough\nsetUnderline\nsetSuperscript\nsetSubscript\nsetBold\nsetItalic\nsetBoldItalic\nsetFontFamily\nsetTypeface\nsetAlign\nsetClickSpan\nsetUrl\nsetBlur\nsetShader\nsetShadow\nsetSpans\nappend\nappendLine\nappendImage\nappendSpace\ncreate\n```\n\n* ### About SPStatic -> [SPStaticUtils.java][spStatic.java] -> [Demo][spStatic.demo]\n```\nsetDefaultSPUtils\nput\ngetString\ngetInt\ngetLong\ngetFloat\ngetBoolean\ngetAll\ncontains\nremove\nclear\n```\n\n* ### About SP -> [SPUtils.java][sp.java]\n```\ngetInstance\nInstance.put\nInstance.getString\nInstance.getInt\nInstance.getLong\nInstance.getFloat\nInstance.getBoolean\nInstance.getAll\nInstance.contains\nInstance.remove\nInstance.clear\n```\n\n* ### About String -> [StringUtils.java][string.java] -> [Test][string.test]\n```\nisEmpty\nisTrimEmpty\nisSpace\nequals\nequalsIgnoreCase\nnull2Length0\nlength\nupperFirstLetter\nlowerFirstLetter\nreverse\ntoDBC\ntoSBC\ngetString\ngetStringArray\nformat\n```\n\n* ### About Thread -> [ThreadUtils.java][thread.java] -> [Test][thread.test]\n```\nisMainThread\ngetMainHandler\nrunOnUiThread\nrunOnUiThreadDelayed\ngetFixedPool\ngetSinglePool\ngetCachedPool\ngetIoPool\ngetCpuPool\nexecuteByFixed\nexecuteByFixedWithDelay\nexecuteByFixedAtFixRate\nexecuteBySingle\nexecuteBySingleWithDelay\nexecuteBySingleAtFixRate\nexecuteByCached\nexecuteByCachedWithDelay\nexecuteByCachedAtFixRate\nexecuteByIo\nexecuteByIoWithDelay\nexecuteByIoAtFixRate\nexecuteByCpu\nexecuteByCpuWithDelay\nexecuteByCpuAtFixRate\nexecuteByCustom\nexecuteByCustomWithDelay\nexecuteByCustomAtFixRate\ncancel\nsetDeliver\n```\n\n* ### About Time -> [TimeUtils.java][time.java] -> [Test][time.test]\n```\ngetSafeDateFormat\nmillis2String\nstring2Millis\nstring2Date\ndate2String\ndate2Millis\nmillis2Date\ngetTimeSpan\ngetFitTimeSpan\ngetNowMills\ngetNowString\ngetNowDate\ngetTimeSpanByNow\ngetFitTimeSpanByNow\ngetFriendlyTimeSpanByNow\ngetMillis\ngetString\ngetDate\ngetMillisByNow\ngetStringByNow\ngetDateByNow\nisToday\nisLeapYear\ngetChineseWeek\ngetUSWeek\nisAm\nisPm\ngetValueByCalendarField\ngetChineseZodiac\ngetZodiac\n```\n\n* ### About Toast -> [ToastUtils.java][toast.java] -> [Demo][toast.demo]\n```\nmake\nmake.setMode\nmake.setGravity\nmake.setBgColor\nmake.setBgResource\nmake.setTextColor\nmake.setTextSize\nmake.setDurationIsLong\nmake.setLeftIcon\nmake.setTopIcon\nmake.setRightIcon\nmake.setBottomIcon\nmake.setNotUseSystemToast\nmake.show\ngetDefaultMaker\nshowShort\nshowLong\ncancel\n```\n\n* ### About Touch -> [TouchUtils.java][touch.java]\n```\nsetOnTouchListener\n```\n\n* ### About UiMessage -> [UiMessageUtils.java][uiMessage.java] -> [Demo][uiMessage.demo]\n```\nsend\naddListener\nremoveListener\n```\n\n* ### About Uri -> [UriUtils.java][uri.java]\n```\nres2Uri\nfile2Uri\nuri2File\nuri2FileNoCacheCopy\nuri2Bytes\n```\n\n* ### UtilsTransActivity -> [UtilsTransActivity.java][trans.java]\n```\nstart\n```\n\n* ### UtilsTransActivity4MainProcess -> [UtilsTransActivity4MainProcess.java][trans4Main.java]\n```\nstart\n```\n\n* ### About Vibrate -> [VibrateUtils.java][vibrate.java] -> [Demo][vibrate.demo]\n```\nvibrate\ncancel\n```\n\n* ### About View -> [ViewUtils.java][view.java]\n```\nsetViewEnabled\nrunOnUiThread\nrunOnUiThreadDelayed\nisLayoutRtl\nfixScrollViewTopping\nlayoutId2View\n```\n\n* ### About Volume -> [VolumeUtils.java][volume.java]\n```\ngetVolume\nsetVolume\ngetMaxVolume\ngetMinVolume\n```\n\n* ### About Zip -> [ZipUtils.java][zip.java] -> [Test][zip.test]\n```\nzipFiles\nzipFile\nunzipFile\nunzipFileByKeyword\ngetFilesPath\ngetComments\n```\n\n\n\n\n\n\n[activity.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ActivityUtils.java\n[activity.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/activity/ActivityActivity.kt\n\n[adaptScreen.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/AdaptScreenUtils.java\n[adaptScreen.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/adaptScreen/AdaptScreenActivity.kt\n\n[api.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ApiUtils.java\n[api.readme]: https://github.com/Blankj/AndroidUtilCode/blob/master/plugin/api-gradle-plugin\n\n[app.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/AppUtils.java\n[app.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/app/AppActivity.kt\n\n[array.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ArrayUtils.java\n[array.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/ArrayUtilsTest.java\n\n[bar.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/BarUtils.java\n[bar.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/bar/BarActivity.kt\n\n[brightness.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/BrightnessUtils.java\n[brightness.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/brightness/BrightnessActivity.kt\n\n[bus.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/BusUtils.java\n[bus.readme]: https://github.com/Blankj/AndroidUtilCode/blob/master/plugin/bus-gradle-plugin\n\n[cacheDiskStatic.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDiskStaticUtils.java\n[cacheDiskStatic.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/CacheDiskStaticUtilsTest.java\n\n[cacheDisk.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDiskUtils.java\n[cacheDisk.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/CacheDiskUtilsTest.java\n\n[cacheDoubleStatic.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDoubleStaticUtils.java\n[cacheDoubleStatic.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/CacheDoubleStaticUtilsTest.java\n\n[cacheDouble.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDoubleUtils.java\n[cacheDouble.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/CacheDoubleUtilsTest.java\n\n[cacheMemoryStatic.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheMemoryStaticUtils.java\n[cacheMemoryStatic.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/CacheMemoryStaticUtilsTest.java\n\n[cacheMemory.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheMemoryUtils.java\n[cacheMemory.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/CacheMemoryUtilsTest.java\n\n[clean.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CleanUtils.java\n[clean.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/clean/CleanActivity.kt\n\n[click.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ClickUtils.java\n[click.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/click/ClickActivity.kt\n\n[clipboard.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ClipboardUtils.java\n[clipboard.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/clipboard/ClipboardActivity.kt\n\n[clone.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CloneUtils.java\n[clone.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/CloneUtilsTest.java\n\n[close.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CloseUtils.java\n\n[collection.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CollectionUtils.java\n[collection.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/CollectionUtilsTest.java\n\n[color.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ColorUtils.java\n[color.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/ColorUtilsTest.java\n\n[convert.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ConvertUtils.java\n[convert.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/ConvertUtilsTest.java\n\n[crash.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/CrashUtils.java\n\n[debouncing.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/DebouncingUtils.java\n\n[device.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/DeviceUtils.java\n[device.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/device/DeviceActivity.kt\n\n[empty.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/EmptyUtils.java\n[empty.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/EmptyUtilsTest.java\n\n[encode.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/EncodeUtils.java\n[encode.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/EncodeUtilsTest.java\n\n[encrypt.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/EncryptUtils.java\n[encrypt.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/EncryptUtilsTest.java\n\n[fileIo.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/FileIOUtils.java\n[fileIo.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/FileIOUtilsTest.java\n\n[file.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/FileUtils.java\n[file.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/FileUtilsTest.java\n\n[flashlight.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/FlashlightUtils.java\n[flashlight.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/flashlight/FlashlightActivity.kt\n\n[fragment.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/FragmentUtils.java\n[fragment.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/fragment/FragmentActivity.kt\n\n[gson.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/GsonUtils.java\n[gson.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/GsonUtilsTest.java\n\n[image.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ImageUtils.java\n[image.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/image/ImageActivity.kt\n\n[intent.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/IntentUtils.java\n\n[keyboard.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/KeyboardUtils.java\n[keyboard.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/keyboard/KeyboardActivity.kt\n\n[language.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/LanguageUtils.java\n[language.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/language/LanguageActivity.kt\n\n[log.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/LogUtils.java\n[log.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/log/LogActivity.kt\n\n[map.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/MapUtils.java\n[map.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/MapUtilsTest.java\n\n[metaData.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/MetaDataUtils.java\n[metaData.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/metaData/MetaDataActivity.kt\n\n[network.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/NetworkUtils.java\n[network.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/network/NetworkActivity.kt\n\n[notification.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/NotificationUtils.java\n[notification.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/notification/NotificationActivity.kt\n\n[number.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/NumberUtils.java\n[number.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/NumberUtilsTest.java\n\n[object.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ObjectUtils.java\n[object.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/ObjectUtilsTest.java\n\n[path.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/PathUtils.java\n[path.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/path/PathActivity.kt\n\n[permission.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/PermissionUtils.java\n[permission.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/permission/PermissionActivity.kt\n\n[phone.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/PhoneUtils.java\n[phone.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/phone/PhoneActivity.kt\n\n[process.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ProcessUtils.java\n[process.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/process/ProcessActivity.kt\n\n[reflect.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ReflectUtils.java\n[reflect.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/reflect/ReflectUtilsTest.java\n\n[regex.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/RegexUtils.java\n[regex.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/RegexUtilsTest.java\n\n[resource.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ResourceUtils.java\n[resource.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/resource/ResourceActivity.kt\n\n[rom.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/RomUtils.java\n[rom.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/rom/RomActivity.kt\n\n[screen.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ScreenUtils.java\n[screen.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/screen/ScreenActivity.kt\n\n[sdcard.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/SDCardUtils.java\n[sdcard.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/sdcard/SDCardActivity.kt\n\n[service.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ServiceUtils.java\n\n[shadow.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ShadowUtils.java\n[shadow.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/shadow/ShadowActivity.kt\n\n[shell.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ShellUtils.java\n\n[size.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/SizeUtils.java\n\n[snackbar.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/SnackbarUtils.java\n[snackbar.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/snackbar/SnackbarActivity.kt\n\n[span.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/SpanUtils.java\n[span.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/span/SpanActivity.kt\n\n[spStatic.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/SPStaticUtils.java\n[spStatic.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/spStatic/SPStaticActivity.kt\n\n[sp.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/SPUtils.java\n\n[string.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/StringUtils.java\n[string.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/StringUtilsTest.java\n\n[thread.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ThreadUtils.java\n[thread.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/ThreadUtilsTest.java\n\n[time.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/TimeUtils.java\n[time.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/TimeUtilsTest.java\n\n[toast.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ToastUtils.java\n[toast.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/toast/ToastActivity.kt\n\n[touch.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/TouchUtils.java\n\n[uiMessage.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/UiMessageUtils.java\n[uiMessage.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/uiMessage/UiMessageActivity.kt\n\n[uri.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/UriUtils.java\n\n[trans.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/UtilsTransActivity.java\n\n[trans4Main.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/UtilsTransActivity4MainProcess.java\n\n[vibrate.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/VibrateUtils.java\n[vibrate.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/vibrate/VibrateActivity.kt\n\n[view.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ViewUtils.java\n\n[volume.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/VolumeUtils.java\n[volume.demo]: https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/volume/VolumeActivity.kt\n\n[zip.java]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/main/java/com/blankj/utilcode/util/ZipUtils.java\n[zip.test]: https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/ZipUtilsTest.java\n"
  },
  {
    "path": "lib/utilcode/build.gradle",
    "content": "apply {\r\n    plugin \"readme-core\"\r\n}\r\n\r\nreadme {\r\n    readmeFile file('./README.md')\r\n    readmeCnFile file('./README-CN.md')\r\n}\r\n\r\nandroid {\r\n    compileSdkVersion Config.compileSdkVersion\r\n    defaultConfig {\r\n        minSdkVersion Config.minSdkVersion\r\n        versionCode Config.versionCode\r\n        versionName Config.versionName\r\n        consumerProguardFiles 'proguard-rules.pro'\r\n    }\r\n\r\n    buildTypes {\r\n        release {\r\n            minifyEnabled false\r\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\r\n            consumerProguardFiles 'proguard-rules.pro'\r\n        }\r\n    }\r\n}\r\n\r\ndependencies {\r\n    implementation Config.libs.gson.path\r\n\r\n    implementation Config.libs.androidx_appcompat.path\r\n    compileOnly Config.libs.androidx_material.path\r\n\r\n    testImplementation Config.libs.test_junit.path\r\n    testImplementation Config.libs.test_robolectric.path\r\n    testImplementation Config.libs.androidx_appcompat.path\r\n    testImplementation Config.libs.androidx_material.path\r\n    testImplementation Config.libs.eventbus_lib.path\r\n}\r\n\r\next {\r\n    groupId = Config.modules.lib_utilcode.groupId\r\n    artifactId = Config.modules.lib_utilcode.artifactId\r\n    version = Config.modules.lib_utilcode.version\r\n    website = \"https://github.com/Blankj/AndroidUtilCode\"\r\n}\r\napply from: \"${rootDir.path}/config/publish.gradle\""
  },
  {
    "path": "lib/utilcode/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\r\n# By default, the flags in this file are appended to flags specified\r\n# in G:\\Android_IDE\\ADT\\sdk/tools/proguard/proguard-android.txt\r\n# You can edit the include path and order by changing the proguardFiles\r\n# directive in build.gradle.\r\n#\r\n# For more details, see\r\n#   http://developer.android.com/guide/developing/tools/proguard.html\r\n\r\n# Add any project specific keep options here:\r\n\r\n# If your project uses WebView with JS, uncomment the following\r\n# and specify the fully qualified class name to the JavaScript interface\r\n# class:\r\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\r\n#   public *;\r\n#}\r\n-dontwarn com.blankj.utilcode.**\r\n\r\n-keepclassmembers class * {\r\n    @com.blankj.utilcode.util.BusUtils$Bus <methods>;\r\n}\r\n\r\n-keep public class * extends com.blankj.utilcode.util.ApiUtils$BaseApi\r\n-keep,allowobfuscation @interface com.blankj.utilcode.util.ApiUtils$Api\r\n-keep @com.blankj.utilcode.util.ApiUtils$Api class *\r\n\r\n-keepattributes *Annotation*"
  },
  {
    "path": "lib/utilcode/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.blankj.utilcode\">\n\n    <queries>\n        <intent>\n            <action android:name=\"android.intent.action.MAIN\" />\n        </intent>\n        <intent>\n            <action android:name=\"android.intent.action.VIEW\" />\n        </intent>\n    </queries>\n\n    <application>\n\n        <activity\n            android:name=\"com.blankj.utilcode.util.UtilsTransActivity4MainProcess\"\n            android:exported=\"false\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:theme=\"@style/ActivityTranslucent\"\n            android:windowSoftInputMode=\"stateHidden|stateAlwaysHidden\" />\n\n        <activity\n            android:name=\"com.blankj.utilcode.util.UtilsTransActivity\"\n            android:exported=\"false\"\n            android:configChanges=\"orientation|keyboardHidden|screenSize\"\n            android:multiprocess=\"true\"\n            android:theme=\"@style/ActivityTranslucent\"\n            android:windowSoftInputMode=\"stateHidden|stateAlwaysHidden\" />\n\n        <provider\n            android:name=\"com.blankj.utilcode.util.UtilsFileProvider\"\n            android:authorities=\"${applicationId}.utilcode.fileprovider\"\n            android:exported=\"false\"\n            android:grantUriPermissions=\"true\">\n            <meta-data\n                android:name=\"android.support.FILE_PROVIDER_PATHS\"\n                android:resource=\"@xml/util_code_provider_paths\" />\n        </provider>\n\n        <service\n            android:name=\"com.blankj.utilcode.util.MessengerUtils$ServerService\"\n            android:exported=\"false\">\n            <intent-filter>\n                <action android:name=\"${applicationId}.messenger\" />\n            </intent-filter>\n        </service>\n    </application>\n</manifest>"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/constant/CacheConstants.java",
    "content": "package com.blankj.utilcode.constant;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/06/13\n *     desc  : constants of cache\n * </pre>\n */\npublic interface CacheConstants {\n    int SEC  = 1;\n    int MIN  = 60;\n    int HOUR = 3600;\n    int DAY  = 86400;\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/constant/MemoryConstants.java",
    "content": "package com.blankj.utilcode.constant;\n\nimport androidx.annotation.IntDef;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/03/13\n *     desc  : constants of memory\n * </pre>\n */\npublic final class MemoryConstants {\n\n    public static final int BYTE = 1;\n    public static final int KB   = 1024;\n    public static final int MB   = 1048576;\n    public static final int GB   = 1073741824;\n\n    @IntDef({BYTE, KB, MB, GB})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface Unit {\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/constant/PermissionConstants.java",
    "content": "package com.blankj.utilcode.constant;\n\nimport android.Manifest.permission;\nimport android.annotation.SuppressLint;\nimport android.os.Build;\nimport androidx.annotation.StringDef;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/12/29\n *     desc  : constants of permission\n * </pre>\n */\n@SuppressLint(\"InlinedApi\")\npublic final class PermissionConstants {\n\n    public static final String CALENDAR             = \"CALENDAR\";\n    public static final String CAMERA               = \"CAMERA\";\n    public static final String CONTACTS             = \"CONTACTS\";\n    public static final String LOCATION             = \"LOCATION\";\n    public static final String MICROPHONE           = \"MICROPHONE\";\n    public static final String PHONE                = \"PHONE\";\n    public static final String SENSORS              = \"SENSORS\";\n    public static final String SMS                  = \"SMS\";\n    public static final String STORAGE              = \"STORAGE\";\n    public static final String ACTIVITY_RECOGNITION = \"ACTIVITY_RECOGNITION\";\n\n    private static final String[] GROUP_CALENDAR             = {\n            permission.READ_CALENDAR, permission.WRITE_CALENDAR\n    };\n    private static final String[] GROUP_CAMERA               = {\n            permission.CAMERA\n    };\n    private static final String[] GROUP_CONTACTS             = {\n            permission.READ_CONTACTS, permission.WRITE_CONTACTS, permission.GET_ACCOUNTS\n    };\n    private static final String[] GROUP_LOCATION             = {\n            permission.ACCESS_FINE_LOCATION, permission.ACCESS_COARSE_LOCATION, permission.ACCESS_BACKGROUND_LOCATION\n    };\n    private static final String[] GROUP_MICROPHONE           = {\n            permission.RECORD_AUDIO\n    };\n    private static final String[] GROUP_PHONE                = {\n            permission.READ_PHONE_STATE, permission.READ_PHONE_NUMBERS, permission.CALL_PHONE,\n            permission.READ_CALL_LOG, permission.WRITE_CALL_LOG, permission.ADD_VOICEMAIL,\n            permission.USE_SIP, permission.PROCESS_OUTGOING_CALLS, permission.ANSWER_PHONE_CALLS\n    };\n    private static final String[] GROUP_PHONE_BELOW_O        = {\n            permission.READ_PHONE_STATE, permission.READ_PHONE_NUMBERS, permission.CALL_PHONE,\n            permission.READ_CALL_LOG, permission.WRITE_CALL_LOG, permission.ADD_VOICEMAIL,\n            permission.USE_SIP, permission.PROCESS_OUTGOING_CALLS\n    };\n    private static final String[] GROUP_SENSORS              = {\n            permission.BODY_SENSORS\n    };\n    private static final String[] GROUP_SMS                  = {\n            permission.SEND_SMS, permission.RECEIVE_SMS, permission.READ_SMS,\n            permission.RECEIVE_WAP_PUSH, permission.RECEIVE_MMS,\n    };\n    private static final String[] GROUP_STORAGE              = {\n            permission.READ_EXTERNAL_STORAGE, permission.WRITE_EXTERNAL_STORAGE,\n    };\n    private static final String[] GROUP_ACTIVITY_RECOGNITION = {\n            permission.ACTIVITY_RECOGNITION,\n    };\n\n    @StringDef({CALENDAR, CAMERA, CONTACTS, LOCATION, MICROPHONE, PHONE, SENSORS, SMS, STORAGE,})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface PermissionGroup {\n    }\n\n    public static String[] getPermissions(@PermissionGroup final String permission) {\n        if (permission == null) return new String[0];\n        switch (permission) {\n            case CALENDAR:\n                return GROUP_CALENDAR;\n            case CAMERA:\n                return GROUP_CAMERA;\n            case CONTACTS:\n                return GROUP_CONTACTS;\n            case LOCATION:\n                return GROUP_LOCATION;\n            case MICROPHONE:\n                return GROUP_MICROPHONE;\n            case PHONE:\n                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {\n                    return GROUP_PHONE_BELOW_O;\n                } else {\n                    return GROUP_PHONE;\n                }\n            case SENSORS:\n                return GROUP_SENSORS;\n            case SMS:\n                return GROUP_SMS;\n            case STORAGE:\n                return GROUP_STORAGE;\n            case ACTIVITY_RECOGNITION:\n                return GROUP_ACTIVITY_RECOGNITION;\n        }\n        return new String[]{permission};\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/constant/RegexConstants.java",
    "content": "package com.blankj.utilcode.constant;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/03/13\n *     desc  : constants of regex\n * </pre>\n */\npublic final class RegexConstants {\n\n    /**\n     * Regex of simple mobile.\n     */\n    public static final String REGEX_MOBILE_SIMPLE = \"^[1]\\\\d{10}$\";\n    /**\n     * Regex of exact mobile.\n     * <p>china mobile: 134(0-8), 135, 136, 137, 138, 139, 147, 150, 151, 152, 157, 158, 159, 165, 172, 178, 182, 183, 184, 187, 188, 195, 197, 198</p>\n     * <p>china unicom: 130, 131, 132, 145, 155, 156, 166, 167, 175, 176, 185, 186, 196</p>\n     * <p>china telecom: 133, 149, 153, 162, 173, 177, 180, 181, 189, 190, 191, 199</p>\n     * <p>china broadcasting: 192</p>\n     * <p>global star: 1349</p>\n     * <p>virtual operator: 170, 171</p>\n     */\n    public static final String REGEX_MOBILE_EXACT  = \"^((13[0-9])|(14[579])|(15[0-35-9])|(16[2567])|(17[0-35-8])|(18[0-9])|(19[0-35-9]))\\\\d{8}$\";\n    /**\n     * Regex of telephone number.\n     */\n    public static final String REGEX_TEL           = \"^0\\\\d{2,3}[- ]?\\\\d{7,8}$\";\n    /**\n     * Regex of id card number which length is 15.\n     */\n    public static final String REGEX_ID_CARD15     = \"^[1-9]\\\\d{7}((0\\\\d)|(1[0-2]))(([0|1|2]\\\\d)|3[0-1])\\\\d{3}$\";\n    /**\n     * Regex of id card number which length is 18.\n     */\n    public static final String REGEX_ID_CARD18     = \"^[1-9]\\\\d{5}[1-9]\\\\d{3}((0\\\\d)|(1[0-2]))(([0|1|2]\\\\d)|3[0-1])\\\\d{3}([0-9Xx])$\";\n    /**\n     * Regex of email.\n     */\n    public static final String REGEX_EMAIL         = \"^\\\\w+([-+.]\\\\w+)*@\\\\w+([-.]\\\\w+)*\\\\.\\\\w+([-.]\\\\w+)*$\";\n    /**\n     * Regex of url.\n     */\n    public static final String REGEX_URL           = \"[a-zA-z]+://[^\\\\s]*\";\n    /**\n     * Regex of Chinese character.\n     */\n    public static final String REGEX_ZH            = \"^[\\\\u4e00-\\\\u9fa5]+$\";\n    /**\n     * Regex of username.\n     * <p>scope for \"a-z\", \"A-Z\", \"0-9\", \"_\", \"Chinese character\"</p>\n     * <p>can't end with \"_\"</p>\n     * <p>length is between 6 to 20</p>\n     */\n    public static final String REGEX_USERNAME      = \"^[\\\\w\\\\u4e00-\\\\u9fa5]{6,20}(?<!_)$\";\n    /**\n     * Regex of date which pattern is \"yyyy-MM-dd\".\n     */\n    public static final String REGEX_DATE          = \"^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$\";\n    /**\n     * Regex of ip address.\n     */\n    public static final String REGEX_IP            = \"((2[0-4]\\\\d|25[0-5]|[01]?\\\\d\\\\d?)\\\\.){3}(2[0-4]\\\\d|25[0-5]|[01]?\\\\d\\\\d?)\";\n\n    ///////////////////////////////////////////////////////////////////////////\n    // The following come from http://tool.oschina.net/regex\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Regex of double-byte characters.\n     */\n    public static final String REGEX_DOUBLE_BYTE_CHAR     = \"[^\\\\x00-\\\\xff]\";\n    /**\n     * Regex of blank line.\n     */\n    public static final String REGEX_BLANK_LINE           = \"\\\\n\\\\s*\\\\r\";\n    /**\n     * Regex of QQ number.\n     */\n    public static final String REGEX_QQ_NUM               = \"[1-9][0-9]{4,}\";\n    /**\n     * Regex of postal code in China.\n     */\n    public static final String REGEX_CHINA_POSTAL_CODE    = \"[1-9]\\\\d{5}(?!\\\\d)\";\n    /**\n     * Regex of integer.\n     */\n    public static final String REGEX_INTEGER              = \"^(-?[1-9]\\\\d*)|0$\";\n    /**\n     * Regex of positive integer.\n     */\n    public static final String REGEX_POSITIVE_INTEGER     = \"^[1-9]\\\\d*$\";\n    /**\n     * Regex of negative integer.\n     */\n    public static final String REGEX_NEGATIVE_INTEGER     = \"^-[1-9]\\\\d*$\";\n    /**\n     * Regex of non-negative integer.\n     */\n    public static final String REGEX_NOT_NEGATIVE_INTEGER = \"^[1-9]\\\\d*|0$\";\n    /**\n     * Regex of non-positive integer.\n     */\n    public static final String REGEX_NOT_POSITIVE_INTEGER = \"^-[1-9]\\\\d*|0$\";\n    /**\n     * Regex of positive float.\n     */\n    public static final String REGEX_FLOAT                = \"^-?([1-9]\\\\d*\\\\.\\\\d*|0\\\\.\\\\d*[1-9]\\\\d*|0?\\\\.0+|0)$\";\n    /**\n     * Regex of positive float.\n     */\n    public static final String REGEX_POSITIVE_FLOAT       = \"^[1-9]\\\\d*\\\\.\\\\d*|0\\\\.\\\\d*[1-9]\\\\d*$\";\n    /**\n     * Regex of negative float.\n     */\n    public static final String REGEX_NEGATIVE_FLOAT       = \"^-[1-9]\\\\d*\\\\.\\\\d*|-0\\\\.\\\\d*[1-9]\\\\d*$\";\n    /**\n     * Regex of positive float.\n     */\n    public static final String REGEX_NOT_NEGATIVE_FLOAT   = \"^[1-9]\\\\d*\\\\.\\\\d*|0\\\\.\\\\d*[1-9]\\\\d*|0?\\\\.0+|0$\";\n    /**\n     * Regex of negative float.\n     */\n    public static final String REGEX_NOT_POSITIVE_FLOAT   = \"^(-([1-9]\\\\d*\\\\.\\\\d*|0\\\\.\\\\d*[1-9]\\\\d*))|0?\\\\.0+|0$\";\n\n    ///////////////////////////////////////////////////////////////////////////\n    // If u want more please visit http://toutiao.com/i6231678548520731137\n    ///////////////////////////////////////////////////////////////////////////\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/constant/TimeConstants.java",
    "content": "package com.blankj.utilcode.constant;\n\nimport androidx.annotation.IntDef;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/03/13\n *     desc  : constants of time\n * </pre>\n */\npublic final class TimeConstants {\n\n    public static final int MSEC = 1;\n    public static final int SEC  = 1000;\n    public static final int MIN  = 60000;\n    public static final int HOUR = 3600000;\n    public static final int DAY  = 86400000;\n\n    @IntDef({MSEC, SEC, MIN, HOUR, DAY})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface Unit {\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ActivityUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.app.Activity;\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.content.ContextWrapper;\nimport android.content.Intent;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ResolveInfo;\nimport android.graphics.drawable.Drawable;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.view.View;\n\nimport java.lang.ref.WeakReference;\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport androidx.annotation.AnimRes;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.app.ActivityOptionsCompat;\nimport androidx.core.util.Pair;\nimport androidx.fragment.app.Fragment;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/09/23\n *     desc  : utils about activity\n * </pre>\n */\npublic final class ActivityUtils {\n\n    private ActivityUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Add callbacks of activity lifecycle.\n     *\n     * @param callbacks The callbacks.\n     */\n    public static void addActivityLifecycleCallbacks(@Nullable final Utils.ActivityLifecycleCallbacks callbacks) {\n        UtilsBridge.addActivityLifecycleCallbacks(callbacks);\n    }\n\n    /**\n     * Add callbacks of activity lifecycle.\n     *\n     * @param activity  The activity.\n     * @param callbacks The callbacks.\n     */\n    public static void addActivityLifecycleCallbacks(@Nullable final Activity activity,\n                                                     @Nullable final Utils.ActivityLifecycleCallbacks callbacks) {\n        UtilsBridge.addActivityLifecycleCallbacks(activity, callbacks);\n    }\n\n    /**\n     * Remove callbacks of activity lifecycle.\n     *\n     * @param callbacks The callbacks.\n     */\n    public static void removeActivityLifecycleCallbacks(@Nullable final Utils.ActivityLifecycleCallbacks callbacks) {\n        UtilsBridge.removeActivityLifecycleCallbacks(callbacks);\n    }\n\n    /**\n     * Remove callbacks of activity lifecycle.\n     *\n     * @param activity The activity.\n     */\n    public static void removeActivityLifecycleCallbacks(@Nullable final Activity activity) {\n        UtilsBridge.removeActivityLifecycleCallbacks(activity);\n    }\n\n    /**\n     * Remove callbacks of activity lifecycle.\n     *\n     * @param activity  The activity.\n     * @param callbacks The callbacks.\n     */\n    public static void removeActivityLifecycleCallbacks(@Nullable final Activity activity,\n                                                        @Nullable final Utils.ActivityLifecycleCallbacks callbacks) {\n        UtilsBridge.removeActivityLifecycleCallbacks(activity, callbacks);\n    }\n\n    /**\n     * Return the activity by context.\n     *\n     * @param context The context.\n     * @return the activity by context.\n     */\n    @Nullable\n    public static Activity getActivityByContext(@Nullable Context context) {\n        if (context == null) return null;\n        Activity activity = getActivityByContextInner(context);\n        if (!isActivityAlive(activity)) return null;\n        return activity;\n    }\n\n    @Nullable\n    private static Activity getActivityByContextInner(@Nullable Context context) {\n        if (context == null) return null;\n        List<Context> list = new ArrayList<>();\n        while (context instanceof ContextWrapper) {\n            if (context instanceof Activity) {\n                return (Activity) context;\n            }\n            Activity activity = getActivityFromDecorContext(context);\n            if (activity != null) return activity;\n            list.add(context);\n            context = ((ContextWrapper) context).getBaseContext();\n            if (context == null) {\n                return null;\n            }\n            if (list.contains(context)) {\n                // loop context\n                return null;\n            }\n        }\n        return null;\n    }\n\n    @Nullable\n    private static Activity getActivityFromDecorContext(@Nullable Context context) {\n        if (context == null) return null;\n        if (context.getClass().getName().equals(\"com.android.internal.policy.DecorContext\")) {\n            try {\n                Field mActivityContextField = context.getClass().getDeclaredField(\"mActivityContext\");\n                mActivityContextField.setAccessible(true);\n                //noinspection ConstantConditions,unchecked\n                return ((WeakReference<Activity>) mActivityContextField.get(context)).get();\n            } catch (Exception ignore) {\n            }\n        }\n        return null;\n    }\n\n    /**\n     * Return whether the activity exists.\n     *\n     * @param pkg The name of the package.\n     * @param cls The name of the class.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isActivityExists(@NonNull final String pkg,\n                                           @NonNull final String cls) {\n        Intent intent = new Intent();\n        intent.setClassName(pkg, cls);\n        PackageManager pm = Utils.getApp().getPackageManager();\n        return !(pm.resolveActivity(intent, 0) == null ||\n                intent.resolveActivity(pm) == null ||\n                pm.queryIntentActivities(intent, 0).size() == 0);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param clz The activity class.\n     */\n    public static void startActivity(@NonNull final Class<? extends Activity> clz) {\n        Context context = getTopActivityOrApp();\n        startActivity(context, null, context.getPackageName(), clz.getName(), null);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param clz     The activity class.\n     * @param options Additional options for how the Activity should be started.\n     */\n    public static void startActivity(@NonNull final Class<? extends Activity> clz,\n                                     @Nullable final Bundle options) {\n        Context context = getTopActivityOrApp();\n        startActivity(context, null, context.getPackageName(), clz.getName(), options);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param clz       The activity class.\n     * @param enterAnim A resource ID of the animation resource to use for the\n     *                  incoming activity.\n     * @param exitAnim  A resource ID of the animation resource to use for the\n     *                  outgoing activity.\n     */\n    public static void startActivity(@NonNull final Class<? extends Activity> clz,\n                                     @AnimRes final int enterAnim,\n                                     @AnimRes final int exitAnim) {\n        Context context = getTopActivityOrApp();\n        startActivity(context, null, context.getPackageName(), clz.getName(),\n                getOptionsBundle(context, enterAnim, exitAnim));\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN && context instanceof Activity) {\n            ((Activity) context).overridePendingTransition(enterAnim, exitAnim);\n        }\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param activity The activity.\n     * @param clz      The activity class.\n     */\n    public static void startActivity(@NonNull final Activity activity,\n                                     @NonNull final Class<? extends Activity> clz) {\n        startActivity(activity, null, activity.getPackageName(), clz.getName(), null);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param activity The activity.\n     * @param clz      The activity class.\n     * @param options  Additional options for how the Activity should be started.\n     */\n    public static void startActivity(@NonNull final Activity activity,\n                                     @NonNull final Class<? extends Activity> clz,\n                                     @Nullable final Bundle options) {\n        startActivity(activity, null, activity.getPackageName(), clz.getName(), options);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param activity       The activity.\n     * @param clz            The activity class.\n     * @param sharedElements The names of the shared elements to transfer to the called\n     *                       Activity and their associated Views.\n     */\n    public static void startActivity(@NonNull final Activity activity,\n                                     @NonNull final Class<? extends Activity> clz,\n                                     final View... sharedElements) {\n        startActivity(activity, null, activity.getPackageName(), clz.getName(),\n                getOptionsBundle(activity, sharedElements));\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param activity  The activity.\n     * @param clz       The activity class.\n     * @param enterAnim A resource ID of the animation resource to use for the\n     *                  incoming activity.\n     * @param exitAnim  A resource ID of the animation resource to use for the\n     *                  outgoing activity.\n     */\n    public static void startActivity(@NonNull final Activity activity,\n                                     @NonNull final Class<? extends Activity> clz,\n                                     @AnimRes final int enterAnim,\n                                     @AnimRes final int exitAnim) {\n        startActivity(activity, null, activity.getPackageName(), clz.getName(),\n                getOptionsBundle(activity, enterAnim, exitAnim));\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n            activity.overridePendingTransition(enterAnim, exitAnim);\n        }\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras The Bundle of extras to add to this intent.\n     * @param clz    The activity class.\n     */\n    public static void startActivity(@NonNull final Bundle extras,\n                                     @NonNull final Class<? extends Activity> clz) {\n        Context context = getTopActivityOrApp();\n        startActivity(context, extras, context.getPackageName(), clz.getName(), null);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras  The Bundle of extras to add to this intent.\n     * @param clz     The activity class.\n     * @param options Additional options for how the Activity should be started.\n     */\n    public static void startActivity(@NonNull final Bundle extras,\n                                     @NonNull final Class<? extends Activity> clz,\n                                     @Nullable final Bundle options) {\n        Context context = getTopActivityOrApp();\n        startActivity(context, extras, context.getPackageName(), clz.getName(), options);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras    The Bundle of extras to add to this intent.\n     * @param clz       The activity class.\n     * @param enterAnim A resource ID of the animation resource to use for the\n     *                  incoming activity.\n     * @param exitAnim  A resource ID of the animation resource to use for the\n     *                  outgoing activity.\n     */\n    public static void startActivity(@NonNull final Bundle extras,\n                                     @NonNull final Class<? extends Activity> clz,\n                                     @AnimRes final int enterAnim,\n                                     @AnimRes final int exitAnim) {\n        Context context = getTopActivityOrApp();\n        startActivity(context, extras, context.getPackageName(), clz.getName(),\n                getOptionsBundle(context, enterAnim, exitAnim));\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN && context instanceof Activity) {\n            ((Activity) context).overridePendingTransition(enterAnim, exitAnim);\n        }\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras   The Bundle of extras to add to this intent.\n     * @param activity The activity.\n     * @param clz      The activity class.\n     */\n    public static void startActivity(@NonNull final Bundle extras,\n                                     @NonNull final Activity activity,\n                                     @NonNull final Class<? extends Activity> clz) {\n        startActivity(activity, extras, activity.getPackageName(), clz.getName(), null);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras   The Bundle of extras to add to this intent.\n     * @param activity The activity.\n     * @param clz      The activity class.\n     * @param options  Additional options for how the Activity should be started.\n     */\n    public static void startActivity(@NonNull final Bundle extras,\n                                     @NonNull final Activity activity,\n                                     @NonNull final Class<? extends Activity> clz,\n                                     @Nullable final Bundle options) {\n        startActivity(activity, extras, activity.getPackageName(), clz.getName(), options);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras         The Bundle of extras to add to this intent.\n     * @param activity       The activity.\n     * @param clz            The activity class.\n     * @param sharedElements The names of the shared elements to transfer to the called\n     *                       Activity and their associated Views.\n     */\n    public static void startActivity(@NonNull final Bundle extras,\n                                     @NonNull final Activity activity,\n                                     @NonNull final Class<? extends Activity> clz,\n                                     final View... sharedElements) {\n        startActivity(activity, extras, activity.getPackageName(), clz.getName(),\n                getOptionsBundle(activity, sharedElements));\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras    The Bundle of extras to add to this intent.\n     * @param activity  The activity.\n     * @param clz       The activity class.\n     * @param enterAnim A resource ID of the animation resource to use for the\n     *                  incoming activity.\n     * @param exitAnim  A resource ID of the animation resource to use for the\n     *                  outgoing activity.\n     */\n    public static void startActivity(@NonNull final Bundle extras,\n                                     @NonNull final Activity activity,\n                                     @NonNull final Class<? extends Activity> clz,\n                                     @AnimRes final int enterAnim,\n                                     @AnimRes final int exitAnim) {\n        startActivity(activity, extras, activity.getPackageName(), clz.getName(),\n                getOptionsBundle(activity, enterAnim, exitAnim));\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n            activity.overridePendingTransition(enterAnim, exitAnim);\n        }\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param pkg The name of the package.\n     * @param cls The name of the class.\n     */\n    public static void startActivity(@NonNull final String pkg,\n                                     @NonNull final String cls) {\n        startActivity(getTopActivityOrApp(), null, pkg, cls, null);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param pkg     The name of the package.\n     * @param cls     The name of the class.\n     * @param options Additional options for how the Activity should be started.\n     */\n    public static void startActivity(@NonNull final String pkg,\n                                     @NonNull final String cls,\n                                     @Nullable final Bundle options) {\n        startActivity(getTopActivityOrApp(), null, pkg, cls, options);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param pkg       The name of the package.\n     * @param cls       The name of the class.\n     * @param enterAnim A resource ID of the animation resource to use for the\n     *                  incoming activity.\n     * @param exitAnim  A resource ID of the animation resource to use for the\n     *                  outgoing activity.\n     */\n    public static void startActivity(@NonNull final String pkg,\n                                     @NonNull final String cls,\n                                     @AnimRes final int enterAnim,\n                                     @AnimRes final int exitAnim) {\n        Context context = getTopActivityOrApp();\n        startActivity(context, null, pkg, cls, getOptionsBundle(context, enterAnim, exitAnim));\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN && context instanceof Activity) {\n            ((Activity) context).overridePendingTransition(enterAnim, exitAnim);\n        }\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param activity The activity.\n     * @param pkg      The name of the package.\n     * @param cls      The name of the class.\n     */\n    public static void startActivity(@NonNull final Activity activity,\n                                     @NonNull final String pkg,\n                                     @NonNull final String cls) {\n        startActivity(activity, null, pkg, cls, null);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param activity The activity.\n     * @param pkg      The name of the package.\n     * @param cls      The name of the class.\n     * @param options  Additional options for how the Activity should be started.\n     */\n    public static void startActivity(@NonNull final Activity activity,\n                                     @NonNull final String pkg,\n                                     @NonNull final String cls,\n                                     @Nullable final Bundle options) {\n        startActivity(activity, null, pkg, cls, options);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param activity       The activity.\n     * @param pkg            The name of the package.\n     * @param cls            The name of the class.\n     * @param sharedElements The names of the shared elements to transfer to the called\n     *                       Activity and their associated Views.\n     */\n    public static void startActivity(@NonNull final Activity activity,\n                                     @NonNull final String pkg,\n                                     @NonNull final String cls,\n                                     final View... sharedElements) {\n        startActivity(activity, null, pkg, cls, getOptionsBundle(activity, sharedElements));\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param activity  The activity.\n     * @param pkg       The name of the package.\n     * @param cls       The name of the class.\n     * @param enterAnim A resource ID of the animation resource to use for the\n     *                  incoming activity.\n     * @param exitAnim  A resource ID of the animation resource to use for the\n     *                  outgoing activity.\n     */\n    public static void startActivity(@NonNull final Activity activity,\n                                     @NonNull final String pkg,\n                                     @NonNull final String cls,\n                                     @AnimRes final int enterAnim,\n                                     @AnimRes final int exitAnim) {\n        startActivity(activity, null, pkg, cls, getOptionsBundle(activity, enterAnim, exitAnim));\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n            activity.overridePendingTransition(enterAnim, exitAnim);\n        }\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras The Bundle of extras to add to this intent.\n     * @param pkg    The name of the package.\n     * @param cls    The name of the class.\n     */\n    public static void startActivity(@NonNull final Bundle extras,\n                                     @NonNull final String pkg,\n                                     @NonNull final String cls) {\n        startActivity(getTopActivityOrApp(), extras, pkg, cls, null);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras  The Bundle of extras to add to this intent.\n     * @param pkg     The name of the package.\n     * @param cls     The name of the class.\n     * @param options Additional options for how the Activity should be started.\n     */\n    public static void startActivity(@NonNull final Bundle extras,\n                                     @NonNull final String pkg,\n                                     @NonNull final String cls,\n                                     @Nullable final Bundle options) {\n        startActivity(getTopActivityOrApp(), extras, pkg, cls, options);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras    The Bundle of extras to add to this intent.\n     * @param pkg       The name of the package.\n     * @param cls       The name of the class.\n     * @param enterAnim A resource ID of the animation resource to use for the\n     *                  incoming activity.\n     * @param exitAnim  A resource ID of the animation resource to use for the\n     *                  outgoing activity.\n     */\n    public static void startActivity(@NonNull final Bundle extras,\n                                     @NonNull final String pkg,\n                                     @NonNull final String cls,\n                                     @AnimRes final int enterAnim,\n                                     @AnimRes final int exitAnim) {\n        Context context = getTopActivityOrApp();\n        startActivity(context, extras, pkg, cls, getOptionsBundle(context, enterAnim, exitAnim));\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN && context instanceof Activity) {\n            ((Activity) context).overridePendingTransition(enterAnim, exitAnim);\n        }\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param activity The activity.\n     * @param extras   The Bundle of extras to add to this intent.\n     * @param pkg      The name of the package.\n     * @param cls      The name of the class.\n     */\n    public static void startActivity(@NonNull final Bundle extras,\n                                     @NonNull final Activity activity,\n                                     @NonNull final String pkg,\n                                     @NonNull final String cls) {\n        startActivity(activity, extras, pkg, cls, null);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras   The Bundle of extras to add to this intent.\n     * @param activity The activity.\n     * @param pkg      The name of the package.\n     * @param cls      The name of the class.\n     * @param options  Additional options for how the Activity should be started.\n     */\n    public static void startActivity(@NonNull final Bundle extras,\n                                     @NonNull final Activity activity,\n                                     @NonNull final String pkg,\n                                     @NonNull final String cls,\n                                     @Nullable final Bundle options) {\n        startActivity(activity, extras, pkg, cls, options);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras         The Bundle of extras to add to this intent.\n     * @param activity       The activity.\n     * @param pkg            The name of the package.\n     * @param cls            The name of the class.\n     * @param sharedElements The names of the shared elements to transfer to the called\n     *                       Activity and their associated Views.\n     */\n    public static void startActivity(@NonNull final Bundle extras,\n                                     @NonNull final Activity activity,\n                                     @NonNull final String pkg,\n                                     @NonNull final String cls,\n                                     final View... sharedElements) {\n        startActivity(activity, extras, pkg, cls, getOptionsBundle(activity, sharedElements));\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras    The Bundle of extras to add to this intent.\n     * @param pkg       The name of the package.\n     * @param cls       The name of the class.\n     * @param enterAnim A resource ID of the animation resource to use for the\n     *                  incoming activity.\n     * @param exitAnim  A resource ID of the animation resource to use for the\n     *                  outgoing activity.\n     */\n    public static void startActivity(@NonNull final Bundle extras,\n                                     @NonNull final Activity activity,\n                                     @NonNull final String pkg,\n                                     @NonNull final String cls,\n                                     @AnimRes final int enterAnim,\n                                     @AnimRes final int exitAnim) {\n        startActivity(activity, extras, pkg, cls, getOptionsBundle(activity, enterAnim, exitAnim));\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n            activity.overridePendingTransition(enterAnim, exitAnim);\n        }\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param intent The description of the activity to start.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean startActivity(@NonNull final Intent intent) {\n        return startActivity(intent, getTopActivityOrApp(), null);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param intent  The description of the activity to start.\n     * @param options Additional options for how the Activity should be started.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean startActivity(@NonNull final Intent intent,\n                                        @Nullable final Bundle options) {\n        return startActivity(intent, getTopActivityOrApp(), options);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param intent    The description of the activity to start.\n     * @param enterAnim A resource ID of the animation resource to use for the\n     *                  incoming activity.\n     * @param exitAnim  A resource ID of the animation resource to use for the\n     *                  outgoing activity.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean startActivity(@NonNull final Intent intent,\n                                        @AnimRes final int enterAnim,\n                                        @AnimRes final int exitAnim) {\n        Context context = getTopActivityOrApp();\n        boolean isSuccess = startActivity(intent, context, getOptionsBundle(context, enterAnim, exitAnim));\n        if (isSuccess) {\n            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN && context instanceof Activity) {\n                ((Activity) context).overridePendingTransition(enterAnim, exitAnim);\n            }\n        }\n        return isSuccess;\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param activity The activity.\n     * @param intent   The description of the activity to start.\n     */\n    public static void startActivity(@NonNull final Activity activity,\n                                     @NonNull final Intent intent) {\n        startActivity(intent, activity, null);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param activity The activity.\n     * @param intent   The description of the activity to start.\n     * @param options  Additional options for how the Activity should be started.\n     */\n    public static void startActivity(@NonNull final Activity activity,\n                                     @NonNull final Intent intent,\n                                     @Nullable final Bundle options) {\n        startActivity(intent, activity, options);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param activity       The activity.\n     * @param intent         The description of the activity to start.\n     * @param sharedElements The names of the shared elements to transfer to the called\n     *                       Activity and their associated Views.\n     */\n    public static void startActivity(@NonNull final Activity activity,\n                                     @NonNull final Intent intent,\n                                     final View... sharedElements) {\n        startActivity(intent, activity, getOptionsBundle(activity, sharedElements));\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param activity  The activity.\n     * @param intent    The description of the activity to start.\n     * @param enterAnim A resource ID of the animation resource to use for the\n     *                  incoming activity.\n     * @param exitAnim  A resource ID of the animation resource to use for the\n     *                  outgoing activity.\n     */\n    public static void startActivity(@NonNull final Activity activity,\n                                     @NonNull final Intent intent,\n                                     @AnimRes final int enterAnim,\n                                     @AnimRes final int exitAnim) {\n        startActivity(intent, activity, getOptionsBundle(activity, enterAnim, exitAnim));\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n            activity.overridePendingTransition(enterAnim, exitAnim);\n        }\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param activity    The activity.\n     * @param clz         The activity class.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     */\n    public static void startActivityForResult(@NonNull final Activity activity,\n                                              @NonNull final Class<? extends Activity> clz,\n                                              final int requestCode) {\n        startActivityForResult(activity, null, activity.getPackageName(), clz.getName(),\n                requestCode, null);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param activity    The activity.\n     * @param clz         The activity class.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     * @param options     Additional options for how the Activity should be started.\n     */\n    public static void startActivityForResult(@NonNull final Activity activity,\n                                              @NonNull final Class<? extends Activity> clz,\n                                              final int requestCode,\n                                              @Nullable final Bundle options) {\n        startActivityForResult(activity, null, activity.getPackageName(), clz.getName(),\n                requestCode, options);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param activity       The activity.\n     * @param clz            The activity class.\n     * @param requestCode    if &gt;= 0, this code will be returned in\n     *                       onActivityResult() when the activity exits.\n     * @param sharedElements The names of the shared elements to transfer to the called\n     *                       Activity and their associated Views.\n     */\n    public static void startActivityForResult(@NonNull final Activity activity,\n                                              @NonNull final Class<? extends Activity> clz,\n                                              final int requestCode,\n                                              final View... sharedElements) {\n        startActivityForResult(activity, null, activity.getPackageName(), clz.getName(),\n                requestCode, getOptionsBundle(activity, sharedElements));\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param activity    The activity.\n     * @param clz         The activity class.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     * @param enterAnim   A resource ID of the animation resource to use for the\n     *                    incoming activity.\n     * @param exitAnim    A resource ID of the animation resource to use for the\n     *                    outgoing activity.\n     */\n    public static void startActivityForResult(@NonNull final Activity activity,\n                                              @NonNull final Class<? extends Activity> clz,\n                                              final int requestCode,\n                                              @AnimRes final int enterAnim,\n                                              @AnimRes final int exitAnim) {\n        startActivityForResult(activity, null, activity.getPackageName(), clz.getName(),\n                requestCode, getOptionsBundle(activity, enterAnim, exitAnim));\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n            activity.overridePendingTransition(enterAnim, exitAnim);\n        }\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras      The Bundle of extras to add to this intent.\n     * @param activity    The activity.\n     * @param clz         The activity class.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     */\n    public static void startActivityForResult(@NonNull final Bundle extras,\n                                              @NonNull final Activity activity,\n                                              @NonNull final Class<? extends Activity> clz,\n                                              final int requestCode) {\n        startActivityForResult(activity, extras, activity.getPackageName(), clz.getName(),\n                requestCode, null);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras      The Bundle of extras to add to this intent.\n     * @param activity    The activity.\n     * @param clz         The activity class.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     * @param options     Additional options for how the Activity should be started.\n     */\n    public static void startActivityForResult(@NonNull final Bundle extras,\n                                              @NonNull final Activity activity,\n                                              @NonNull final Class<? extends Activity> clz,\n                                              final int requestCode,\n                                              @Nullable final Bundle options) {\n        startActivityForResult(activity, extras, activity.getPackageName(), clz.getName(),\n                requestCode, options);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras         The Bundle of extras to add to this intent.\n     * @param activity       The activity.\n     * @param clz            The activity class.\n     * @param requestCode    if &gt;= 0, this code will be returned in\n     *                       onActivityResult() when the activity exits.\n     * @param sharedElements The names of the shared elements to transfer to the called\n     *                       Activity and their associated Views.\n     */\n    public static void startActivityForResult(@NonNull final Bundle extras,\n                                              @NonNull final Activity activity,\n                                              @NonNull final Class<? extends Activity> clz,\n                                              final int requestCode,\n                                              final View... sharedElements) {\n        startActivityForResult(activity, extras, activity.getPackageName(), clz.getName(),\n                requestCode, getOptionsBundle(activity, sharedElements));\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras      The Bundle of extras to add to this intent.\n     * @param activity    The activity.\n     * @param clz         The activity class.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     * @param enterAnim   A resource ID of the animation resource to use for the\n     *                    incoming activity.\n     * @param exitAnim    A resource ID of the animation resource to use for the\n     *                    outgoing activity.\n     */\n    public static void startActivityForResult(@NonNull final Bundle extras,\n                                              @NonNull final Activity activity,\n                                              @NonNull final Class<? extends Activity> clz,\n                                              final int requestCode,\n                                              @AnimRes final int enterAnim,\n                                              @AnimRes final int exitAnim) {\n        startActivityForResult(activity, extras, activity.getPackageName(), clz.getName(),\n                requestCode, getOptionsBundle(activity, enterAnim, exitAnim));\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n            activity.overridePendingTransition(enterAnim, exitAnim);\n        }\n    }\n\n    /**\n     * Start the activity for result.\n     *\n     * @param activity    The activity.\n     * @param extras      The Bundle of extras to add to this intent.\n     * @param pkg         The name of the package.\n     * @param cls         The name of the class.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     */\n    public static void startActivityForResult(@NonNull final Bundle extras,\n                                              @NonNull final Activity activity,\n                                              @NonNull final String pkg,\n                                              @NonNull final String cls,\n                                              final int requestCode) {\n        startActivityForResult(activity, extras, pkg, cls, requestCode, null);\n    }\n\n    /**\n     * Start the activity for result.\n     *\n     * @param extras      The Bundle of extras to add to this intent.\n     * @param activity    The activity.\n     * @param pkg         The name of the package.\n     * @param cls         The name of the class.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     * @param options     Additional options for how the Activity should be started.\n     */\n    public static void startActivityForResult(@NonNull final Bundle extras,\n                                              @NonNull final Activity activity,\n                                              @NonNull final String pkg,\n                                              @NonNull final String cls,\n                                              final int requestCode,\n                                              @Nullable final Bundle options) {\n        startActivityForResult(activity, extras, pkg, cls, requestCode, options);\n    }\n\n    /**\n     * Start the activity for result.\n     *\n     * @param extras         The Bundle of extras to add to this intent.\n     * @param activity       The activity.\n     * @param pkg            The name of the package.\n     * @param cls            The name of the class.\n     * @param requestCode    if &gt;= 0, this code will be returned in\n     *                       onActivityResult() when the activity exits.\n     * @param sharedElements The names of the shared elements to transfer to the called\n     *                       Activity and their associated Views.\n     */\n    public static void startActivityForResult(@NonNull final Bundle extras,\n                                              @NonNull final Activity activity,\n                                              @NonNull final String pkg,\n                                              @NonNull final String cls,\n                                              final int requestCode,\n                                              final View... sharedElements) {\n        startActivityForResult(activity, extras, pkg, cls,\n                requestCode, getOptionsBundle(activity, sharedElements));\n    }\n\n    /**\n     * Start the activity for result.\n     *\n     * @param extras      The Bundle of extras to add to this intent.\n     * @param pkg         The name of the package.\n     * @param cls         The name of the class.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     * @param enterAnim   A resource ID of the animation resource to use for the\n     *                    incoming activity.\n     * @param exitAnim    A resource ID of the animation resource to use for the\n     *                    outgoing activity.\n     */\n    public static void startActivityForResult(@NonNull final Bundle extras,\n                                              @NonNull final Activity activity,\n                                              @NonNull final String pkg,\n                                              @NonNull final String cls,\n                                              final int requestCode,\n                                              @AnimRes final int enterAnim,\n                                              @AnimRes final int exitAnim) {\n        startActivityForResult(activity, extras, pkg, cls,\n                requestCode, getOptionsBundle(activity, enterAnim, exitAnim));\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n            activity.overridePendingTransition(enterAnim, exitAnim);\n        }\n    }\n\n    /**\n     * Start the activity for result.\n     *\n     * @param activity    The activity.\n     * @param intent      The description of the activity to start.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     */\n    public static void startActivityForResult(@NonNull final Activity activity,\n                                              @NonNull final Intent intent,\n                                              final int requestCode) {\n        startActivityForResult(intent, activity, requestCode, null);\n    }\n\n    /**\n     * Start the activity for result.\n     *\n     * @param activity    The activity.\n     * @param intent      The description of the activity to start.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     * @param options     Additional options for how the Activity should be started.\n     */\n    public static void startActivityForResult(@NonNull final Activity activity,\n                                              @NonNull final Intent intent,\n                                              final int requestCode,\n                                              @Nullable final Bundle options) {\n        startActivityForResult(intent, activity, requestCode, options);\n    }\n\n    /**\n     * Start the activity for result.\n     *\n     * @param activity       The activity.\n     * @param intent         The description of the activity to start.\n     * @param requestCode    if &gt;= 0, this code will be returned in\n     *                       onActivityResult() when the activity exits.\n     * @param sharedElements The names of the shared elements to transfer to the called\n     *                       Activity and their associated Views.\n     */\n    public static void startActivityForResult(@NonNull final Activity activity,\n                                              @NonNull final Intent intent,\n                                              final int requestCode,\n                                              final View... sharedElements) {\n        startActivityForResult(intent, activity,\n                requestCode, getOptionsBundle(activity, sharedElements));\n    }\n\n    /**\n     * Start the activity for result.\n     *\n     * @param activity    The activity.\n     * @param intent      The description of the activity to start.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     * @param enterAnim   A resource ID of the animation resource to use for the\n     *                    incoming activity.\n     * @param exitAnim    A resource ID of the animation resource to use for the\n     *                    outgoing activity.\n     */\n    public static void startActivityForResult(@NonNull final Activity activity,\n                                              @NonNull final Intent intent,\n                                              final int requestCode,\n                                              @AnimRes final int enterAnim,\n                                              @AnimRes final int exitAnim) {\n        startActivityForResult(intent, activity,\n                requestCode, getOptionsBundle(activity, enterAnim, exitAnim));\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n            activity.overridePendingTransition(enterAnim, exitAnim);\n        }\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param fragment    The fragment.\n     * @param clz         The activity class.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     */\n    public static void startActivityForResult(@NonNull final Fragment fragment,\n                                              @NonNull final Class<? extends Activity> clz,\n                                              final int requestCode) {\n        startActivityForResult(fragment, null, Utils.getApp().getPackageName(), clz.getName(),\n                requestCode, null);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param fragment    The fragment.\n     * @param clz         The activity class.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     * @param options     Additional options for how the Activity should be started.\n     */\n    public static void startActivityForResult(@NonNull final Fragment fragment,\n                                              @NonNull final Class<? extends Activity> clz,\n                                              final int requestCode,\n                                              @Nullable final Bundle options) {\n        startActivityForResult(fragment, null, Utils.getApp().getPackageName(), clz.getName(),\n                requestCode, options);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param fragment       The fragment.\n     * @param clz            The activity class.\n     * @param requestCode    if &gt;= 0, this code will be returned in\n     *                       onActivityResult() when the activity exits.\n     * @param sharedElements The names of the shared elements to transfer to the called\n     *                       Activity and their associated Views.\n     */\n    public static void startActivityForResult(@NonNull final Fragment fragment,\n                                              @NonNull final Class<? extends Activity> clz,\n                                              final int requestCode,\n                                              final View... sharedElements) {\n        startActivityForResult(fragment, null, Utils.getApp().getPackageName(), clz.getName(),\n                requestCode, getOptionsBundle(fragment, sharedElements));\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param fragment    The fragment.\n     * @param clz         The activity class.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     * @param enterAnim   A resource ID of the animation resource to use for the\n     *                    incoming activity.\n     * @param exitAnim    A resource ID of the animation resource to use for the\n     *                    outgoing activity.\n     */\n    public static void startActivityForResult(@NonNull final Fragment fragment,\n                                              @NonNull final Class<? extends Activity> clz,\n                                              final int requestCode,\n                                              @AnimRes final int enterAnim,\n                                              @AnimRes final int exitAnim) {\n        startActivityForResult(fragment, null, Utils.getApp().getPackageName(), clz.getName(),\n                requestCode, getOptionsBundle(fragment, enterAnim, exitAnim));\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras      The Bundle of extras to add to this intent.\n     * @param fragment    The fragment.\n     * @param clz         The activity class.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     */\n    public static void startActivityForResult(@NonNull final Bundle extras,\n                                              @NonNull final Fragment fragment,\n                                              @NonNull final Class<? extends Activity> clz,\n                                              final int requestCode) {\n        startActivityForResult(fragment, extras, Utils.getApp().getPackageName(), clz.getName(),\n                requestCode, null);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras      The Bundle of extras to add to this intent.\n     * @param fragment    The fragment.\n     * @param clz         The activity class.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     * @param options     Additional options for how the Activity should be started.\n     */\n    public static void startActivityForResult(@NonNull final Bundle extras,\n                                              @NonNull final Fragment fragment,\n                                              @NonNull final Class<? extends Activity> clz,\n                                              final int requestCode,\n                                              @Nullable final Bundle options) {\n        startActivityForResult(fragment, extras, Utils.getApp().getPackageName(), clz.getName(),\n                requestCode, options);\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras         The Bundle of extras to add to this intent.\n     * @param fragment       The fragment.\n     * @param clz            The activity class.\n     * @param requestCode    if &gt;= 0, this code will be returned in\n     *                       onActivityResult() when the activity exits.\n     * @param sharedElements The names of the shared elements to transfer to the called\n     *                       Activity and their associated Views.\n     */\n    public static void startActivityForResult(@NonNull final Bundle extras,\n                                              @NonNull final Fragment fragment,\n                                              @NonNull final Class<? extends Activity> clz,\n                                              final int requestCode,\n                                              final View... sharedElements) {\n        startActivityForResult(fragment, extras, Utils.getApp().getPackageName(), clz.getName(),\n                requestCode, getOptionsBundle(fragment, sharedElements));\n    }\n\n    /**\n     * Start the activity.\n     *\n     * @param extras      The Bundle of extras to add to this intent.\n     * @param fragment    The fragment.\n     * @param clz         The activity class.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     * @param enterAnim   A resource ID of the animation resource to use for the\n     *                    incoming activity.\n     * @param exitAnim    A resource ID of the animation resource to use for the\n     *                    outgoing activity.\n     */\n    public static void startActivityForResult(@NonNull final Bundle extras,\n                                              @NonNull final Fragment fragment,\n                                              @NonNull final Class<? extends Activity> clz,\n                                              final int requestCode,\n                                              @AnimRes final int enterAnim,\n                                              @AnimRes final int exitAnim) {\n        startActivityForResult(fragment, extras, Utils.getApp().getPackageName(), clz.getName(),\n                requestCode, getOptionsBundle(fragment, enterAnim, exitAnim));\n    }\n\n    /**\n     * Start the activity for result.\n     *\n     * @param fragment    The fragment.\n     * @param extras      The Bundle of extras to add to this intent.\n     * @param pkg         The name of the package.\n     * @param cls         The name of the class.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     */\n    public static void startActivityForResult(@NonNull final Bundle extras,\n                                              @NonNull final Fragment fragment,\n                                              @NonNull final String pkg,\n                                              @NonNull final String cls,\n                                              final int requestCode) {\n        startActivityForResult(fragment, extras, pkg, cls, requestCode, null);\n    }\n\n    /**\n     * Start the activity for result.\n     *\n     * @param extras      The Bundle of extras to add to this intent.\n     * @param fragment    The fragment.\n     * @param pkg         The name of the package.\n     * @param cls         The name of the class.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     * @param options     Additional options for how the Activity should be started.\n     */\n    public static void startActivityForResult(@NonNull final Bundle extras,\n                                              @NonNull final Fragment fragment,\n                                              @NonNull final String pkg,\n                                              @NonNull final String cls,\n                                              final int requestCode,\n                                              @Nullable final Bundle options) {\n        startActivityForResult(fragment, extras, pkg, cls, requestCode, options);\n    }\n\n    /**\n     * Start the activity for result.\n     *\n     * @param extras         The Bundle of extras to add to this intent.\n     * @param fragment       The fragment.\n     * @param pkg            The name of the package.\n     * @param cls            The name of the class.\n     * @param requestCode    if &gt;= 0, this code will be returned in\n     *                       onActivityResult() when the activity exits.\n     * @param sharedElements The names of the shared elements to transfer to the called\n     *                       Activity and their associated Views.\n     */\n    public static void startActivityForResult(@NonNull final Bundle extras,\n                                              @NonNull final Fragment fragment,\n                                              @NonNull final String pkg,\n                                              @NonNull final String cls,\n                                              final int requestCode,\n                                              final View... sharedElements) {\n        startActivityForResult(fragment, extras, pkg, cls,\n                requestCode, getOptionsBundle(fragment, sharedElements));\n    }\n\n    /**\n     * Start the activity for result.\n     *\n     * @param extras      The Bundle of extras to add to this intent.\n     * @param pkg         The name of the package.\n     * @param cls         The name of the class.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     * @param enterAnim   A resource ID of the animation resource to use for the\n     *                    incoming activity.\n     * @param exitAnim    A resource ID of the animation resource to use for the\n     *                    outgoing activity.\n     */\n    public static void startActivityForResult(@NonNull final Bundle extras,\n                                              @NonNull final Fragment fragment,\n                                              @NonNull final String pkg,\n                                              @NonNull final String cls,\n                                              final int requestCode,\n                                              @AnimRes final int enterAnim,\n                                              @AnimRes final int exitAnim) {\n        startActivityForResult(fragment, extras, pkg, cls,\n                requestCode, getOptionsBundle(fragment, enterAnim, exitAnim));\n    }\n\n    /**\n     * Start the activity for result.\n     *\n     * @param fragment    The fragment.\n     * @param intent      The description of the activity to start.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     */\n    public static void startActivityForResult(@NonNull final Fragment fragment,\n                                              @NonNull final Intent intent,\n                                              final int requestCode) {\n        startActivityForResult(intent, fragment, requestCode, null);\n    }\n\n    /**\n     * Start the activity for result.\n     *\n     * @param fragment    The fragment.\n     * @param intent      The description of the activity to start.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     * @param options     Additional options for how the Activity should be started.\n     */\n    public static void startActivityForResult(@NonNull final Fragment fragment,\n                                              @NonNull final Intent intent,\n                                              final int requestCode,\n                                              @Nullable final Bundle options) {\n        startActivityForResult(intent, fragment, requestCode, options);\n    }\n\n    /**\n     * Start the activity for result.\n     *\n     * @param fragment       The fragment.\n     * @param intent         The description of the activity to start.\n     * @param requestCode    if &gt;= 0, this code will be returned in\n     *                       onActivityResult() when the activity exits.\n     * @param sharedElements The names of the shared elements to transfer to the called\n     *                       Activity and their associated Views.\n     */\n    public static void startActivityForResult(@NonNull final Fragment fragment,\n                                              @NonNull final Intent intent,\n                                              final int requestCode,\n                                              final View... sharedElements) {\n        startActivityForResult(intent, fragment,\n                requestCode, getOptionsBundle(fragment, sharedElements));\n    }\n\n    /**\n     * Start the activity for result.\n     *\n     * @param fragment    The fragment.\n     * @param intent      The description of the activity to start.\n     * @param requestCode if &gt;= 0, this code will be returned in\n     *                    onActivityResult() when the activity exits.\n     * @param enterAnim   A resource ID of the animation resource to use for the\n     *                    incoming activity.\n     * @param exitAnim    A resource ID of the animation resource to use for the\n     *                    outgoing activity.\n     */\n    public static void startActivityForResult(@NonNull final Fragment fragment,\n                                              @NonNull final Intent intent,\n                                              final int requestCode,\n                                              @AnimRes final int enterAnim,\n                                              @AnimRes final int exitAnim) {\n        startActivityForResult(intent, fragment,\n                requestCode, getOptionsBundle(fragment, enterAnim, exitAnim));\n    }\n\n    /**\n     * Start activities.\n     *\n     * @param intents The descriptions of the activities to start.\n     */\n    public static void startActivities(@NonNull final Intent[] intents) {\n        startActivities(intents, getTopActivityOrApp(), null);\n    }\n\n    /**\n     * Start activities.\n     *\n     * @param intents The descriptions of the activities to start.\n     * @param options Additional options for how the Activity should be started.\n     */\n    public static void startActivities(@NonNull final Intent[] intents,\n                                       @Nullable final Bundle options) {\n        startActivities(intents, getTopActivityOrApp(), options);\n    }\n\n    /**\n     * Start activities.\n     *\n     * @param intents   The descriptions of the activities to start.\n     * @param enterAnim A resource ID of the animation resource to use for the\n     *                  incoming activity.\n     * @param exitAnim  A resource ID of the animation resource to use for the\n     *                  outgoing activity.\n     */\n    public static void startActivities(@NonNull final Intent[] intents,\n                                       @AnimRes final int enterAnim,\n                                       @AnimRes final int exitAnim) {\n        Context context = getTopActivityOrApp();\n        startActivities(intents, context, getOptionsBundle(context, enterAnim, exitAnim));\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN && context instanceof Activity) {\n            ((Activity) context).overridePendingTransition(enterAnim, exitAnim);\n        }\n    }\n\n    /**\n     * Start activities.\n     *\n     * @param activity The activity.\n     * @param intents  The descriptions of the activities to start.\n     */\n    public static void startActivities(@NonNull final Activity activity,\n                                       @NonNull final Intent[] intents) {\n        startActivities(intents, activity, null);\n    }\n\n    /**\n     * Start activities.\n     *\n     * @param activity The activity.\n     * @param intents  The descriptions of the activities to start.\n     * @param options  Additional options for how the Activity should be started.\n     */\n    public static void startActivities(@NonNull final Activity activity,\n                                       @NonNull final Intent[] intents,\n                                       @Nullable final Bundle options) {\n        startActivities(intents, activity, options);\n    }\n\n    /**\n     * Start activities.\n     *\n     * @param activity  The activity.\n     * @param intents   The descriptions of the activities to start.\n     * @param enterAnim A resource ID of the animation resource to use for the\n     *                  incoming activity.\n     * @param exitAnim  A resource ID of the animation resource to use for the\n     *                  outgoing activity.\n     */\n    public static void startActivities(@NonNull final Activity activity,\n                                       @NonNull final Intent[] intents,\n                                       @AnimRes final int enterAnim,\n                                       @AnimRes final int exitAnim) {\n        startActivities(intents, activity, getOptionsBundle(activity, enterAnim, exitAnim));\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {\n            activity.overridePendingTransition(enterAnim, exitAnim);\n        }\n    }\n\n    /**\n     * Start home activity.\n     */\n    public static void startHomeActivity() {\n        Intent homeIntent = new Intent(Intent.ACTION_MAIN);\n        homeIntent.addCategory(Intent.CATEGORY_HOME);\n        homeIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n        startActivity(homeIntent);\n    }\n\n    /**\n     * Start the launcher activity.\n     */\n    public static void startLauncherActivity() {\n        startLauncherActivity(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Start the launcher activity.\n     *\n     * @param pkg The name of the package.\n     */\n    public static void startLauncherActivity(@NonNull final String pkg) {\n        String launcherActivity = getLauncherActivity(pkg);\n        if (TextUtils.isEmpty(launcherActivity)) return;\n        startActivity(pkg, launcherActivity);\n    }\n\n    /**\n     * Return the list of activity.\n     *\n     * @return the list of activity\n     */\n    public static List<Activity> getActivityList() {\n        return UtilsBridge.getActivityList();\n    }\n\n    /**\n     * Return the name of launcher activity.\n     *\n     * @return the name of launcher activity\n     */\n    public static String getLauncherActivity() {\n        return getLauncherActivity(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the name of launcher activity.\n     *\n     * @param pkg The name of the package.\n     * @return the name of launcher activity\n     */\n    public static String getLauncherActivity(@NonNull final String pkg) {\n        if (UtilsBridge.isSpace(pkg)) return \"\";\n        Intent intent = new Intent(Intent.ACTION_MAIN, null);\n        intent.addCategory(Intent.CATEGORY_LAUNCHER);\n        intent.setPackage(pkg);\n        PackageManager pm = Utils.getApp().getPackageManager();\n        List<ResolveInfo> info = pm.queryIntentActivities(intent, 0);\n        if (info == null || info.size() == 0) {\n            return \"\";\n        }\n        return info.get(0).activityInfo.name;\n    }\n\n    /**\n     * Return the list of main activities.\n     *\n     * @return the list of main activities\n     */\n    public static List<String> getMainActivities() {\n        return getMainActivities(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the list of main activities.\n     *\n     * @param pkg The name of the package.\n     * @return the list of main activities\n     */\n    public static List<String> getMainActivities(@NonNull final String pkg) {\n        List<String> ret = new ArrayList<>();\n        Intent intent = new Intent(Intent.ACTION_MAIN, null);\n        intent.setPackage(pkg);\n        PackageManager pm = Utils.getApp().getPackageManager();\n        List<ResolveInfo> info = pm.queryIntentActivities(intent, 0);\n        int size = info.size();\n        if (size == 0) return ret;\n        for (int i = 0; i < size; i++) {\n            ResolveInfo ri = info.get(i);\n            if (ri.activityInfo.processName.equals(pkg)) {\n                ret.add(ri.activityInfo.name);\n            }\n        }\n        return ret;\n    }\n\n    /**\n     * Return the top activity in activity's stack.\n     *\n     * @return the top activity in activity's stack\n     */\n    public static Activity getTopActivity() {\n        return UtilsBridge.getTopActivity();\n    }\n\n    /**\n     * Return whether the activity is alive.\n     *\n     * @param context The context.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isActivityAlive(final Context context) {\n        return isActivityAlive(getActivityByContext(context));\n    }\n\n    /**\n     * Return whether the activity is alive.\n     *\n     * @param activity The activity.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isActivityAlive(final Activity activity) {\n        return activity != null && !activity.isFinishing()\n                && (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1 || !activity.isDestroyed());\n    }\n\n    /**\n     * Return whether the activity exists in activity's stack.\n     *\n     * @param activity The activity.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isActivityExistsInStack(@NonNull final Activity activity) {\n        List<Activity> activities = UtilsBridge.getActivityList();\n        for (Activity aActivity : activities) {\n            if (aActivity.equals(activity)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Return whether the activity exists in activity's stack.\n     *\n     * @param clz The activity class.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isActivityExistsInStack(@NonNull final Class<? extends Activity> clz) {\n        List<Activity> activities = UtilsBridge.getActivityList();\n        for (Activity aActivity : activities) {\n            if (aActivity.getClass().equals(clz)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Finish the activity.\n     *\n     * @param activity The activity.\n     */\n    public static void finishActivity(@NonNull final Activity activity) {\n        finishActivity(activity, false);\n    }\n\n    /**\n     * Finish the activity.\n     *\n     * @param activity   The activity.\n     * @param isLoadAnim True to use animation for the outgoing activity, false otherwise.\n     */\n    public static void finishActivity(@NonNull final Activity activity, final boolean isLoadAnim) {\n        activity.finish();\n        if (!isLoadAnim) {\n            activity.overridePendingTransition(0, 0);\n        }\n    }\n\n    /**\n     * Finish the activity.\n     *\n     * @param activity  The activity.\n     * @param enterAnim A resource ID of the animation resource to use for the\n     *                  incoming activity.\n     * @param exitAnim  A resource ID of the animation resource to use for the\n     *                  outgoing activity.\n     */\n    public static void finishActivity(@NonNull final Activity activity,\n                                      @AnimRes final int enterAnim,\n                                      @AnimRes final int exitAnim) {\n        activity.finish();\n        activity.overridePendingTransition(enterAnim, exitAnim);\n    }\n\n    /**\n     * Finish the activity.\n     *\n     * @param clz The activity class.\n     */\n    public static void finishActivity(@NonNull final Class<? extends Activity> clz) {\n        finishActivity(clz, false);\n    }\n\n    /**\n     * Finish the activity.\n     *\n     * @param clz        The activity class.\n     * @param isLoadAnim True to use animation for the outgoing activity, false otherwise.\n     */\n    public static void finishActivity(@NonNull final Class<? extends Activity> clz,\n                                      final boolean isLoadAnim) {\n        List<Activity> activities = UtilsBridge.getActivityList();\n        for (Activity activity : activities) {\n            if (activity.getClass().equals(clz)) {\n                activity.finish();\n                if (!isLoadAnim) {\n                    activity.overridePendingTransition(0, 0);\n                }\n            }\n        }\n    }\n\n    /**\n     * Finish the activity.\n     *\n     * @param clz       The activity class.\n     * @param enterAnim A resource ID of the animation resource to use for the\n     *                  incoming activity.\n     * @param exitAnim  A resource ID of the animation resource to use for the\n     *                  outgoing activity.\n     */\n    public static void finishActivity(@NonNull final Class<? extends Activity> clz,\n                                      @AnimRes final int enterAnim,\n                                      @AnimRes final int exitAnim) {\n        List<Activity> activities = UtilsBridge.getActivityList();\n        for (Activity activity : activities) {\n            if (activity.getClass().equals(clz)) {\n                activity.finish();\n                activity.overridePendingTransition(enterAnim, exitAnim);\n            }\n        }\n    }\n\n    /**\n     * Finish to the activity.\n     *\n     * @param activity      The activity.\n     * @param isIncludeSelf True to include the activity, false otherwise.\n     */\n    public static boolean finishToActivity(@NonNull final Activity activity,\n                                           final boolean isIncludeSelf) {\n        return finishToActivity(activity, isIncludeSelf, false);\n    }\n\n    /**\n     * Finish to the activity.\n     *\n     * @param activity      The activity.\n     * @param isIncludeSelf True to include the activity, false otherwise.\n     * @param isLoadAnim    True to use animation for the outgoing activity, false otherwise.\n     */\n    public static boolean finishToActivity(@NonNull final Activity activity,\n                                           final boolean isIncludeSelf,\n                                           final boolean isLoadAnim) {\n        List<Activity> activities = UtilsBridge.getActivityList();\n        for (Activity act : activities) {\n            if (act.equals(activity)) {\n                if (isIncludeSelf) {\n                    finishActivity(act, isLoadAnim);\n                }\n                return true;\n            }\n            finishActivity(act, isLoadAnim);\n        }\n        return false;\n    }\n\n    /**\n     * Finish to the activity.\n     *\n     * @param activity      The activity.\n     * @param isIncludeSelf True to include the activity, false otherwise.\n     * @param enterAnim     A resource ID of the animation resource to use for the\n     *                      incoming activity.\n     * @param exitAnim      A resource ID of the animation resource to use for the\n     *                      outgoing activity.\n     */\n    public static boolean finishToActivity(@NonNull final Activity activity,\n                                           final boolean isIncludeSelf,\n                                           @AnimRes final int enterAnim,\n                                           @AnimRes final int exitAnim) {\n        List<Activity> activities = UtilsBridge.getActivityList();\n        for (Activity act : activities) {\n            if (act.equals(activity)) {\n                if (isIncludeSelf) {\n                    finishActivity(act, enterAnim, exitAnim);\n                }\n                return true;\n            }\n            finishActivity(act, enterAnim, exitAnim);\n        }\n        return false;\n    }\n\n    /**\n     * Finish to the activity.\n     *\n     * @param clz           The activity class.\n     * @param isIncludeSelf True to include the activity, false otherwise.\n     */\n    public static boolean finishToActivity(@NonNull final Class<? extends Activity> clz,\n                                           final boolean isIncludeSelf) {\n        return finishToActivity(clz, isIncludeSelf, false);\n    }\n\n    /**\n     * Finish to the activity.\n     *\n     * @param clz           The activity class.\n     * @param isIncludeSelf True to include the activity, false otherwise.\n     * @param isLoadAnim    True to use animation for the outgoing activity, false otherwise.\n     */\n    public static boolean finishToActivity(@NonNull final Class<? extends Activity> clz,\n                                           final boolean isIncludeSelf,\n                                           final boolean isLoadAnim) {\n        List<Activity> activities = UtilsBridge.getActivityList();\n        for (Activity act : activities) {\n            if (act.getClass().equals(clz)) {\n                if (isIncludeSelf) {\n                    finishActivity(act, isLoadAnim);\n                }\n                return true;\n            }\n            finishActivity(act, isLoadAnim);\n        }\n        return false;\n    }\n\n    /**\n     * Finish to the activity.\n     *\n     * @param clz           The activity class.\n     * @param isIncludeSelf True to include the activity, false otherwise.\n     * @param enterAnim     A resource ID of the animation resource to use for the\n     *                      incoming activity.\n     * @param exitAnim      A resource ID of the animation resource to use for the\n     *                      outgoing activity.\n     */\n    public static boolean finishToActivity(@NonNull final Class<? extends Activity> clz,\n                                           final boolean isIncludeSelf,\n                                           @AnimRes final int enterAnim,\n                                           @AnimRes final int exitAnim) {\n        List<Activity> activities = UtilsBridge.getActivityList();\n        for (Activity act : activities) {\n            if (act.getClass().equals(clz)) {\n                if (isIncludeSelf) {\n                    finishActivity(act, enterAnim, exitAnim);\n                }\n                return true;\n            }\n            finishActivity(act, enterAnim, exitAnim);\n        }\n        return false;\n    }\n\n    /**\n     * Finish the activities whose type not equals the activity class.\n     *\n     * @param clz The activity class.\n     */\n    public static void finishOtherActivities(@NonNull final Class<? extends Activity> clz) {\n        finishOtherActivities(clz, false);\n    }\n\n\n    /**\n     * Finish the activities whose type not equals the activity class.\n     *\n     * @param clz        The activity class.\n     * @param isLoadAnim True to use animation for the outgoing activity, false otherwise.\n     */\n    public static void finishOtherActivities(@NonNull final Class<? extends Activity> clz,\n                                             final boolean isLoadAnim) {\n        List<Activity> activities = UtilsBridge.getActivityList();\n        for (Activity act : activities) {\n            if (!act.getClass().equals(clz)) {\n                finishActivity(act, isLoadAnim);\n            }\n        }\n    }\n\n    /**\n     * Finish the activities whose type not equals the activity class.\n     *\n     * @param clz       The activity class.\n     * @param enterAnim A resource ID of the animation resource to use for the\n     *                  incoming activity.\n     * @param exitAnim  A resource ID of the animation resource to use for the\n     *                  outgoing activity.\n     */\n    public static void finishOtherActivities(@NonNull final Class<? extends Activity> clz,\n                                             @AnimRes final int enterAnim,\n                                             @AnimRes final int exitAnim) {\n        List<Activity> activities = UtilsBridge.getActivityList();\n        for (Activity act : activities) {\n            if (!act.getClass().equals(clz)) {\n                finishActivity(act, enterAnim, exitAnim);\n            }\n        }\n    }\n\n    /**\n     * Finish all of activities.\n     */\n    public static void finishAllActivities() {\n        finishAllActivities(false);\n    }\n\n    /**\n     * Finish all of activities.\n     *\n     * @param isLoadAnim True to use animation for the outgoing activity, false otherwise.\n     */\n    public static void finishAllActivities(final boolean isLoadAnim) {\n        List<Activity> activityList = UtilsBridge.getActivityList();\n        for (Activity act : activityList) {\n            // sActivityList remove the index activity at onActivityDestroyed\n            act.finish();\n            if (!isLoadAnim) {\n                act.overridePendingTransition(0, 0);\n            }\n        }\n    }\n\n    /**\n     * Finish all of activities.\n     *\n     * @param enterAnim A resource ID of the animation resource to use for the\n     *                  incoming activity.\n     * @param exitAnim  A resource ID of the animation resource to use for the\n     *                  outgoing activity.\n     */\n    public static void finishAllActivities(@AnimRes final int enterAnim,\n                                           @AnimRes final int exitAnim) {\n        List<Activity> activityList = UtilsBridge.getActivityList();\n        for (Activity act : activityList) {\n            // sActivityList remove the index activity at onActivityDestroyed\n            act.finish();\n            act.overridePendingTransition(enterAnim, exitAnim);\n        }\n    }\n\n    /**\n     * Finish all of activities except the newest activity.\n     */\n    public static void finishAllActivitiesExceptNewest() {\n        finishAllActivitiesExceptNewest(false);\n    }\n\n    /**\n     * Finish all of activities except the newest activity.\n     *\n     * @param isLoadAnim True to use animation for the outgoing activity, false otherwise.\n     */\n    public static void finishAllActivitiesExceptNewest(final boolean isLoadAnim) {\n        List<Activity> activities = UtilsBridge.getActivityList();\n        for (int i = 1; i < activities.size(); i++) {\n            finishActivity(activities.get(i), isLoadAnim);\n        }\n    }\n\n    /**\n     * Finish all of activities except the newest activity.\n     *\n     * @param enterAnim A resource ID of the animation resource to use for the\n     *                  incoming activity.\n     * @param exitAnim  A resource ID of the animation resource to use for the\n     *                  outgoing activity.\n     */\n    public static void finishAllActivitiesExceptNewest(@AnimRes final int enterAnim,\n                                                       @AnimRes final int exitAnim) {\n        List<Activity> activities = UtilsBridge.getActivityList();\n        for (int i = 1; i < activities.size(); i++) {\n            finishActivity(activities.get(i), enterAnim, exitAnim);\n        }\n    }\n\n    /**\n     * Return the icon of activity.\n     *\n     * @param activity The activity.\n     * @return the icon of activity\n     */\n    @Nullable\n    public static Drawable getActivityIcon(@NonNull final Activity activity) {\n        return getActivityIcon(activity.getComponentName());\n    }\n\n    /**\n     * Return the icon of activity.\n     *\n     * @param clz The activity class.\n     * @return the icon of activity\n     */\n    @Nullable\n    public static Drawable getActivityIcon(@NonNull final Class<? extends Activity> clz) {\n        return getActivityIcon(new ComponentName(Utils.getApp(), clz));\n    }\n\n    /**\n     * Return the icon of activity.\n     *\n     * @param activityName The name of activity.\n     * @return the icon of activity\n     */\n    @Nullable\n    public static Drawable getActivityIcon(@NonNull final ComponentName activityName) {\n        PackageManager pm = Utils.getApp().getPackageManager();\n        try {\n            return pm.getActivityIcon(activityName);\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n            return null;\n        }\n    }\n\n    /**\n     * Return the logo of activity.\n     *\n     * @param activity The activity.\n     * @return the logo of activity\n     */\n    @Nullable\n    public static Drawable getActivityLogo(@NonNull final Activity activity) {\n        return getActivityLogo(activity.getComponentName());\n    }\n\n    /**\n     * Return the logo of activity.\n     *\n     * @param clz The activity class.\n     * @return the logo of activity\n     */\n    @Nullable\n    public static Drawable getActivityLogo(@NonNull final Class<? extends Activity> clz) {\n        return getActivityLogo(new ComponentName(Utils.getApp(), clz));\n    }\n\n    /**\n     * Return the logo of activity.\n     *\n     * @param activityName The name of activity.\n     * @return the logo of activity\n     */\n    @Nullable\n    public static Drawable getActivityLogo(@NonNull final ComponentName activityName) {\n        PackageManager pm = Utils.getApp().getPackageManager();\n        try {\n            return pm.getActivityLogo(activityName);\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n            return null;\n        }\n    }\n\n    private static void startActivity(final Context context,\n                                      final Bundle extras,\n                                      final String pkg,\n                                      final String cls,\n                                      @Nullable final Bundle options) {\n        Intent intent = new Intent();\n        if (extras != null) intent.putExtras(extras);\n        intent.setComponent(new ComponentName(pkg, cls));\n        startActivity(intent, context, options);\n    }\n\n    private static boolean startActivity(final Intent intent,\n                                         final Context context,\n                                         final Bundle options) {\n        if (!(context instanceof Activity)) {\n            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n        }\n        try {\n            if (options != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n                context.startActivity(intent, options);\n            } else {\n                context.startActivity(intent);\n            }\n        } catch (Exception e) {\n            Log.e(\"ActivityUtils\", \"An exception occurred in startActivity, error message: \" + e.getLocalizedMessage());\n            return false;\n        }\n        return true;\n    }\n\n    private static boolean startActivityForResult(final Activity activity,\n                                                  final Bundle extras,\n                                                  final String pkg,\n                                                  final String cls,\n                                                  final int requestCode,\n                                                  @Nullable final Bundle options) {\n        Intent intent = new Intent();\n        if (extras != null) intent.putExtras(extras);\n        intent.setComponent(new ComponentName(pkg, cls));\n        return startActivityForResult(intent, activity, requestCode, options);\n    }\n\n    private static boolean startActivityForResult(final Intent intent,\n                                                  final Activity activity,\n                                                  final int requestCode,\n                                                  @Nullable final Bundle options) {\n        try {\n            if (options != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n                activity.startActivityForResult(intent, requestCode, options);\n            } else {\n                activity.startActivityForResult(intent, requestCode);\n            }\n        } catch (Exception e) {\n            Log.e(\"ActivityUtils\", \"An exception occurred in startActivityForResult, error message: \" + e.getLocalizedMessage());\n            return false;\n        }\n        return true;\n    }\n\n    private static void startActivities(final Intent[] intents,\n                                        final Context context,\n                                        @Nullable final Bundle options) {\n        if (!(context instanceof Activity)) {\n            for (Intent intent : intents) {\n                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n            }\n        }\n        if (options != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n            context.startActivities(intents, options);\n        } else {\n            context.startActivities(intents);\n        }\n    }\n\n    private static boolean startActivityForResult(final Fragment fragment,\n                                                  final Bundle extras,\n                                                  final String pkg,\n                                                  final String cls,\n                                                  final int requestCode,\n                                                  @Nullable final Bundle options) {\n        Intent intent = new Intent();\n        if (extras != null) intent.putExtras(extras);\n        intent.setComponent(new ComponentName(pkg, cls));\n        return startActivityForResult(intent, fragment, requestCode, options);\n    }\n\n    private static boolean startActivityForResult(final Intent intent,\n                                                  final Fragment fragment,\n                                                  final int requestCode,\n                                                  @Nullable final Bundle options) {\n        if (fragment.getActivity() == null) {\n            Log.e(\"ActivityUtils\", \"Fragment \" + fragment + \" not attached to Activity\");\n            return false;\n        }\n        try {\n            if (options != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n                fragment.startActivityForResult(intent, requestCode, options);\n            } else {\n                fragment.startActivityForResult(intent, requestCode);\n            }\n        } catch (Exception e) {\n            Log.e(\"ActivityUtils\", \"An exception occurred in fragment.startActivityForResult, error message: \" + e.getLocalizedMessage());\n            return false;\n        }\n        return true;\n    }\n\n    private static Bundle getOptionsBundle(final Fragment fragment,\n                                           final int enterAnim,\n                                           final int exitAnim) {\n        Activity activity = fragment.getActivity();\n        if (activity == null) return null;\n        return ActivityOptionsCompat.makeCustomAnimation(activity, enterAnim, exitAnim).toBundle();\n    }\n\n    private static Bundle getOptionsBundle(final Context context,\n                                           final int enterAnim,\n                                           final int exitAnim) {\n        return ActivityOptionsCompat.makeCustomAnimation(context, enterAnim, exitAnim).toBundle();\n    }\n\n    private static Bundle getOptionsBundle(final Fragment fragment,\n                                           final View[] sharedElements) {\n        Activity activity = fragment.getActivity();\n        if (activity == null) return null;\n        return getOptionsBundle(activity, sharedElements);\n    }\n\n    private static Bundle getOptionsBundle(final Activity activity,\n                                           final View[] sharedElements) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return null;\n        if (sharedElements == null) return null;\n        int len = sharedElements.length;\n        if (len <= 0) return null;\n        @SuppressWarnings(\"unchecked\")\n        Pair<View, String>[] pairs = new Pair[len];\n        for (int i = 0; i < len; i++) {\n            pairs[i] = Pair.create(sharedElements[i], sharedElements[i].getTransitionName());\n        }\n        return ActivityOptionsCompat.makeSceneTransitionAnimation(activity, pairs).toBundle();\n    }\n\n    private static Context getTopActivityOrApp() {\n        if (UtilsBridge.isAppForeground()) {\n            Activity topActivity = getTopActivity();\n            return topActivity == null ? Utils.getApp() : topActivity;\n        } else {\n            return Utils.getApp();\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/AdaptScreenUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.content.res.Resources;\nimport android.util.DisplayMetrics;\n\nimport java.lang.reflect.Field;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport androidx.annotation.NonNull;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/11/15\n *     desc  : utils about adapt screen\n * </pre>\n */\npublic final class AdaptScreenUtils {\n\n    private static List<Field> sMetricsFields;\n\n    private AdaptScreenUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Adapt for the horizontal screen, and call it in {@link android.app.Activity#getResources()}.\n     */\n    @NonNull\n    public static Resources adaptWidth(@NonNull final Resources resources, final int designWidth) {\n        float newXdpi = (resources.getDisplayMetrics().widthPixels * 72f) / designWidth;\n        applyDisplayMetrics(resources, newXdpi);\n        return resources;\n    }\n\n    /**\n     * Adapt for the vertical screen, and call it in {@link android.app.Activity#getResources()}.\n     */\n    @NonNull\n    public static Resources adaptHeight(@NonNull final Resources resources, final int designHeight) {\n        return adaptHeight(resources, designHeight, false);\n    }\n\n    /**\n     * Adapt for the vertical screen, and call it in {@link android.app.Activity#getResources()}.\n     */\n    @NonNull\n    public static Resources adaptHeight(@NonNull final Resources resources, final int designHeight, final boolean includeNavBar) {\n        float screenHeight = (resources.getDisplayMetrics().heightPixels\n                + (includeNavBar ? getNavBarHeight(resources) : 0)) * 72f;\n        float newXdpi = screenHeight / designHeight;\n        applyDisplayMetrics(resources, newXdpi);\n        return resources;\n    }\n\n    private static int getNavBarHeight(@NonNull final Resources resources) {\n        int resourceId = resources.getIdentifier(\"navigation_bar_height\", \"dimen\", \"android\");\n        if (resourceId != 0) {\n            return resources.getDimensionPixelSize(resourceId);\n        } else {\n            return 0;\n        }\n    }\n\n    /**\n     * @param resources The resources.\n     * @return the resource\n     */\n    @NonNull\n    public static Resources closeAdapt(@NonNull final Resources resources) {\n        float newXdpi = Resources.getSystem().getDisplayMetrics().density * 72f;\n        applyDisplayMetrics(resources, newXdpi);\n        return resources;\n    }\n\n    /**\n     * Value of pt to value of px.\n     *\n     * @param ptValue The value of pt.\n     * @return value of px\n     */\n    public static int pt2Px(final float ptValue) {\n        DisplayMetrics metrics = Utils.getApp().getResources().getDisplayMetrics();\n        return (int) (ptValue * metrics.xdpi / 72f + 0.5);\n    }\n\n    /**\n     * Value of px to value of pt.\n     *\n     * @param pxValue The value of px.\n     * @return value of pt\n     */\n    public static int px2Pt(final float pxValue) {\n        DisplayMetrics metrics = Utils.getApp().getResources().getDisplayMetrics();\n        return (int) (pxValue * 72 / metrics.xdpi + 0.5);\n    }\n\n    private static void applyDisplayMetrics(@NonNull final Resources resources, final float newXdpi) {\n        resources.getDisplayMetrics().xdpi = newXdpi;\n        Utils.getApp().getResources().getDisplayMetrics().xdpi = newXdpi;\n        applyOtherDisplayMetrics(resources, newXdpi);\n    }\n\n    static Runnable getPreLoadRunnable() {\n        return new Runnable() {\n            @Override\n            public void run() {\n                preLoad();\n            }\n        };\n    }\n\n    private static void preLoad() {\n        applyDisplayMetrics(Resources.getSystem(), Resources.getSystem().getDisplayMetrics().xdpi);\n    }\n\n    private static void applyOtherDisplayMetrics(final Resources resources, final float newXdpi) {\n        if (sMetricsFields == null) {\n            sMetricsFields = new ArrayList<>();\n            Class resCls = resources.getClass();\n            Field[] declaredFields = resCls.getDeclaredFields();\n            while (declaredFields != null && declaredFields.length > 0) {\n                for (Field field : declaredFields) {\n                    if (field.getType().isAssignableFrom(DisplayMetrics.class)) {\n                        field.setAccessible(true);\n                        DisplayMetrics tmpDm = getMetricsFromField(resources, field);\n                        if (tmpDm != null) {\n                            sMetricsFields.add(field);\n                            tmpDm.xdpi = newXdpi;\n                        }\n                    }\n                }\n                resCls = resCls.getSuperclass();\n                if (resCls != null) {\n                    declaredFields = resCls.getDeclaredFields();\n                } else {\n                    break;\n                }\n            }\n        } else {\n            applyMetricsFields(resources, newXdpi);\n        }\n    }\n\n    private static void applyMetricsFields(final Resources resources, final float newXdpi) {\n        for (Field metricsField : sMetricsFields) {\n            try {\n                DisplayMetrics dm = (DisplayMetrics) metricsField.get(resources);\n                if (dm != null) dm.xdpi = newXdpi;\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    private static DisplayMetrics getMetricsFromField(final Resources resources, final Field field) {\n        try {\n            return (DisplayMetrics) field.get(resources);\n        } catch (Exception ignore) {\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ApiUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.util.Log;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/09\n *     desc  : utils about api\n * </pre>\n */\npublic final class ApiUtils {\n\n    private static final String TAG = \"ApiUtils\";\n\n    private Map<Class, BaseApi> mApiMap           = new ConcurrentHashMap<>();\n    private Map<Class, Class>   mInjectApiImplMap = new HashMap<>();\n\n    private ApiUtils() {\n        init();\n    }\n\n    /**\n     * It'll be injected the implClasses who have {@link ApiUtils.Api} annotation\n     * by function of {@link ApiUtils#registerImpl} when execute transform task.\n     */\n    private void init() {/*inject*/}\n\n    private void registerImpl(Class implClass) {\n        mInjectApiImplMap.put(implClass.getSuperclass(), implClass);\n    }\n\n    /**\n     * Get api.\n     *\n     * @param apiClass The class of api.\n     * @param <T>      The type.\n     * @return the api\n     */\n    @Nullable\n    public static <T extends BaseApi> T getApi(@NonNull final Class<T> apiClass) {\n        return getInstance().getApiInner(apiClass);\n    }\n\n    public static void register(@Nullable Class<? extends BaseApi> implClass) {\n        if (implClass == null) return;\n        getInstance().registerImpl(implClass);\n    }\n\n    @NonNull\n    public static String toString_() {\n        return getInstance().toString();\n    }\n\n    @Override\n    public String toString() {\n        return \"ApiUtils: \" + mInjectApiImplMap;\n    }\n\n    private static ApiUtils getInstance() {\n        return LazyHolder.INSTANCE;\n    }\n\n    private <Result> Result getApiInner(Class apiClass) {\n        BaseApi api = mApiMap.get(apiClass);\n        if (api != null) {\n            return (Result) api;\n        }\n        synchronized (apiClass) {\n            api = mApiMap.get(apiClass);\n            if (api != null) {\n                return (Result) api;\n            }\n            Class implClass = mInjectApiImplMap.get(apiClass);\n            if (implClass != null) {\n                try {\n                    api = (BaseApi) implClass.newInstance();\n                    mApiMap.put(apiClass, api);\n                    return (Result) api;\n                } catch (Exception ignore) {\n                    Log.e(TAG, \"The <\" + implClass + \"> has no parameterless constructor.\");\n                    return null;\n                }\n            } else {\n                Log.e(TAG, \"The <\" + apiClass + \"> doesn't implement.\");\n                return null;\n            }\n        }\n    }\n\n    private static class LazyHolder {\n        private static final ApiUtils INSTANCE = new ApiUtils();\n    }\n\n    @Target({ElementType.TYPE})\n    @Retention(RetentionPolicy.CLASS)\n    public @interface Api {\n        boolean isMock() default false;\n    }\n\n    public static class BaseApi {\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/AppUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.app.Activity;\nimport android.app.ActivityManager;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageInfo;\nimport android.content.pm.PackageManager;\nimport android.content.pm.Signature;\nimport android.content.pm.SigningInfo;\nimport android.graphics.drawable.Drawable;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/02\n *     desc  : utils about app\n * </pre>\n */\npublic final class AppUtils {\n\n    private AppUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Register the status of application changed listener.\n     *\n     * @param listener The status of application changed listener\n     */\n    public static void registerAppStatusChangedListener(@NonNull final Utils.OnAppStatusChangedListener listener) {\n        UtilsBridge.addOnAppStatusChangedListener(listener);\n    }\n\n    /**\n     * Unregister the status of application changed listener.\n     *\n     * @param listener The status of application changed listener\n     */\n    public static void unregisterAppStatusChangedListener(@NonNull final Utils.OnAppStatusChangedListener listener) {\n        UtilsBridge.removeOnAppStatusChangedListener(listener);\n    }\n\n    /**\n     * Install the app.\n     * <p>Target APIs greater than 25 must hold\n     * {@code <uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\" />}</p>\n     *\n     * @param filePath The path of file.\n     */\n    public static void installApp(final String filePath) {\n        installApp(UtilsBridge.getFileByPath(filePath));\n    }\n\n    /**\n     * Install the app.\n     * <p>Target APIs greater than 25 must hold\n     * {@code <uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\" />}</p>\n     *\n     * @param file The file.\n     */\n    public static void installApp(final File file) {\n        Intent installAppIntent = UtilsBridge.getInstallAppIntent(file);\n        if (installAppIntent == null) return;\n        Utils.getApp().startActivity(installAppIntent);\n    }\n\n    /**\n     * Install the app.\n     * <p>Target APIs greater than 25 must hold\n     * {@code <uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\" />}</p>\n     *\n     * @param uri The uri.\n     */\n    public static void installApp(final Uri uri) {\n        Intent installAppIntent = UtilsBridge.getInstallAppIntent(uri);\n        if (installAppIntent == null) return;\n        Utils.getApp().startActivity(installAppIntent);\n    }\n\n    /**\n     * Uninstall the app.\n     * <p>Target APIs greater than 25 must hold\n     * Must hold {@code <uses-permission android:name=\"android.permission.REQUEST_DELETE_PACKAGES\" />}</p>\n     *\n     * @param packageName The name of the package.\n     */\n    public static void uninstallApp(final String packageName) {\n        if (UtilsBridge.isSpace(packageName)) return;\n        Utils.getApp().startActivity(UtilsBridge.getUninstallAppIntent(packageName));\n    }\n\n    /**\n     * Return whether the app is installed.\n     *\n     * @param pkgName The name of the package.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isAppInstalled(final String pkgName) {\n        if (UtilsBridge.isSpace(pkgName)) return false;\n        PackageManager pm = Utils.getApp().getPackageManager();\n        try {\n            return pm.getApplicationInfo(pkgName, 0).enabled;\n        } catch (PackageManager.NameNotFoundException e) {\n            return false;\n        }\n    }\n\n    /**\n     * Return whether the application with root permission.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isAppRoot() {\n        ShellUtils.CommandResult result = UtilsBridge.execCmd(\"echo root\", true);\n        return result.result == 0;\n    }\n\n    /**\n     * Return whether it is a debug application.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isAppDebug() {\n        return isAppDebug(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return whether it is a debug application.\n     *\n     * @param packageName The name of the package.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isAppDebug(final String packageName) {\n        if (UtilsBridge.isSpace(packageName)) return false;\n        try {\n            PackageManager pm = Utils.getApp().getPackageManager();\n            ApplicationInfo ai = pm.getApplicationInfo(packageName, 0);\n            return (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n            return false;\n        }\n    }\n\n    /**\n     * Return whether it is a system application.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isAppSystem() {\n        return isAppSystem(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return whether it is a system application.\n     *\n     * @param packageName The name of the package.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isAppSystem(final String packageName) {\n        if (UtilsBridge.isSpace(packageName)) return false;\n        try {\n            PackageManager pm = Utils.getApp().getPackageManager();\n            ApplicationInfo ai = pm.getApplicationInfo(packageName, 0);\n            return (ai.flags & ApplicationInfo.FLAG_SYSTEM) != 0;\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n            return false;\n        }\n    }\n\n    /**\n     * Return whether application is foreground.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isAppForeground() {\n        return UtilsBridge.isAppForeground();\n    }\n\n    /**\n     * Return whether application is foreground.\n     * <p>Target APIs greater than 21 must hold\n     * {@code <uses-permission android:name=\"android.permission.PACKAGE_USAGE_STATS\" />}</p>\n     *\n     * @param pkgName The name of the package.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isAppForeground(@NonNull final String pkgName) {\n        return !UtilsBridge.isSpace(pkgName) && pkgName.equals(UtilsBridge.getForegroundProcessName());\n    }\n\n    /**\n     * Return whether application is running.\n     *\n     * @param pkgName The name of the package.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isAppRunning(final String pkgName) {\n        if (UtilsBridge.isSpace(pkgName)) return false;\n        ActivityManager am = (ActivityManager) Utils.getApp().getSystemService(Context.ACTIVITY_SERVICE);\n        if (am != null) {\n            List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(Integer.MAX_VALUE);\n            if (taskInfo != null && taskInfo.size() > 0) {\n                for (ActivityManager.RunningTaskInfo aInfo : taskInfo) {\n                    if (aInfo.baseActivity != null) {\n                        if (pkgName.equals(aInfo.baseActivity.getPackageName())) {\n                            return true;\n                        }\n                    }\n                }\n            }\n            List<ActivityManager.RunningServiceInfo> serviceInfo = am.getRunningServices(Integer.MAX_VALUE);\n            if (serviceInfo != null && serviceInfo.size() > 0) {\n                for (ActivityManager.RunningServiceInfo aInfo : serviceInfo) {\n                    if (pkgName.equals(aInfo.service.getPackageName())) {\n                        return true;\n                    }\n                }\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Launch the application.\n     *\n     * @param packageName The name of the package.\n     */\n    public static void launchApp(final String packageName) {\n        if (UtilsBridge.isSpace(packageName)) return;\n        Intent launchAppIntent = UtilsBridge.getLaunchAppIntent(packageName);\n        if (launchAppIntent == null) {\n            Log.e(\"AppUtils\", \"Didn't exist launcher activity.\");\n            return;\n        }\n        Utils.getApp().startActivity(launchAppIntent);\n    }\n\n    /**\n     * Relaunch the application.\n     */\n    public static void relaunchApp() {\n        relaunchApp(false);\n    }\n\n    /**\n     * Relaunch the application.\n     *\n     * @param isKillProcess True to kill the process, false otherwise.\n     */\n    public static void relaunchApp(final boolean isKillProcess) {\n        Intent intent = UtilsBridge.getLaunchAppIntent(Utils.getApp().getPackageName());\n        if (intent == null) {\n            Log.e(\"AppUtils\", \"Didn't exist launcher activity.\");\n            return;\n        }\n        intent.addFlags(\n                Intent.FLAG_ACTIVITY_NEW_TASK\n                        | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK\n        );\n        Utils.getApp().startActivity(intent);\n        if (!isKillProcess) return;\n        android.os.Process.killProcess(android.os.Process.myPid());\n        System.exit(0);\n    }\n\n    /**\n     * Launch the application's details settings.\n     */\n    public static void launchAppDetailsSettings() {\n        launchAppDetailsSettings(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Launch the application's details settings.\n     *\n     * @param pkgName The name of the package.\n     */\n    public static void launchAppDetailsSettings(final String pkgName) {\n        if (UtilsBridge.isSpace(pkgName)) return;\n        Intent intent = UtilsBridge.getLaunchAppDetailsSettingsIntent(pkgName, true);\n        if (!UtilsBridge.isIntentAvailable(intent)) return;\n        Utils.getApp().startActivity(intent);\n    }\n\n    /**\n     * Launch the application's details settings.\n     *\n     * @param activity    The activity.\n     * @param requestCode The requestCode.\n     */\n    public static void launchAppDetailsSettings(final Activity activity, final int requestCode) {\n        launchAppDetailsSettings(activity, requestCode, Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Launch the application's details settings.\n     *\n     * @param activity    The activity.\n     * @param requestCode The requestCode.\n     * @param pkgName     The name of the package.\n     */\n    public static void launchAppDetailsSettings(final Activity activity, final int requestCode, final String pkgName) {\n        if (activity == null || UtilsBridge.isSpace(pkgName)) return;\n        Intent intent = UtilsBridge.getLaunchAppDetailsSettingsIntent(pkgName, false);\n        if (!UtilsBridge.isIntentAvailable(intent)) return;\n        activity.startActivityForResult(intent, requestCode);\n    }\n\n    /**\n     * Exit the application.\n     */\n    public static void exitApp() {\n        UtilsBridge.finishAllActivities();\n        System.exit(0);\n    }\n\n    /**\n     * Return the application's icon.\n     *\n     * @return the application's icon\n     */\n    @Nullable\n    public static Drawable getAppIcon() {\n        return getAppIcon(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the application's icon.\n     *\n     * @param packageName The name of the package.\n     * @return the application's icon\n     */\n    @Nullable\n    public static Drawable getAppIcon(final String packageName) {\n        if (UtilsBridge.isSpace(packageName)) return null;\n        try {\n            PackageManager pm = Utils.getApp().getPackageManager();\n            PackageInfo pi = pm.getPackageInfo(packageName, 0);\n            return pi == null ? null : pi.applicationInfo.loadIcon(pm);\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n            return null;\n        }\n    }\n\n    /**\n     * Return the application's icon resource identifier.\n     *\n     * @return the application's icon resource identifier\n     */\n    public static int getAppIconId() {\n        return getAppIconId(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the application's icon resource identifier.\n     *\n     * @param packageName The name of the package.\n     * @return the application's icon resource identifier\n     */\n    public static int getAppIconId(final String packageName) {\n        if (UtilsBridge.isSpace(packageName)) return 0;\n        try {\n            PackageManager pm = Utils.getApp().getPackageManager();\n            PackageInfo pi = pm.getPackageInfo(packageName, 0);\n            return pi == null ? 0 : pi.applicationInfo.icon;\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n            return 0;\n        }\n    }\n\n\n    /**\n     * Return true if this is the first ever time that the application is installed on the device.\n     *\n     * @return true if this is the first ever time that the application is installed on the device.\n     */\n    public static boolean isFirstTimeInstall() {\n        try {\n            long firstInstallTime = Utils.getApp().getPackageManager().getPackageInfo(getAppPackageName(), 0).firstInstallTime;\n            long lastUpdateTime = Utils.getApp().getPackageManager().getPackageInfo(getAppPackageName(), 0).lastUpdateTime;\n            return firstInstallTime == lastUpdateTime;\n        } catch (Exception e) {\n            return false;\n        }\n    }\n\n    /**\n     * Return true if app was previously installed and this one is an update/upgrade to that one, returns false if this is a fresh installation and not an update/upgrade.\n     *\n     * @return true if app was previously installed and this one is an update/upgrade to that one, returns false if this is a fresh installation and not an update/upgrade.\n     */\n    public static boolean isAppUpgraded() {\n        try {\n            long firstInstallTime = Utils.getApp().getPackageManager().getPackageInfo(getAppPackageName(), 0).firstInstallTime;\n            long lastUpdateTime = Utils.getApp().getPackageManager().getPackageInfo(getAppPackageName(), 0).lastUpdateTime;\n            return firstInstallTime != lastUpdateTime;\n        } catch (Exception e) {\n            return false;\n        }\n    }\n\n\n    /**\n     * Return the application's package name.\n     *\n     * @return the application's package name\n     */\n    @NonNull\n    public static String getAppPackageName() {\n        return Utils.getApp().getPackageName();\n    }\n\n    /**\n     * Return the application's name.\n     *\n     * @return the application's name\n     */\n    @NonNull\n    public static String getAppName() {\n        return getAppName(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the application's name.\n     *\n     * @param packageName The name of the package.\n     * @return the application's name\n     */\n    @NonNull\n    public static String getAppName(final String packageName) {\n        if (UtilsBridge.isSpace(packageName)) return \"\";\n        try {\n            PackageManager pm = Utils.getApp().getPackageManager();\n            PackageInfo pi = pm.getPackageInfo(packageName, 0);\n            return pi == null ? \"\" : pi.applicationInfo.loadLabel(pm).toString();\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n            return \"\";\n        }\n    }\n\n    /**\n     * Return the application's path.\n     *\n     * @return the application's path\n     */\n    @NonNull\n    public static String getAppPath() {\n        return getAppPath(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the application's path.\n     *\n     * @param packageName The name of the package.\n     * @return the application's path\n     */\n    @NonNull\n    public static String getAppPath(final String packageName) {\n        if (UtilsBridge.isSpace(packageName)) return \"\";\n        try {\n            PackageManager pm = Utils.getApp().getPackageManager();\n            PackageInfo pi = pm.getPackageInfo(packageName, 0);\n            return pi == null ? \"\" : pi.applicationInfo.sourceDir;\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n            return \"\";\n        }\n    }\n\n    /**\n     * Return the application's version name.\n     *\n     * @return the application's version name\n     */\n    @NonNull\n    public static String getAppVersionName() {\n        return getAppVersionName(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the application's version name.\n     *\n     * @param packageName The name of the package.\n     * @return the application's version name\n     */\n    @NonNull\n    public static String getAppVersionName(final String packageName) {\n        if (UtilsBridge.isSpace(packageName)) return \"\";\n        try {\n            PackageManager pm = Utils.getApp().getPackageManager();\n            PackageInfo pi = pm.getPackageInfo(packageName, 0);\n            return pi == null ? \"\" : pi.versionName;\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n            return \"\";\n        }\n    }\n\n    /**\n     * Return the application's version code.\n     *\n     * @return the application's version code\n     */\n    public static int getAppVersionCode() {\n        return getAppVersionCode(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the application's version code.\n     *\n     * @param packageName The name of the package.\n     * @return the application's version code\n     */\n    public static int getAppVersionCode(final String packageName) {\n        if (UtilsBridge.isSpace(packageName)) return -1;\n        try {\n            PackageManager pm = Utils.getApp().getPackageManager();\n            PackageInfo pi = pm.getPackageInfo(packageName, 0);\n            return pi == null ? -1 : pi.versionCode;\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n            return -1;\n        }\n    }\n\n    /**\n     * Return the application's minimum sdk version code.\n     *\n     * @return the application's minimum sdk version code\n     */\n    public static int getAppMinSdkVersion() {\n        return getAppMinSdkVersion(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the application's minimum sdk version code.\n     *\n     * @param packageName The name of the package.\n     * @return the application's minimum sdk version code\n     */\n    public static int getAppMinSdkVersion(final String packageName) {\n        if (UtilsBridge.isSpace(packageName)) return -1;\n        if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.N) return -1;\n        try {\n            PackageManager pm = Utils.getApp().getPackageManager();\n            PackageInfo pi = pm.getPackageInfo(packageName, 0);\n            if (null == pi) return -1;\n            ApplicationInfo ai = pi.applicationInfo;\n            return null == ai ? -1 : ai.minSdkVersion;\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n            return -1;\n        }\n    }\n\n    /**\n     * Return the application's target sdk version code.\n     *\n     * @return the application's target sdk version code\n     */\n    public static int getAppTargetSdkVersion() {\n        return getAppTargetSdkVersion(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the application's target sdk version code.\n     *\n     * @param packageName The name of the package.\n     * @return the application's target sdk version code\n     */\n    public static int getAppTargetSdkVersion(final String packageName) {\n        if (UtilsBridge.isSpace(packageName)) return -1;\n        try {\n            PackageManager pm = Utils.getApp().getPackageManager();\n            PackageInfo pi = pm.getPackageInfo(packageName, 0);\n            if (null == pi) return -1;\n            ApplicationInfo ai = pi.applicationInfo;\n            return null == ai ? -1 : ai.targetSdkVersion;\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n            return -1;\n        }\n    }\n\n    /**\n     * Return the application's signature.\n     *\n     * @return the application's signature\n     */\n    @Nullable\n    public static Signature[] getAppSignatures() {\n        return getAppSignatures(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the application's signature.\n     *\n     * @param packageName The name of the package.\n     * @return the application's signature\n     */\n    @Nullable\n    public static Signature[] getAppSignatures(final String packageName) {\n        if (UtilsBridge.isSpace(packageName)) return null;\n        try {\n            PackageManager pm = Utils.getApp().getPackageManager();\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n                PackageInfo pi = pm.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES);\n                if (pi == null) return null;\n\n                SigningInfo signingInfo = pi.signingInfo;\n                if (signingInfo.hasMultipleSigners()) {\n                    return signingInfo.getApkContentsSigners();\n                } else {\n                    return signingInfo.getSigningCertificateHistory();\n                }\n            } else {\n                PackageInfo pi = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);\n                if (pi == null) return null;\n\n                return pi.signatures;\n            }\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n            return null;\n        }\n    }\n\n    /**\n     * Return the application's signature.\n     *\n     * @param file The file.\n     * @return the application's signature\n     */\n    @Nullable\n    public static Signature[] getAppSignatures(final File file) {\n        if (file == null) return null;\n        PackageManager pm = Utils.getApp().getPackageManager();\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n            PackageInfo pi = pm.getPackageArchiveInfo(file.getAbsolutePath(), PackageManager.GET_SIGNING_CERTIFICATES);\n            if (pi == null) return null;\n\n            SigningInfo signingInfo = pi.signingInfo;\n            if (signingInfo.hasMultipleSigners()) {\n                return signingInfo.getApkContentsSigners();\n            } else {\n                return signingInfo.getSigningCertificateHistory();\n            }\n        } else {\n            PackageInfo pi = pm.getPackageArchiveInfo(file.getAbsolutePath(), PackageManager.GET_SIGNATURES);\n            if (pi == null) return null;\n\n            return pi.signatures;\n        }\n    }\n\n    /**\n     * Return the application's signature for SHA1 value.\n     *\n     * @return the application's signature for SHA1 value\n     */\n    @NonNull\n    public static List<String> getAppSignaturesSHA1() {\n        return getAppSignaturesSHA1(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the application's signature for SHA1 value.\n     *\n     * @param packageName The name of the package.\n     * @return the application's signature for SHA1 value\n     */\n    @NonNull\n    public static List<String> getAppSignaturesSHA1(final String packageName) {\n        return getAppSignaturesHash(packageName, \"SHA1\");\n    }\n\n    /**\n     * Return the application's signature for SHA256 value.\n     *\n     * @return the application's signature for SHA256 value\n     */\n    @NonNull\n    public static List<String> getAppSignaturesSHA256() {\n        return getAppSignaturesSHA256(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the application's signature for SHA256 value.\n     *\n     * @param packageName The name of the package.\n     * @return the application's signature for SHA256 value\n     */\n    @NonNull\n    public static List<String> getAppSignaturesSHA256(final String packageName) {\n        return getAppSignaturesHash(packageName, \"SHA256\");\n    }\n\n    /**\n     * Return the application's signature for MD5 value.\n     *\n     * @return the application's signature for MD5 value\n     */\n    @NonNull\n    public static List<String> getAppSignaturesMD5() {\n        return getAppSignaturesMD5(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the application's signature for MD5 value.\n     *\n     * @param packageName The name of the package.\n     * @return the application's signature for MD5 value\n     */\n    @NonNull\n    public static List<String> getAppSignaturesMD5(final String packageName) {\n        return getAppSignaturesHash(packageName, \"MD5\");\n    }\n\n    /**\n     * Return the application's user-ID.\n     *\n     * @return the application's signature for MD5 value\n     */\n    public static int getAppUid() {\n        return getAppUid(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the application's user-ID.\n     *\n     * @param pkgName The name of the package.\n     * @return the application's signature for MD5 value\n     */\n    public static int getAppUid(String pkgName) {\n        try {\n            return Utils.getApp().getPackageManager().getApplicationInfo(pkgName, 0).uid;\n        } catch (Exception e) {\n            e.printStackTrace();\n            return -1;\n        }\n    }\n\n    private static List<String> getAppSignaturesHash(final String packageName, final String algorithm) {\n        ArrayList<String> result = new ArrayList<>();\n        if (UtilsBridge.isSpace(packageName)) return result;\n        Signature[] signatures = getAppSignatures(packageName);\n        if (signatures == null || signatures.length <= 0) return result;\n        for (Signature signature : signatures) {\n            String hash = UtilsBridge.bytes2HexString(UtilsBridge.hashTemplate(signature.toByteArray(), algorithm))\n                    .replaceAll(\"(?<=[0-9A-F]{2})[0-9A-F]{2}\", \":$0\");\n            result.add(hash);\n        }\n        return result;\n    }\n\n    /**\n     * Return the application's information.\n     * <ul>\n     * <li>name of package</li>\n     * <li>icon</li>\n     * <li>name</li>\n     * <li>path of package</li>\n     * <li>version name</li>\n     * <li>version code</li>\n     * <li>minimum sdk version code</li>\n     * <li>target sdk version code</li>\n     * <li>is system</li>\n     * </ul>\n     *\n     * @return the application's information\n     */\n    @Nullable\n    public static AppInfo getAppInfo() {\n        return getAppInfo(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the application's information.\n     * <ul>\n     * <li>name of package</li>\n     * <li>icon</li>\n     * <li>name</li>\n     * <li>path of package</li>\n     * <li>version name</li>\n     * <li>version code</li>\n     * <li>minimum sdk version code</li>\n     * <li>target sdk version code</li>\n     * <li>is system</li>\n     * </ul>\n     *\n     * @param packageName The name of the package.\n     * @return the application's information\n     */\n    @Nullable\n    public static AppInfo getAppInfo(final String packageName) {\n        try {\n            PackageManager pm = Utils.getApp().getPackageManager();\n            if (pm == null) return null;\n            return getBean(pm, pm.getPackageInfo(packageName, 0));\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n            return null;\n        }\n    }\n\n    /**\n     * Return the applications' information.\n     *\n     * @return the applications' information\n     */\n    @NonNull\n    public static List<AppInfo> getAppsInfo() {\n        List<AppInfo> list = new ArrayList<>();\n        PackageManager pm = Utils.getApp().getPackageManager();\n        if (pm == null) return list;\n        List<PackageInfo> installedPackages = pm.getInstalledPackages(0);\n        for (PackageInfo pi : installedPackages) {\n            AppInfo ai = getBean(pm, pi);\n            if (ai == null) continue;\n            list.add(ai);\n        }\n        return list;\n    }\n\n    /**\n     * Return the application's package information.\n     *\n     * @return the application's package information\n     */\n    @Nullable\n    public static AppUtils.AppInfo getApkInfo(final File apkFile) {\n        if (apkFile == null || !apkFile.isFile() || !apkFile.exists()) return null;\n        return getApkInfo(apkFile.getAbsolutePath());\n    }\n\n    /**\n     * Return the application's package information.\n     *\n     * @return the application's package information\n     */\n    @Nullable\n    public static AppUtils.AppInfo getApkInfo(final String apkFilePath) {\n        if (UtilsBridge.isSpace(apkFilePath)) return null;\n        PackageManager pm = Utils.getApp().getPackageManager();\n        if (pm == null) return null;\n        PackageInfo pi = pm.getPackageArchiveInfo(apkFilePath, 0);\n        if (pi == null) return null;\n        ApplicationInfo appInfo = pi.applicationInfo;\n        appInfo.sourceDir = apkFilePath;\n        appInfo.publicSourceDir = apkFilePath;\n        return getBean(pm, pi);\n    }\n\n\n    /**\n     * Return whether the application was first installed.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isFirstTimeInstalled() {\n        try {\n            PackageInfo pi = Utils.getApp().getPackageManager().getPackageInfo(Utils.getApp().getPackageName(), 0);\n            return pi.firstInstallTime == pi.lastUpdateTime;\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n            return true;\n        }\n    }\n\n    private static AppInfo getBean(final PackageManager pm, final PackageInfo pi) {\n        if (pi == null) return null;\n        String versionName = pi.versionName;\n        int versionCode = pi.versionCode;\n        String packageName = pi.packageName;\n        ApplicationInfo ai = pi.applicationInfo;\n        if (ai == null) {\n            return new AppInfo(packageName, \"\", null, \"\", versionName, versionCode, -1, -1, false);\n        }\n        String name = ai.loadLabel(pm).toString();\n        Drawable icon = ai.loadIcon(pm);\n        String packagePath = ai.sourceDir;\n        int minSdkVersion = -1;\n        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {\n            minSdkVersion = ai.minSdkVersion;\n        }\n        int targetSdkVersion = ai.targetSdkVersion;\n        boolean isSystem = (ApplicationInfo.FLAG_SYSTEM & ai.flags) != 0;\n        return new AppInfo(packageName, name, icon, packagePath, versionName, versionCode, minSdkVersion, targetSdkVersion, isSystem);\n    }\n\n    /**\n     * The application's information.\n     */\n    public static class AppInfo {\n\n        private String packageName;\n        private String name;\n        private Drawable icon;\n        private String packagePath;\n        private String versionName;\n        private int versionCode;\n        private int minSdkVersion;\n        private int targetSdkVersion;\n        private boolean isSystem;\n\n        public Drawable getIcon() {\n            return icon;\n        }\n\n        public void setIcon(final Drawable icon) {\n            this.icon = icon;\n        }\n\n        public boolean isSystem() {\n            return isSystem;\n        }\n\n        public void setSystem(final boolean isSystem) {\n            this.isSystem = isSystem;\n        }\n\n        public String getPackageName() {\n            return packageName;\n        }\n\n        public void setPackageName(final String packageName) {\n            this.packageName = packageName;\n        }\n\n        public String getName() {\n            return name;\n        }\n\n        public void setName(final String name) {\n            this.name = name;\n        }\n\n        public String getPackagePath() {\n            return packagePath;\n        }\n\n        public void setPackagePath(final String packagePath) {\n            this.packagePath = packagePath;\n        }\n\n        public int getVersionCode() {\n            return versionCode;\n        }\n\n        public void setVersionCode(final int versionCode) {\n            this.versionCode = versionCode;\n        }\n\n        public String getVersionName() {\n            return versionName;\n        }\n\n        public void setVersionName(final String versionName) {\n            this.versionName = versionName;\n        }\n\n        public int getMinSdkVersion() {\n            return minSdkVersion;\n        }\n\n        public void setMinSdkVersion(int minSdkVersion) {\n            this.minSdkVersion = minSdkVersion;\n        }\n\n        public int getTargetSdkVersion() {\n            return targetSdkVersion;\n        }\n\n        public void setTargetSdkVersion(int targetSdkVersion) {\n            this.targetSdkVersion = targetSdkVersion;\n        }\n\n        public AppInfo(String packageName, String name, Drawable icon, String packagePath, String versionName, int versionCode, int minSdkVersion, int targetSdkVersion, boolean isSystem) {\n            this.setName(name);\n            this.setIcon(icon);\n            this.setPackageName(packageName);\n            this.setPackagePath(packagePath);\n            this.setVersionName(versionName);\n            this.setVersionCode(versionCode);\n            this.setMinSdkVersion(minSdkVersion);\n            this.setTargetSdkVersion(targetSdkVersion);\n            this.setSystem(isSystem);\n        }\n\n        @Override\n        @NonNull\n        public String toString() {\n            return \"{\" +\n                    \"\\n    pkg name: \" + getPackageName() +\n                    \"\\n    app icon: \" + getIcon() +\n                    \"\\n    app name: \" + getName() +\n                    \"\\n    app path: \" + getPackagePath() +\n                    \"\\n    app v name: \" + getVersionName() +\n                    \"\\n    app v code: \" + getVersionCode() +\n                    \"\\n    app v min: \" + getMinSdkVersion() +\n                    \"\\n    app v target: \" + getTargetSdkVersion() +\n                    \"\\n    is system: \" + isSystem() +\n                    \"\\n}\";\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ArrayUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport java.lang.reflect.Array;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.LinkedList;\nimport java.util.List;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/10\n *     desc  : utils about array\n * </pre>\n */\npublic class ArrayUtils {\n\n    public static final int INDEX_NOT_FOUND = -1;\n\n    private ArrayUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Returns a new array only of those given elements.\n     *\n     * @param array The array.\n     * @return a new array only of those given elements.\n     */\n    @NonNull\n    public static <T> T[] newArray(T... array) {\n        return array;\n    }\n\n    @NonNull\n    public static long[] newLongArray(long... array) {\n        return array;\n    }\n\n    @NonNull\n    public static int[] newIntArray(int... array) {\n        return array;\n    }\n\n    @NonNull\n    public static short[] newShortArray(short... array) {\n        return array;\n    }\n\n    @NonNull\n    public static char[] newCharArray(char... array) {\n        return array;\n    }\n\n    @NonNull\n    public static byte[] newByteArray(byte... array) {\n        return array;\n    }\n\n    @NonNull\n    public static double[] newDoubleArray(double... array) {\n        return array;\n    }\n\n    @NonNull\n    public static float[] newFloatArray(float... array) {\n        return array;\n    }\n\n    @NonNull\n    public static boolean[] newBooleanArray(boolean... array) {\n        return array;\n    }\n\n    /**\n     * Return the array is empty.\n     *\n     * @param array The array.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isEmpty(@Nullable Object array) {\n        return getLength(array) == 0;\n    }\n\n    /**\n     * Return the size of array.\n     *\n     * @param array The array.\n     * @return the size of array\n     */\n    public static int getLength(@Nullable Object array) {\n        if (array == null) return 0;\n        return Array.getLength(array);\n    }\n\n    public static boolean isSameLength(@Nullable Object array1, @Nullable Object array2) {\n        return getLength(array1) == getLength(array2);\n    }\n\n    /**\n     * Get the value of the specified index of the array.\n     *\n     * @param array The array.\n     * @param index The index into the array.\n     * @return the value of the specified index of the array\n     */\n    @Nullable\n    public static Object get(@Nullable Object array, int index) {\n        return get(array, index, null);\n    }\n\n    /**\n     * Get the value of the specified index of the array.\n     *\n     * @param array        The array.\n     * @param index        The index into the array.\n     * @param defaultValue The default value.\n     * @return the value of the specified index of the array\n     */\n    @Nullable\n    public static Object get(@Nullable Object array, int index, @Nullable Object defaultValue) {\n        if (array == null) return defaultValue;\n        try {\n            return Array.get(array, index);\n        } catch (Exception ignore) {\n            return defaultValue;\n        }\n    }\n\n    /**\n     * Set the value of the specified index of the array.\n     *\n     * @param array The array.\n     * @param index The index into the array.\n     * @param value The new value of the indexed component.\n     */\n    public static void set(@Nullable Object array, int index, @Nullable Object value) {\n        if (array == null) return;\n        Array.set(array, index, value);\n    }\n\n    /**\n     * Return whether the two arrays are equals.\n     *\n     * @param a  One array.\n     * @param a2 The other array.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean equals(@Nullable Object[] a, @Nullable Object[] a2) {\n        return Arrays.deepEquals(a, a2);\n    }\n\n    public static boolean equals(boolean[] a, boolean[] a2) {\n        return Arrays.equals(a, a2);\n    }\n\n    public static boolean equals(byte[] a, byte[] a2) {\n        return Arrays.equals(a, a2);\n    }\n\n    public static boolean equals(char[] a, char[] a2) {\n        return Arrays.equals(a, a2);\n    }\n\n    public static boolean equals(double[] a, double[] a2) {\n        return Arrays.equals(a, a2);\n    }\n\n    public static boolean equals(float[] a, float[] a2) {\n        return Arrays.equals(a, a2);\n    }\n\n    public static boolean equals(int[] a, int[] a2) {\n        return Arrays.equals(a, a2);\n    }\n\n    public static boolean equals(short[] a, short[] a2) {\n        return Arrays.equals(a, a2);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // reverse\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * <p>Reverses the order of the given array.</p>\n     *\n     * <p>There is no special handling for multi-dimensional arrays.</p>\n     *\n     * <p>This method does nothing for a <code>null</code> input array.</p>\n     *\n     * @param array the array to reverse, may be <code>null</code>\n     */\n    public static <T> void reverse(T[] array) {\n        if (array == null) {\n            return;\n        }\n        int i = 0;\n        int j = array.length - 1;\n        T tmp;\n        while (j > i) {\n            tmp = array[j];\n            array[j] = array[i];\n            array[i] = tmp;\n            j--;\n            i++;\n        }\n    }\n\n    public static void reverse(long[] array) {\n        if (array == null) {\n            return;\n        }\n        int i = 0;\n        int j = array.length - 1;\n        long tmp;\n        while (j > i) {\n            tmp = array[j];\n            array[j] = array[i];\n            array[i] = tmp;\n            j--;\n            i++;\n        }\n    }\n\n    public static void reverse(int[] array) {\n        if (array == null) {\n            return;\n        }\n        int i = 0;\n        int j = array.length - 1;\n        int tmp;\n        while (j > i) {\n            tmp = array[j];\n            array[j] = array[i];\n            array[i] = tmp;\n            j--;\n            i++;\n        }\n    }\n\n    public static void reverse(short[] array) {\n        if (array == null) {\n            return;\n        }\n        int i = 0;\n        int j = array.length - 1;\n        short tmp;\n        while (j > i) {\n            tmp = array[j];\n            array[j] = array[i];\n            array[i] = tmp;\n            j--;\n            i++;\n        }\n    }\n\n    public static void reverse(char[] array) {\n        if (array == null) {\n            return;\n        }\n        int i = 0;\n        int j = array.length - 1;\n        char tmp;\n        while (j > i) {\n            tmp = array[j];\n            array[j] = array[i];\n            array[i] = tmp;\n            j--;\n            i++;\n        }\n    }\n\n    public static void reverse(byte[] array) {\n        if (array == null) {\n            return;\n        }\n        int i = 0;\n        int j = array.length - 1;\n        byte tmp;\n        while (j > i) {\n            tmp = array[j];\n            array[j] = array[i];\n            array[i] = tmp;\n            j--;\n            i++;\n        }\n    }\n\n    public static void reverse(double[] array) {\n        if (array == null) {\n            return;\n        }\n        int i = 0;\n        int j = array.length - 1;\n        double tmp;\n        while (j > i) {\n            tmp = array[j];\n            array[j] = array[i];\n            array[i] = tmp;\n            j--;\n            i++;\n        }\n    }\n\n    public static void reverse(float[] array) {\n        if (array == null) {\n            return;\n        }\n        int i = 0;\n        int j = array.length - 1;\n        float tmp;\n        while (j > i) {\n            tmp = array[j];\n            array[j] = array[i];\n            array[i] = tmp;\n            j--;\n            i++;\n        }\n    }\n\n    public static void reverse(boolean[] array) {\n        if (array == null) {\n            return;\n        }\n        int i = 0;\n        int j = array.length - 1;\n        boolean tmp;\n        while (j > i) {\n            tmp = array[j];\n            array[j] = array[i];\n            array[i] = tmp;\n            j--;\n            i++;\n        }\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // copy\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * <p>Copies the specified array and handling\n     * <code>null</code>.</p>\n     *\n     * <p>The objects in the array are not cloned, thus there is no special\n     * handling for multi-dimensional arrays.</p>\n     *\n     * <p>This method returns <code>null</code> for a <code>null</code> input array.</p>\n     *\n     * @param array the array to shallow clone, may be <code>null</code>\n     * @return the cloned array, <code>null</code> if <code>null</code> input\n     */\n    @Nullable\n    public static <T> T[] copy(@Nullable T[] array) {\n        if (array == null) return null;\n        return subArray(array, 0, array.length);\n    }\n\n    @Nullable\n    public static long[] copy(@Nullable long[] array) {\n        if (array == null) return null;\n        return subArray(array, 0, array.length);\n    }\n\n    @Nullable\n    public static int[] copy(@Nullable int[] array) {\n        if (array == null) return null;\n        return subArray(array, 0, array.length);\n    }\n\n    @Nullable\n    public static short[] copy(@Nullable short[] array) {\n        if (array == null) return null;\n        return subArray(array, 0, array.length);\n    }\n\n    @Nullable\n    public static char[] copy(@Nullable char[] array) {\n        if (array == null) return null;\n        return subArray(array, 0, array.length);\n    }\n\n    @Nullable\n    public static byte[] copy(@Nullable byte[] array) {\n        if (array == null) return null;\n        return subArray(array, 0, array.length);\n    }\n\n    @Nullable\n    public static double[] copy(@Nullable double[] array) {\n        if (array == null) return null;\n        return subArray(array, 0, array.length);\n    }\n\n    @Nullable\n    public static float[] copy(@Nullable float[] array) {\n        if (array == null) return null;\n        return subArray(array, 0, array.length);\n    }\n\n    @Nullable\n    public static boolean[] copy(@Nullable boolean[] array) {\n        if (array == null) return null;\n        return subArray(array, 0, array.length);\n    }\n\n    @Nullable\n    private static Object realCopy(@Nullable Object array) {\n        if (array == null) return null;\n        return realSubArray(array, 0, getLength(array));\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // subArray\n    ///////////////////////////////////////////////////////////////////////////\n\n    @Nullable\n    public static <T> T[] subArray(@Nullable T[] array, int startIndexInclusive, int endIndexExclusive) {\n        //noinspection unchecked\n        return (T[]) realSubArray(array, startIndexInclusive, endIndexExclusive);\n    }\n\n    @Nullable\n    public static long[] subArray(@Nullable long[] array, int startIndexInclusive, int endIndexExclusive) {\n        return (long[]) realSubArray(array, startIndexInclusive, endIndexExclusive);\n    }\n\n    @Nullable\n    public static int[] subArray(@Nullable int[] array, int startIndexInclusive, int endIndexExclusive) {\n        return (int[]) realSubArray(array, startIndexInclusive, endIndexExclusive);\n    }\n\n    @Nullable\n    public static short[] subArray(@Nullable short[] array, int startIndexInclusive, int endIndexExclusive) {\n        return (short[]) realSubArray(array, startIndexInclusive, endIndexExclusive);\n    }\n\n    @Nullable\n    public static char[] subArray(@Nullable char[] array, int startIndexInclusive, int endIndexExclusive) {\n        return (char[]) realSubArray(array, startIndexInclusive, endIndexExclusive);\n    }\n\n    @Nullable\n    public static byte[] subArray(@Nullable byte[] array, int startIndexInclusive, int endIndexExclusive) {\n        return (byte[]) realSubArray(array, startIndexInclusive, endIndexExclusive);\n    }\n\n    @Nullable\n    public static double[] subArray(@Nullable double[] array, int startIndexInclusive, int endIndexExclusive) {\n        return (double[]) realSubArray(array, startIndexInclusive, endIndexExclusive);\n    }\n\n    @Nullable\n    public static float[] subArray(@Nullable float[] array, int startIndexInclusive, int endIndexExclusive) {\n        return (float[]) realSubArray(array, startIndexInclusive, endIndexExclusive);\n    }\n\n    @Nullable\n    public static boolean[] subArray(@Nullable boolean[] array, int startIndexInclusive, int endIndexExclusive) {\n        return (boolean[]) realSubArray(array, startIndexInclusive, endIndexExclusive);\n    }\n\n    @Nullable\n    private static Object realSubArray(@Nullable Object array, int startIndexInclusive, int endIndexExclusive) {\n        if (array == null) {\n            return null;\n        }\n        if (startIndexInclusive < 0) {\n            startIndexInclusive = 0;\n        }\n        int length = getLength(array);\n        if (endIndexExclusive > length) {\n            endIndexExclusive = length;\n        }\n        int newSize = endIndexExclusive - startIndexInclusive;\n        Class type = array.getClass().getComponentType();\n        if (newSize <= 0) {\n            return Array.newInstance(type, 0);\n        }\n        Object subArray = Array.newInstance(type, newSize);\n        System.arraycopy(array, startIndexInclusive, subArray, 0, newSize);\n        return subArray;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // add\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * <p>Copies the given array and adds the given element at the end of the new array.</p>\n     *\n     * <p>The new array contains the same elements of the input\n     * array plus the given element in the last position. The component type of\n     * the new array is the same as that of the input array.</p>\n     *\n     * <p>If the input array is <code>null</code>, a new one element array is returned\n     * whose component type is the same as the element.</p>\n     *\n     * <pre>\n     * ArrayUtils.realAdd(null, null)      = [null]\n     * ArrayUtils.realAdd(null, \"a\")       = [\"a\"]\n     * ArrayUtils.realAdd([\"a\"], null)     = [\"a\", null]\n     * ArrayUtils.realAdd([\"a\"], \"b\")      = [\"a\", \"b\"]\n     * ArrayUtils.realAdd([\"a\", \"b\"], \"c\") = [\"a\", \"b\", \"c\"]\n     * </pre>\n     *\n     * @param array   the array to \"realAdd\" the element to, may be <code>null</code>\n     * @param element the object to realAdd\n     * @return A new array containing the existing elements plus the new element\n     */\n    @NonNull\n    public static <T> T[] add(@Nullable T[] array, @Nullable T element) {\n        Class type = array != null ? array.getClass() : (element != null ? element.getClass() : Object.class);\n        return (T[]) realAddOne(array, element, type);\n    }\n\n    @NonNull\n    public static boolean[] add(@Nullable boolean[] array, boolean element) {\n        return (boolean[]) realAddOne(array, element, Boolean.TYPE);\n    }\n\n    @NonNull\n    public static byte[] add(@Nullable byte[] array, byte element) {\n        return (byte[]) realAddOne(array, element, Byte.TYPE);\n    }\n\n    @NonNull\n    public static char[] add(@Nullable char[] array, char element) {\n        return (char[]) realAddOne(array, element, Character.TYPE);\n    }\n\n    @NonNull\n    public static double[] add(@Nullable double[] array, double element) {\n        return (double[]) realAddOne(array, element, Double.TYPE);\n    }\n\n    @NonNull\n    public static float[] add(@Nullable float[] array, float element) {\n        return (float[]) realAddOne(array, element, Float.TYPE);\n    }\n\n    @NonNull\n    public static int[] add(@Nullable int[] array, int element) {\n        return (int[]) realAddOne(array, element, Integer.TYPE);\n    }\n\n    @NonNull\n    public static long[] add(@Nullable long[] array, long element) {\n        return (long[]) realAddOne(array, element, Long.TYPE);\n    }\n\n    @NonNull\n    public static short[] add(@Nullable short[] array, short element) {\n        return (short[]) realAddOne(array, element, Short.TYPE);\n    }\n\n    @NonNull\n    private static Object realAddOne(@Nullable Object array, @Nullable Object element, Class newArrayComponentType) {\n        Object newArray;\n        int arrayLength = 0;\n        if (array != null) {\n            arrayLength = getLength(array);\n            newArray = Array.newInstance(array.getClass().getComponentType(), arrayLength + 1);\n            System.arraycopy(array, 0, newArray, 0, arrayLength);\n        } else {\n            newArray = Array.newInstance(newArrayComponentType, 1);\n        }\n        Array.set(newArray, arrayLength, element);\n        return newArray;\n    }\n\n    /**\n     * <p>Adds all the elements of the given arrays into a new array.</p>\n     * <p>The new array contains all of the element of <code>array1</code> followed\n     * by all of the elements <code>array2</code>. When an array is returned, it is always\n     * a new array.</p>\n     *\n     * <pre>\n     * ArrayUtils.add(null, null)     = null\n     * ArrayUtils.add(array1, null)   = copy of array1\n     * ArrayUtils.add(null, array2)   = copy of array2\n     * ArrayUtils.add([], [])         = []\n     * ArrayUtils.add([null], [null]) = [null, null]\n     * ArrayUtils.add([\"a\", \"b\", \"c\"], [\"1\", \"2\", \"3\"]) = [\"a\", \"b\", \"c\", \"1\", \"2\", \"3\"]\n     * </pre>\n     *\n     * @param array1 the first array whose elements are added to the new array, may be <code>null</code>\n     * @param array2 the second array whose elements are added to the new array, may be <code>null</code>\n     * @return The new array, <code>null</code> if <code>null</code> array inputs.\n     * The type of the new array is the type of the first array.\n     */\n    @Nullable\n    public static <T> T[] add(@Nullable T[] array1, @Nullable T[] array2) {\n        return (T[]) realAddArr(array1, array2);\n    }\n\n    @Nullable\n    public static boolean[] add(@Nullable boolean[] array1, @Nullable boolean[] array2) {\n        return (boolean[]) realAddArr(array1, array2);\n    }\n\n    @Nullable\n    public static char[] add(@Nullable char[] array1, @Nullable char[] array2) {\n        return (char[]) realAddArr(array1, array2);\n    }\n\n    @Nullable\n    public static byte[] add(@Nullable byte[] array1, @Nullable byte[] array2) {\n        return (byte[]) realAddArr(array1, array2);\n    }\n\n    @Nullable\n    public static short[] add(@Nullable short[] array1, @Nullable short[] array2) {\n        return (short[]) realAddArr(array1, array2);\n    }\n\n    @Nullable\n    public static int[] add(@Nullable int[] array1, @Nullable int[] array2) {\n        return (int[]) realAddArr(array1, array2);\n    }\n\n    @Nullable\n    public static long[] add(@Nullable long[] array1, @Nullable long[] array2) {\n        return (long[]) realAddArr(array1, array2);\n    }\n\n    @Nullable\n    public static float[] add(@Nullable float[] array1, @Nullable float[] array2) {\n        return (float[]) realAddArr(array1, array2);\n    }\n\n    @Nullable\n    public static double[] add(@Nullable double[] array1, @Nullable double[] array2) {\n        return (double[]) realAddArr(array1, array2);\n    }\n\n    private static Object realAddArr(@Nullable Object array1, @Nullable Object array2) {\n        if (array1 == null && array2 == null) return null;\n        if (array1 == null) {\n            return realCopy(array2);\n        }\n        if (array2 == null) {\n            return realCopy(array1);\n        }\n        int len1 = getLength(array1);\n        int len2 = getLength(array2);\n        Object joinedArray = Array.newInstance(array1.getClass().getComponentType(), len1 + len2);\n        System.arraycopy(array1, 0, joinedArray, 0, len1);\n        System.arraycopy(array2, 0, joinedArray, len1, len2);\n        return joinedArray;\n    }\n\n    /**\n     * <p>Inserts the specified element at the specified position in the array.\n     * Shifts the element currently at that position (if any) and any subsequent\n     * elements to the right (adds one to their indices).</p>\n     *\n     * <p>This method returns a new array with the same elements of the input\n     * array plus the given element on the specified position. The component\n     * type of the returned array is always the same as that of the input\n     * array.</p>\n     *\n     * <p>If the input array is <code>null</code>, a new one element array is returned\n     * whose component type is the same as the element.</p>\n     *\n     * <pre>\n     * ArrayUtils.add(null, 0, null)        = null\n     * ArrayUtils.add(null, 0, [\"a\"])       = [\"a\"]\n     * ArrayUtils.add([\"a\"], 1, null)       = [\"a\"]\n     * ArrayUtils.add([\"a\"], 1, [\"b\"])      = [\"a\", \"b\"]\n     * ArrayUtils.add([\"a\", \"b\"], 2, [\"c\"]) = [\"a\", \"b\", \"c\"]\n     * </pre>\n     *\n     * @param array1 the array to realAdd the element to, may be <code>null</code>\n     * @param index  the position of the new object\n     * @param array2 the array to realAdd\n     * @return A new array containing the existing elements and the new element\n     * @throws IndexOutOfBoundsException if the index is out of range\n     *                                   (index < 0 || index > array.length).\n     */\n    @Nullable\n    public static <T> T[] add(@Nullable T[] array1, int index, @Nullable T[] array2) {\n        Class clss;\n        if (array1 != null) {\n            clss = array1.getClass().getComponentType();\n        } else if (array2 != null) {\n            clss = array2.getClass().getComponentType();\n        } else {\n            return null;\n        }\n        return (T[]) realAddArr(array1, index, array2, clss);\n    }\n\n    @Nullable\n    public static boolean[] add(@Nullable boolean[] array1, int index, @Nullable boolean[] array2) {\n        Object result = realAddArr(array1, index, array2, Boolean.TYPE);\n        if (result == null) return null;\n        return (boolean[]) result;\n    }\n\n    public static char[] add(@Nullable char[] array1, int index, @Nullable char[] array2) {\n        Object result = realAddArr(array1, index, array2, Character.TYPE);\n        if (result == null) return null;\n        return (char[]) result;\n    }\n\n    @Nullable\n    public static byte[] add(@Nullable byte[] array1, int index, @Nullable byte[] array2) {\n        Object result = realAddArr(array1, index, array2, Byte.TYPE);\n        if (result == null) return null;\n        return (byte[]) result;\n    }\n\n    @Nullable\n    public static short[] add(@Nullable short[] array1, int index, @Nullable short[] array2) {\n        Object result = realAddArr(array1, index, array2, Short.TYPE);\n        if (result == null) return null;\n        return (short[]) result;\n    }\n\n    @Nullable\n    public static int[] add(@Nullable int[] array1, int index, @Nullable int[] array2) {\n        Object result = realAddArr(array1, index, array2, Integer.TYPE);\n        if (result == null) return null;\n        return (int[]) result;\n    }\n\n    @Nullable\n    public static long[] add(@Nullable long[] array1, int index, @Nullable long[] array2) {\n        Object result = realAddArr(array1, index, array2, Long.TYPE);\n        if (result == null) return null;\n        return (long[]) result;\n    }\n\n    @Nullable\n    public static float[] add(@Nullable float[] array1, int index, @Nullable float[] array2) {\n        Object result = realAddArr(array1, index, array2, Float.TYPE);\n        if (result == null) return null;\n        return (float[]) result;\n    }\n\n    @Nullable\n    public static double[] add(@Nullable double[] array1, int index, @Nullable double[] array2) {\n        Object result = realAddArr(array1, index, array2, Double.TYPE);\n        if (result == null) return null;\n        return (double[]) result;\n    }\n\n    @Nullable\n    private static Object realAddArr(@Nullable Object array1, int index, @Nullable Object array2, Class clss) {\n        if (array1 == null && array2 == null) return null;\n        int len1 = getLength(array1);\n        int len2 = getLength(array2);\n        if (len1 == 0) {\n            if (index != 0) {\n                throw new IndexOutOfBoundsException(\"Index: \" + index + \", array1 Length: 0\");\n            }\n            return realCopy(array2);\n        }\n        if (len2 == 0) {\n            return realCopy(array1);\n        }\n        if (index > len1 || index < 0) {\n            throw new IndexOutOfBoundsException(\"Index: \" + index + \", array1 Length: \" + len1);\n        }\n        Object joinedArray = Array.newInstance(array1.getClass().getComponentType(), len1 + len2);\n        if (index == len1) {\n            System.arraycopy(array1, 0, joinedArray, 0, len1);\n            System.arraycopy(array2, 0, joinedArray, len1, len2);\n        } else if (index == 0) {\n            System.arraycopy(array2, 0, joinedArray, 0, len2);\n            System.arraycopy(array1, 0, joinedArray, len2, len1);\n        } else {\n            System.arraycopy(array1, 0, joinedArray, 0, index);\n            System.arraycopy(array2, 0, joinedArray, index, len2);\n            System.arraycopy(array1, index, joinedArray, index + len2, len1 - index);\n        }\n        return joinedArray;\n    }\n\n    /**\n     * <p>Inserts the specified element at the specified position in the array.\n     * Shifts the element currently at that position (if any) and any subsequent\n     * elements to the right (adds one to their indices).</p>\n     *\n     * <p>This method returns a new array with the same elements of the input\n     * array plus the given element on the specified position. The component\n     * type of the returned array is always the same as that of the input\n     * array.</p>\n     *\n     * <p>If the input array is <code>null</code>, a new one element array is returned\n     * whose component type is the same as the element.</p>\n     *\n     * <pre>\n     * ArrayUtils.add(null, 0, null)      = [null]\n     * ArrayUtils.add(null, 0, \"a\")       = [\"a\"]\n     * ArrayUtils.add([\"a\"], 1, null)     = [\"a\", null]\n     * ArrayUtils.add([\"a\"], 1, \"b\")      = [\"a\", \"b\"]\n     * ArrayUtils.add([\"a\", \"b\"], 3, \"c\") = [\"a\", \"b\", \"c\"]\n     * </pre>\n     *\n     * @param array   the array to realAdd the element to, may be <code>null</code>\n     * @param index   the position of the new object\n     * @param element the object to realAdd\n     * @return A new array containing the existing elements and the new element\n     * @throws IndexOutOfBoundsException if the index is out of range\n     *                                   (index < 0 || index > array.length).\n     */\n    @NonNull\n    public static <T> T[] add(@Nullable T[] array, int index, @Nullable T element) {\n        Class clss;\n        if (array != null) {\n            clss = array.getClass().getComponentType();\n        } else if (element != null) {\n            clss = element.getClass();\n        } else {\n            return (T[]) new Object[]{null};\n        }\n        return (T[]) realAdd(array, index, element, clss);\n    }\n\n    @NonNull\n    public static boolean[] add(@Nullable boolean[] array, int index, boolean element) {\n        return (boolean[]) realAdd(array, index, element, Boolean.TYPE);\n    }\n\n    @NonNull\n    public static char[] add(@Nullable char[] array, int index, char element) {\n        return (char[]) realAdd(array, index, element, Character.TYPE);\n    }\n\n    @NonNull\n    public static byte[] add(@Nullable byte[] array, int index, byte element) {\n        return (byte[]) realAdd(array, index, element, Byte.TYPE);\n    }\n\n    @NonNull\n    public static short[] add(@Nullable short[] array, int index, short element) {\n        return (short[]) realAdd(array, index, element, Short.TYPE);\n    }\n\n    @NonNull\n    public static int[] add(@Nullable int[] array, int index, int element) {\n        return (int[]) realAdd(array, index, element, Integer.TYPE);\n    }\n\n    @NonNull\n    public static long[] add(@Nullable long[] array, int index, long element) {\n        return (long[]) realAdd(array, index, element, Long.TYPE);\n    }\n\n    @NonNull\n    public static float[] add(@Nullable float[] array, int index, float element) {\n        return (float[]) realAdd(array, index, element, Float.TYPE);\n    }\n\n    @NonNull\n    public static double[] add(@Nullable double[] array, int index, double element) {\n        return (double[]) realAdd(array, index, element, Double.TYPE);\n    }\n\n    @NonNull\n    private static Object realAdd(@Nullable Object array, int index, @Nullable Object element, Class clss) {\n        if (array == null) {\n            if (index != 0) {\n                throw new IndexOutOfBoundsException(\"Index: \" + index + \", Length: 0\");\n            }\n            Object joinedArray = Array.newInstance(clss, 1);\n            Array.set(joinedArray, 0, element);\n            return joinedArray;\n        }\n        int length = Array.getLength(array);\n        if (index > length || index < 0) {\n            throw new IndexOutOfBoundsException(\"Index: \" + index + \", Length: \" + length);\n        }\n        Object result = Array.newInstance(clss, length + 1);\n        System.arraycopy(array, 0, result, 0, index);\n        Array.set(result, index, element);\n        if (index < length) {\n            System.arraycopy(array, index, result, index + 1, length - index);\n        }\n        return result;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // remove\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * <p>Removes the element at the specified position from the specified array.\n     * All subsequent elements are shifted to the left (substracts one from\n     * their indices).</p>\n     *\n     * <p>This method returns a new array with the same elements of the input\n     * array except the element on the specified position. The component\n     * type of the returned array is always the same as that of the input\n     * array.</p>\n     *\n     * <p>If the input array is <code>null</code>, an IndexOutOfBoundsException\n     * will be thrown, because in that case no valid index can be specified.</p>\n     *\n     * <pre>\n     * ArrayUtils.remove([\"a\"], 0)           = []\n     * ArrayUtils.remove([\"a\", \"b\"], 0)      = [\"b\"]\n     * ArrayUtils.remove([\"a\", \"b\"], 1)      = [\"a\"]\n     * ArrayUtils.remove([\"a\", \"b\", \"c\"], 1) = [\"a\", \"c\"]\n     * </pre>\n     *\n     * @param array the array to remove the element from, may be <code>null</code>\n     * @param index the position of the element to be removed\n     * @return A new array containing the existing elements except the element\n     * at the specified position.\n     * @throws IndexOutOfBoundsException if the index is out of range\n     *                                   (index < 0 || index >= array.length)\n     */\n    @Nullable\n    public static Object[] remove(@Nullable Object[] array, int index) {\n        if (array == null) return null;\n        return (Object[]) remove((Object) array, index);\n    }\n\n    /**\n     * <p>Removes the first occurrence of the specified element from the\n     * specified array. All subsequent elements are shifted to the left\n     * (substracts one from their indices). If the array doesn't contains\n     * such an element, no elements are removed from the array.</p>\n     *\n     * <p>This method returns a new array with the same elements of the input\n     * array except the first occurrence of the specified element. The component\n     * type of the returned array is always the same as that of the input\n     * array.</p>\n     *\n     * <pre>\n     * ArrayUtils.removeElement(null, \"a\")            = null\n     * ArrayUtils.removeElement([], \"a\")              = []\n     * ArrayUtils.removeElement([\"a\"], \"b\")           = [\"a\"]\n     * ArrayUtils.removeElement([\"a\", \"b\"], \"a\")      = [\"b\"]\n     * ArrayUtils.removeElement([\"a\", \"b\", \"a\"], \"a\") = [\"b\", \"a\"]\n     * </pre>\n     *\n     * @param array   the array to remove the element from, may be <code>null</code>\n     * @param element the element to be removed\n     * @return A new array containing the existing elements except the first\n     * occurrence of the specified element.\n     */\n    @Nullable\n    public static Object[] removeElement(@Nullable Object[] array, @Nullable Object element) {\n        int index = indexOf(array, element);\n        if (index == INDEX_NOT_FOUND) {\n            return copy(array);\n        }\n        return remove(array, index);\n    }\n\n    @Nullable\n    public static boolean[] remove(@Nullable boolean[] array, int index) {\n        if (array == null) return null;\n        return (boolean[]) remove((Object) array, index);\n    }\n\n    @Nullable\n    public static boolean[] removeElement(@Nullable boolean[] array, boolean element) {\n        int index = indexOf(array, element);\n        if (index == INDEX_NOT_FOUND) {\n            return copy(array);\n        }\n        return remove(array, index);\n    }\n\n    @Nullable\n    public static byte[] remove(@Nullable byte[] array, int index) {\n        if (array == null) return null;\n        return (byte[]) remove((Object) array, index);\n    }\n\n    @Nullable\n    public static byte[] removeElement(@Nullable byte[] array, byte element) {\n        int index = indexOf(array, element);\n        if (index == INDEX_NOT_FOUND) {\n            return copy(array);\n        }\n        return remove(array, index);\n    }\n\n    @Nullable\n    public static char[] remove(@Nullable char[] array, int index) {\n        if (array == null) return null;\n        return (char[]) remove((Object) array, index);\n    }\n\n    @Nullable\n    public static char[] removeElement(@Nullable char[] array, char element) {\n        int index = indexOf(array, element);\n        if (index == INDEX_NOT_FOUND) {\n            return copy(array);\n        }\n        return remove(array, index);\n    }\n\n    @Nullable\n    public static double[] remove(@Nullable double[] array, int index) {\n        if (array == null) return null;\n        return (double[]) remove((Object) array, index);\n    }\n\n    @Nullable\n    public static double[] removeElement(@Nullable double[] array, double element) {\n        int index = indexOf(array, element);\n        if (index == INDEX_NOT_FOUND) {\n            return copy(array);\n        }\n        //noinspection ConstantConditions\n        return remove(array, index);\n    }\n\n    @Nullable\n    public static float[] remove(@Nullable float[] array, int index) {\n        if (array == null) return null;\n        return (float[]) remove((Object) array, index);\n    }\n\n    @Nullable\n    public static float[] removeElement(@Nullable float[] array, float element) {\n        int index = indexOf(array, element);\n        if (index == INDEX_NOT_FOUND) {\n            return copy(array);\n        }\n        return remove(array, index);\n    }\n\n    @Nullable\n    public static int[] remove(@Nullable int[] array, int index) {\n        if (array == null) return null;\n        return (int[]) remove((Object) array, index);\n    }\n\n    @Nullable\n    public static int[] removeElement(@Nullable int[] array, int element) {\n        int index = indexOf(array, element);\n        if (index == INDEX_NOT_FOUND) {\n            return copy(array);\n        }\n        return remove(array, index);\n    }\n\n    @Nullable\n    public static long[] remove(@Nullable long[] array, int index) {\n        if (array == null) return null;\n        return (long[]) remove((Object) array, index);\n    }\n\n    @Nullable\n    public static long[] removeElement(@Nullable long[] array, long element) {\n        int index = indexOf(array, element);\n        if (index == INDEX_NOT_FOUND) {\n            return copy(array);\n        }\n        return remove(array, index);\n    }\n\n    @Nullable\n    public static short[] remove(@Nullable short[] array, int index) {\n        if (array == null) return null;\n        return (short[]) remove((Object) array, index);\n    }\n\n    @Nullable\n    public static short[] removeElement(@Nullable short[] array, short element) {\n        int index = indexOf(array, element);\n        if (index == INDEX_NOT_FOUND) {\n            return copy(array);\n        }\n        return remove(array, index);\n    }\n\n    @NonNull\n    private static Object remove(@NonNull Object array, int index) {\n        int length = getLength(array);\n        if (index < 0 || index >= length) {\n            throw new IndexOutOfBoundsException(\"Index: \" + index + \", Length: \" + length);\n        }\n\n        Object result = Array.newInstance(array.getClass().getComponentType(), length - 1);\n        System.arraycopy(array, 0, result, 0, index);\n        if (index < length - 1) {\n            System.arraycopy(array, index + 1, result, index, length - index - 1);\n        }\n\n        return result;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // object indexOf\n    ///////////////////////////////////////////////////////////////////////////\n\n    public static int indexOf(@Nullable Object[] array, @Nullable Object objectToFind) {\n        return indexOf(array, objectToFind, 0);\n    }\n\n    public static int indexOf(@Nullable Object[] array, @Nullable final Object objectToFind, int startIndex) {\n        if (array == null) {\n            return INDEX_NOT_FOUND;\n        }\n        if (startIndex < 0) {\n            startIndex = 0;\n        }\n        if (objectToFind == null) {\n            for (int i = startIndex; i < array.length; i++) {\n                if (array[i] == null) {\n                    return i;\n                }\n            }\n        } else {\n            for (int i = startIndex; i < array.length; i++) {\n                if (objectToFind.equals(array[i])) {\n                    return i;\n                }\n            }\n        }\n        return INDEX_NOT_FOUND;\n    }\n\n    public static int lastIndexOf(@Nullable Object[] array, @Nullable Object objectToFind) {\n        return lastIndexOf(array, objectToFind, Integer.MAX_VALUE);\n    }\n\n    public static int lastIndexOf(@Nullable Object[] array, @Nullable Object objectToFind, int startIndex) {\n        if (array == null) {\n            return INDEX_NOT_FOUND;\n        }\n        if (startIndex < 0) {\n            return INDEX_NOT_FOUND;\n        } else if (startIndex >= array.length) {\n            startIndex = array.length - 1;\n        }\n        if (objectToFind == null) {\n            for (int i = startIndex; i >= 0; i--) {\n                if (array[i] == null) {\n                    return i;\n                }\n            }\n        } else {\n            for (int i = startIndex; i >= 0; i--) {\n                if (objectToFind.equals(array[i])) {\n                    return i;\n                }\n            }\n        }\n        return INDEX_NOT_FOUND;\n    }\n\n    public static boolean contains(@Nullable Object[] array, @Nullable Object objectToFind) {\n        return indexOf(array, objectToFind) != INDEX_NOT_FOUND;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // long indexOf\n    ///////////////////////////////////////////////////////////////////////////\n\n    public static int indexOf(@Nullable long[] array, long valueToFind) {\n        return indexOf(array, valueToFind, 0);\n    }\n\n    public static int indexOf(@Nullable long[] array, long valueToFind, int startIndex) {\n        if (array == null) {\n            return INDEX_NOT_FOUND;\n        }\n        if (startIndex < 0) {\n            startIndex = 0;\n        }\n        for (int i = startIndex; i < array.length; i++) {\n            if (valueToFind == array[i]) {\n                return i;\n            }\n        }\n        return INDEX_NOT_FOUND;\n    }\n\n    public static int lastIndexOf(@Nullable long[] array, long valueToFind) {\n        return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);\n    }\n\n    public static int lastIndexOf(@Nullable long[] array, long valueToFind, int startIndex) {\n        if (array == null) {\n            return INDEX_NOT_FOUND;\n        }\n        if (startIndex < 0) {\n            return INDEX_NOT_FOUND;\n        } else if (startIndex >= array.length) {\n            startIndex = array.length - 1;\n        }\n        for (int i = startIndex; i >= 0; i--) {\n            if (valueToFind == array[i]) {\n                return i;\n            }\n        }\n        return INDEX_NOT_FOUND;\n    }\n\n    public static boolean contains(@Nullable long[] array, long valueToFind) {\n        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // int indexOf\n    ///////////////////////////////////////////////////////////////////////////\n\n    public static int indexOf(@Nullable int[] array, int valueToFind) {\n        return indexOf(array, valueToFind, 0);\n    }\n\n    public static int indexOf(@Nullable int[] array, int valueToFind, int startIndex) {\n        if (array == null) {\n            return INDEX_NOT_FOUND;\n        }\n        if (startIndex < 0) {\n            startIndex = 0;\n        }\n        for (int i = startIndex; i < array.length; i++) {\n            if (valueToFind == array[i]) {\n                return i;\n            }\n        }\n        return INDEX_NOT_FOUND;\n    }\n\n    public static int lastIndexOf(@Nullable int[] array, int valueToFind) {\n        return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);\n    }\n\n    public static int lastIndexOf(@Nullable int[] array, int valueToFind, int startIndex) {\n        if (array == null) {\n            return INDEX_NOT_FOUND;\n        }\n        if (startIndex < 0) {\n            return INDEX_NOT_FOUND;\n        } else if (startIndex >= array.length) {\n            startIndex = array.length - 1;\n        }\n        for (int i = startIndex; i >= 0; i--) {\n            if (valueToFind == array[i]) {\n                return i;\n            }\n        }\n        return INDEX_NOT_FOUND;\n    }\n\n    public static boolean contains(@Nullable int[] array, int valueToFind) {\n        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // short indexOf\n    ///////////////////////////////////////////////////////////////////////////\n\n    public static int indexOf(@Nullable short[] array, short valueToFind) {\n        return indexOf(array, valueToFind, 0);\n    }\n\n    public static int indexOf(@Nullable short[] array, short valueToFind, int startIndex) {\n        if (array == null) {\n            return INDEX_NOT_FOUND;\n        }\n        if (startIndex < 0) {\n            startIndex = 0;\n        }\n        for (int i = startIndex; i < array.length; i++) {\n            if (valueToFind == array[i]) {\n                return i;\n            }\n        }\n        return INDEX_NOT_FOUND;\n    }\n\n    public static int lastIndexOf(@Nullable short[] array, short valueToFind) {\n        return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);\n    }\n\n    public static int lastIndexOf(@Nullable short[] array, short valueToFind, int startIndex) {\n        if (array == null) {\n            return INDEX_NOT_FOUND;\n        }\n        if (startIndex < 0) {\n            return INDEX_NOT_FOUND;\n        } else if (startIndex >= array.length) {\n            startIndex = array.length - 1;\n        }\n        for (int i = startIndex; i >= 0; i--) {\n            if (valueToFind == array[i]) {\n                return i;\n            }\n        }\n        return INDEX_NOT_FOUND;\n    }\n\n    public static boolean contains(@Nullable short[] array, short valueToFind) {\n        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // char indexOf\n    ///////////////////////////////////////////////////////////////////////////\n\n    public static int indexOf(@Nullable char[] array, char valueToFind) {\n        return indexOf(array, valueToFind, 0);\n    }\n\n    public static int indexOf(@Nullable char[] array, char valueToFind, int startIndex) {\n        if (array == null) {\n            return INDEX_NOT_FOUND;\n        }\n        if (startIndex < 0) {\n            startIndex = 0;\n        }\n        for (int i = startIndex; i < array.length; i++) {\n            if (valueToFind == array[i]) {\n                return i;\n            }\n        }\n        return INDEX_NOT_FOUND;\n    }\n\n    public static int lastIndexOf(@Nullable char[] array, char valueToFind) {\n        return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);\n    }\n\n    public static int lastIndexOf(@Nullable char[] array, char valueToFind, int startIndex) {\n        if (array == null) {\n            return INDEX_NOT_FOUND;\n        }\n        if (startIndex < 0) {\n            return INDEX_NOT_FOUND;\n        } else if (startIndex >= array.length) {\n            startIndex = array.length - 1;\n        }\n        for (int i = startIndex; i >= 0; i--) {\n            if (valueToFind == array[i]) {\n                return i;\n            }\n        }\n        return INDEX_NOT_FOUND;\n    }\n\n    public static boolean contains(@Nullable char[] array, char valueToFind) {\n        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // byte indexOf\n    ///////////////////////////////////////////////////////////////////////////\n\n    public static int indexOf(@Nullable byte[] array, byte valueToFind) {\n        return indexOf(array, valueToFind, 0);\n    }\n\n    public static int indexOf(@Nullable byte[] array, byte valueToFind, int startIndex) {\n        if (array == null) {\n            return INDEX_NOT_FOUND;\n        }\n        if (startIndex < 0) {\n            startIndex = 0;\n        }\n        for (int i = startIndex; i < array.length; i++) {\n            if (valueToFind == array[i]) {\n                return i;\n            }\n        }\n        return INDEX_NOT_FOUND;\n    }\n\n    public static int lastIndexOf(@Nullable byte[] array, byte valueToFind) {\n        return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);\n    }\n\n    public static int lastIndexOf(@Nullable byte[] array, byte valueToFind, int startIndex) {\n        if (array == null) {\n            return INDEX_NOT_FOUND;\n        }\n        if (startIndex < 0) {\n            return INDEX_NOT_FOUND;\n        } else if (startIndex >= array.length) {\n            startIndex = array.length - 1;\n        }\n        for (int i = startIndex; i >= 0; i--) {\n            if (valueToFind == array[i]) {\n                return i;\n            }\n        }\n        return INDEX_NOT_FOUND;\n    }\n\n    public static boolean contains(@Nullable byte[] array, byte valueToFind) {\n        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // double indexOf\n    ///////////////////////////////////////////////////////////////////////////\n\n    public static int indexOf(@Nullable double[] array, double valueToFind) {\n        return indexOf(array, valueToFind, 0);\n    }\n\n    public static int indexOf(@Nullable double[] array, double valueToFind, double tolerance) {\n        return indexOf(array, valueToFind, 0, tolerance);\n    }\n\n    public static int indexOf(@Nullable double[] array, double valueToFind, int startIndex) {\n        if (ArrayUtils.isEmpty(array)) {\n            return INDEX_NOT_FOUND;\n        }\n        if (startIndex < 0) {\n            startIndex = 0;\n        }\n        for (int i = startIndex; i < array.length; i++) {\n            if (valueToFind == array[i]) {\n                return i;\n            }\n        }\n        return INDEX_NOT_FOUND;\n    }\n\n    public static int indexOf(@Nullable double[] array, double valueToFind, int startIndex, double tolerance) {\n        if (ArrayUtils.isEmpty(array)) {\n            return INDEX_NOT_FOUND;\n        }\n        if (startIndex < 0) {\n            startIndex = 0;\n        }\n        double min = valueToFind - tolerance;\n        double max = valueToFind + tolerance;\n        for (int i = startIndex; i < array.length; i++) {\n            if (array[i] >= min && array[i] <= max) {\n                return i;\n            }\n        }\n        return INDEX_NOT_FOUND;\n    }\n\n    public static int lastIndexOf(@Nullable double[] array, double valueToFind) {\n        return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);\n    }\n\n    public static int lastIndexOf(@Nullable double[] array, double valueToFind, double tolerance) {\n        return lastIndexOf(array, valueToFind, Integer.MAX_VALUE, tolerance);\n    }\n\n    public static int lastIndexOf(@Nullable double[] array, double valueToFind, int startIndex) {\n        if (ArrayUtils.isEmpty(array)) {\n            return INDEX_NOT_FOUND;\n        }\n        if (startIndex < 0) {\n            return INDEX_NOT_FOUND;\n        } else if (startIndex >= array.length) {\n            startIndex = array.length - 1;\n        }\n        for (int i = startIndex; i >= 0; i--) {\n            if (valueToFind == array[i]) {\n                return i;\n            }\n        }\n        return INDEX_NOT_FOUND;\n    }\n\n    public static int lastIndexOf(@Nullable double[] array, double valueToFind, int startIndex, double tolerance) {\n        if (ArrayUtils.isEmpty(array)) {\n            return INDEX_NOT_FOUND;\n        }\n        if (startIndex < 0) {\n            return INDEX_NOT_FOUND;\n        } else if (startIndex >= array.length) {\n            startIndex = array.length - 1;\n        }\n        double min = valueToFind - tolerance;\n        double max = valueToFind + tolerance;\n        for (int i = startIndex; i >= 0; i--) {\n            if (array[i] >= min && array[i] <= max) {\n                return i;\n            }\n        }\n        return INDEX_NOT_FOUND;\n    }\n\n    public static boolean contains(@Nullable double[] array, double valueToFind) {\n        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;\n    }\n\n    public static boolean contains(@Nullable double[] array, double valueToFind, double tolerance) {\n        return indexOf(array, valueToFind, 0, tolerance) != INDEX_NOT_FOUND;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // float indexOf\n    ///////////////////////////////////////////////////////////////////////////\n\n    public static int indexOf(@Nullable float[] array, float valueToFind) {\n        return indexOf(array, valueToFind, 0);\n    }\n\n    public static int indexOf(@Nullable float[] array, float valueToFind, int startIndex) {\n        if (ArrayUtils.isEmpty(array)) {\n            return INDEX_NOT_FOUND;\n        }\n        if (startIndex < 0) {\n            startIndex = 0;\n        }\n        for (int i = startIndex; i < array.length; i++) {\n            if (valueToFind == array[i]) {\n                return i;\n            }\n        }\n        return INDEX_NOT_FOUND;\n    }\n\n    public static int lastIndexOf(@Nullable float[] array, float valueToFind) {\n        return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);\n    }\n\n    public static int lastIndexOf(@Nullable float[] array, float valueToFind, int startIndex) {\n        if (ArrayUtils.isEmpty(array)) {\n            return INDEX_NOT_FOUND;\n        }\n        if (startIndex < 0) {\n            return INDEX_NOT_FOUND;\n        } else if (startIndex >= array.length) {\n            startIndex = array.length - 1;\n        }\n        for (int i = startIndex; i >= 0; i--) {\n            if (valueToFind == array[i]) {\n                return i;\n            }\n        }\n        return INDEX_NOT_FOUND;\n    }\n\n    public static boolean contains(@Nullable float[] array, float valueToFind) {\n        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // bool indexOf\n    ///////////////////////////////////////////////////////////////////////////\n\n    public static int indexOf(@Nullable boolean[] array, boolean valueToFind) {\n        return indexOf(array, valueToFind, 0);\n    }\n\n    public static int indexOf(@Nullable boolean[] array, boolean valueToFind, int startIndex) {\n        if (ArrayUtils.isEmpty(array)) {\n            return INDEX_NOT_FOUND;\n        }\n        if (startIndex < 0) {\n            startIndex = 0;\n        }\n        for (int i = startIndex; i < array.length; i++) {\n            if (valueToFind == array[i]) {\n                return i;\n            }\n        }\n        return INDEX_NOT_FOUND;\n    }\n\n    public static int lastIndexOf(@Nullable boolean[] array, boolean valueToFind) {\n        return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);\n    }\n\n    public static int lastIndexOf(@Nullable boolean[] array, boolean valueToFind, int startIndex) {\n        if (ArrayUtils.isEmpty(array)) {\n            return INDEX_NOT_FOUND;\n        }\n        if (startIndex < 0) {\n            return INDEX_NOT_FOUND;\n        } else if (startIndex >= array.length) {\n            startIndex = array.length - 1;\n        }\n        for (int i = startIndex; i >= 0; i--) {\n            if (valueToFind == array[i]) {\n                return i;\n            }\n        }\n        return INDEX_NOT_FOUND;\n    }\n\n    public static boolean contains(@Nullable boolean[] array, boolean valueToFind) {\n        return indexOf(array, valueToFind) != INDEX_NOT_FOUND;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // char converters\n    ///////////////////////////////////////////////////////////////////////////\n\n    @Nullable\n    public static char[] toPrimitive(@Nullable Character[] array) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new char[0];\n        }\n        final char[] result = new char[array.length];\n        for (int i = 0; i < array.length; i++) {\n            result[i] = array[i].charValue();\n        }\n        return result;\n    }\n\n    @Nullable\n    public static char[] toPrimitive(@Nullable Character[] array, char valueForNull) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new char[0];\n        }\n        final char[] result = new char[array.length];\n        for (int i = 0; i < array.length; i++) {\n            Character b = array[i];\n            result[i] = (b == null ? valueForNull : b.charValue());\n        }\n        return result;\n    }\n\n    @Nullable\n    public static Character[] toObject(@Nullable char[] array) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new Character[0];\n        }\n        final Character[] result = new Character[array.length];\n        for (int i = 0; i < array.length; i++) {\n            result[i] = new Character(array[i]);\n        }\n        return result;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // long converters\n    ///////////////////////////////////////////////////////////////////////////\n\n    @Nullable\n    public static long[] toPrimitive(@Nullable Long[] array) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new long[0];\n        }\n        final long[] result = new long[array.length];\n        for (int i = 0; i < array.length; i++) {\n            result[i] = array[i].longValue();\n        }\n        return result;\n    }\n\n    @Nullable\n    public static long[] toPrimitive(@Nullable Long[] array, long valueForNull) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new long[0];\n        }\n        final long[] result = new long[array.length];\n        for (int i = 0; i < array.length; i++) {\n            Long b = array[i];\n            result[i] = (b == null ? valueForNull : b.longValue());\n        }\n        return result;\n    }\n\n    @Nullable\n    public static Long[] toObject(@Nullable long[] array) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new Long[0];\n        }\n        final Long[] result = new Long[array.length];\n        for (int i = 0; i < array.length; i++) {\n            result[i] = new Long(array[i]);\n        }\n        return result;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // int converters\n    ///////////////////////////////////////////////////////////////////////////\n\n    @Nullable\n    public static int[] toPrimitive(@Nullable Integer[] array) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new int[0];\n        }\n        final int[] result = new int[array.length];\n        for (int i = 0; i < array.length; i++) {\n            result[i] = array[i].intValue();\n        }\n        return result;\n    }\n\n    @Nullable\n    public static int[] toPrimitive(@Nullable Integer[] array, int valueForNull) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new int[0];\n        }\n        final int[] result = new int[array.length];\n        for (int i = 0; i < array.length; i++) {\n            Integer b = array[i];\n            result[i] = (b == null ? valueForNull : b.intValue());\n        }\n        return result;\n    }\n\n    @Nullable\n    public static Integer[] toObject(@Nullable int[] array) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new Integer[0];\n        }\n        final Integer[] result = new Integer[array.length];\n        for (int i = 0; i < array.length; i++) {\n            result[i] = new Integer(array[i]);\n        }\n        return result;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // short converters\n    ///////////////////////////////////////////////////////////////////////////\n\n    @Nullable\n    public static short[] toPrimitive(@Nullable Short[] array) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new short[0];\n        }\n        final short[] result = new short[array.length];\n        for (int i = 0; i < array.length; i++) {\n            result[i] = array[i].shortValue();\n        }\n        return result;\n    }\n\n    @Nullable\n    public static short[] toPrimitive(@Nullable Short[] array, short valueForNull) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new short[0];\n        }\n        final short[] result = new short[array.length];\n        for (int i = 0; i < array.length; i++) {\n            Short b = array[i];\n            result[i] = (b == null ? valueForNull : b.shortValue());\n        }\n        return result;\n    }\n\n    @Nullable\n    public static Short[] toObject(@Nullable short[] array) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new Short[0];\n        }\n        final Short[] result = new Short[array.length];\n        for (int i = 0; i < array.length; i++) {\n            result[i] = new Short(array[i]);\n        }\n        return result;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // byte converters\n    ///////////////////////////////////////////////////////////////////////////\n\n    @Nullable\n    public static byte[] toPrimitive(@Nullable Byte[] array) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new byte[0];\n        }\n        final byte[] result = new byte[array.length];\n        for (int i = 0; i < array.length; i++) {\n            result[i] = array[i].byteValue();\n        }\n        return result;\n    }\n\n    @Nullable\n    public static byte[] toPrimitive(@Nullable Byte[] array, byte valueForNull) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new byte[0];\n        }\n        final byte[] result = new byte[array.length];\n        for (int i = 0; i < array.length; i++) {\n            Byte b = array[i];\n            result[i] = (b == null ? valueForNull : b.byteValue());\n        }\n        return result;\n    }\n\n    @Nullable\n    public static Byte[] toObject(@Nullable byte[] array) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new Byte[0];\n        }\n        final Byte[] result = new Byte[array.length];\n        for (int i = 0; i < array.length; i++) {\n            result[i] = new Byte(array[i]);\n        }\n        return result;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // double converters\n    ///////////////////////////////////////////////////////////////////////////\n\n    @Nullable\n    public static double[] toPrimitive(@Nullable Double[] array) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new double[0];\n        }\n        final double[] result = new double[array.length];\n        for (int i = 0; i < array.length; i++) {\n            result[i] = array[i].doubleValue();\n        }\n        return result;\n    }\n\n    @Nullable\n    public static double[] toPrimitive(@Nullable Double[] array, double valueForNull) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new double[0];\n        }\n        final double[] result = new double[array.length];\n        for (int i = 0; i < array.length; i++) {\n            Double b = array[i];\n            result[i] = (b == null ? valueForNull : b.doubleValue());\n        }\n        return result;\n    }\n\n    @Nullable\n    public static Double[] toObject(@Nullable double[] array) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new Double[0];\n        }\n        final Double[] result = new Double[array.length];\n        for (int i = 0; i < array.length; i++) {\n            result[i] = new Double(array[i]);\n        }\n        return result;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // float converters\n    ///////////////////////////////////////////////////////////////////////////\n\n    @Nullable\n    public static float[] toPrimitive(@Nullable Float[] array) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new float[0];\n        }\n        final float[] result = new float[array.length];\n        for (int i = 0; i < array.length; i++) {\n            result[i] = array[i].floatValue();\n        }\n        return result;\n    }\n\n    @Nullable\n    public static float[] toPrimitive(@Nullable Float[] array, float valueForNull) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new float[0];\n        }\n        final float[] result = new float[array.length];\n        for (int i = 0; i < array.length; i++) {\n            Float b = array[i];\n            result[i] = (b == null ? valueForNull : b.floatValue());\n        }\n        return result;\n    }\n\n    @Nullable\n    public static Float[] toObject(@Nullable float[] array) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new Float[0];\n        }\n        final Float[] result = new Float[array.length];\n        for (int i = 0; i < array.length; i++) {\n            result[i] = new Float(array[i]);\n        }\n        return result;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // boolean converters\n    ///////////////////////////////////////////////////////////////////////////\n\n    @Nullable\n    public static boolean[] toPrimitive(@Nullable Boolean[] array) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new boolean[0];\n        }\n        final boolean[] result = new boolean[array.length];\n        for (int i = 0; i < array.length; i++) {\n            result[i] = array[i].booleanValue();\n        }\n        return result;\n    }\n\n    @Nullable\n    public static boolean[] toPrimitive(@Nullable Boolean[] array, boolean valueForNull) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new boolean[0];\n        }\n        final boolean[] result = new boolean[array.length];\n        for (int i = 0; i < array.length; i++) {\n            Boolean b = array[i];\n            result[i] = (b == null ? valueForNull : b.booleanValue());\n        }\n        return result;\n    }\n\n    @Nullable\n    public static Boolean[] toObject(@Nullable boolean[] array) {\n        if (array == null) {\n            return null;\n        } else if (array.length == 0) {\n            return new Boolean[0];\n        }\n        final Boolean[] result = new Boolean[array.length];\n        for (int i = 0; i < array.length; i++) {\n            result[i] = (array[i] ? Boolean.TRUE : Boolean.FALSE);\n        }\n        return result;\n    }\n\n    @NonNull\n    public static <T> List<T> asList(@Nullable T... array) {\n        if (array == null || array.length == 0) {\n            return Collections.emptyList();\n        }\n        return Arrays.asList(array);\n    }\n\n    @NonNull\n    public static <T> List<T> asUnmodifiableList(@Nullable T... array) {\n        return Collections.unmodifiableList(asList(array));\n    }\n\n    @NonNull\n    public static <T> List<T> asArrayList(@Nullable T... array) {\n        List<T> list = new ArrayList<>();\n        if (array == null || array.length == 0) return list;\n        list.addAll(Arrays.asList(array));\n        return list;\n    }\n\n    @NonNull\n    public static <T> List<T> asLinkedList(@Nullable T... array) {\n        List<T> list = new LinkedList<>();\n        if (array == null || array.length == 0) return list;\n        list.addAll(Arrays.asList(array));\n        return list;\n    }\n\n    public static <T> void sort(@Nullable T[] array, Comparator<? super T> c) {\n        if (array == null || array.length < 2) return;\n        Arrays.sort(array, c);\n    }\n\n    public static void sort(@Nullable byte[] array) {\n        if (array == null || array.length < 2) return;\n        Arrays.sort(array);\n    }\n\n    public static void sort(@Nullable char[] array) {\n        if (array == null || array.length < 2) return;\n        Arrays.sort(array);\n    }\n\n    public static void sort(@Nullable double[] array) {\n        if (array == null || array.length < 2) return;\n        Arrays.sort(array);\n    }\n\n    public static void sort(@Nullable float[] array) {\n        if (array == null || array.length < 2) return;\n        Arrays.sort(array);\n    }\n\n    public static void sort(@Nullable int[] array) {\n        if (array == null || array.length < 2) return;\n        Arrays.sort(array);\n    }\n\n    public static void sort(@Nullable long[] array) {\n        if (array == null || array.length < 2) return;\n        Arrays.sort(array);\n    }\n\n    public static void sort(@Nullable short[] array) {\n        if (array == null || array.length < 2) return;\n        Arrays.sort(array);\n    }\n\n    /**\n     * Executes the given closure on each element in the array.\n     * <p>\n     * If the input array or closure is null, there is no change made.\n     *\n     * @param array   The array.\n     * @param closure the closure to perform, may be null\n     */\n    public static <E> void forAllDo(@Nullable Object array, @Nullable Closure<E> closure) {\n        if (array == null || closure == null) return;\n        if (array instanceof Object[]) {\n            Object[] objects = (Object[]) array;\n            for (int i = 0, length = objects.length; i < length; i++) {\n                Object ele = objects[i];\n                closure.execute(i, (E) ele);\n            }\n        } else if (array instanceof boolean[]) {\n            boolean[] booleans = (boolean[]) array;\n            for (int i = 0, length = booleans.length; i < length; i++) {\n                boolean ele = booleans[i];\n                closure.execute(i, (E) (ele ? Boolean.TRUE : Boolean.FALSE));\n            }\n        } else if (array instanceof byte[]) {\n            byte[] bytes = (byte[]) array;\n            for (int i = 0, length = bytes.length; i < length; i++) {\n                byte ele = bytes[i];\n                closure.execute(i, (E) Byte.valueOf(ele));\n            }\n        } else if (array instanceof char[]) {\n            char[] chars = (char[]) array;\n            for (int i = 0, length = chars.length; i < length; i++) {\n                char ele = chars[i];\n                closure.execute(i, (E) Character.valueOf(ele));\n            }\n        } else if (array instanceof short[]) {\n            short[] shorts = (short[]) array;\n            for (int i = 0, length = shorts.length; i < length; i++) {\n                short ele = shorts[i];\n                closure.execute(i, (E) Short.valueOf(ele));\n            }\n        } else if (array instanceof int[]) {\n            int[] ints = (int[]) array;\n            for (int i = 0, length = ints.length; i < length; i++) {\n                int ele = ints[i];\n                closure.execute(i, (E) Integer.valueOf(ele));\n            }\n        } else if (array instanceof long[]) {\n            long[] longs = (long[]) array;\n            for (int i = 0, length = longs.length; i < length; i++) {\n                long ele = longs[i];\n                closure.execute(i, (E) Long.valueOf(ele));\n            }\n        } else if (array instanceof float[]) {\n            float[] floats = (float[]) array;\n            for (int i = 0, length = floats.length; i < length; i++) {\n                float ele = floats[i];\n                closure.execute(i, (E) Float.valueOf(ele));\n            }\n        } else if (array instanceof double[]) {\n            double[] doubles = (double[]) array;\n            for (int i = 0, length = doubles.length; i < length; i++) {\n                double ele = doubles[i];\n                closure.execute(i, (E) Double.valueOf(ele));\n            }\n        } else {\n            throw new IllegalArgumentException(\"Not an array: \" + array.getClass());\n        }\n    }\n\n    /**\n     * Return the string of array.\n     *\n     * @param array The array.\n     * @return the string of array\n     */\n    @NonNull\n    public static String toString(@Nullable Object array) {\n        if (array == null) return \"null\";\n        if (array instanceof Object[]) {\n            return Arrays.deepToString((Object[]) array);\n        } else if (array instanceof boolean[]) {\n            return Arrays.toString((boolean[]) array);\n        } else if (array instanceof byte[]) {\n            return Arrays.toString((byte[]) array);\n        } else if (array instanceof char[]) {\n            return Arrays.toString((char[]) array);\n        } else if (array instanceof double[]) {\n            return Arrays.toString((double[]) array);\n        } else if (array instanceof float[]) {\n            return Arrays.toString((float[]) array);\n        } else if (array instanceof int[]) {\n            return Arrays.toString((int[]) array);\n        } else if (array instanceof long[]) {\n            return Arrays.toString((long[]) array);\n        } else if (array instanceof short[]) {\n            return Arrays.toString((short[]) array);\n        }\n        throw new IllegalArgumentException(\"Array has incompatible type: \" + array.getClass());\n    }\n\n    public interface Closure<E> {\n        void execute(int index, E item);\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/BarUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport static android.Manifest.permission.EXPAND_STATUS_BAR;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.res.Resources;\nimport android.graphics.Color;\nimport android.graphics.Point;\nimport android.os.Build;\nimport android.provider.Settings;\nimport android.util.TypedValue;\nimport android.view.Display;\nimport android.view.KeyCharacterMap;\nimport android.view.KeyEvent;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.view.ViewGroup;\nimport android.view.ViewGroup.MarginLayoutParams;\nimport android.view.Window;\nimport android.view.WindowManager;\n\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.RequiresApi;\nimport androidx.annotation.RequiresPermission;\nimport androidx.drawerlayout.widget.DrawerLayout;\n\nimport java.lang.reflect.Method;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/09/23\n *     desc  : utils about bar\n * </pre>\n */\npublic final class BarUtils {\n\n    ///////////////////////////////////////////////////////////////////////////\n    // status bar\n    ///////////////////////////////////////////////////////////////////////////\n\n    private static final String TAG_STATUS_BAR = \"TAG_STATUS_BAR\";\n    private static final String TAG_OFFSET = \"TAG_OFFSET\";\n    private static final int KEY_OFFSET = -123;\n\n    private BarUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Return the status bar's height.\n     *\n     * @return the status bar's height\n     */\n    public static int getStatusBarHeight() {\n        Resources resources = Resources.getSystem();\n        int resourceId = resources.getIdentifier(\"status_bar_height\", \"dimen\", \"android\");\n        return resources.getDimensionPixelSize(resourceId);\n    }\n\n    /**\n     * Set the status bar's visibility.\n     *\n     * @param activity  The activity.\n     * @param isVisible True to set status bar visible, false otherwise.\n     */\n    public static void setStatusBarVisibility(@NonNull final Activity activity,\n                                              final boolean isVisible) {\n        setStatusBarVisibility(activity.getWindow(), isVisible);\n    }\n\n    /**\n     * Set the status bar's visibility.\n     *\n     * @param window    The window.\n     * @param isVisible True to set status bar visible, false otherwise.\n     */\n    public static void setStatusBarVisibility(@NonNull final Window window,\n                                              final boolean isVisible) {\n        if (isVisible) {\n            window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);\n            showStatusBarView(window);\n            addMarginTopEqualStatusBarHeight(window);\n        } else {\n            window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);\n            hideStatusBarView(window);\n            subtractMarginTopEqualStatusBarHeight(window);\n        }\n    }\n\n    /**\n     * Return whether the status bar is visible.\n     *\n     * @param activity The activity.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isStatusBarVisible(@NonNull final Activity activity) {\n        int flags = activity.getWindow().getAttributes().flags;\n        return (flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) == 0;\n    }\n\n    /**\n     * Set the status bar's light mode.\n     *\n     * @param activity    The activity.\n     * @param isLightMode True to set status bar light mode, false otherwise.\n     */\n    public static void setStatusBarLightMode(@NonNull final Activity activity,\n                                             final boolean isLightMode) {\n        setStatusBarLightMode(activity.getWindow(), isLightMode);\n    }\n\n    /**\n     * Set the status bar's light mode.\n     *\n     * @param window      The window.\n     * @param isLightMode True to set status bar light mode, false otherwise.\n     */\n    public static void setStatusBarLightMode(@NonNull final Window window,\n                                             final boolean isLightMode) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            View decorView = window.getDecorView();\n            int vis = decorView.getSystemUiVisibility();\n            if (isLightMode) {\n                vis |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;\n            } else {\n                vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;\n            }\n            decorView.setSystemUiVisibility(vis);\n        }\n    }\n\n    /**\n     * Is the status bar light mode.\n     *\n     * @param activity The activity.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isStatusBarLightMode(@NonNull final Activity activity) {\n        return isStatusBarLightMode(activity.getWindow());\n    }\n\n    /**\n     * Is the status bar light mode.\n     *\n     * @param window The window.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isStatusBarLightMode(@NonNull final Window window) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            View decorView = window.getDecorView();\n            int vis = decorView.getSystemUiVisibility();\n            return (vis & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0;\n        }\n        return false;\n    }\n\n    /**\n     * Add the top margin size equals status bar's height for view.\n     *\n     * @param view The view.\n     */\n    public static void addMarginTopEqualStatusBarHeight(@NonNull View view) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;\n        view.setTag(TAG_OFFSET);\n        Object haveSetOffset = view.getTag(KEY_OFFSET);\n        if (haveSetOffset != null && (Boolean) haveSetOffset) return;\n        MarginLayoutParams layoutParams = (MarginLayoutParams) view.getLayoutParams();\n        layoutParams.setMargins(layoutParams.leftMargin,\n                layoutParams.topMargin + getStatusBarHeight(),\n                layoutParams.rightMargin,\n                layoutParams.bottomMargin);\n        view.setTag(KEY_OFFSET, true);\n    }\n\n    /**\n     * Subtract the top margin size equals status bar's height for view.\n     *\n     * @param view The view.\n     */\n    public static void subtractMarginTopEqualStatusBarHeight(@NonNull View view) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;\n        Object haveSetOffset = view.getTag(KEY_OFFSET);\n        if (haveSetOffset == null || !(Boolean) haveSetOffset) return;\n        MarginLayoutParams layoutParams = (MarginLayoutParams) view.getLayoutParams();\n        layoutParams.setMargins(layoutParams.leftMargin,\n                layoutParams.topMargin - getStatusBarHeight(),\n                layoutParams.rightMargin,\n                layoutParams.bottomMargin);\n        view.setTag(KEY_OFFSET, false);\n    }\n\n    private static void addMarginTopEqualStatusBarHeight(@NonNull final Window window) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;\n        View withTag = window.getDecorView().findViewWithTag(TAG_OFFSET);\n        if (withTag == null) return;\n        addMarginTopEqualStatusBarHeight(withTag);\n    }\n\n    private static void subtractMarginTopEqualStatusBarHeight(@NonNull final Window window) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;\n        View withTag = window.getDecorView().findViewWithTag(TAG_OFFSET);\n        if (withTag == null) return;\n        subtractMarginTopEqualStatusBarHeight(withTag);\n    }\n\n    /**\n     * Set the status bar's color.\n     *\n     * @param activity The activity.\n     * @param color    The status bar's color.\n     */\n    public static View setStatusBarColor(@NonNull final Activity activity,\n                                         @ColorInt final int color) {\n        return setStatusBarColor(activity, color, false);\n    }\n\n    /**\n     * Set the status bar's color.\n     *\n     * @param activity The activity.\n     * @param color    The status bar's color.\n     * @param isDecor  True to add fake status bar in DecorView,\n     *                 false to add fake status bar in ContentView.\n     */\n    public static View setStatusBarColor(@NonNull final Activity activity,\n                                         @ColorInt final int color,\n                                         final boolean isDecor) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return null;\n        transparentStatusBar(activity);\n        return applyStatusBarColor(activity, color, isDecor);\n    }\n\n\n    /**\n     * Set the status bar's color.\n     *\n     * @param window The window.\n     * @param color  The status bar's color.\n     */\n    public static View setStatusBarColor(@NonNull final Window window,\n                                         @ColorInt final int color) {\n        return setStatusBarColor(window, color, false);\n    }\n\n    /**\n     * Set the status bar's color.\n     *\n     * @param window  The window.\n     * @param color   The status bar's color.\n     * @param isDecor True to add fake status bar in DecorView,\n     *                false to add fake status bar in ContentView.\n     */\n    public static View setStatusBarColor(@NonNull final Window window,\n                                         @ColorInt final int color,\n                                         final boolean isDecor) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return null;\n        transparentStatusBar(window);\n        return applyStatusBarColor(window, color, isDecor);\n    }\n\n    /**\n     * Set the status bar's color.\n     *\n     * @param fakeStatusBar The fake status bar view.\n     * @param color         The status bar's color.\n     */\n    public static void setStatusBarColor(@NonNull final View fakeStatusBar,\n                                         @ColorInt final int color) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;\n        Activity activity = UtilsBridge.getActivityByContext(fakeStatusBar.getContext());\n        if (activity == null) return;\n        transparentStatusBar(activity);\n        fakeStatusBar.setVisibility(View.VISIBLE);\n        ViewGroup.LayoutParams layoutParams = fakeStatusBar.getLayoutParams();\n        layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;\n        layoutParams.height = getStatusBarHeight();\n        fakeStatusBar.setBackgroundColor(color);\n    }\n\n    /**\n     * Set the custom status bar.\n     *\n     * @param fakeStatusBar The fake status bar view.\n     */\n    public static void setStatusBarCustom(@NonNull final View fakeStatusBar) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;\n        Activity activity = UtilsBridge.getActivityByContext(fakeStatusBar.getContext());\n        if (activity == null) return;\n        transparentStatusBar(activity);\n        fakeStatusBar.setVisibility(View.VISIBLE);\n        ViewGroup.LayoutParams layoutParams = fakeStatusBar.getLayoutParams();\n        if (layoutParams == null) {\n            layoutParams = new ViewGroup.LayoutParams(\n                    ViewGroup.LayoutParams.MATCH_PARENT,\n                    getStatusBarHeight()\n            );\n            fakeStatusBar.setLayoutParams(layoutParams);\n        } else {\n            layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT;\n            layoutParams.height = getStatusBarHeight();\n        }\n    }\n\n    /**\n     * Set the status bar's color for DrawerLayout.\n     * <p>DrawLayout must add {@code android:fitsSystemWindows=\"true\"}</p>\n     *\n     * @param drawer        The DrawLayout.\n     * @param fakeStatusBar The fake status bar view.\n     * @param color         The status bar's color.\n     */\n    public static void setStatusBarColor4Drawer(@NonNull final DrawerLayout drawer,\n                                                @NonNull final View fakeStatusBar,\n                                                @ColorInt final int color) {\n        setStatusBarColor4Drawer(drawer, fakeStatusBar, color, false);\n    }\n\n    /**\n     * Set the status bar's color for DrawerLayout.\n     * <p>DrawLayout must add {@code android:fitsSystemWindows=\"true\"}</p>\n     *\n     * @param drawer        The DrawLayout.\n     * @param fakeStatusBar The fake status bar view.\n     * @param color         The status bar's color.\n     * @param isTop         True to set DrawerLayout at the top layer, false otherwise.\n     */\n    public static void setStatusBarColor4Drawer(@NonNull final DrawerLayout drawer,\n                                                @NonNull final View fakeStatusBar,\n                                                @ColorInt final int color,\n                                                final boolean isTop) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;\n        Activity activity = UtilsBridge.getActivityByContext(fakeStatusBar.getContext());\n        if (activity == null) return;\n        transparentStatusBar(activity);\n        drawer.setFitsSystemWindows(false);\n        setStatusBarColor(fakeStatusBar, color);\n        for (int i = 0, count = drawer.getChildCount(); i < count; i++) {\n            drawer.getChildAt(i).setFitsSystemWindows(false);\n        }\n        if (isTop) {\n            hideStatusBarView(activity);\n        } else {\n            setStatusBarColor(activity, color, false);\n        }\n    }\n\n    private static View applyStatusBarColor(@NonNull final Activity activity,\n                                            final int color,\n                                            boolean isDecor) {\n        return applyStatusBarColor(activity.getWindow(), color, isDecor);\n    }\n\n    private static View applyStatusBarColor(@NonNull final Window window,\n                                            final int color,\n                                            boolean isDecor) {\n        ViewGroup parent = isDecor ?\n                (ViewGroup) window.getDecorView() :\n                (ViewGroup) window.findViewById(android.R.id.content);\n        View fakeStatusBarView = parent.findViewWithTag(TAG_STATUS_BAR);\n        if (fakeStatusBarView != null) {\n            if (fakeStatusBarView.getVisibility() == View.GONE) {\n                fakeStatusBarView.setVisibility(View.VISIBLE);\n            }\n            fakeStatusBarView.setBackgroundColor(color);\n        } else {\n            fakeStatusBarView = createStatusBarView(window.getContext(), color);\n            parent.addView(fakeStatusBarView);\n        }\n        return fakeStatusBarView;\n    }\n\n    private static void hideStatusBarView(@NonNull final Activity activity) {\n        hideStatusBarView(activity.getWindow());\n    }\n\n    private static void hideStatusBarView(@NonNull final Window window) {\n        ViewGroup decorView = (ViewGroup) window.getDecorView();\n        View fakeStatusBarView = decorView.findViewWithTag(TAG_STATUS_BAR);\n        if (fakeStatusBarView == null) return;\n        fakeStatusBarView.setVisibility(View.GONE);\n    }\n\n    private static void showStatusBarView(@NonNull final Window window) {\n        ViewGroup decorView = (ViewGroup) window.getDecorView();\n        View fakeStatusBarView = decorView.findViewWithTag(TAG_STATUS_BAR);\n        if (fakeStatusBarView == null) return;\n        fakeStatusBarView.setVisibility(View.VISIBLE);\n    }\n\n    private static View createStatusBarView(@NonNull final Context context,\n                                            final int color) {\n        View statusBarView = new View(context);\n        statusBarView.setLayoutParams(new ViewGroup.LayoutParams(\n                ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight()));\n        statusBarView.setBackgroundColor(color);\n        statusBarView.setTag(TAG_STATUS_BAR);\n        return statusBarView;\n    }\n\n    public static void transparentStatusBar(@NonNull final Activity activity) {\n        transparentStatusBar(activity.getWindow());\n    }\n\n    public static void transparentStatusBar(@NonNull final Window window) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);\n            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);\n            int option = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;\n            int vis = window.getDecorView().getSystemUiVisibility();\n            window.getDecorView().setSystemUiVisibility(option | vis);\n            window.setStatusBarColor(Color.TRANSPARENT);\n        } else {\n            window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);\n        }\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // action bar\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Return the action bar's height.\n     *\n     * @return the action bar's height\n     */\n    public static int getActionBarHeight() {\n        TypedValue tv = new TypedValue();\n        if (Utils.getApp().getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true)) {\n            return TypedValue.complexToDimensionPixelSize(\n                    tv.data, Utils.getApp().getResources().getDisplayMetrics()\n            );\n        }\n        return 0;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // notification bar\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Set the notification bar's visibility.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.EXPAND_STATUS_BAR\" />}</p>\n     *\n     * @param isVisible True to set notification bar visible, false otherwise.\n     */\n    @RequiresPermission(EXPAND_STATUS_BAR)\n    public static void setNotificationBarVisibility(final boolean isVisible) {\n        String methodName;\n        if (isVisible) {\n            methodName = (Build.VERSION.SDK_INT <= 16) ? \"expand\" : \"expandNotificationsPanel\";\n        } else {\n            methodName = (Build.VERSION.SDK_INT <= 16) ? \"collapse\" : \"collapsePanels\";\n        }\n        invokePanels(methodName);\n    }\n\n    private static void invokePanels(final String methodName) {\n        try {\n            @SuppressLint(\"WrongConstant\")\n            Object service = Utils.getApp().getSystemService(\"statusbar\");\n            @SuppressLint(\"PrivateApi\")\n            Class<?> statusBarManager = Class.forName(\"android.app.StatusBarManager\");\n            Method expand = statusBarManager.getMethod(methodName);\n            expand.invoke(service);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // navigation bar\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Return the navigation bar's height.\n     *\n     * @return the navigation bar's height\n     */\n    public static int getNavBarHeight() {\n        Resources res = Resources.getSystem();\n        int resourceId = res.getIdentifier(\"navigation_bar_height\", \"dimen\", \"android\");\n        if (resourceId != 0) {\n            return res.getDimensionPixelSize(resourceId);\n        } else {\n            return 0;\n        }\n    }\n\n    /**\n     * Set the navigation bar's visibility.\n     *\n     * @param activity  The activity.\n     * @param isVisible True to set navigation bar visible, false otherwise.\n     */\n    public static void setNavBarVisibility(@NonNull final Activity activity, boolean isVisible) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;\n        setNavBarVisibility(activity.getWindow(), isVisible);\n\n    }\n\n    /**\n     * Set the navigation bar's visibility.\n     *\n     * @param window    The window.\n     * @param isVisible True to set navigation bar visible, false otherwise.\n     */\n    public static void setNavBarVisibility(@NonNull final Window window, boolean isVisible) {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) return;\n        final ViewGroup decorView = (ViewGroup) window.getDecorView();\n        for (int i = 0, count = decorView.getChildCount(); i < count; i++) {\n            final View child = decorView.getChildAt(i);\n            final int id = child.getId();\n            if (id != View.NO_ID) {\n                String resourceEntryName = getResNameById(id);\n                if (\"navigationBarBackground\".equals(resourceEntryName)) {\n                    child.setVisibility(isVisible ? View.VISIBLE : View.INVISIBLE);\n                }\n            }\n        }\n        final int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION\n                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION\n                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;\n        if (isVisible) {\n            decorView.setSystemUiVisibility(decorView.getSystemUiVisibility() & ~uiOptions);\n        } else {\n            decorView.setSystemUiVisibility(decorView.getSystemUiVisibility() | uiOptions);\n        }\n    }\n\n    /**\n     * Return whether the navigation bar visible.\n     * <p>Call it in onWindowFocusChanged will get right result.</p>\n     *\n     * @param activity The activity.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isNavBarVisible(@NonNull final Activity activity) {\n        return isNavBarVisible(activity.getWindow());\n    }\n\n    /**\n     * Return whether the navigation bar visible.\n     * <p>Call it in onWindowFocusChanged will get right result.</p>\n     *\n     * @param window The window.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isNavBarVisible(@NonNull final Window window) {\n        boolean isVisible = false;\n        ViewGroup decorView = (ViewGroup) window.getDecorView();\n        for (int i = 0, count = decorView.getChildCount(); i < count; i++) {\n            final View child = decorView.getChildAt(i);\n            final int id = child.getId();\n            if (id != View.NO_ID) {\n                String resourceEntryName = getResNameById(id);\n                if (\"navigationBarBackground\".equals(resourceEntryName)\n                        && child.getVisibility() == View.VISIBLE) {\n                    isVisible = true;\n                    break;\n                }\n            }\n        }\n        if (isVisible) {\n            // 对于三星手机，android10以下非OneUI2的版本，比如 s8，note8 等设备上，\n            // 导航栏显示存在bug：\"当用户隐藏导航栏时显示输入法的时候导航栏会跟随显示\"，会导致隐藏输入法之后判断错误\n            // 这个问题在 OneUI 2 & android 10 版本已修复\n            if (UtilsBridge.isSamsung()\n                    && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1\n                    && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {\n                try {\n                    return Settings.Global.getInt(Utils.getApp().getContentResolver(), \"navigationbar_hide_bar_enabled\") == 0;\n                } catch (Exception ignore) {\n                }\n            }\n\n            int visibility = decorView.getSystemUiVisibility();\n            isVisible = (visibility & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;\n        }\n\n        return isVisible;\n    }\n\n    private static String getResNameById(int id) {\n        try {\n            return Utils.getApp().getResources().getResourceEntryName(id);\n        } catch (Exception ignore) {\n            return \"\";\n        }\n    }\n\n    /**\n     * Set the navigation bar's color.\n     *\n     * @param activity The activity.\n     * @param color    The navigation bar's color.\n     */\n    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)\n    public static void setNavBarColor(@NonNull final Activity activity, @ColorInt final int color) {\n        setNavBarColor(activity.getWindow(), color);\n    }\n\n    /**\n     * Set the navigation bar's color.\n     *\n     * @param window The window.\n     * @param color  The navigation bar's color.\n     */\n    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)\n    public static void setNavBarColor(@NonNull final Window window, @ColorInt final int color) {\n        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);\n        window.setNavigationBarColor(color);\n    }\n\n    /**\n     * Return the color of navigation bar.\n     *\n     * @param activity The activity.\n     * @return the color of navigation bar\n     */\n    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)\n    public static int getNavBarColor(@NonNull final Activity activity) {\n        return getNavBarColor(activity.getWindow());\n    }\n\n    /**\n     * Return the color of navigation bar.\n     *\n     * @param window The window.\n     * @return the color of navigation bar\n     */\n    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)\n    public static int getNavBarColor(@NonNull final Window window) {\n        return window.getNavigationBarColor();\n    }\n\n    /**\n     * Return whether the navigation bar visible.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isSupportNavBar() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            WindowManager wm = (WindowManager) Utils.getApp().getSystemService(Context.WINDOW_SERVICE);\n            if (wm == null) return false;\n            Display display = wm.getDefaultDisplay();\n            Point size = new Point();\n            Point realSize = new Point();\n            display.getSize(size);\n            display.getRealSize(realSize);\n            return realSize.y != size.y || realSize.x != size.x;\n        }\n        boolean menu = ViewConfiguration.get(Utils.getApp()).hasPermanentMenuKey();\n        boolean back = KeyCharacterMap.deviceHasKey(KeyEvent.KEYCODE_BACK);\n        return !menu && !back;\n    }\n\n    /**\n     * Set the nav bar's light mode.\n     *\n     * @param activity    The activity.\n     * @param isLightMode True to set nav bar light mode, false otherwise.\n     */\n    public static void setNavBarLightMode(@NonNull final Activity activity,\n                                          final boolean isLightMode) {\n        setNavBarLightMode(activity.getWindow(), isLightMode);\n    }\n\n    /**\n     * Set the nav bar's light mode.\n     *\n     * @param window      The window.\n     * @param isLightMode True to set nav bar light mode, false otherwise.\n     */\n    public static void setNavBarLightMode(@NonNull final Window window,\n                                          final boolean isLightMode) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n            View decorView = window.getDecorView();\n            int vis = decorView.getSystemUiVisibility();\n            if (isLightMode) {\n                vis |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;\n            } else {\n                vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;\n            }\n            decorView.setSystemUiVisibility(vis);\n        }\n    }\n\n    /**\n     * Is the nav bar light mode.\n     *\n     * @param activity The activity.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isNavBarLightMode(@NonNull final Activity activity) {\n        return isNavBarLightMode(activity.getWindow());\n    }\n\n    /**\n     * Is the nav bar light mode.\n     *\n     * @param window The window.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isNavBarLightMode(@NonNull final Window window) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n            View decorView = window.getDecorView();\n            int vis = decorView.getSystemUiVisibility();\n            return (vis & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR) != 0;\n        }\n        return false;\n    }\n\n    public static void transparentNavBar(@NonNull final Activity activity) {\n        transparentNavBar(activity.getWindow());\n    }\n\n    public static void transparentNavBar(@NonNull final Window window) {\n        if (Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.JELLY_BEAN) return;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n            window.setNavigationBarContrastEnforced(false);\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            window.setNavigationBarColor(Color.TRANSPARENT);\n        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {\n            if ((window.getAttributes().flags & WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION) == 0) {\n                window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);\n            }\n        }\n        View decorView = window.getDecorView();\n        int vis = decorView.getSystemUiVisibility();\n        int option = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;\n        decorView.setSystemUiVisibility(vis | option);\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/BrightnessUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.content.ContentResolver;\nimport android.provider.Settings;\nimport android.view.Window;\nimport android.view.WindowManager;\n\nimport androidx.annotation.IntRange;\nimport androidx.annotation.NonNull;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/02/08\n *     desc  : utils about brightness\n * </pre>\n */\npublic final class BrightnessUtils {\n\n    private BrightnessUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Return whether automatic brightness mode is enabled.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isAutoBrightnessEnabled() {\n        try {\n            int mode = Settings.System.getInt(\n                    Utils.getApp().getContentResolver(),\n                    Settings.System.SCREEN_BRIGHTNESS_MODE\n            );\n            return mode == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;\n        } catch (Settings.SettingNotFoundException e) {\n            e.printStackTrace();\n            return false;\n        }\n    }\n\n    /**\n     * Enable or disable automatic brightness mode.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.WRITE_SETTINGS\" />}</p>\n     *\n     * @param enabled True to enabled, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean setAutoBrightnessEnabled(final boolean enabled) {\n        return Settings.System.putInt(\n                Utils.getApp().getContentResolver(),\n                Settings.System.SCREEN_BRIGHTNESS_MODE,\n                enabled ? Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC\n                        : Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL\n        );\n    }\n\n    /**\n     * 获取屏幕亮度\n     *\n     * @return 屏幕亮度 0-255\n     */\n    public static int getBrightness() {\n        try {\n            return Settings.System.getInt(\n                    Utils.getApp().getContentResolver(),\n                    Settings.System.SCREEN_BRIGHTNESS\n            );\n        } catch (Settings.SettingNotFoundException e) {\n            e.printStackTrace();\n            return 0;\n        }\n    }\n\n    /**\n     * 设置屏幕亮度\n     * <p>需添加权限 {@code <uses-permission android:name=\"android.permission.WRITE_SETTINGS\" />}</p>\n     * 并得到授权\n     *\n     * @param brightness 亮度值\n     */\n    public static boolean setBrightness(@IntRange(from = 0, to = 255) final int brightness) {\n        ContentResolver resolver = Utils.getApp().getContentResolver();\n        boolean b = Settings.System.putInt(resolver, Settings.System.SCREEN_BRIGHTNESS, brightness);\n        resolver.notifyChange(Settings.System.getUriFor(\"screen_brightness\"), null);\n        return b;\n    }\n\n    /**\n     * 设置窗口亮度\n     *\n     * @param window     窗口\n     * @param brightness 亮度值\n     */\n    public static void setWindowBrightness(@NonNull final Window window,\n                                           @IntRange(from = 0, to = 255) final int brightness) {\n        WindowManager.LayoutParams lp = window.getAttributes();\n        lp.screenBrightness = brightness / 255f;\n        window.setAttributes(lp);\n    }\n\n    /**\n     * 获取窗口亮度\n     *\n     * @param window 窗口\n     * @return 屏幕亮度 0-255\n     */\n    public static int getWindowBrightness(@NonNull final Window window) {\n        WindowManager.LayoutParams lp = window.getAttributes();\n        float brightness = lp.screenBrightness;\n        if (brightness < 0) return getBrightness();\n        return (int) (brightness * 255);\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/BusUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.util.Log;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.CopyOnWriteArraySet;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/10/02\n *     desc  : utils about bus\n * </pre>\n */\npublic final class BusUtils {\n\n    private static final Object NULL = \"nULl\";\n    private static final String TAG  = \"BusUtils\";\n\n    private final Map<String, List<BusInfo>>       mTag_BusInfoListMap          = new ConcurrentHashMap<>();\n    private final Map<String, Set<Object>>         mClassName_BusesMap          = new ConcurrentHashMap<>();\n    private final Map<String, List<String>>        mClassName_TagsMap           = new ConcurrentHashMap<>();\n    private final Map<String, Map<String, Object>> mClassName_Tag_Arg4StickyMap = new ConcurrentHashMap<>();\n\n    private BusUtils() {\n        init();\n    }\n\n    /**\n     * It'll be injected the bus who have {@link Bus} annotation\n     * by function of {@link BusUtils#registerBus} when execute transform task.\n     */\n    private void init() {/*inject*/}\n\n    private void registerBus(String tag,\n                             String className, String funName, String paramType, String paramName,\n                             boolean sticky, String threadMode) {\n        registerBus(tag, className, funName, paramType, paramName, sticky, threadMode, 0);\n    }\n\n    private void registerBus(String tag,\n                             String className, String funName, String paramType, String paramName,\n                             boolean sticky, String threadMode, int priority) {\n        List<BusInfo> busInfoList = mTag_BusInfoListMap.get(tag);\n        if (busInfoList == null) {\n            busInfoList = new CopyOnWriteArrayList<>();\n            mTag_BusInfoListMap.put(tag, busInfoList);\n        }\n        busInfoList.add(new BusInfo(tag, className, funName, paramType, paramName, sticky, threadMode, priority));\n    }\n\n    public static void register(@Nullable final Object bus) {\n        getInstance().registerInner(bus);\n    }\n\n    public static void unregister(@Nullable final Object bus) {\n        getInstance().unregisterInner(bus);\n    }\n\n    public static void post(@NonNull final String tag) {\n        post(tag, NULL);\n    }\n\n    public static void post(@NonNull final String tag, @NonNull final Object arg) {\n        getInstance().postInner(tag, arg);\n    }\n\n    public static void postSticky(@NonNull final String tag) {\n        postSticky(tag, NULL);\n    }\n\n    public static void postSticky(@NonNull final String tag, final Object arg) {\n        getInstance().postStickyInner(tag, arg);\n    }\n\n    public static void removeSticky(final String tag) {\n        getInstance().removeStickyInner(tag);\n    }\n\n    public static String toString_() {\n        return getInstance().toString();\n    }\n\n    @Override\n    public String toString() {\n        return \"BusUtils: \" + mTag_BusInfoListMap;\n    }\n\n    private static BusUtils getInstance() {\n        return LazyHolder.INSTANCE;\n    }\n\n    private void registerInner(@Nullable final Object bus) {\n        if (bus == null) return;\n        Class<?> aClass = bus.getClass();\n        String className = aClass.getName();\n        boolean isNeedRecordTags = false;\n        synchronized (mClassName_BusesMap) {\n            Set<Object> buses = mClassName_BusesMap.get(className);\n            if (buses == null) {\n                buses = new CopyOnWriteArraySet<>();\n                mClassName_BusesMap.put(className, buses);\n                isNeedRecordTags = true;\n            }\n            if (buses.contains(bus)) {\n                Log.w(TAG, \"The bus of <\" + bus + \"> already registered.\");\n                return;\n            } else {\n                buses.add(bus);\n            }\n        }\n        if (isNeedRecordTags) {\n            recordTags(aClass, className);\n        }\n        consumeStickyIfExist(bus);\n    }\n\n    private void recordTags(Class<?> aClass, String className) {\n        List<String> tags = mClassName_TagsMap.get(className);\n        if (tags == null) {\n            synchronized (mClassName_TagsMap) {\n                tags = mClassName_TagsMap.get(className);\n                if (tags == null) {\n                    tags = new CopyOnWriteArrayList<>();\n                    for (Map.Entry<String, List<BusInfo>> entry : mTag_BusInfoListMap.entrySet()) {\n                        for (BusInfo busInfo : entry.getValue()) {\n                            try {\n                                if (Class.forName(busInfo.className).isAssignableFrom(aClass)) {\n                                    tags.add(entry.getKey());\n                                    busInfo.subClassNames.add(className);\n                                }\n                            } catch (ClassNotFoundException e) {\n                                e.printStackTrace();\n                            }\n                        }\n                    }\n                    mClassName_TagsMap.put(className, tags);\n                }\n            }\n        }\n    }\n\n    private void consumeStickyIfExist(final Object bus) {\n        Map<String, Object> tagArgMap = mClassName_Tag_Arg4StickyMap.get(bus.getClass().getName());\n        if (tagArgMap == null) return;\n        synchronized (mClassName_Tag_Arg4StickyMap) {\n            for (Map.Entry<String, Object> tagArgEntry : tagArgMap.entrySet()) {\n                consumeSticky(bus, tagArgEntry.getKey(), tagArgEntry.getValue());\n            }\n        }\n    }\n\n    private void consumeSticky(final Object bus, final String tag, final Object arg) {\n        List<BusInfo> busInfoList = mTag_BusInfoListMap.get(tag);\n        if (busInfoList == null) {\n            Log.e(TAG, \"The bus of tag <\" + tag + \"> is not exists.\");\n            return;\n        }\n        for (BusInfo busInfo : busInfoList) {\n            if (!busInfo.subClassNames.contains(bus.getClass().getName())) {\n                continue;\n            }\n            if (!busInfo.sticky) {\n                continue;\n            }\n\n            synchronized (mClassName_Tag_Arg4StickyMap) {\n                Map<String, Object> tagArgMap = mClassName_Tag_Arg4StickyMap.get(busInfo.className);\n                if (tagArgMap == null || !tagArgMap.containsKey(tag)) {\n                    continue;\n                }\n                invokeBus(bus, arg, busInfo, true);\n            }\n        }\n    }\n\n    private void unregisterInner(final Object bus) {\n        if (bus == null) return;\n        String className = bus.getClass().getName();\n        synchronized (mClassName_BusesMap) {\n            Set<Object> buses = mClassName_BusesMap.get(className);\n            if (buses == null || !buses.contains(bus)) {\n                Log.e(TAG, \"The bus of <\" + bus + \"> was not registered before.\");\n                return;\n            }\n            buses.remove(bus);\n        }\n    }\n\n    private void postInner(final String tag, final Object arg) {\n        postInner(tag, arg, false);\n    }\n\n    private void postInner(final String tag, final Object arg, final boolean sticky) {\n        List<BusInfo> busInfoList = mTag_BusInfoListMap.get(tag);\n        if (busInfoList == null) {\n            Log.e(TAG, \"The bus of tag <\" + tag + \"> is not exists.\");\n            if (mTag_BusInfoListMap.isEmpty()) {\n                Log.e(TAG, \"Please check whether the bus plugin is applied.\");\n            }\n            return;\n        }\n        for (BusInfo busInfo : busInfoList) {\n            invokeBus(arg, busInfo, sticky);\n        }\n    }\n\n    private void invokeBus(Object arg, BusInfo busInfo, boolean sticky) {\n        invokeBus(null, arg, busInfo, sticky);\n    }\n\n    private void invokeBus(Object bus, Object arg, BusInfo busInfo, boolean sticky) {\n        if (busInfo.method == null) {\n            Method method = getMethodByBusInfo(busInfo);\n            if (method == null) {\n                return;\n            }\n            busInfo.method = method;\n        }\n        invokeMethod(bus, arg, busInfo, sticky);\n    }\n\n    private Method getMethodByBusInfo(BusInfo busInfo) {\n        try {\n            if (\"\".equals(busInfo.paramType)) {\n                return Class.forName(busInfo.className).getDeclaredMethod(busInfo.funName);\n            } else {\n                return Class.forName(busInfo.className).getDeclaredMethod(busInfo.funName, getClassName(busInfo.paramType));\n            }\n        } catch (ClassNotFoundException e) {\n            e.printStackTrace();\n        } catch (NoSuchMethodException e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    private Class getClassName(String paramType) throws ClassNotFoundException {\n        switch (paramType) {\n            case \"boolean\":\n                return boolean.class;\n            case \"int\":\n                return int.class;\n            case \"long\":\n                return long.class;\n            case \"short\":\n                return short.class;\n            case \"byte\":\n                return byte.class;\n            case \"double\":\n                return double.class;\n            case \"float\":\n                return float.class;\n            case \"char\":\n                return char.class;\n            default:\n                return Class.forName(paramType);\n        }\n    }\n\n    private void invokeMethod(final Object arg, final BusInfo busInfo, final boolean sticky) {\n        invokeMethod(null, arg, busInfo, sticky);\n    }\n\n    private void invokeMethod(final Object bus, final Object arg, final BusInfo busInfo, final boolean sticky) {\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                realInvokeMethod(bus, arg, busInfo, sticky);\n            }\n        };\n        switch (busInfo.threadMode) {\n            case \"MAIN\":\n                ThreadUtils.runOnUiThread(runnable);\n                return;\n            case \"IO\":\n                ThreadUtils.getIoPool().execute(runnable);\n                return;\n            case \"CPU\":\n                ThreadUtils.getCpuPool().execute(runnable);\n                return;\n            case \"CACHED\":\n                ThreadUtils.getCachedPool().execute(runnable);\n                return;\n            case \"SINGLE\":\n                ThreadUtils.getSinglePool().execute(runnable);\n                return;\n            default:\n                runnable.run();\n        }\n    }\n\n    private void realInvokeMethod(Object bus, Object arg, BusInfo busInfo, boolean sticky) {\n        Set<Object> buses = new HashSet<>();\n        if (bus == null) {\n            for (String subClassName : busInfo.subClassNames) {\n                Set<Object> subBuses = mClassName_BusesMap.get(subClassName);\n                if (subBuses != null && !subBuses.isEmpty()) {\n                    buses.addAll(subBuses);\n                }\n            }\n            if (buses.size() == 0) {\n                if (!sticky) {\n                    Log.e(TAG, \"The \" + busInfo + \" was not registered before.\");\n                }\n                return;\n            }\n        } else {\n            buses.add(bus);\n        }\n        invokeBuses(arg, busInfo, buses);\n    }\n\n    private void invokeBuses(Object arg, BusInfo busInfo, Set<Object> buses) {\n        try {\n            if (arg == NULL) {\n                for (Object bus : buses) {\n                    busInfo.method.invoke(bus);\n                }\n            } else {\n                for (Object bus : buses) {\n                    busInfo.method.invoke(bus, arg);\n                }\n            }\n        } catch (IllegalAccessException e) {\n            e.printStackTrace();\n        } catch (InvocationTargetException e) {\n            e.printStackTrace();\n        }\n    }\n\n    private void postStickyInner(final String tag, final Object arg) {\n        List<BusInfo> busInfoList = mTag_BusInfoListMap.get(tag);\n        if (busInfoList == null) {\n            Log.e(TAG, \"The bus of tag <\" + tag + \"> is not exists.\");\n            return;\n        }\n        // 获取多对象，然后消费各个 busInfoList\n        for (BusInfo busInfo : busInfoList) {\n            if (!busInfo.sticky) { // not sticky bus will post directly.\n                invokeBus(arg, busInfo, false);\n                continue;\n            }\n            synchronized (mClassName_Tag_Arg4StickyMap) {\n                Map<String, Object> tagArgMap = mClassName_Tag_Arg4StickyMap.get(busInfo.className);\n                if (tagArgMap == null) {\n                    tagArgMap = new ConcurrentHashMap<>();\n                    mClassName_Tag_Arg4StickyMap.put(busInfo.className, tagArgMap);\n                }\n                tagArgMap.put(tag, arg);\n            }\n            invokeBus(arg, busInfo, true);\n        }\n    }\n\n    private void removeStickyInner(final String tag) {\n        List<BusInfo> busInfoList = mTag_BusInfoListMap.get(tag);\n        if (busInfoList == null) {\n            Log.e(TAG, \"The bus of tag <\" + tag + \"> is not exists.\");\n            return;\n        }\n        for (BusInfo busInfo : busInfoList) {\n            if (!busInfo.sticky) {\n                continue;\n            }\n            synchronized (mClassName_Tag_Arg4StickyMap) {\n                Map<String, Object> tagArgMap = mClassName_Tag_Arg4StickyMap.get(busInfo.className);\n                if (tagArgMap == null || !tagArgMap.containsKey(tag)) {\n                    return;\n                }\n                tagArgMap.remove(tag);\n            }\n        }\n    }\n\n    static void registerBus4Test(String tag,\n                                 String className, String funName, String paramType, String paramName,\n                                 boolean sticky, String threadMode, int priority) {\n        getInstance().registerBus(tag, className, funName, paramType, paramName, sticky, threadMode, priority);\n    }\n\n    private static final class BusInfo {\n\n        String       tag;\n        String       className;\n        String       funName;\n        String       paramType;\n        String       paramName;\n        boolean      sticky;\n        String       threadMode;\n        int          priority;\n        Method       method;\n        List<String> subClassNames;\n\n        BusInfo(String tag, String className, String funName, String paramType, String paramName,\n                boolean sticky, String threadMode, int priority) {\n            this.tag = tag;\n            this.className = className;\n            this.funName = funName;\n            this.paramType = paramType;\n            this.paramName = paramName;\n            this.sticky = sticky;\n            this.threadMode = threadMode;\n            this.priority = priority;\n            this.subClassNames = new CopyOnWriteArrayList<>();\n        }\n\n        @Override\n        public String toString() {\n            return \"BusInfo { tag : \" + tag +\n                    \", desc: \" + getDesc() +\n                    \", sticky: \" + sticky +\n                    \", threadMode: \" + threadMode +\n                    \", method: \" + method +\n                    \", priority: \" + priority +\n                    \" }\";\n        }\n\n        private String getDesc() {\n            return className + \"#\" + funName +\n                    (\"\".equals(paramType) ? \"()\" : (\"(\" + paramType + \" \" + paramName + \")\"));\n        }\n    }\n\n    public enum ThreadMode {\n        MAIN, IO, CPU, CACHED, SINGLE, POSTING\n    }\n\n    @Target({ElementType.METHOD})\n    @Retention(RetentionPolicy.CLASS)\n    public @interface Bus {\n        String tag();\n\n        boolean sticky() default false;\n\n        ThreadMode threadMode() default ThreadMode.POSTING;\n\n        int priority() default 0;\n    }\n\n    private static class LazyHolder {\n        private static final BusUtils INSTANCE = new BusUtils();\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDiskStaticUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.graphics.Bitmap;\nimport android.graphics.drawable.Drawable;\nimport android.os.Parcelable;\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport java.io.Serializable;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/01/04\n *     desc  : utils about disk cache\n * </pre>\n */\npublic final class CacheDiskStaticUtils {\n\n    private static CacheDiskUtils sDefaultCacheDiskUtils;\n\n    /**\n     * Set the default instance of {@link CacheDiskUtils}.\n     *\n     * @param cacheDiskUtils The default instance of {@link CacheDiskUtils}.\n     */\n    public static void setDefaultCacheDiskUtils(@Nullable final CacheDiskUtils cacheDiskUtils) {\n        sDefaultCacheDiskUtils = cacheDiskUtils;\n    }\n\n    /**\n     * Put bytes in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public static void put(@NonNull final String key, @Nullable final byte[] value) {\n        put(key, value, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Put bytes in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public static void put(@NonNull final String key, @Nullable final byte[] value, final int saveTime) {\n        put(key, value, saveTime, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Return the bytes in cache.\n     *\n     * @param key The key of cache.\n     * @return the bytes if cache exists or null otherwise\n     */\n    public static byte[] getBytes(@NonNull final String key) {\n        return getBytes(key, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Return the bytes in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the bytes if cache exists or defaultValue otherwise\n     */\n    public static byte[] getBytes(@NonNull final String key, @Nullable final byte[] defaultValue) {\n        return getBytes(key, defaultValue, getDefaultCacheDiskUtils());\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about String\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put string value in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public static void put(@NonNull final String key, @Nullable final String value) {\n        put(key, value, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Put string value in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public static void put(@NonNull final String key, @Nullable final String value, final int saveTime) {\n        put(key, value, saveTime, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Return the string value in cache.\n     *\n     * @param key The key of cache.\n     * @return the string value if cache exists or null otherwise\n     */\n    public static String getString(@NonNull final String key) {\n        return getString(key, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Return the string value in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the string value if cache exists or defaultValue otherwise\n     */\n    public static String getString(@NonNull final String key, @Nullable final String defaultValue) {\n        return getString(key, defaultValue, getDefaultCacheDiskUtils());\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about JSONObject\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put JSONObject in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public static void put(@NonNull final String key, @Nullable final JSONObject value) {\n        put(key, value, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Put JSONObject in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public static void put(@NonNull final String key,\n                           @Nullable final JSONObject value,\n                           final int saveTime) {\n        put(key, value, saveTime, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Return the JSONObject in cache.\n     *\n     * @param key The key of cache.\n     * @return the JSONObject if cache exists or null otherwise\n     */\n    public static JSONObject getJSONObject(@NonNull final String key) {\n        return getJSONObject(key, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Return the JSONObject in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the JSONObject if cache exists or defaultValue otherwise\n     */\n    public static JSONObject getJSONObject(@NonNull final String key, @Nullable final JSONObject defaultValue) {\n        return getJSONObject(key, defaultValue, getDefaultCacheDiskUtils());\n    }\n\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about JSONArray\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put JSONArray in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public static void put(@NonNull final String key, @Nullable final JSONArray value) {\n        put(key, value, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Put JSONArray in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public static void put(@NonNull final String key, @Nullable final JSONArray value, final int saveTime) {\n        put(key, value, saveTime, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Return the JSONArray in cache.\n     *\n     * @param key The key of cache.\n     * @return the JSONArray if cache exists or null otherwise\n     */\n    public static JSONArray getJSONArray(@NonNull final String key) {\n        return getJSONArray(key, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Return the JSONArray in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the JSONArray if cache exists or defaultValue otherwise\n     */\n    public static JSONArray getJSONArray(@NonNull final String key, @Nullable final JSONArray defaultValue) {\n        return getJSONArray(key, defaultValue, getDefaultCacheDiskUtils());\n    }\n\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Bitmap\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put bitmap in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public static void put(@NonNull final String key, @Nullable final Bitmap value) {\n        put(key, value, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Put bitmap in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public static void put(@NonNull final String key, @Nullable final Bitmap value, final int saveTime) {\n        put(key, value, saveTime, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Return the bitmap in cache.\n     *\n     * @param key The key of cache.\n     * @return the bitmap if cache exists or null otherwise\n     */\n    public static Bitmap getBitmap(@NonNull final String key) {\n        return getBitmap(key, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Return the bitmap in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the bitmap if cache exists or defaultValue otherwise\n     */\n    public static Bitmap getBitmap(@NonNull final String key, @Nullable final Bitmap defaultValue) {\n        return getBitmap(key, defaultValue, getDefaultCacheDiskUtils());\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Drawable\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put drawable in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public static void put(@NonNull final String key, @Nullable final Drawable value) {\n        put(key, value, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Put drawable in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public static void put(@NonNull final String key, @Nullable final Drawable value, final int saveTime) {\n        put(key, value, saveTime, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Return the drawable in cache.\n     *\n     * @param key The key of cache.\n     * @return the drawable if cache exists or null otherwise\n     */\n    public static Drawable getDrawable(@NonNull final String key) {\n        return getDrawable(key, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Return the drawable in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the drawable if cache exists or defaultValue otherwise\n     */\n    public static Drawable getDrawable(@NonNull final String key, final @Nullable Drawable defaultValue) {\n        return getDrawable(key, defaultValue, getDefaultCacheDiskUtils());\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Parcelable\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put parcelable in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public static void put(@NonNull final String key, @Nullable final Parcelable value) {\n        put(key, value, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Put parcelable in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public static void put(@NonNull final String key, @Nullable final Parcelable value, final int saveTime) {\n        put(key, value, saveTime, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Return the parcelable in cache.\n     *\n     * @param key     The key of cache.\n     * @param creator The creator.\n     * @param <T>     The value type.\n     * @return the parcelable if cache exists or null otherwise\n     */\n    public static <T> T getParcelable(@NonNull final String key,\n                                      @NonNull final Parcelable.Creator<T> creator) {\n        return getParcelable(key, creator, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Return the parcelable in cache.\n     *\n     * @param key          The key of cache.\n     * @param creator      The creator.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @param <T>          The value type.\n     * @return the parcelable if cache exists or defaultValue otherwise\n     */\n    public static <T> T getParcelable(@NonNull final String key,\n                                      @NonNull final Parcelable.Creator<T> creator,\n                                      @Nullable final T defaultValue) {\n        return getParcelable(key, creator, defaultValue, getDefaultCacheDiskUtils());\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Serializable\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put serializable in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public static void put(@NonNull final String key, @Nullable final Serializable value) {\n        put(key, value, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Put serializable in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public static void put(@NonNull final String key, @Nullable final Serializable value, final int saveTime) {\n        put(key, value, saveTime, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Return the serializable in cache.\n     *\n     * @param key The key of cache.\n     * @return the bitmap if cache exists or null otherwise\n     */\n    public static Object getSerializable(@NonNull final String key) {\n        return getSerializable(key, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Return the serializable in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the bitmap if cache exists or defaultValue otherwise\n     */\n    public static Object getSerializable(@NonNull final String key, @Nullable final Object defaultValue) {\n        return getSerializable(key, defaultValue, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Return the size of cache, in bytes.\n     *\n     * @return the size of cache, in bytes\n     */\n    public static long getCacheSize() {\n        return getCacheSize(getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Return the count of cache.\n     *\n     * @return the count of cache\n     */\n    public static int getCacheCount() {\n        return getCacheCount(getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Remove the cache by key.\n     *\n     * @param key The key of cache.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean remove(@NonNull final String key) {\n        return remove(key, getDefaultCacheDiskUtils());\n    }\n\n    /**\n     * Clear all of the cache.\n     *\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean clear() {\n        return clear(getDefaultCacheDiskUtils());\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // dividing line\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put bytes in cache.\n     *\n     * @param key            The key of cache.\n     * @param value          The value of cache.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           @Nullable final byte[] value,\n                           @NonNull final CacheDiskUtils cacheDiskUtils) {\n        cacheDiskUtils.put(key, value);\n    }\n\n    /**\n     * Put bytes in cache.\n     *\n     * @param key            The key of cache.\n     * @param value          The value of cache.\n     * @param saveTime       The save time of cache, in seconds.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           @Nullable final byte[] value,\n                           final int saveTime,\n                           @NonNull final CacheDiskUtils cacheDiskUtils) {\n        cacheDiskUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the bytes in cache.\n     *\n     * @param key            The key of cache.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     * @return the bytes if cache exists or null otherwise\n     */\n    public static byte[] getBytes(@NonNull final String key, @NonNull final CacheDiskUtils cacheDiskUtils) {\n        return cacheDiskUtils.getBytes(key);\n    }\n\n    /**\n     * Return the bytes in cache.\n     *\n     * @param key            The key of cache.\n     * @param defaultValue   The default value if the cache doesn't exist.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     * @return the bytes if cache exists or defaultValue otherwise\n     */\n    public static byte[] getBytes(@NonNull final String key,\n                                  @Nullable final byte[] defaultValue,\n                                  @NonNull final CacheDiskUtils cacheDiskUtils) {\n        return cacheDiskUtils.getBytes(key, defaultValue);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about String\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put string value in cache.\n     *\n     * @param key            The key of cache.\n     * @param value          The value of cache.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           @Nullable final String value,\n                           @NonNull final CacheDiskUtils cacheDiskUtils) {\n        cacheDiskUtils.put(key, value);\n    }\n\n    /**\n     * Put string value in cache.\n     *\n     * @param key            The key of cache.\n     * @param value          The value of cache.\n     * @param saveTime       The save time of cache, in seconds.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           @Nullable final String value,\n                           final int saveTime,\n                           @NonNull final CacheDiskUtils cacheDiskUtils) {\n        cacheDiskUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the string value in cache.\n     *\n     * @param key            The key of cache.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     * @return the string value if cache exists or null otherwise\n     */\n    public static String getString(@NonNull final String key, @NonNull final CacheDiskUtils cacheDiskUtils) {\n        return cacheDiskUtils.getString(key);\n    }\n\n    /**\n     * Return the string value in cache.\n     *\n     * @param key            The key of cache.\n     * @param defaultValue   The default value if the cache doesn't exist.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     * @return the string value if cache exists or defaultValue otherwise\n     */\n    public static String getString(@NonNull final String key,\n                                   @Nullable final String defaultValue,\n                                   @NonNull final CacheDiskUtils cacheDiskUtils) {\n        return cacheDiskUtils.getString(key, defaultValue);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about JSONObject\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put JSONObject in cache.\n     *\n     * @param key            The key of cache.\n     * @param value          The value of cache.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           @Nullable final JSONObject value,\n                           @NonNull final CacheDiskUtils cacheDiskUtils) {\n        cacheDiskUtils.put(key, value);\n    }\n\n    /**\n     * Put JSONObject in cache.\n     *\n     * @param key            The key of cache.\n     * @param value          The value of cache.\n     * @param saveTime       The save time of cache, in seconds.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           @Nullable final JSONObject value,\n                           final int saveTime,\n                           @NonNull final CacheDiskUtils cacheDiskUtils) {\n        cacheDiskUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the JSONObject in cache.\n     *\n     * @param key            The key of cache.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     * @return the JSONObject if cache exists or null otherwise\n     */\n    public static JSONObject getJSONObject(@NonNull final String key, @NonNull final CacheDiskUtils cacheDiskUtils) {\n        return cacheDiskUtils.getJSONObject(key);\n    }\n\n    /**\n     * Return the JSONObject in cache.\n     *\n     * @param key            The key of cache.\n     * @param defaultValue   The default value if the cache doesn't exist.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     * @return the JSONObject if cache exists or defaultValue otherwise\n     */\n    public static JSONObject getJSONObject(@NonNull final String key,\n                                           @Nullable final JSONObject defaultValue,\n                                           @NonNull final CacheDiskUtils cacheDiskUtils) {\n        return cacheDiskUtils.getJSONObject(key, defaultValue);\n    }\n\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about JSONArray\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put JSONArray in cache.\n     *\n     * @param key            The key of cache.\n     * @param value          The value of cache.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           @Nullable final JSONArray value,\n                           @NonNull final CacheDiskUtils cacheDiskUtils) {\n        cacheDiskUtils.put(key, value);\n    }\n\n    /**\n     * Put JSONArray in cache.\n     *\n     * @param key            The key of cache.\n     * @param value          The value of cache.\n     * @param saveTime       The save time of cache, in seconds.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           @Nullable final JSONArray value,\n                           final int saveTime,\n                           @NonNull final CacheDiskUtils cacheDiskUtils) {\n        cacheDiskUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the JSONArray in cache.\n     *\n     * @param key            The key of cache.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     * @return the JSONArray if cache exists or null otherwise\n     */\n    public static JSONArray getJSONArray(@NonNull final String key, @NonNull final CacheDiskUtils cacheDiskUtils) {\n        return cacheDiskUtils.getJSONArray(key);\n    }\n\n    /**\n     * Return the JSONArray in cache.\n     *\n     * @param key            The key of cache.\n     * @param defaultValue   The default value if the cache doesn't exist.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     * @return the JSONArray if cache exists or defaultValue otherwise\n     */\n    public static JSONArray getJSONArray(@NonNull final String key,\n                                         @Nullable final JSONArray defaultValue,\n                                         @NonNull final CacheDiskUtils cacheDiskUtils) {\n        return cacheDiskUtils.getJSONArray(key, defaultValue);\n    }\n\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Bitmap\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put bitmap in cache.\n     *\n     * @param key            The key of cache.\n     * @param value          The value of cache.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           @Nullable final Bitmap value,\n                           @NonNull final CacheDiskUtils cacheDiskUtils) {\n        cacheDiskUtils.put(key, value);\n    }\n\n    /**\n     * Put bitmap in cache.\n     *\n     * @param key            The key of cache.\n     * @param value          The value of cache.\n     * @param saveTime       The save time of cache, in seconds.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           @Nullable final Bitmap value,\n                           final int saveTime,\n                           @NonNull final CacheDiskUtils cacheDiskUtils) {\n        cacheDiskUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the bitmap in cache.\n     *\n     * @param key            The key of cache.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     * @return the bitmap if cache exists or null otherwise\n     */\n    public static Bitmap getBitmap(@NonNull final String key, @NonNull final CacheDiskUtils cacheDiskUtils) {\n        return cacheDiskUtils.getBitmap(key);\n    }\n\n    /**\n     * Return the bitmap in cache.\n     *\n     * @param key            The key of cache.\n     * @param defaultValue   The default value if the cache doesn't exist.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     * @return the bitmap if cache exists or defaultValue otherwise\n     */\n    public static Bitmap getBitmap(@NonNull final String key,\n                                   @Nullable final Bitmap defaultValue,\n                                   @NonNull final CacheDiskUtils cacheDiskUtils) {\n        return cacheDiskUtils.getBitmap(key, defaultValue);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Drawable\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put drawable in cache.\n     *\n     * @param key            The key of cache.\n     * @param value          The value of cache.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           @Nullable final Drawable value,\n                           @NonNull final CacheDiskUtils cacheDiskUtils) {\n        cacheDiskUtils.put(key, value);\n    }\n\n    /**\n     * Put drawable in cache.\n     *\n     * @param key            The key of cache.\n     * @param value          The value of cache.\n     * @param saveTime       The save time of cache, in seconds.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           @Nullable final Drawable value,\n                           final int saveTime,\n                           @NonNull final CacheDiskUtils cacheDiskUtils) {\n        cacheDiskUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the drawable in cache.\n     *\n     * @param key            The key of cache.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     * @return the drawable if cache exists or null otherwise\n     */\n    public static Drawable getDrawable(@NonNull final String key, @NonNull final CacheDiskUtils cacheDiskUtils) {\n        return cacheDiskUtils.getDrawable(key);\n    }\n\n    /**\n     * Return the drawable in cache.\n     *\n     * @param key            The key of cache.\n     * @param defaultValue   The default value if the cache doesn't exist.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     * @return the drawable if cache exists or defaultValue otherwise\n     */\n    public static Drawable getDrawable(@NonNull final String key,\n                                       @Nullable final Drawable defaultValue,\n                                       @NonNull final CacheDiskUtils cacheDiskUtils) {\n        return cacheDiskUtils.getDrawable(key, defaultValue);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Parcelable\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put parcelable in cache.\n     *\n     * @param key            The key of cache.\n     * @param value          The value of cache.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           @Nullable final Parcelable value,\n                           @NonNull final CacheDiskUtils cacheDiskUtils) {\n        cacheDiskUtils.put(key, value);\n    }\n\n    /**\n     * Put parcelable in cache.\n     *\n     * @param key            The key of cache.\n     * @param value          The value of cache.\n     * @param saveTime       The save time of cache, in seconds.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           @Nullable final Parcelable value,\n                           final int saveTime,\n                           @NonNull final CacheDiskUtils cacheDiskUtils) {\n        cacheDiskUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the parcelable in cache.\n     *\n     * @param key            The key of cache.\n     * @param creator        The creator.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     * @param <T>            The value type.\n     * @return the parcelable if cache exists or null otherwise\n     */\n    public static <T> T getParcelable(@NonNull final String key,\n                                      @NonNull final Parcelable.Creator<T> creator,\n                                      @NonNull final CacheDiskUtils cacheDiskUtils) {\n        return cacheDiskUtils.getParcelable(key, creator);\n    }\n\n    /**\n     * Return the parcelable in cache.\n     *\n     * @param key            The key of cache.\n     * @param creator        The creator.\n     * @param defaultValue   The default value if the cache doesn't exist.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     * @param <T>            The value type.\n     * @return the parcelable if cache exists or defaultValue otherwise\n     */\n    public static <T> T getParcelable(@NonNull final String key,\n                                      @NonNull final Parcelable.Creator<T> creator,\n                                      @Nullable final T defaultValue,\n                                      @NonNull final CacheDiskUtils cacheDiskUtils) {\n        return cacheDiskUtils.getParcelable(key, creator, defaultValue);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Serializable\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put serializable in cache.\n     *\n     * @param key            The key of cache.\n     * @param value          The value of cache.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           @Nullable final Serializable value,\n                           @NonNull final CacheDiskUtils cacheDiskUtils) {\n        cacheDiskUtils.put(key, value);\n    }\n\n    /**\n     * Put serializable in cache.\n     *\n     * @param key            The key of cache.\n     * @param value          The value of cache.\n     * @param saveTime       The save time of cache, in seconds.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           @Nullable final Serializable value,\n                           final int saveTime,\n                           @NonNull final CacheDiskUtils cacheDiskUtils) {\n        cacheDiskUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the serializable in cache.\n     *\n     * @param key            The key of cache.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     * @return the bitmap if cache exists or null otherwise\n     */\n    public static Object getSerializable(@NonNull final String key, @NonNull final CacheDiskUtils cacheDiskUtils) {\n        return cacheDiskUtils.getSerializable(key);\n    }\n\n    /**\n     * Return the serializable in cache.\n     *\n     * @param key            The key of cache.\n     * @param defaultValue   The default value if the cache doesn't exist.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     * @return the bitmap if cache exists or defaultValue otherwise\n     */\n    public static Object getSerializable(@NonNull final String key,\n                                         @Nullable final Object defaultValue,\n                                         @NonNull final CacheDiskUtils cacheDiskUtils) {\n        return cacheDiskUtils.getSerializable(key, defaultValue);\n    }\n\n    /**\n     * Return the size of cache, in bytes.\n     *\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     * @return the size of cache, in bytes\n     */\n    public static long getCacheSize(@NonNull final CacheDiskUtils cacheDiskUtils) {\n        return cacheDiskUtils.getCacheSize();\n    }\n\n    /**\n     * Return the count of cache.\n     *\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     * @return the count of cache\n     */\n    public static int getCacheCount(@NonNull final CacheDiskUtils cacheDiskUtils) {\n        return cacheDiskUtils.getCacheCount();\n    }\n\n    /**\n     * Remove the cache by key.\n     *\n     * @param key            The key of cache.\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean remove(@NonNull final String key, @NonNull final CacheDiskUtils cacheDiskUtils) {\n        return cacheDiskUtils.remove(key);\n    }\n\n    /**\n     * Clear all of the cache.\n     *\n     * @param cacheDiskUtils The instance of {@link CacheDiskUtils}.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean clear(@NonNull final CacheDiskUtils cacheDiskUtils) {\n        return cacheDiskUtils.clear();\n    }\n\n    @NonNull\n    private static CacheDiskUtils getDefaultCacheDiskUtils() {\n        return sDefaultCacheDiskUtils != null ? sDefaultCacheDiskUtils : CacheDiskUtils.getInstance();\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDiskUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.graphics.Bitmap;\nimport android.graphics.drawable.Drawable;\nimport android.os.Parcelable;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\n\nimport com.blankj.utilcode.constant.CacheConstants;\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport java.io.File;\nimport java.io.FilenameFilter;\nimport java.io.Serializable;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicLong;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/05/24\n *     desc  : utils about disk cache\n * </pre>\n */\npublic final class CacheDiskUtils implements CacheConstants {\n\n    private static final long   DEFAULT_MAX_SIZE  = Long.MAX_VALUE;\n    private static final int    DEFAULT_MAX_COUNT = Integer.MAX_VALUE;\n    private static final String CACHE_PREFIX      = \"cdu_\";\n    private static final String TYPE_BYTE         = \"by_\";\n    private static final String TYPE_STRING       = \"st_\";\n    private static final String TYPE_JSON_OBJECT  = \"jo_\";\n    private static final String TYPE_JSON_ARRAY   = \"ja_\";\n    private static final String TYPE_BITMAP       = \"bi_\";\n    private static final String TYPE_DRAWABLE     = \"dr_\";\n    private static final String TYPE_PARCELABLE   = \"pa_\";\n    private static final String TYPE_SERIALIZABLE = \"se_\";\n\n    private static final Map<String, CacheDiskUtils> CACHE_MAP = new HashMap<>();\n\n    private final String           mCacheKey;\n    private final File             mCacheDir;\n    private final long             mMaxSize;\n    private final int              mMaxCount;\n    private       DiskCacheManager mDiskCacheManager;\n\n    /**\n     * Return the single {@link CacheDiskUtils} instance.\n     * <p>cache directory: /data/data/package/cache/cacheUtils</p>\n     * <p>cache size: unlimited</p>\n     * <p>cache count: unlimited</p>\n     *\n     * @return the single {@link CacheDiskUtils} instance\n     */\n    public static CacheDiskUtils getInstance() {\n        return getInstance(\"\", DEFAULT_MAX_SIZE, DEFAULT_MAX_COUNT);\n    }\n\n    /**\n     * Return the single {@link CacheDiskUtils} instance.\n     * <p>cache directory: /data/data/package/cache/cacheUtils</p>\n     * <p>cache size: unlimited</p>\n     * <p>cache count: unlimited</p>\n     *\n     * @param cacheName The name of cache.\n     * @return the single {@link CacheDiskUtils} instance\n     */\n    public static CacheDiskUtils getInstance(final String cacheName) {\n        return getInstance(cacheName, DEFAULT_MAX_SIZE, DEFAULT_MAX_COUNT);\n    }\n\n    /**\n     * Return the single {@link CacheDiskUtils} instance.\n     * <p>cache directory: /data/data/package/cache/cacheUtils</p>\n     *\n     * @param maxSize  The max size of cache, in bytes.\n     * @param maxCount The max count of cache.\n     * @return the single {@link CacheDiskUtils} instance\n     */\n    public static CacheDiskUtils getInstance(final long maxSize, final int maxCount) {\n        return getInstance(\"\", maxSize, maxCount);\n    }\n\n    /**\n     * Return the single {@link CacheDiskUtils} instance.\n     * <p>cache directory: /data/data/package/cache/cacheName</p>\n     *\n     * @param cacheName The name of cache.\n     * @param maxSize   The max size of cache, in bytes.\n     * @param maxCount  The max count of cache.\n     * @return the single {@link CacheDiskUtils} instance\n     */\n    public static CacheDiskUtils getInstance(String cacheName, final long maxSize, final int maxCount) {\n        if (UtilsBridge.isSpace(cacheName)) cacheName = \"cacheUtils\";\n        File file = new File(Utils.getApp().getCacheDir(), cacheName);\n        return getInstance(file, maxSize, maxCount);\n    }\n\n    /**\n     * Return the single {@link CacheDiskUtils} instance.\n     * <p>cache size: unlimited</p>\n     * <p>cache count: unlimited</p>\n     *\n     * @param cacheDir The directory of cache.\n     * @return the single {@link CacheDiskUtils} instance\n     */\n    public static CacheDiskUtils getInstance(@NonNull final File cacheDir) {\n        return getInstance(cacheDir, DEFAULT_MAX_SIZE, DEFAULT_MAX_COUNT);\n    }\n\n    /**\n     * Return the single {@link CacheDiskUtils} instance.\n     *\n     * @param cacheDir The directory of cache.\n     * @param maxSize  The max size of cache, in bytes.\n     * @param maxCount The max count of cache.\n     * @return the single {@link CacheDiskUtils} instance\n     */\n    public static CacheDiskUtils getInstance(@NonNull final File cacheDir,\n                                             final long maxSize,\n                                             final int maxCount) {\n        final String cacheKey = cacheDir.getAbsoluteFile() + \"_\" + maxSize + \"_\" + maxCount;\n        CacheDiskUtils cache = CACHE_MAP.get(cacheKey);\n        if (cache == null) {\n            synchronized (CacheDiskUtils.class) {\n                cache = CACHE_MAP.get(cacheKey);\n                if (cache == null) {\n                    cache = new CacheDiskUtils(cacheKey, cacheDir, maxSize, maxCount);\n                    CACHE_MAP.put(cacheKey, cache);\n                }\n            }\n        }\n        return cache;\n    }\n\n    private CacheDiskUtils(final String cacheKey,\n                           final File cacheDir,\n                           final long maxSize,\n                           final int maxCount) {\n        mCacheKey = cacheKey;\n        mCacheDir = cacheDir;\n        mMaxSize = maxSize;\n        mMaxCount = maxCount;\n    }\n\n    private DiskCacheManager getDiskCacheManager() {\n        if (mCacheDir.exists()) {\n            if (mDiskCacheManager == null) {\n                mDiskCacheManager = new DiskCacheManager(mCacheDir, mMaxSize, mMaxCount);\n            }\n        } else {\n            if (mCacheDir.mkdirs()) {\n                mDiskCacheManager = new DiskCacheManager(mCacheDir, mMaxSize, mMaxCount);\n            } else {\n                Log.e(\"CacheDiskUtils\", \"can't make dirs in \" + mCacheDir.getAbsolutePath());\n            }\n        }\n        return mDiskCacheManager;\n    }\n\n    @Override\n    public String toString() {\n        return mCacheKey + \"@\" + Integer.toHexString(hashCode());\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about bytes\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put bytes in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public void put(@NonNull final String key, final byte[] value) {\n        put(key, value, -1);\n    }\n\n    /**\n     * Put bytes in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public void put(@NonNull final String key, final byte[] value, final int saveTime) {\n        realPutBytes(TYPE_BYTE + key, value, saveTime);\n    }\n\n    private void realPutBytes(final String key, byte[] value, int saveTime) {\n        if (value == null) return;\n        DiskCacheManager diskCacheManager = getDiskCacheManager();\n        if (diskCacheManager == null) return;\n        if (saveTime >= 0) value = DiskCacheHelper.newByteArrayWithTime(saveTime, value);\n        File file = diskCacheManager.getFileBeforePut(key);\n        UtilsBridge.writeFileFromBytes(file, value);\n        diskCacheManager.updateModify(file);\n        diskCacheManager.put(file);\n    }\n\n\n    /**\n     * Return the bytes in cache.\n     *\n     * @param key The key of cache.\n     * @return the bytes if cache exists or null otherwise\n     */\n    public byte[] getBytes(@NonNull final String key) {\n        return getBytes(key, null);\n    }\n\n    /**\n     * Return the bytes in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the bytes if cache exists or defaultValue otherwise\n     */\n    public byte[] getBytes(@NonNull final String key, final byte[] defaultValue) {\n        return realGetBytes(TYPE_BYTE + key, defaultValue);\n    }\n\n    private byte[] realGetBytes(@NonNull final String key) {\n        return realGetBytes(key, null);\n    }\n\n    private byte[] realGetBytes(@NonNull final String key, final byte[] defaultValue) {\n        DiskCacheManager diskCacheManager = getDiskCacheManager();\n        if (diskCacheManager == null) return defaultValue;\n        final File file = diskCacheManager.getFileIfExists(key);\n        if (file == null) return defaultValue;\n        byte[] data = UtilsBridge.readFile2Bytes(file);\n        if (DiskCacheHelper.isDue(data)) {\n            diskCacheManager.removeByKey(key);\n            return defaultValue;\n        }\n        diskCacheManager.updateModify(file);\n        return DiskCacheHelper.getDataWithoutDueTime(data);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about String\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put string value in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public void put(@NonNull final String key, final String value) {\n        put(key, value, -1);\n    }\n\n    /**\n     * Put string value in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public void put(@NonNull final String key, final String value, final int saveTime) {\n        realPutBytes(TYPE_STRING + key, UtilsBridge.string2Bytes(value), saveTime);\n    }\n\n    /**\n     * Return the string value in cache.\n     *\n     * @param key The key of cache.\n     * @return the string value if cache exists or null otherwise\n     */\n    public String getString(@NonNull final String key) {\n        return getString(key, null);\n    }\n\n    /**\n     * Return the string value in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the string value if cache exists or defaultValue otherwise\n     */\n    public String getString(@NonNull final String key, final String defaultValue) {\n        byte[] bytes = realGetBytes(TYPE_STRING + key);\n        if (bytes == null) return defaultValue;\n        return UtilsBridge.bytes2String(bytes);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about JSONObject\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put JSONObject in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public void put(@NonNull final String key, final JSONObject value) {\n        put(key, value, -1);\n    }\n\n    /**\n     * Put JSONObject in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public void put(@NonNull final String key,\n                    final JSONObject value,\n                    final int saveTime) {\n        realPutBytes(TYPE_JSON_OBJECT + key, UtilsBridge.jsonObject2Bytes(value), saveTime);\n    }\n\n    /**\n     * Return the JSONObject in cache.\n     *\n     * @param key The key of cache.\n     * @return the JSONObject if cache exists or null otherwise\n     */\n    public JSONObject getJSONObject(@NonNull final String key) {\n        return getJSONObject(key, null);\n    }\n\n    /**\n     * Return the JSONObject in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the JSONObject if cache exists or defaultValue otherwise\n     */\n    public JSONObject getJSONObject(@NonNull final String key, final JSONObject defaultValue) {\n        byte[] bytes = realGetBytes(TYPE_JSON_OBJECT + key);\n        if (bytes == null) return defaultValue;\n        return UtilsBridge.bytes2JSONObject(bytes);\n    }\n\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about JSONArray\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put JSONArray in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public void put(@NonNull final String key, final JSONArray value) {\n        put(key, value, -1);\n    }\n\n    /**\n     * Put JSONArray in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public void put(@NonNull final String key, final JSONArray value, final int saveTime) {\n        realPutBytes(TYPE_JSON_ARRAY + key, UtilsBridge.jsonArray2Bytes(value), saveTime);\n    }\n\n    /**\n     * Return the JSONArray in cache.\n     *\n     * @param key The key of cache.\n     * @return the JSONArray if cache exists or null otherwise\n     */\n    public JSONArray getJSONArray(@NonNull final String key) {\n        return getJSONArray(key, null);\n    }\n\n    /**\n     * Return the JSONArray in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the JSONArray if cache exists or defaultValue otherwise\n     */\n    public JSONArray getJSONArray(@NonNull final String key, final JSONArray defaultValue) {\n        byte[] bytes = realGetBytes(TYPE_JSON_ARRAY + key);\n        if (bytes == null) return defaultValue;\n        return UtilsBridge.bytes2JSONArray(bytes);\n    }\n\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Bitmap\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put bitmap in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public void put(@NonNull final String key, final Bitmap value) {\n        put(key, value, -1);\n    }\n\n    /**\n     * Put bitmap in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public void put(@NonNull final String key, final Bitmap value, final int saveTime) {\n        realPutBytes(TYPE_BITMAP + key, UtilsBridge.bitmap2Bytes(value), saveTime);\n    }\n\n    /**\n     * Return the bitmap in cache.\n     *\n     * @param key The key of cache.\n     * @return the bitmap if cache exists or null otherwise\n     */\n    public Bitmap getBitmap(@NonNull final String key) {\n        return getBitmap(key, null);\n    }\n\n    /**\n     * Return the bitmap in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the bitmap if cache exists or defaultValue otherwise\n     */\n    public Bitmap getBitmap(@NonNull final String key, final Bitmap defaultValue) {\n        byte[] bytes = realGetBytes(TYPE_BITMAP + key);\n        if (bytes == null) return defaultValue;\n        return UtilsBridge.bytes2Bitmap(bytes);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Drawable\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put drawable in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public void put(@NonNull final String key, final Drawable value) {\n        put(key, value, -1);\n    }\n\n    /**\n     * Put drawable in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public void put(@NonNull final String key, final Drawable value, final int saveTime) {\n        realPutBytes(TYPE_DRAWABLE + key, UtilsBridge.drawable2Bytes(value), saveTime);\n    }\n\n    /**\n     * Return the drawable in cache.\n     *\n     * @param key The key of cache.\n     * @return the drawable if cache exists or null otherwise\n     */\n    public Drawable getDrawable(@NonNull final String key) {\n        return getDrawable(key, null);\n    }\n\n    /**\n     * Return the drawable in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the drawable if cache exists or defaultValue otherwise\n     */\n    public Drawable getDrawable(@NonNull final String key, final Drawable defaultValue) {\n        byte[] bytes = realGetBytes(TYPE_DRAWABLE + key);\n        if (bytes == null) return defaultValue;\n        return UtilsBridge.bytes2Drawable(bytes);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Parcelable\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put parcelable in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public void put(@NonNull final String key, final Parcelable value) {\n        put(key, value, -1);\n    }\n\n    /**\n     * Put parcelable in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public void put(@NonNull final String key, final Parcelable value, final int saveTime) {\n        realPutBytes(TYPE_PARCELABLE + key, UtilsBridge.parcelable2Bytes(value), saveTime);\n    }\n\n    /**\n     * Return the parcelable in cache.\n     *\n     * @param key     The key of cache.\n     * @param creator The creator.\n     * @param <T>     The value type.\n     * @return the parcelable if cache exists or null otherwise\n     */\n    public <T> T getParcelable(@NonNull final String key,\n                               @NonNull final Parcelable.Creator<T> creator) {\n        return getParcelable(key, creator, null);\n    }\n\n    /**\n     * Return the parcelable in cache.\n     *\n     * @param key          The key of cache.\n     * @param creator      The creator.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @param <T>          The value type.\n     * @return the parcelable if cache exists or defaultValue otherwise\n     */\n    public <T> T getParcelable(@NonNull final String key,\n                               @NonNull final Parcelable.Creator<T> creator,\n                               final T defaultValue) {\n        byte[] bytes = realGetBytes(TYPE_PARCELABLE + key);\n        if (bytes == null) return defaultValue;\n        return UtilsBridge.bytes2Parcelable(bytes, creator);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Serializable\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put serializable in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public void put(@NonNull final String key, final Serializable value) {\n        put(key, value, -1);\n    }\n\n    /**\n     * Put serializable in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public void put(@NonNull final String key, final Serializable value, final int saveTime) {\n        realPutBytes(TYPE_SERIALIZABLE + key, UtilsBridge.serializable2Bytes(value), saveTime);\n    }\n\n    /**\n     * Return the serializable in cache.\n     *\n     * @param key The key of cache.\n     * @return the bitmap if cache exists or null otherwise\n     */\n    public Object getSerializable(@NonNull final String key) {\n        return getSerializable(key, null);\n    }\n\n    /**\n     * Return the serializable in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the bitmap if cache exists or defaultValue otherwise\n     */\n    public Object getSerializable(@NonNull final String key, final Object defaultValue) {\n        byte[] bytes = realGetBytes(TYPE_SERIALIZABLE + key);\n        if (bytes == null) return defaultValue;\n        return UtilsBridge.bytes2Object(bytes);\n    }\n\n    /**\n     * Return the size of cache, in bytes.\n     *\n     * @return the size of cache, in bytes\n     */\n    public long getCacheSize() {\n        DiskCacheManager diskCacheManager = getDiskCacheManager();\n        if (diskCacheManager == null) return 0;\n        return diskCacheManager.getCacheSize();\n    }\n\n    /**\n     * Return the count of cache.\n     *\n     * @return the count of cache\n     */\n    public int getCacheCount() {\n        DiskCacheManager diskCacheManager = getDiskCacheManager();\n        if (diskCacheManager == null) return 0;\n        return diskCacheManager.getCacheCount();\n    }\n\n    /**\n     * Remove the cache by key.\n     *\n     * @param key The key of cache.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public boolean remove(@NonNull final String key) {\n        DiskCacheManager diskCacheManager = getDiskCacheManager();\n        if (diskCacheManager == null) return true;\n        return diskCacheManager.removeByKey(TYPE_BYTE + key)\n                && diskCacheManager.removeByKey(TYPE_STRING + key)\n                && diskCacheManager.removeByKey(TYPE_JSON_OBJECT + key)\n                && diskCacheManager.removeByKey(TYPE_JSON_ARRAY + key)\n                && diskCacheManager.removeByKey(TYPE_BITMAP + key)\n                && diskCacheManager.removeByKey(TYPE_DRAWABLE + key)\n                && diskCacheManager.removeByKey(TYPE_PARCELABLE + key)\n                && diskCacheManager.removeByKey(TYPE_SERIALIZABLE + key);\n    }\n\n    /**\n     * Clear all of the cache.\n     *\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public boolean clear() {\n        DiskCacheManager diskCacheManager = getDiskCacheManager();\n        if (diskCacheManager == null) return true;\n        return diskCacheManager.clear();\n    }\n\n    private static final class DiskCacheManager {\n        private final AtomicLong      cacheSize;\n        private final AtomicInteger   cacheCount;\n        private final long            sizeLimit;\n        private final int             countLimit;\n        private final Map<File, Long> lastUsageDates\n                = Collections.synchronizedMap(new HashMap<File, Long>());\n        private final File            cacheDir;\n        private final Thread          mThread;\n\n        private DiskCacheManager(final File cacheDir, final long sizeLimit, final int countLimit) {\n            this.cacheDir = cacheDir;\n            this.sizeLimit = sizeLimit;\n            this.countLimit = countLimit;\n            cacheSize = new AtomicLong();\n            cacheCount = new AtomicInteger();\n            mThread = new Thread(new Runnable() {\n                @Override\n                public void run() {\n                    int size = 0;\n                    int count = 0;\n                    final File[] cachedFiles = cacheDir.listFiles(new FilenameFilter() {\n                        @Override\n                        public boolean accept(File dir, String name) {\n                            return name.startsWith(CACHE_PREFIX);\n                        }\n                    });\n                    if (cachedFiles != null) {\n                        for (File cachedFile : cachedFiles) {\n                            size += cachedFile.length();\n                            count += 1;\n                            lastUsageDates.put(cachedFile, cachedFile.lastModified());\n                        }\n                        cacheSize.getAndAdd(size);\n                        cacheCount.getAndAdd(count);\n                    }\n                }\n            });\n            mThread.start();\n        }\n\n        private long getCacheSize() {\n            wait2InitOk();\n            return cacheSize.get();\n        }\n\n        private int getCacheCount() {\n            wait2InitOk();\n            return cacheCount.get();\n        }\n\n        private File getFileBeforePut(final String key) {\n            wait2InitOk();\n            File file = new File(cacheDir, getCacheNameByKey(key));\n            if (file.exists()) {\n                cacheCount.addAndGet(-1);\n                cacheSize.addAndGet(-file.length());\n            }\n            return file;\n        }\n\n        private void wait2InitOk() {\n            try {\n                mThread.join();\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n        }\n\n        private File getFileIfExists(final String key) {\n            File file = new File(cacheDir, getCacheNameByKey(key));\n            if (!file.exists()) return null;\n            return file;\n        }\n\n        private String getCacheNameByKey(final String key) {\n            return CACHE_PREFIX + key.substring(0, 3) + key.substring(3).hashCode();\n        }\n\n        private void put(final File file) {\n            cacheCount.addAndGet(1);\n            cacheSize.addAndGet(file.length());\n            while (cacheCount.get() > countLimit || cacheSize.get() > sizeLimit) {\n                cacheSize.addAndGet(-removeOldest());\n                cacheCount.addAndGet(-1);\n            }\n        }\n\n        private void updateModify(final File file) {\n            Long millis = System.currentTimeMillis();\n            file.setLastModified(millis);\n            lastUsageDates.put(file, millis);\n        }\n\n        private boolean removeByKey(final String key) {\n            File file = getFileIfExists(key);\n            if (file == null) return true;\n            if (!file.delete()) return false;\n            cacheSize.addAndGet(-file.length());\n            cacheCount.addAndGet(-1);\n            lastUsageDates.remove(file);\n            return true;\n        }\n\n        private boolean clear() {\n            File[] files = cacheDir.listFiles(new FilenameFilter() {\n                @Override\n                public boolean accept(File dir, String name) {\n                    return name.startsWith(CACHE_PREFIX);\n                }\n            });\n            if (files == null || files.length <= 0) return true;\n            boolean flag = true;\n            for (File file : files) {\n                if (!file.delete()) {\n                    flag = false;\n                    continue;\n                }\n                cacheSize.addAndGet(-file.length());\n                cacheCount.addAndGet(-1);\n                lastUsageDates.remove(file);\n            }\n            if (flag) {\n                lastUsageDates.clear();\n                cacheSize.set(0);\n                cacheCount.set(0);\n            }\n            return flag;\n        }\n\n        /**\n         * Remove the oldest files.\n         *\n         * @return the size of oldest files, in bytes\n         */\n        private long removeOldest() {\n            if (lastUsageDates.isEmpty()) return 0;\n            Long oldestUsage = Long.MAX_VALUE;\n            File oldestFile = null;\n            Set<Map.Entry<File, Long>> entries = lastUsageDates.entrySet();\n            synchronized (lastUsageDates) {\n                for (Map.Entry<File, Long> entry : entries) {\n                    Long lastValueUsage = entry.getValue();\n                    if (lastValueUsage < oldestUsage) {\n                        oldestUsage = lastValueUsage;\n                        oldestFile = entry.getKey();\n                    }\n                }\n            }\n            if (oldestFile == null) return 0;\n            long fileSize = oldestFile.length();\n            if (oldestFile.delete()) {\n                lastUsageDates.remove(oldestFile);\n                return fileSize;\n            }\n            return 0;\n        }\n    }\n\n    private static final class DiskCacheHelper {\n\n        static final int TIME_INFO_LEN = 14;\n\n        private static byte[] newByteArrayWithTime(final int second, final byte[] data) {\n            byte[] time = createDueTime(second).getBytes();\n            byte[] content = new byte[time.length + data.length];\n            System.arraycopy(time, 0, content, 0, time.length);\n            System.arraycopy(data, 0, content, time.length, data.length);\n            return content;\n        }\n\n        /**\n         * Return the string of due time.\n         *\n         * @param seconds The seconds.\n         * @return the string of due time\n         */\n        private static String createDueTime(final int seconds) {\n            return String.format(\n                    Locale.getDefault(), \"_$%010d$_\",\n                    System.currentTimeMillis() / 1000 + seconds\n            );\n        }\n\n        private static boolean isDue(final byte[] data) {\n            long millis = getDueTime(data);\n            return millis != -1 && System.currentTimeMillis() > millis;\n        }\n\n        private static long getDueTime(final byte[] data) {\n            if (hasTimeInfo(data)) {\n                String millis = new String(copyOfRange(data, 2, 12));\n                try {\n                    return Long.parseLong(millis) * 1000;\n                } catch (NumberFormatException e) {\n                    return -1;\n                }\n            }\n            return -1;\n        }\n\n        private static byte[] getDataWithoutDueTime(final byte[] data) {\n            if (hasTimeInfo(data)) {\n                return copyOfRange(data, TIME_INFO_LEN, data.length);\n            }\n            return data;\n        }\n\n        private static byte[] copyOfRange(final byte[] original, final int from, final int to) {\n            int newLength = to - from;\n            if (newLength < 0) throw new IllegalArgumentException(from + \" > \" + to);\n            byte[] copy = new byte[newLength];\n            System.arraycopy(original, from, copy, 0, Math.min(original.length - from, newLength));\n            return copy;\n        }\n\n        private static boolean hasTimeInfo(final byte[] data) {\n            return data != null\n                    && data.length >= TIME_INFO_LEN\n                    && data[0] == '_'\n                    && data[1] == '$'\n                    && data[12] == '$'\n                    && data[13] == '_';\n        }\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDoubleStaticUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.graphics.Bitmap;\nimport android.graphics.drawable.Drawable;\nimport android.os.Parcelable;\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport java.io.Serializable;\n\nimport androidx.annotation.NonNull;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/01/04\n *     desc  : utils about double cache\n * </pre>\n */\npublic final class CacheDoubleStaticUtils {\n\n    private static CacheDoubleUtils sDefaultCacheDoubleUtils;\n\n    /**\n     * Set the default instance of {@link CacheDoubleUtils}.\n     *\n     * @param cacheDoubleUtils The default instance of {@link CacheDoubleUtils}.\n     */\n    public static void setDefaultCacheDoubleUtils(final CacheDoubleUtils cacheDoubleUtils) {\n        sDefaultCacheDoubleUtils = cacheDoubleUtils;\n    }\n\n    /**\n     * Put bytes in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public static void put(@NonNull final String key, final byte[] value) {\n        put(key, value, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Put bytes in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public static void put(@NonNull final String key, byte[] value, final int saveTime) {\n        put(key, value, saveTime, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Return the bytes in cache.\n     *\n     * @param key The key of cache.\n     * @return the bytes if cache exists or null otherwise\n     */\n    public static byte[] getBytes(@NonNull final String key) {\n        return getBytes(key, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Return the bytes in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the bytes if cache exists or defaultValue otherwise\n     */\n    public static byte[] getBytes(@NonNull final String key, final byte[] defaultValue) {\n        return getBytes(key, defaultValue, getDefaultCacheDoubleUtils());\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about String\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put string value in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public static void put(@NonNull final String key, final String value) {\n        put(key, value, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Put string value in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public static void put(@NonNull final String key, final String value, final int saveTime) {\n        put(key, value, saveTime, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Return the string value in cache.\n     *\n     * @param key The key of cache.\n     * @return the string value if cache exists or null otherwise\n     */\n    public static String getString(@NonNull final String key) {\n        return getString(key, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Return the string value in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the string value if cache exists or defaultValue otherwise\n     */\n    public static String getString(@NonNull final String key, final String defaultValue) {\n        return getString(key, defaultValue, getDefaultCacheDoubleUtils());\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about JSONObject\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put JSONObject in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public static void put(@NonNull final String key, final JSONObject value) {\n        put(key, value, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Put JSONObject in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public static void put(@NonNull final String key,\n                           final JSONObject value,\n                           final int saveTime) {\n        put(key, value, saveTime, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Return the JSONObject in cache.\n     *\n     * @param key The key of cache.\n     * @return the JSONObject if cache exists or null otherwise\n     */\n    public static JSONObject getJSONObject(@NonNull final String key) {\n        return getJSONObject(key, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Return the JSONObject in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the JSONObject if cache exists or defaultValue otherwise\n     */\n    public static JSONObject getJSONObject(@NonNull final String key, final JSONObject defaultValue) {\n        return getJSONObject(key, defaultValue, getDefaultCacheDoubleUtils());\n    }\n\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about JSONArray\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put JSONArray in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public static void put(@NonNull final String key, final JSONArray value) {\n        put(key, value, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Put JSONArray in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public static void put(@NonNull final String key, final JSONArray value, final int saveTime) {\n        put(key, value, saveTime, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Return the JSONArray in cache.\n     *\n     * @param key The key of cache.\n     * @return the JSONArray if cache exists or null otherwise\n     */\n    public static JSONArray getJSONArray(@NonNull final String key) {\n        return getJSONArray(key, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Return the JSONArray in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the JSONArray if cache exists or defaultValue otherwise\n     */\n    public static JSONArray getJSONArray(@NonNull final String key, final JSONArray defaultValue) {\n        return getJSONArray(key, defaultValue, getDefaultCacheDoubleUtils());\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // Bitmap cache\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put bitmap in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public static void put(@NonNull final String key, final Bitmap value) {\n        put(key, value, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Put bitmap in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public static void put(@NonNull final String key, final Bitmap value, final int saveTime) {\n        put(key, value, saveTime, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Return the bitmap in cache.\n     *\n     * @param key The key of cache.\n     * @return the bitmap if cache exists or null otherwise\n     */\n    public static Bitmap getBitmap(@NonNull final String key) {\n        return getBitmap(key, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Return the bitmap in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the bitmap if cache exists or defaultValue otherwise\n     */\n    public static Bitmap getBitmap(@NonNull final String key, final Bitmap defaultValue) {\n        return getBitmap(key, defaultValue, getDefaultCacheDoubleUtils());\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Drawable\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put drawable in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public static void put(@NonNull final String key, final Drawable value) {\n        put(key, value, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Put drawable in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public static void put(@NonNull final String key, final Drawable value, final int saveTime) {\n        put(key, value, saveTime, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Return the drawable in cache.\n     *\n     * @param key The key of cache.\n     * @return the drawable if cache exists or null otherwise\n     */\n    public static Drawable getDrawable(@NonNull final String key) {\n        return getDrawable(key, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Return the drawable in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the drawable if cache exists or defaultValue otherwise\n     */\n    public static Drawable getDrawable(@NonNull final String key, final Drawable defaultValue) {\n        return getDrawable(key, defaultValue, getDefaultCacheDoubleUtils());\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Parcelable\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put parcelable in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public static void put(@NonNull final String key, final Parcelable value) {\n        put(key, value, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Put parcelable in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public static void put(@NonNull final String key, final Parcelable value, final int saveTime) {\n        put(key, value, saveTime, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Return the parcelable in cache.\n     *\n     * @param key     The key of cache.\n     * @param creator The creator.\n     * @param <T>     The value type.\n     * @return the parcelable if cache exists or null otherwise\n     */\n    public static <T> T getParcelable(@NonNull final String key,\n                                      @NonNull final Parcelable.Creator<T> creator) {\n        return getParcelable(key, creator, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Return the parcelable in cache.\n     *\n     * @param key          The key of cache.\n     * @param creator      The creator.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @param <T>          The value type.\n     * @return the parcelable if cache exists or defaultValue otherwise\n     */\n    public static <T> T getParcelable(@NonNull final String key,\n                                      @NonNull final Parcelable.Creator<T> creator,\n                                      final T defaultValue) {\n        return getParcelable(key, creator, defaultValue, getDefaultCacheDoubleUtils());\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Serializable\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put serializable in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public static void put(@NonNull final String key, final Serializable value) {\n        put(key, value, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Put serializable in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public static void put(@NonNull final String key, final Serializable value, final int saveTime) {\n        put(key, value, saveTime, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Return the serializable in cache.\n     *\n     * @param key The key of cache.\n     * @return the bitmap if cache exists or null otherwise\n     */\n    public static Object getSerializable(@NonNull final String key) {\n        return getSerializable(key, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Return the serializable in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the bitmap if cache exists or defaultValue otherwise\n     */\n    public static Object getSerializable(@NonNull final String key, final Object defaultValue) {\n        return getSerializable(key, defaultValue, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Return the size of cache in disk.\n     *\n     * @return the size of cache in disk\n     */\n    public static long getCacheDiskSize() {\n        return getCacheDiskSize(getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Return the count of cache in disk.\n     *\n     * @return the count of cache in disk\n     */\n    public static int getCacheDiskCount() {\n        return getCacheDiskCount(getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Return the count of cache in memory.\n     *\n     * @return the count of cache in memory.\n     */\n    public static int getCacheMemoryCount() {\n        return getCacheMemoryCount(getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Remove the cache by key.\n     *\n     * @param key The key of cache.\n     */\n    public static void remove(@NonNull String key) {\n        remove(key, getDefaultCacheDoubleUtils());\n    }\n\n    /**\n     * Clear all of the cache.\n     */\n    public static void clear() {\n        clear(getDefaultCacheDoubleUtils());\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // dividing line\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put bytes in cache.\n     *\n     * @param key              The key of cache.\n     * @param value            The value of cache.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final byte[] value,\n                           @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        cacheDoubleUtils.put(key, value);\n    }\n\n    /**\n     * Put bytes in cache.\n     *\n     * @param key              The key of cache.\n     * @param value            The value of cache.\n     * @param saveTime         The save time of cache, in seconds.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final byte[] value,\n                           final int saveTime,\n                           @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        cacheDoubleUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the bytes in cache.\n     *\n     * @param key              The key of cache.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     * @return the bytes if cache exists or null otherwise\n     */\n    public static byte[] getBytes(@NonNull final String key, @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        return cacheDoubleUtils.getBytes(key);\n    }\n\n    /**\n     * Return the bytes in cache.\n     *\n     * @param key              The key of cache.\n     * @param defaultValue     The default value if the cache doesn't exist.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     * @return the bytes if cache exists or defaultValue otherwise\n     */\n    public static byte[] getBytes(@NonNull final String key,\n                                  final byte[] defaultValue,\n                                  @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        return cacheDoubleUtils.getBytes(key, defaultValue);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about String\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put string value in cache.\n     *\n     * @param key              The key of cache.\n     * @param value            The value of cache.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final String value,\n                           @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        cacheDoubleUtils.put(key, value);\n    }\n\n    /**\n     * Put string value in cache.\n     *\n     * @param key              The key of cache.\n     * @param value            The value of cache.\n     * @param saveTime         The save time of cache, in seconds.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final String value,\n                           final int saveTime,\n                           @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        cacheDoubleUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the string value in cache.\n     *\n     * @param key              The key of cache.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     * @return the string value if cache exists or null otherwise\n     */\n    public static String getString(@NonNull final String key, @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        return cacheDoubleUtils.getString(key);\n    }\n\n    /**\n     * Return the string value in cache.\n     *\n     * @param key              The key of cache.\n     * @param defaultValue     The default value if the cache doesn't exist.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     * @return the string value if cache exists or defaultValue otherwise\n     */\n    public static String getString(@NonNull final String key,\n                                   final String defaultValue,\n                                   @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        return cacheDoubleUtils.getString(key, defaultValue);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about JSONObject\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put JSONObject in cache.\n     *\n     * @param key              The key of cache.\n     * @param value            The value of cache.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final JSONObject value,\n                           @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        cacheDoubleUtils.put(key, value);\n    }\n\n    /**\n     * Put JSONObject in cache.\n     *\n     * @param key              The key of cache.\n     * @param value            The value of cache.\n     * @param saveTime         The save time of cache, in seconds.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final JSONObject value,\n                           final int saveTime,\n                           @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        cacheDoubleUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the JSONObject in cache.\n     *\n     * @param key              The key of cache.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     * @return the JSONObject if cache exists or null otherwise\n     */\n    public static JSONObject getJSONObject(@NonNull final String key,\n                                           @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        return cacheDoubleUtils.getJSONObject(key);\n    }\n\n    /**\n     * Return the JSONObject in cache.\n     *\n     * @param key              The key of cache.\n     * @param defaultValue     The default value if the cache doesn't exist.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     * @return the JSONObject if cache exists or defaultValue otherwise\n     */\n    public static JSONObject getJSONObject(@NonNull final String key,\n                                           final JSONObject defaultValue,\n                                           @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        return cacheDoubleUtils.getJSONObject(key, defaultValue);\n    }\n\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about JSONArray\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put JSONArray in cache.\n     *\n     * @param key              The key of cache.\n     * @param value            The value of cache.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final JSONArray value,\n                           @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        cacheDoubleUtils.put(key, value);\n    }\n\n    /**\n     * Put JSONArray in cache.\n     *\n     * @param key              The key of cache.\n     * @param value            The value of cache.\n     * @param saveTime         The save time of cache, in seconds.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final JSONArray value,\n                           final int saveTime,\n                           @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        cacheDoubleUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the JSONArray in cache.\n     *\n     * @param key              The key of cache.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     * @return the JSONArray if cache exists or null otherwise\n     */\n    public static JSONArray getJSONArray(@NonNull final String key, @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        return cacheDoubleUtils.getJSONArray(key);\n    }\n\n    /**\n     * Return the JSONArray in cache.\n     *\n     * @param key              The key of cache.\n     * @param defaultValue     The default value if the cache doesn't exist.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     * @return the JSONArray if cache exists or defaultValue otherwise\n     */\n    public static JSONArray getJSONArray(@NonNull final String key,\n                                         final JSONArray defaultValue,\n                                         @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        return cacheDoubleUtils.getJSONArray(key, defaultValue);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // Bitmap cache\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put bitmap in cache.\n     *\n     * @param key              The key of cache.\n     * @param value            The value of cache.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final Bitmap value,\n                           @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        cacheDoubleUtils.put(key, value);\n    }\n\n    /**\n     * Put bitmap in cache.\n     *\n     * @param key              The key of cache.\n     * @param value            The value of cache.\n     * @param saveTime         The save time of cache, in seconds.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final Bitmap value,\n                           final int saveTime,\n                           @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        cacheDoubleUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the bitmap in cache.\n     *\n     * @param key              The key of cache.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     * @return the bitmap if cache exists or null otherwise\n     */\n    public static Bitmap getBitmap(@NonNull final String key, @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        return cacheDoubleUtils.getBitmap(key);\n    }\n\n    /**\n     * Return the bitmap in cache.\n     *\n     * @param key              The key of cache.\n     * @param defaultValue     The default value if the cache doesn't exist.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     * @return the bitmap if cache exists or defaultValue otherwise\n     */\n    public static Bitmap getBitmap(@NonNull final String key,\n                                   final Bitmap defaultValue,\n                                   @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        return cacheDoubleUtils.getBitmap(key, defaultValue);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Drawable\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put drawable in cache.\n     *\n     * @param key              The key of cache.\n     * @param value            The value of cache.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final Drawable value,\n                           @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        cacheDoubleUtils.put(key, value);\n    }\n\n    /**\n     * Put drawable in cache.\n     *\n     * @param key              The key of cache.\n     * @param value            The value of cache.\n     * @param saveTime         The save time of cache, in seconds.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final Drawable value,\n                           final int saveTime,\n                           @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        cacheDoubleUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the drawable in cache.\n     *\n     * @param key              The key of cache.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     * @return the drawable if cache exists or null otherwise\n     */\n    public static Drawable getDrawable(@NonNull final String key, @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        return cacheDoubleUtils.getDrawable(key);\n    }\n\n    /**\n     * Return the drawable in cache.\n     *\n     * @param key              The key of cache.\n     * @param defaultValue     The default value if the cache doesn't exist.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     * @return the drawable if cache exists or defaultValue otherwise\n     */\n    public static Drawable getDrawable(@NonNull final String key,\n                                       final Drawable defaultValue,\n                                       @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        return cacheDoubleUtils.getDrawable(key, defaultValue);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Parcelable\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put parcelable in cache.\n     *\n     * @param key              The key of cache.\n     * @param value            The value of cache.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final Parcelable value,\n                           @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        cacheDoubleUtils.put(key, value);\n    }\n\n    /**\n     * Put parcelable in cache.\n     *\n     * @param key              The key of cache.\n     * @param value            The value of cache.\n     * @param saveTime         The save time of cache, in seconds.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final Parcelable value,\n                           final int saveTime,\n                           @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        cacheDoubleUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the parcelable in cache.\n     *\n     * @param key              The key of cache.\n     * @param creator          The creator.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     * @param <T>              The value type.\n     * @return the parcelable if cache exists or null otherwise\n     */\n    public static <T> T getParcelable(@NonNull final String key,\n                                      @NonNull final Parcelable.Creator<T> creator,\n                                      @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        return cacheDoubleUtils.getParcelable(key, creator);\n    }\n\n    /**\n     * Return the parcelable in cache.\n     *\n     * @param key              The key of cache.\n     * @param creator          The creator.\n     * @param defaultValue     The default value if the cache doesn't exist.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     * @param <T>              The value type.\n     * @return the parcelable if cache exists or defaultValue otherwise\n     */\n    public static <T> T getParcelable(@NonNull final String key,\n                                      @NonNull final Parcelable.Creator<T> creator,\n                                      final T defaultValue,\n                                      @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        return cacheDoubleUtils.getParcelable(key, creator, defaultValue);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Serializable\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put serializable in cache.\n     *\n     * @param key              The key of cache.\n     * @param value            The value of cache.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final Serializable value,\n                           @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        cacheDoubleUtils.put(key, value);\n    }\n\n    /**\n     * Put serializable in cache.\n     *\n     * @param key              The key of cache.\n     * @param value            The value of cache.\n     * @param saveTime         The save time of cache, in seconds.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final Serializable value,\n                           final int saveTime,\n                           @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        cacheDoubleUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the serializable in cache.\n     *\n     * @param key              The key of cache.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     * @return the bitmap if cache exists or null otherwise\n     */\n    public static Object getSerializable(@NonNull final String key, @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        return cacheDoubleUtils.getSerializable(key);\n    }\n\n    /**\n     * Return the serializable in cache.\n     *\n     * @param key              The key of cache.\n     * @param defaultValue     The default value if the cache doesn't exist.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     * @return the bitmap if cache exists or defaultValue otherwise\n     */\n    public static Object getSerializable(@NonNull final String key,\n                                         final Object defaultValue,\n                                         @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        return cacheDoubleUtils.getSerializable(key, defaultValue);\n    }\n\n    /**\n     * Return the size of cache in disk.\n     *\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     * @return the size of cache in disk\n     */\n    public static long getCacheDiskSize(@NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        return cacheDoubleUtils.getCacheDiskSize();\n    }\n\n    /**\n     * Return the count of cache in disk.\n     *\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     * @return the count of cache in disk\n     */\n    public static int getCacheDiskCount(@NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        return cacheDoubleUtils.getCacheDiskCount();\n    }\n\n    /**\n     * Return the count of cache in memory.\n     *\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     * @return the count of cache in memory.\n     */\n    public static int getCacheMemoryCount(@NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        return cacheDoubleUtils.getCacheMemoryCount();\n    }\n\n    /**\n     * Remove the cache by key.\n     *\n     * @param key              The key of cache.\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     */\n    public static void remove(@NonNull String key, @NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        cacheDoubleUtils.remove(key);\n    }\n\n    /**\n     * Clear all of the cache.\n     *\n     * @param cacheDoubleUtils The instance of {@link CacheDoubleUtils}.\n     */\n    public static void clear(@NonNull final CacheDoubleUtils cacheDoubleUtils) {\n        cacheDoubleUtils.clear();\n    }\n\n    private static CacheDoubleUtils getDefaultCacheDoubleUtils() {\n        return sDefaultCacheDoubleUtils != null ? sDefaultCacheDoubleUtils : CacheDoubleUtils.getInstance();\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheDoubleUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.graphics.Bitmap;\nimport android.graphics.drawable.Drawable;\nimport android.os.Parcelable;\nimport androidx.annotation.NonNull;\n\nimport com.blankj.utilcode.constant.CacheConstants;\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/06/13\n *     desc  : utils about double cache\n * </pre>\n */\npublic final class CacheDoubleUtils implements CacheConstants {\n\n    private static final Map<String, CacheDoubleUtils> CACHE_MAP = new HashMap<>();\n\n    private final CacheMemoryUtils mCacheMemoryUtils;\n    private final CacheDiskUtils   mCacheDiskUtils;\n\n    /**\n     * Return the single {@link CacheDoubleUtils} instance.\n     *\n     * @return the single {@link CacheDoubleUtils} instance\n     */\n    public static CacheDoubleUtils getInstance() {\n        return getInstance(CacheMemoryUtils.getInstance(), CacheDiskUtils.getInstance());\n    }\n\n    /**\n     * Return the single {@link CacheDoubleUtils} instance.\n     *\n     * @param cacheMemoryUtils The instance of {@link CacheMemoryUtils}.\n     * @param cacheDiskUtils   The instance of {@link CacheDiskUtils}.\n     * @return the single {@link CacheDoubleUtils} instance\n     */\n    public static CacheDoubleUtils getInstance(@NonNull final CacheMemoryUtils cacheMemoryUtils,\n                                               @NonNull final CacheDiskUtils cacheDiskUtils) {\n        final String cacheKey = cacheDiskUtils.toString() + \"_\" + cacheMemoryUtils.toString();\n        CacheDoubleUtils cache = CACHE_MAP.get(cacheKey);\n        if (cache == null) {\n            synchronized (CacheDoubleUtils.class) {\n                cache = CACHE_MAP.get(cacheKey);\n                if (cache == null) {\n                    cache = new CacheDoubleUtils(cacheMemoryUtils, cacheDiskUtils);\n                    CACHE_MAP.put(cacheKey, cache);\n                }\n            }\n        }\n        return cache;\n    }\n\n    private CacheDoubleUtils(CacheMemoryUtils cacheMemoryUtils, CacheDiskUtils cacheUtils) {\n        mCacheMemoryUtils = cacheMemoryUtils;\n        mCacheDiskUtils = cacheUtils;\n    }\n\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about bytes\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put bytes in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public void put(@NonNull final String key, final byte[] value) {\n        put(key, value, -1);\n    }\n\n    /**\n     * Put bytes in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public void put(@NonNull final String key, byte[] value, final int saveTime) {\n        mCacheMemoryUtils.put(key, value, saveTime);\n        mCacheDiskUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the bytes in cache.\n     *\n     * @param key The key of cache.\n     * @return the bytes if cache exists or null otherwise\n     */\n    public byte[] getBytes(@NonNull final String key) {\n        return getBytes(key, null);\n    }\n\n    /**\n     * Return the bytes in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the bytes if cache exists or defaultValue otherwise\n     */\n    public byte[] getBytes(@NonNull final String key, final byte[] defaultValue) {\n        byte[] obj = mCacheMemoryUtils.get(key);\n        if (obj != null) return obj;\n        byte[] bytes = mCacheDiskUtils.getBytes(key);\n        if (bytes != null) {\n            mCacheMemoryUtils.put(key, bytes);\n            return bytes;\n        }\n        return defaultValue;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about String\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put string value in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public void put(@NonNull final String key, final String value) {\n        put(key, value, -1);\n    }\n\n    /**\n     * Put string value in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public void put(@NonNull final String key, final String value, final int saveTime) {\n        mCacheMemoryUtils.put(key, value, saveTime);\n        mCacheDiskUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the string value in cache.\n     *\n     * @param key The key of cache.\n     * @return the string value if cache exists or null otherwise\n     */\n    public String getString(@NonNull final String key) {\n        return getString(key, null);\n    }\n\n    /**\n     * Return the string value in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the string value if cache exists or defaultValue otherwise\n     */\n    public String getString(@NonNull final String key, final String defaultValue) {\n        String obj = mCacheMemoryUtils.get(key);\n        if (obj != null) return obj;\n        String string = mCacheDiskUtils.getString(key);\n        if (string != null) {\n            mCacheMemoryUtils.put(key, string);\n            return string;\n        }\n        return defaultValue;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about JSONObject\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put JSONObject in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public void put(@NonNull final String key, final JSONObject value) {\n        put(key, value, -1);\n    }\n\n    /**\n     * Put JSONObject in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public void put(@NonNull final String key,\n                    final JSONObject value,\n                    final int saveTime) {\n        mCacheMemoryUtils.put(key, value, saveTime);\n        mCacheDiskUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the JSONObject in cache.\n     *\n     * @param key The key of cache.\n     * @return the JSONObject if cache exists or null otherwise\n     */\n    public JSONObject getJSONObject(@NonNull final String key) {\n        return getJSONObject(key, null);\n    }\n\n    /**\n     * Return the JSONObject in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the JSONObject if cache exists or defaultValue otherwise\n     */\n    public JSONObject getJSONObject(@NonNull final String key, final JSONObject defaultValue) {\n        JSONObject obj = mCacheMemoryUtils.get(key);\n        if (obj != null) return obj;\n        JSONObject jsonObject = mCacheDiskUtils.getJSONObject(key);\n        if (jsonObject != null) {\n            mCacheMemoryUtils.put(key, jsonObject);\n            return jsonObject;\n        }\n        return defaultValue;\n    }\n\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about JSONArray\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put JSONArray in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public void put(@NonNull final String key, final JSONArray value) {\n        put(key, value, -1);\n    }\n\n    /**\n     * Put JSONArray in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public void put(@NonNull final String key, final JSONArray value, final int saveTime) {\n        mCacheMemoryUtils.put(key, value, saveTime);\n        mCacheDiskUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the JSONArray in cache.\n     *\n     * @param key The key of cache.\n     * @return the JSONArray if cache exists or null otherwise\n     */\n    public JSONArray getJSONArray(@NonNull final String key) {\n        return getJSONArray(key, null);\n    }\n\n    /**\n     * Return the JSONArray in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the JSONArray if cache exists or defaultValue otherwise\n     */\n    public JSONArray getJSONArray(@NonNull final String key, final JSONArray defaultValue) {\n        JSONArray obj = mCacheMemoryUtils.get(key);\n        if (obj != null) return obj;\n        JSONArray jsonArray = mCacheDiskUtils.getJSONArray(key);\n        if (jsonArray != null) {\n            mCacheMemoryUtils.put(key, jsonArray);\n            return jsonArray;\n        }\n        return defaultValue;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // Bitmap cache\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put bitmap in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public void put(@NonNull final String key, final Bitmap value) {\n        put(key, value, -1);\n    }\n\n    /**\n     * Put bitmap in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public void put(@NonNull final String key, final Bitmap value, final int saveTime) {\n        mCacheMemoryUtils.put(key, value, saveTime);\n        mCacheDiskUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the bitmap in cache.\n     *\n     * @param key The key of cache.\n     * @return the bitmap if cache exists or null otherwise\n     */\n    public Bitmap getBitmap(@NonNull final String key) {\n        return getBitmap(key, null);\n    }\n\n    /**\n     * Return the bitmap in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the bitmap if cache exists or defaultValue otherwise\n     */\n    public Bitmap getBitmap(@NonNull final String key, final Bitmap defaultValue) {\n        Bitmap obj = mCacheMemoryUtils.get(key);\n        if (obj != null) return obj;\n        Bitmap bitmap = mCacheDiskUtils.getBitmap(key);\n        if (bitmap != null) {\n            mCacheMemoryUtils.put(key, bitmap);\n            return bitmap;\n        }\n        return defaultValue;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Drawable\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put drawable in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public void put(@NonNull final String key, final Drawable value) {\n        put(key, value, -1);\n    }\n\n    /**\n     * Put drawable in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public void put(@NonNull final String key, final Drawable value, final int saveTime) {\n        mCacheMemoryUtils.put(key, value, saveTime);\n        mCacheDiskUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the drawable in cache.\n     *\n     * @param key The key of cache.\n     * @return the drawable if cache exists or null otherwise\n     */\n    public Drawable getDrawable(@NonNull final String key) {\n        return getDrawable(key, null);\n    }\n\n    /**\n     * Return the drawable in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the drawable if cache exists or defaultValue otherwise\n     */\n    public Drawable getDrawable(@NonNull final String key, final Drawable defaultValue) {\n        Drawable obj = mCacheMemoryUtils.get(key);\n        if (obj != null) return obj;\n        Drawable drawable = mCacheDiskUtils.getDrawable(key);\n        if (drawable != null) {\n            mCacheMemoryUtils.put(key, drawable);\n            return drawable;\n        }\n        return defaultValue;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Parcelable\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put parcelable in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public void put(@NonNull final String key, final Parcelable value) {\n        put(key, value, -1);\n    }\n\n    /**\n     * Put parcelable in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public void put(@NonNull final String key, final Parcelable value, final int saveTime) {\n        mCacheMemoryUtils.put(key, value, saveTime);\n        mCacheDiskUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the parcelable in cache.\n     *\n     * @param key     The key of cache.\n     * @param creator The creator.\n     * @param <T>     The value type.\n     * @return the parcelable if cache exists or null otherwise\n     */\n    public <T> T getParcelable(@NonNull final String key,\n                               @NonNull final Parcelable.Creator<T> creator) {\n        return getParcelable(key, creator, null);\n    }\n\n    /**\n     * Return the parcelable in cache.\n     *\n     * @param key          The key of cache.\n     * @param creator      The creator.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @param <T>          The value type.\n     * @return the parcelable if cache exists or defaultValue otherwise\n     */\n    public <T> T getParcelable(@NonNull final String key,\n                               @NonNull final Parcelable.Creator<T> creator,\n                               final T defaultValue) {\n        T value = mCacheMemoryUtils.get(key);\n        if (value != null) return value;\n        T val = mCacheDiskUtils.getParcelable(key, creator);\n        if (val != null) {\n            mCacheMemoryUtils.put(key, val);\n            return val;\n        }\n        return defaultValue;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about Serializable\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put serializable in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public void put(@NonNull final String key, final Serializable value) {\n        put(key, value, -1);\n    }\n\n    /**\n     * Put serializable in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public void put(@NonNull final String key, final Serializable value, final int saveTime) {\n        mCacheMemoryUtils.put(key, value, saveTime);\n        mCacheDiskUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the serializable in cache.\n     *\n     * @param key The key of cache.\n     * @return the bitmap if cache exists or null otherwise\n     */\n    public Object getSerializable(@NonNull final String key) {\n        return getSerializable(key, null);\n    }\n\n    /**\n     * Return the serializable in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @return the bitmap if cache exists or defaultValue otherwise\n     */\n    public Object getSerializable(@NonNull final String key, final Object defaultValue) {\n        Object obj = mCacheMemoryUtils.get(key);\n        if (obj != null) return obj;\n        Object serializable = mCacheDiskUtils.getSerializable(key);\n        if (serializable != null) {\n            mCacheMemoryUtils.put(key, serializable);\n            return serializable;\n        }\n        return defaultValue;\n    }\n\n    /**\n     * Return the size of cache in disk.\n     *\n     * @return the size of cache in disk\n     */\n    public long getCacheDiskSize() {\n        return mCacheDiskUtils.getCacheSize();\n    }\n\n    /**\n     * Return the count of cache in disk.\n     *\n     * @return the count of cache in disk\n     */\n    public int getCacheDiskCount() {\n        return mCacheDiskUtils.getCacheCount();\n    }\n\n    /**\n     * Return the count of cache in memory.\n     *\n     * @return the count of cache in memory.\n     */\n    public int getCacheMemoryCount() {\n        return mCacheMemoryUtils.getCacheCount();\n    }\n\n    /**\n     * Remove the cache by key.\n     *\n     * @param key The key of cache.\n     */\n    public void remove(@NonNull String key) {\n        mCacheMemoryUtils.remove(key);\n        mCacheDiskUtils.remove(key);\n    }\n\n    /**\n     * Clear all of the cache.\n     */\n    public void clear() {\n        mCacheMemoryUtils.clear();\n        mCacheDiskUtils.clear();\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheMemoryStaticUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport androidx.annotation.NonNull;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/01/04\n *     desc  : utils about memory cache\n * </pre>\n */\npublic final class CacheMemoryStaticUtils {\n\n    private static CacheMemoryUtils sDefaultCacheMemoryUtils;\n\n    /**\n     * Set the default instance of {@link CacheMemoryUtils}.\n     *\n     * @param cacheMemoryUtils The default instance of {@link CacheMemoryUtils}.\n     */\n    public static void setDefaultCacheMemoryUtils(final CacheMemoryUtils cacheMemoryUtils) {\n        sDefaultCacheMemoryUtils = cacheMemoryUtils;\n    }\n\n    /**\n     * Put bytes in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public static void put(@NonNull final String key, final Object value) {\n        put(key, value, getDefaultCacheMemoryUtils());\n    }\n\n    /**\n     * Put bytes in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public static void put(@NonNull final String key, final Object value, int saveTime) {\n        put(key, value, saveTime, getDefaultCacheMemoryUtils());\n    }\n\n    /**\n     * Return the value in cache.\n     *\n     * @param key The key of cache.\n     * @param <T> The value type.\n     * @return the value if cache exists or null otherwise\n     */\n    public static <T> T get(@NonNull final String key) {\n        return get(key, getDefaultCacheMemoryUtils());\n    }\n\n    /**\n     * Return the value in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @param <T>          The value type.\n     * @return the value if cache exists or defaultValue otherwise\n     */\n    public static <T> T get(@NonNull final String key, final T defaultValue) {\n        return get(key, defaultValue, getDefaultCacheMemoryUtils());\n    }\n\n    /**\n     * Return the count of cache.\n     *\n     * @return the count of cache\n     */\n    public static int getCacheCount() {\n        return getCacheCount(getDefaultCacheMemoryUtils());\n    }\n\n    /**\n     * Remove the cache by key.\n     *\n     * @param key The key of cache.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static Object remove(@NonNull final String key) {\n        return remove(key, getDefaultCacheMemoryUtils());\n    }\n\n    /**\n     * Clear all of the cache.\n     */\n    public static void clear() {\n        clear(getDefaultCacheMemoryUtils());\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // dividing line\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put bytes in cache.\n     *\n     * @param key              The key of cache.\n     * @param value            The value of cache.\n     * @param cacheMemoryUtils The instance of {@link CacheMemoryUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final Object value,\n                           @NonNull final CacheMemoryUtils cacheMemoryUtils) {\n        cacheMemoryUtils.put(key, value);\n    }\n\n    /**\n     * Put bytes in cache.\n     *\n     * @param key              The key of cache.\n     * @param value            The value of cache.\n     * @param saveTime         The save time of cache, in seconds.\n     * @param cacheMemoryUtils The instance of {@link CacheMemoryUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final Object value,\n                           int saveTime,\n                           @NonNull final CacheMemoryUtils cacheMemoryUtils) {\n        cacheMemoryUtils.put(key, value, saveTime);\n    }\n\n    /**\n     * Return the value in cache.\n     *\n     * @param key              The key of cache.\n     * @param cacheMemoryUtils The instance of {@link CacheMemoryUtils}.\n     * @param <T>              The value type.\n     * @return the value if cache exists or null otherwise\n     */\n    public static <T> T get(@NonNull final String key, @NonNull final CacheMemoryUtils cacheMemoryUtils) {\n        return cacheMemoryUtils.get(key);\n    }\n\n    /**\n     * Return the value in cache.\n     *\n     * @param key              The key of cache.\n     * @param defaultValue     The default value if the cache doesn't exist.\n     * @param cacheMemoryUtils The instance of {@link CacheMemoryUtils}.\n     * @param <T>              The value type.\n     * @return the value if cache exists or defaultValue otherwise\n     */\n    public static <T> T get(@NonNull final String key,\n                            final T defaultValue,\n                            @NonNull final CacheMemoryUtils cacheMemoryUtils) {\n        return cacheMemoryUtils.get(key, defaultValue);\n    }\n\n    /**\n     * Return the count of cache.\n     *\n     * @param cacheMemoryUtils The instance of {@link CacheMemoryUtils}.\n     * @return the count of cache\n     */\n    public static int getCacheCount(@NonNull final CacheMemoryUtils cacheMemoryUtils) {\n        return cacheMemoryUtils.getCacheCount();\n    }\n\n    /**\n     * Remove the cache by key.\n     *\n     * @param key              The key of cache.\n     * @param cacheMemoryUtils The instance of {@link CacheMemoryUtils}.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static Object remove(@NonNull final String key, @NonNull final CacheMemoryUtils cacheMemoryUtils) {\n        return cacheMemoryUtils.remove(key);\n    }\n\n    /**\n     * Clear all of the cache.\n     *\n     * @param cacheMemoryUtils The instance of {@link CacheMemoryUtils}.\n     */\n    public static void clear(@NonNull final CacheMemoryUtils cacheMemoryUtils) {\n        cacheMemoryUtils.clear();\n    }\n\n    private static CacheMemoryUtils getDefaultCacheMemoryUtils() {\n        return sDefaultCacheMemoryUtils != null ? sDefaultCacheMemoryUtils : CacheMemoryUtils.getInstance();\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/CacheMemoryUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport androidx.annotation.NonNull;\nimport androidx.collection.LruCache;\n\nimport com.blankj.utilcode.constant.CacheConstants;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/05/24\n *     desc  : utils about memory cache\n * </pre>\n */\npublic final class CacheMemoryUtils implements CacheConstants {\n\n    private static final int DEFAULT_MAX_COUNT = 256;\n\n    private static final Map<String, CacheMemoryUtils> CACHE_MAP = new HashMap<>();\n\n    private final String                       mCacheKey;\n    private final LruCache<String, CacheValue> mMemoryCache;\n\n    /**\n     * Return the single {@link CacheMemoryUtils} instance.\n     *\n     * @return the single {@link CacheMemoryUtils} instance\n     */\n    public static CacheMemoryUtils getInstance() {\n        return getInstance(DEFAULT_MAX_COUNT);\n    }\n\n    /**\n     * Return the single {@link CacheMemoryUtils} instance.\n     *\n     * @param maxCount The max count of cache.\n     * @return the single {@link CacheMemoryUtils} instance\n     */\n    public static CacheMemoryUtils getInstance(final int maxCount) {\n        return getInstance(String.valueOf(maxCount), maxCount);\n    }\n\n    /**\n     * Return the single {@link CacheMemoryUtils} instance.\n     *\n     * @param cacheKey The key of cache.\n     * @param maxCount The max count of cache.\n     * @return the single {@link CacheMemoryUtils} instance\n     */\n    public static CacheMemoryUtils getInstance(final String cacheKey, final int maxCount) {\n        CacheMemoryUtils cache = CACHE_MAP.get(cacheKey);\n        if (cache == null) {\n            synchronized (CacheMemoryUtils.class) {\n                cache = CACHE_MAP.get(cacheKey);\n                if (cache == null) {\n                    cache = new CacheMemoryUtils(cacheKey, new LruCache<String, CacheValue>(maxCount));\n                    CACHE_MAP.put(cacheKey, cache);\n                }\n            }\n        }\n        return cache;\n    }\n\n    private CacheMemoryUtils(String cacheKey, LruCache<String, CacheValue> memoryCache) {\n        mCacheKey = cacheKey;\n        mMemoryCache = memoryCache;\n    }\n\n    @Override\n    public String toString() {\n        return mCacheKey + \"@\" + Integer.toHexString(hashCode());\n    }\n\n    /**\n     * Put bytes in cache.\n     *\n     * @param key   The key of cache.\n     * @param value The value of cache.\n     */\n    public void put(@NonNull final String key, final Object value) {\n        put(key, value, -1);\n    }\n\n    /**\n     * Put bytes in cache.\n     *\n     * @param key      The key of cache.\n     * @param value    The value of cache.\n     * @param saveTime The save time of cache, in seconds.\n     */\n    public void put(@NonNull final String key, final Object value, int saveTime) {\n        if (value == null) return;\n        long dueTime = saveTime < 0 ? -1 : System.currentTimeMillis() + saveTime * 1000;\n        mMemoryCache.put(key, new CacheValue(dueTime, value));\n    }\n\n    /**\n     * Return the value in cache.\n     *\n     * @param key The key of cache.\n     * @param <T> The value type.\n     * @return the value if cache exists or null otherwise\n     */\n    public <T> T get(@NonNull final String key) {\n        return get(key, null);\n    }\n\n    /**\n     * Return the value in cache.\n     *\n     * @param key          The key of cache.\n     * @param defaultValue The default value if the cache doesn't exist.\n     * @param <T>          The value type.\n     * @return the value if cache exists or defaultValue otherwise\n     */\n    public <T> T get(@NonNull final String key, final T defaultValue) {\n        CacheValue val = mMemoryCache.get(key);\n        if (val == null) return defaultValue;\n        if (val.dueTime == -1 || val.dueTime >= System.currentTimeMillis()) {\n            //noinspection unchecked\n            return (T) val.value;\n        }\n        mMemoryCache.remove(key);\n        return defaultValue;\n    }\n\n    /**\n     * Return the count of cache.\n     *\n     * @return the count of cache\n     */\n    public int getCacheCount() {\n        return mMemoryCache.size();\n    }\n\n    /**\n     * Remove the cache by key.\n     *\n     * @param key The key of cache.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public Object remove(@NonNull final String key) {\n        CacheValue remove = mMemoryCache.remove(key);\n        if (remove == null) return null;\n        return remove.value;\n    }\n\n    /**\n     * Clear all of the cache.\n     */\n    public void clear() {\n        mMemoryCache.evictAll();\n    }\n\n    private static final class CacheValue {\n        long   dueTime;\n        Object value;\n\n        CacheValue(long dueTime, Object value) {\n            this.dueTime = dueTime;\n            this.value = value;\n        }\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/CleanUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.app.ActivityManager;\nimport android.content.Context;\nimport android.os.Build;\nimport android.os.Environment;\n\nimport java.io.File;\n\nimport androidx.annotation.RequiresApi;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/09/27\n *     desc  : utils about clean\n * </pre>\n */\npublic final class CleanUtils {\n\n    private CleanUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Clean the internal cache.\n     * <p>directory: /data/data/package/cache</p>\n     *\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean cleanInternalCache() {\n        return UtilsBridge.deleteAllInDir(Utils.getApp().getCacheDir());\n    }\n\n    /**\n     * Clean the internal files.\n     * <p>directory: /data/data/package/files</p>\n     *\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean cleanInternalFiles() {\n        return UtilsBridge.deleteAllInDir(Utils.getApp().getFilesDir());\n    }\n\n    /**\n     * Clean the internal databases.\n     * <p>directory: /data/data/package/databases</p>\n     *\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean cleanInternalDbs() {\n        return UtilsBridge.deleteAllInDir(new File(Utils.getApp().getFilesDir().getParent(), \"databases\"));\n    }\n\n    /**\n     * Clean the internal database by name.\n     * <p>directory: /data/data/package/databases/dbName</p>\n     *\n     * @param dbName The name of database.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean cleanInternalDbByName(final String dbName) {\n        return Utils.getApp().deleteDatabase(dbName);\n    }\n\n    /**\n     * Clean the internal shared preferences.\n     * <p>directory: /data/data/package/shared_prefs</p>\n     *\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean cleanInternalSp() {\n        return UtilsBridge.deleteAllInDir(new File(Utils.getApp().getFilesDir().getParent(), \"shared_prefs\"));\n    }\n\n    /**\n     * Clean the external cache.\n     * <p>directory: /storage/emulated/0/android/data/package/cache</p>\n     *\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean cleanExternalCache() {\n        return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())\n                && UtilsBridge.deleteAllInDir(Utils.getApp().getExternalCacheDir());\n    }\n\n    /**\n     * Clean the custom directory.\n     *\n     * @param dirPath The path of directory.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean cleanCustomDir(final String dirPath) {\n        return UtilsBridge.deleteAllInDir(UtilsBridge.getFileByPath(dirPath));\n    }\n\n    @RequiresApi(api = Build.VERSION_CODES.KITKAT)\n    public static void cleanAppUserData() {\n        ActivityManager am = (ActivityManager) Utils.getApp().getSystemService(Context.ACTIVITY_SERVICE);\n        //noinspection ConstantConditions\n        am.clearApplicationUserData();\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ClickUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.content.res.Resources;\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.ColorFilter;\nimport android.graphics.ColorMatrix;\nimport android.graphics.ColorMatrixColorFilter;\nimport android.graphics.Paint;\nimport android.graphics.Rect;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.graphics.drawable.ColorDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.graphics.drawable.StateListDrawable;\nimport android.os.Build;\nimport android.os.SystemClock;\nimport android.util.Log;\nimport android.util.StateSet;\nimport android.view.MotionEvent;\nimport android.view.TouchDelegate;\nimport android.view.View;\n\nimport androidx.annotation.IntRange;\nimport androidx.annotation.NonNull;\nimport androidx.core.view.ViewCompat;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/06/12\n *     desc  : utils about click\n * </pre>\n */\npublic class ClickUtils {\n\n    private static final int   PRESSED_VIEW_SCALE_TAG           = -1;\n    private static final float PRESSED_VIEW_SCALE_DEFAULT_VALUE = -0.06f;\n\n    private static final int   PRESSED_VIEW_ALPHA_TAG           = -2;\n    private static final int   PRESSED_VIEW_ALPHA_SRC_TAG       = -3;\n    private static final float PRESSED_VIEW_ALPHA_DEFAULT_VALUE = 0.8f;\n\n    private static final int   PRESSED_BG_ALPHA_STYLE         = 4;\n    private static final float PRESSED_BG_ALPHA_DEFAULT_VALUE = 0.9f;\n\n    private static final int   PRESSED_BG_DARK_STYLE         = 5;\n    private static final float PRESSED_BG_DARK_DEFAULT_VALUE = 0.9f;\n\n    private static final long DEBOUNCING_DEFAULT_VALUE = 1000;\n\n    private ClickUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Apply scale animation for the views' click.\n     *\n     * @param views The views.\n     */\n    public static void applyPressedViewScale(final View... views) {\n        applyPressedViewScale(views, null);\n    }\n\n    /**\n     * Apply scale animation for the views' click.\n     *\n     * @param views        The views.\n     * @param scaleFactors The factors of scale for the views.\n     */\n    public static void applyPressedViewScale(final View[] views, final float[] scaleFactors) {\n        if (views == null || views.length == 0) {\n            return;\n        }\n        for (int i = 0; i < views.length; i++) {\n            if (scaleFactors == null || i >= scaleFactors.length) {\n                applyPressedViewScale(views[i], PRESSED_VIEW_SCALE_DEFAULT_VALUE);\n            } else {\n                applyPressedViewScale(views[i], scaleFactors[i]);\n            }\n        }\n    }\n\n    /**\n     * Apply scale animation for the views' click.\n     *\n     * @param view        The view.\n     * @param scaleFactor The factor of scale for the view.\n     */\n    public static void applyPressedViewScale(final View view, final float scaleFactor) {\n        if (view == null) {\n            return;\n        }\n        view.setTag(PRESSED_VIEW_SCALE_TAG, scaleFactor);\n        view.setClickable(true);\n        view.setOnTouchListener(OnUtilsTouchListener.getInstance());\n    }\n\n    /**\n     * Apply alpha for the views' click.\n     *\n     * @param views The views.\n     */\n    public static void applyPressedViewAlpha(final View... views) {\n        applyPressedViewAlpha(views, null);\n    }\n\n    /**\n     * Apply alpha for the views' click.\n     *\n     * @param views  The views.\n     * @param alphas The alphas for the views.\n     */\n    public static void applyPressedViewAlpha(final View[] views, final float[] alphas) {\n        if (views == null || views.length == 0) return;\n        for (int i = 0; i < views.length; i++) {\n            if (alphas == null || i >= alphas.length) {\n                applyPressedViewAlpha(views[i], PRESSED_VIEW_ALPHA_DEFAULT_VALUE);\n            } else {\n                applyPressedViewAlpha(views[i], alphas[i]);\n            }\n        }\n    }\n\n\n    /**\n     * Apply scale animation for the views' click.\n     *\n     * @param view  The view.\n     * @param alpha The alpha for the view.\n     */\n    public static void applyPressedViewAlpha(final View view, final float alpha) {\n        if (view == null) {\n            return;\n        }\n        view.setTag(PRESSED_VIEW_ALPHA_TAG, alpha);\n        view.setTag(PRESSED_VIEW_ALPHA_SRC_TAG, view.getAlpha());\n        view.setClickable(true);\n        view.setOnTouchListener(OnUtilsTouchListener.getInstance());\n    }\n\n    /**\n     * Apply alpha for the view's background.\n     *\n     * @param view The views.\n     */\n    public static void applyPressedBgAlpha(View view) {\n        applyPressedBgAlpha(view, PRESSED_BG_ALPHA_DEFAULT_VALUE);\n    }\n\n    /**\n     * Apply alpha for the view's background.\n     *\n     * @param view  The views.\n     * @param alpha The alpha.\n     */\n    public static void applyPressedBgAlpha(View view, float alpha) {\n        applyPressedBgStyle(view, PRESSED_BG_ALPHA_STYLE, alpha);\n    }\n\n    /**\n     * Apply alpha of dark for the view's background.\n     *\n     * @param view The views.\n     */\n    public static void applyPressedBgDark(View view) {\n        applyPressedBgDark(view, PRESSED_BG_DARK_DEFAULT_VALUE);\n    }\n\n    /**\n     * Apply alpha of dark for the view's background.\n     *\n     * @param view      The views.\n     * @param darkAlpha The alpha of dark.\n     */\n    public static void applyPressedBgDark(View view, float darkAlpha) {\n        applyPressedBgStyle(view, PRESSED_BG_DARK_STYLE, darkAlpha);\n    }\n\n    private static void applyPressedBgStyle(View view, int style, float value) {\n        if (view == null) return;\n        Drawable background = view.getBackground();\n        Object tag = view.getTag(-style);\n        if (tag instanceof Drawable) {\n            ViewCompat.setBackground(view, (Drawable) tag);\n        } else {\n            background = createStyleDrawable(background, style, value);\n            ViewCompat.setBackground(view, background);\n            view.setTag(-style, background);\n        }\n    }\n\n    private static Drawable createStyleDrawable(Drawable src, int style, float value) {\n        if (src == null) {\n            src = new ColorDrawable(0);\n        }\n        if (src.getConstantState() == null) return src;\n\n        Drawable pressed = src.getConstantState().newDrawable().mutate();\n        if (style == PRESSED_BG_ALPHA_STYLE) {\n            pressed = createAlphaDrawable(pressed, value);\n        } else if (style == PRESSED_BG_DARK_STYLE) {\n            pressed = createDarkDrawable(pressed, value);\n        }\n\n        Drawable disable = src.getConstantState().newDrawable().mutate();\n        disable = createAlphaDrawable(disable, 0.5f);\n\n        StateListDrawable drawable = new StateListDrawable();\n        drawable.addState(new int[]{android.R.attr.state_pressed}, pressed);\n        drawable.addState(new int[]{-android.R.attr.state_enabled}, disable);\n        drawable.addState(StateSet.WILD_CARD, src);\n        return drawable;\n    }\n\n    private static Drawable createAlphaDrawable(Drawable drawable, float alpha) {\n        ClickDrawableWrapper drawableWrapper = new ClickDrawableWrapper(drawable);\n        drawableWrapper.setAlpha((int) (alpha * 255));\n        return drawableWrapper;\n    }\n\n    private static Drawable createDarkDrawable(Drawable drawable, float alpha) {\n        ClickDrawableWrapper drawableWrapper = new ClickDrawableWrapper(drawable);\n        drawableWrapper.setColorFilter(getDarkColorFilter(alpha));\n        return drawableWrapper;\n    }\n\n    private static ColorMatrixColorFilter getDarkColorFilter(float darkAlpha) {\n        return new ColorMatrixColorFilter(new ColorMatrix(new float[]{\n                darkAlpha, 0, 0, 0, 0,\n                0, darkAlpha, 0, 0, 0,\n                0, 0, darkAlpha, 0, 0,\n                0, 0, 0, 2, 0\n        }));\n    }\n\n    /**\n     * Apply single debouncing for the view's click.\n     *\n     * @param view     The view.\n     * @param listener The listener.\n     */\n    public static void applySingleDebouncing(final View view, final View.OnClickListener listener) {\n        applySingleDebouncing(new View[]{view}, listener);\n    }\n\n    /**\n     * Apply single debouncing for the view's click.\n     *\n     * @param view     The view.\n     * @param duration The duration of debouncing.\n     * @param listener The listener.\n     */\n    public static void applySingleDebouncing(final View view, @IntRange(from = 0) long duration,\n                                             final View.OnClickListener listener) {\n        applySingleDebouncing(new View[]{view}, duration, listener);\n    }\n\n    /**\n     * Apply single debouncing for the views' click.\n     *\n     * @param views    The views.\n     * @param listener The listener.\n     */\n    public static void applySingleDebouncing(final View[] views, final View.OnClickListener listener) {\n        applySingleDebouncing(views, DEBOUNCING_DEFAULT_VALUE, listener);\n    }\n\n    /**\n     * Apply single debouncing for the views' click.\n     *\n     * @param views    The views.\n     * @param duration The duration of debouncing.\n     * @param listener The listener.\n     */\n    public static void applySingleDebouncing(final View[] views,\n                                             @IntRange(from = 0) long duration,\n                                             final View.OnClickListener listener) {\n        applyDebouncing(views, false, duration, listener);\n    }\n\n    /**\n     * Apply global debouncing for the view's click.\n     *\n     * @param view     The view.\n     * @param listener The listener.\n     */\n    public static void applyGlobalDebouncing(final View view, final View.OnClickListener listener) {\n        applyGlobalDebouncing(new View[]{view}, listener);\n    }\n\n    /**\n     * Apply global debouncing for the view's click.\n     *\n     * @param view     The view.\n     * @param duration The duration of debouncing.\n     * @param listener The listener.\n     */\n    public static void applyGlobalDebouncing(final View view, @IntRange(from = 0) long duration,\n                                             final View.OnClickListener listener) {\n        applyGlobalDebouncing(new View[]{view}, duration, listener);\n    }\n\n\n    /**\n     * Apply global debouncing for the views' click.\n     *\n     * @param views    The views.\n     * @param listener The listener.\n     */\n    public static void applyGlobalDebouncing(final View[] views, final View.OnClickListener listener) {\n        applyGlobalDebouncing(views, DEBOUNCING_DEFAULT_VALUE, listener);\n    }\n\n    /**\n     * Apply global debouncing for the views' click.\n     *\n     * @param views    The views.\n     * @param duration The duration of debouncing.\n     * @param listener The listener.\n     */\n    public static void applyGlobalDebouncing(final View[] views,\n                                             @IntRange(from = 0) long duration,\n                                             final View.OnClickListener listener) {\n        applyDebouncing(views, true, duration, listener);\n    }\n\n    private static void applyDebouncing(final View[] views,\n                                        final boolean isGlobal,\n                                        @IntRange(from = 0) long duration,\n                                        final View.OnClickListener listener) {\n        if (views == null || views.length == 0 || listener == null) return;\n        for (View view : views) {\n            if (view == null) continue;\n            view.setOnClickListener(new OnDebouncingClickListener(isGlobal, duration) {\n                @Override\n                public void onDebouncingClick(View v) {\n                    listener.onClick(v);\n                }\n            });\n        }\n    }\n\n    /**\n     * Expand the click area of ​​the view\n     *\n     * @param view       The view.\n     * @param expandSize The size.\n     */\n    public static void expandClickArea(@NonNull final View view, final int expandSize) {\n        expandClickArea(view, expandSize, expandSize, expandSize, expandSize);\n    }\n\n    public static void expandClickArea(@NonNull final View view,\n                                       final int expandSizeTop,\n                                       final int expandSizeLeft,\n                                       final int expandSizeRight,\n                                       final int expandSizeBottom) {\n        final View parentView = (View) view.getParent();\n        if (parentView == null) {\n            Log.e(\"ClickUtils\", \"expandClickArea must have parent view.\");\n            return;\n        }\n        parentView.post(new Runnable() {\n            @Override\n            public void run() {\n                final Rect rect = new Rect();\n                view.getHitRect(rect);\n                rect.top -= expandSizeTop;\n                rect.bottom += expandSizeBottom;\n                rect.left -= expandSizeLeft;\n                rect.right += expandSizeRight;\n                parentView.setTouchDelegate(new TouchDelegate(rect, view));\n            }\n        });\n    }\n\n    private static final long TIP_DURATION = 2000L;\n    private static       long sLastClickMillis;\n    private static       int  sClickCount;\n\n    public static void back2HomeFriendly(final CharSequence tip) {\n        back2HomeFriendly(tip, TIP_DURATION, Back2HomeFriendlyListener.DEFAULT);\n    }\n\n    public static void back2HomeFriendly(@NonNull final CharSequence tip,\n                                         final long duration,\n                                         @NonNull Back2HomeFriendlyListener listener) {\n        long nowMillis = SystemClock.elapsedRealtime();\n        if (Math.abs(nowMillis - sLastClickMillis) < duration) {\n            sClickCount++;\n            if (sClickCount == 2) {\n                UtilsBridge.startHomeActivity();\n                listener.dismiss();\n                sLastClickMillis = 0;\n            }\n        } else {\n            sClickCount = 1;\n            listener.show(tip, duration);\n            sLastClickMillis = nowMillis;\n        }\n    }\n\n    public interface Back2HomeFriendlyListener {\n        Back2HomeFriendlyListener DEFAULT = new Back2HomeFriendlyListener() {\n            @Override\n            public void show(CharSequence text, long duration) {\n                UtilsBridge.toastShowShort(text);\n            }\n\n            @Override\n            public void dismiss() {\n                UtilsBridge.toastCancel();\n            }\n        };\n\n        void show(CharSequence text, long duration);\n\n        void dismiss();\n    }\n\n    public static abstract class OnDebouncingClickListener implements View.OnClickListener {\n\n        private static boolean mEnabled = true;\n\n        private static final Runnable ENABLE_AGAIN = new Runnable() {\n            @Override\n            public void run() {\n                mEnabled = true;\n            }\n        };\n\n        private static boolean isValid(@NonNull final View view, final long duration) {\n            return UtilsBridge.isValid(view, duration);\n        }\n\n        private long    mDuration;\n        private boolean mIsGlobal;\n\n        public OnDebouncingClickListener() {\n            this(true, DEBOUNCING_DEFAULT_VALUE);\n        }\n\n        public OnDebouncingClickListener(final boolean isGlobal) {\n            this(isGlobal, DEBOUNCING_DEFAULT_VALUE);\n        }\n\n        public OnDebouncingClickListener(final long duration) {\n            this(true, duration);\n        }\n\n        public OnDebouncingClickListener(final boolean isGlobal, final long duration) {\n            mIsGlobal = isGlobal;\n            mDuration = duration;\n        }\n\n        public abstract void onDebouncingClick(View v);\n\n        @Override\n        public final void onClick(View v) {\n            if (mIsGlobal) {\n                if (mEnabled) {\n                    mEnabled = false;\n                    v.postDelayed(ENABLE_AGAIN, mDuration);\n                    onDebouncingClick(v);\n                }\n            } else {\n                if (isValid(v, mDuration)) {\n                    onDebouncingClick(v);\n                }\n            }\n        }\n    }\n\n    public static abstract class OnMultiClickListener implements View.OnClickListener {\n\n        private static final long INTERVAL_DEFAULT_VALUE = 666;\n\n        private final int  mTriggerClickCount;\n        private final long mClickInterval;\n\n        private long mLastClickTime;\n        private int  mClickCount;\n\n        public OnMultiClickListener(int triggerClickCount) {\n            this(triggerClickCount, INTERVAL_DEFAULT_VALUE);\n        }\n\n        public OnMultiClickListener(int triggerClickCount, long clickInterval) {\n            this.mTriggerClickCount = triggerClickCount;\n            this.mClickInterval = clickInterval;\n        }\n\n        public abstract void onTriggerClick(View v);\n\n        public abstract void onBeforeTriggerClick(View v, int count);\n\n        @Override\n        public void onClick(View v) {\n            if (mTriggerClickCount <= 1) {\n                onTriggerClick(v);\n                return;\n            }\n            long curTime = System.currentTimeMillis();\n\n            if (curTime - mLastClickTime < mClickInterval) {\n                mClickCount++;\n                if (mClickCount == mTriggerClickCount) {\n                    onTriggerClick(v);\n                } else if (mClickCount < mTriggerClickCount) {\n                    onBeforeTriggerClick(v, mClickCount);\n                } else {\n                    mClickCount = 1;\n                    onBeforeTriggerClick(v, mClickCount);\n                }\n            } else {\n                mClickCount = 1;\n                onBeforeTriggerClick(v, mClickCount);\n            }\n            mLastClickTime = curTime;\n        }\n    }\n\n    private static class OnUtilsTouchListener implements View.OnTouchListener {\n\n        public static OnUtilsTouchListener getInstance() {\n            return LazyHolder.INSTANCE;\n        }\n\n        private OnUtilsTouchListener() {/**/}\n\n        @Override\n        public boolean onTouch(final View v, MotionEvent event) {\n            int action = event.getAction();\n            if (action == MotionEvent.ACTION_DOWN) {\n                processScale(v, true);\n                processAlpha(v, true);\n            } else if (action == MotionEvent.ACTION_UP\n                    || action == MotionEvent.ACTION_CANCEL) {\n                processScale(v, false);\n                processAlpha(v, false);\n            }\n            return false;\n        }\n\n        private void processScale(final View view, boolean isDown) {\n            Object tag = view.getTag(PRESSED_VIEW_SCALE_TAG);\n            if (!(tag instanceof Float)) return;\n            float value = isDown ? 1 + (Float) tag : 1;\n            view.animate()\n                    .scaleX(value)\n                    .scaleY(value)\n                    .setDuration(200)\n                    .start();\n        }\n\n        private void processAlpha(final View view, boolean isDown) {\n            Object tag = view.getTag(isDown ? PRESSED_VIEW_ALPHA_TAG : PRESSED_VIEW_ALPHA_SRC_TAG);\n            if (!(tag instanceof Float)) return;\n            view.setAlpha((Float) tag);\n        }\n\n        private static class LazyHolder {\n            private static final OnUtilsTouchListener INSTANCE = new OnUtilsTouchListener();\n        }\n    }\n\n    static class ClickDrawableWrapper extends ShadowUtils.DrawableWrapper {\n\n        private BitmapDrawable mBitmapDrawable = null;\n\n        // 低版本ColorDrawable.setColorFilter无效，这里直接用画笔画上\n        private Paint mColorPaint = null;\n\n        public ClickDrawableWrapper(Drawable drawable) {\n            super(drawable);\n            if (drawable instanceof ColorDrawable) {\n                mColorPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);\n                mColorPaint.setColor(((ColorDrawable) drawable).getColor());\n            }\n        }\n\n        @Override\n        public void setColorFilter(ColorFilter cf) {\n            super.setColorFilter(cf);\n            // 低版本 StateListDrawable.selectDrawable 会重置 ColorFilter\n            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {\n                if (mColorPaint != null) {\n                    mColorPaint.setColorFilter(cf);\n                }\n            }\n        }\n\n        @Override\n        public void setAlpha(int alpha) {\n            super.setAlpha(alpha);\n            // 低版本 StateListDrawable.selectDrawable 会重置 Alpha\n            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {\n                if (mColorPaint != null) {\n                    mColorPaint.setColor(((ColorDrawable) getWrappedDrawable()).getColor());\n                }\n            }\n        }\n\n        @Override\n        public void draw(Canvas canvas) {\n            if (mBitmapDrawable == null) {\n                Bitmap bitmap = Bitmap.createBitmap(getBounds().width(), getBounds().height(), Bitmap.Config.ARGB_8888);\n                Canvas myCanvas = new Canvas(bitmap);\n                if (mColorPaint != null) {\n                    myCanvas.drawRect(getBounds(), mColorPaint);\n                } else {\n                    super.draw(myCanvas);\n                }\n                mBitmapDrawable = new BitmapDrawable(Resources.getSystem(), bitmap);\n                mBitmapDrawable.setBounds(getBounds());\n            }\n            mBitmapDrawable.draw(canvas);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ClipboardUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.content.ClipData;\nimport android.content.ClipDescription;\nimport android.content.ClipboardManager;\nimport android.content.Context;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/09/25\n *     desc  : utils about clipboard\n * </pre>\n */\npublic final class ClipboardUtils {\n\n    private ClipboardUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Copy the text to clipboard.\n     * <p>The label equals name of package.</p>\n     *\n     * @param text The text.\n     */\n    public static void copyText(final CharSequence text) {\n        ClipboardManager cm = (ClipboardManager) Utils.getApp().getSystemService(Context.CLIPBOARD_SERVICE);\n        //noinspection ConstantConditions\n        cm.setPrimaryClip(ClipData.newPlainText(Utils.getApp().getPackageName(), text));\n    }\n\n    /**\n     * Copy the text to clipboard.\n     *\n     * @param label The label.\n     * @param text  The text.\n     */\n    public static void copyText(final CharSequence label, final CharSequence text) {\n        ClipboardManager cm = (ClipboardManager) Utils.getApp().getSystemService(Context.CLIPBOARD_SERVICE);\n        //noinspection ConstantConditions\n        cm.setPrimaryClip(ClipData.newPlainText(label, text));\n    }\n\n    /**\n     * Clear the clipboard.\n     */\n    public static void clear() {\n        ClipboardManager cm = (ClipboardManager) Utils.getApp().getSystemService(Context.CLIPBOARD_SERVICE);\n        //noinspection ConstantConditions\n        cm.setPrimaryClip(ClipData.newPlainText(null, \"\"));\n    }\n\n    /**\n     * Return the label for clipboard.\n     *\n     * @return the label for clipboard\n     */\n    public static CharSequence getLabel() {\n        ClipboardManager cm = (ClipboardManager) Utils.getApp().getSystemService(Context.CLIPBOARD_SERVICE);\n        //noinspection ConstantConditions\n        ClipDescription des = cm.getPrimaryClipDescription();\n        if (des == null) {\n            return \"\";\n        }\n        CharSequence label = des.getLabel();\n        if (label == null) {\n            return \"\";\n        }\n        return label;\n    }\n\n    /**\n     * Return the text for clipboard.\n     *\n     * @return the text for clipboard\n     */\n    public static CharSequence getText() {\n        ClipboardManager cm = (ClipboardManager) Utils.getApp().getSystemService(Context.CLIPBOARD_SERVICE);\n        //noinspection ConstantConditions\n        ClipData clip = cm.getPrimaryClip();\n        if (clip != null && clip.getItemCount() > 0) {\n            CharSequence text = clip.getItemAt(0).coerceToText(Utils.getApp());\n            if (text != null) {\n                return text;\n            }\n        }\n        return \"\";\n    }\n\n    /**\n     * Add the clipboard changed listener.\n     */\n    public static void addChangedListener(final ClipboardManager.OnPrimaryClipChangedListener listener) {\n        ClipboardManager cm = (ClipboardManager) Utils.getApp().getSystemService(Context.CLIPBOARD_SERVICE);\n        //noinspection ConstantConditions\n        cm.addPrimaryClipChangedListener(listener);\n    }\n\n    /**\n     * Remove the clipboard changed listener.\n     */\n    public static void removeChangedListener(final ClipboardManager.OnPrimaryClipChangedListener listener) {\n        ClipboardManager cm = (ClipboardManager) Utils.getApp().getSystemService(Context.CLIPBOARD_SERVICE);\n        //noinspection ConstantConditions\n        cm.removePrimaryClipChangedListener(listener);\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/CloneUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport java.lang.reflect.Type;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/01/30\n *     desc  : utils about clone\n * </pre>\n */\npublic final class CloneUtils {\n\n    private CloneUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Deep clone.\n     *\n     * @param data The data.\n     * @param type The type.\n     * @param <T>  The value type.\n     * @return The object of cloned.\n     */\n    public static <T> T deepClone(final T data, final Type type) {\n        try {\n            return UtilsBridge.fromJson(UtilsBridge.toJson(data), type);\n        } catch (Exception e) {\n            e.printStackTrace();\n            return null;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/CloseUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport java.io.Closeable;\nimport java.io.IOException;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/10/09\n *     desc  : utils about close\n * </pre>\n */\npublic final class CloseUtils {\n\n    private CloseUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Close the io stream.\n     *\n     * @param closeables The closeables.\n     */\n    public static void closeIO(final Closeable... closeables) {\n        if (closeables == null) return;\n        for (Closeable closeable : closeables) {\n            if (closeable != null) {\n                try {\n                    closeable.close();\n                } catch (IOException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n    }\n\n    /**\n     * Close the io stream quietly.\n     *\n     * @param closeables The closeables.\n     */\n    public static void closeIOQuietly(final Closeable... closeables) {\n        if (closeables == null) return;\n        for (Closeable closeable : closeables) {\n            if (closeable != null) {\n                try {\n                    closeable.close();\n                } catch (IOException ignored) {\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/CollectionUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport java.lang.reflect.Array;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.Enumeration;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.ListIterator;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.TreeSet;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/26\n *     desc  : utils about collection\n * </pre>\n */\npublic final class CollectionUtils {\n\n    private CollectionUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // listOf\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Returns a new read-only list of given elements.\n     *\n     * @param array The array.\n     * @return a new read-only list of given elements\n     */\n    @SafeVarargs\n    public static <E> List<E> newUnmodifiableList(E... array) {\n        return Collections.unmodifiableList(newArrayList(array));\n    }\n\n    /**\n     * Returns a new read-only list only of those given elements, that are not null.\n     *\n     * @param array The array.\n     * @return a new read-only list only of those given elements, that are not null\n     */\n    @SafeVarargs\n    public static <E> List<E> newUnmodifiableListNotNull(E... array) {\n        return Collections.unmodifiableList(newArrayListNotNull(array));\n    }\n\n    @SafeVarargs\n    public static <E> ArrayList<E> newArrayList(E... array) {\n        ArrayList<E> list = new ArrayList<>();\n        if (array == null || array.length == 0) return list;\n        for (E e : array) {\n            list.add(e);\n        }\n        return list;\n    }\n\n    @SafeVarargs\n    public static <E> ArrayList<E> newArrayListNotNull(E... array) {\n        ArrayList<E> list = new ArrayList<>();\n        if (array == null || array.length == 0) return list;\n        for (E e : array) {\n            if (e == null) continue;\n            list.add(e);\n        }\n        return list;\n    }\n\n    @SafeVarargs\n    public static <E> LinkedList<E> newLinkedList(E... array) {\n        LinkedList<E> list = new LinkedList<>();\n        if (array == null || array.length == 0) return list;\n        for (E e : array) {\n            list.add(e);\n        }\n        return list;\n    }\n\n    @SafeVarargs\n    public static <E> LinkedList<E> newLinkedListNotNull(E... array) {\n        LinkedList<E> list = new LinkedList<>();\n        if (array == null || array.length == 0) return list;\n        for (E e : array) {\n            if (e == null) continue;\n            list.add(e);\n        }\n        return list;\n    }\n\n    @SafeVarargs\n    public static <E> HashSet<E> newHashSet(E... array) {\n        HashSet<E> set = new HashSet<>();\n        if (array == null || array.length == 0) return set;\n        for (E e : array) {\n            set.add(e);\n        }\n        return set;\n    }\n\n    @SafeVarargs\n    public static <E> HashSet<E> newHashSetNotNull(E... array) {\n        HashSet<E> set = new HashSet<>();\n        if (array == null || array.length == 0) return set;\n        for (E e : array) {\n            if (e == null) continue;\n            set.add(e);\n        }\n        return set;\n    }\n\n    @SafeVarargs\n    public static <E> TreeSet<E> newTreeSet(Comparator<E> comparator, E... array) {\n        TreeSet<E> set = new TreeSet<>(comparator);\n        if (array == null || array.length == 0) return set;\n        for (E e : array) {\n            set.add(e);\n        }\n        return set;\n    }\n\n    @SafeVarargs\n    public static <E> TreeSet<E> newTreeSetNotNull(Comparator<E> comparator, E... array) {\n        TreeSet<E> set = new TreeSet<>(comparator);\n        if (array == null || array.length == 0) return set;\n        for (E e : array) {\n            if (e == null) continue;\n            set.add(e);\n        }\n        return set;\n    }\n\n    public static Collection newSynchronizedCollection(Collection collection) {\n        return Collections.synchronizedCollection(collection);\n    }\n\n    public static Collection newUnmodifiableCollection(Collection collection) {\n        return Collections.unmodifiableCollection(collection);\n    }\n\n    /**\n     * Returns a {@link Collection} containing the union\n     * of the given {@link Collection}s.\n     * <p>\n     * The cardinality of each element in the returned {@link Collection}\n     * will be equal to the maximum of the cardinality of that element\n     * in the two given {@link Collection}s.\n     *\n     * @param a the first collection\n     * @param b the second collection\n     * @return the union of the two collections\n     * @see Collection#addAll\n     */\n    public static Collection union(final Collection a, final Collection b) {\n        if (a == null && b == null) return new ArrayList();\n        if (a == null) return new ArrayList<Object>(b);\n        if (b == null) return new ArrayList<Object>(a);\n        ArrayList<Object> list = new ArrayList<>();\n        Map<Object, Integer> mapA = getCardinalityMap(a);\n        Map<Object, Integer> mapB = getCardinalityMap(b);\n        Set<Object> elts = new HashSet<Object>(a);\n        elts.addAll(b);\n        for (Object obj : elts) {\n            for (int i = 0, m = Math.max(getFreq(obj, mapA), getFreq(obj, mapB)); i < m; i++) {\n                list.add(obj);\n            }\n        }\n        return list;\n    }\n\n    /**\n     * Returns a {@link Collection} containing the intersection\n     * of the given {@link Collection}s.\n     * <p>\n     * The cardinality of each element in the returned {@link Collection}\n     * will be equal to the minimum of the cardinality of that element\n     * in the two given {@link Collection}s.\n     *\n     * @param a the first collection\n     * @param b the second collection\n     * @return the intersection of the two collections\n     * @see Collection#retainAll\n     */\n    public static Collection intersection(final Collection a, final Collection b) {\n        if (a == null || b == null) return new ArrayList();\n        ArrayList<Object> list = new ArrayList<>();\n        Map mapA = getCardinalityMap(a);\n        Map mapB = getCardinalityMap(b);\n        Set<Object> elts = new HashSet<Object>(a);\n        elts.addAll(b);\n        for (Object obj : elts) {\n            for (int i = 0, m = Math.min(getFreq(obj, mapA), getFreq(obj, mapB)); i < m; i++) {\n                list.add(obj);\n            }\n        }\n        return list;\n    }\n\n    private static int getFreq(final Object obj, final Map freqMap) {\n        Integer count = (Integer) freqMap.get(obj);\n        if (count != null) {\n            return count;\n        }\n        return 0;\n    }\n\n    /**\n     * Returns a {@link Collection} containing the exclusive disjunction\n     * (symmetric difference) of the given {@link Collection}s.\n     * <p>\n     * The cardinality of each element <i>e</i> in the returned {@link Collection}\n     * will be equal to\n     * <tt>max(cardinality(<i>e</i>,<i>a</i>),cardinality(<i>e</i>,<i>b</i>)) - min(cardinality(<i>e</i>,<i>a</i>),cardinality(<i>e</i>,<i>b</i>))</tt>.\n     * <p>\n     * This is equivalent to\n     * <tt>{@link #subtract subtract}({@link #union union(a,b)},{@link #intersection intersection(a,b)})</tt>\n     * or\n     * <tt>{@link #union union}({@link #subtract subtract(a,b)},{@link #subtract subtract(b,a)})</tt>.\n     *\n     * @param a the first collection\n     * @param b the second collection\n     * @return the symmetric difference of the two collections\n     */\n    public static Collection disjunction(final Collection a, final Collection b) {\n        if (a == null && b == null) return new ArrayList();\n        if (a == null) return new ArrayList<Object>(b);\n        if (b == null) return new ArrayList<Object>(a);\n        ArrayList<Object> list = new ArrayList<>();\n        Map mapA = getCardinalityMap(a);\n        Map mapB = getCardinalityMap(b);\n        Set<Object> elts = new HashSet<Object>(a);\n        elts.addAll(b);\n        for (Object obj : elts) {\n            for (int i = 0, m = ((Math.max(getFreq(obj, mapA), getFreq(obj, mapB)))\n                    - (Math.min(getFreq(obj, mapA), getFreq(obj, mapB)))); i < m; i++) {\n                list.add(obj);\n            }\n        }\n        return list;\n    }\n\n    /**\n     * Returns a new {@link Collection} containing <tt><i>a</i> - <i>b</i></tt>.\n     * The cardinality of each element <i>e</i> in the returned {@link Collection}\n     * will be the cardinality of <i>e</i> in <i>a</i> minus the cardinality\n     * of <i>e</i> in <i>b</i>, or zero, whichever is greater.\n     *\n     * @param a the collection to subtract from\n     * @param b the collection to subtract\n     * @return a new collection with the results\n     * @see Collection#removeAll\n     */\n    public static Collection subtract(final Collection a, final Collection b) {\n        if (a == null) return new ArrayList();\n        if (b == null) return new ArrayList<Object>(a);\n        ArrayList<Object> list = new ArrayList<Object>(a);\n        for (Object o : b) {\n            list.remove(o);\n        }\n        return list;\n    }\n\n    /**\n     * Returns <code>true</code> iff at least one element is in both collections.\n     * <p>\n     * In other words, this method returns <code>true</code> iff the\n     * {@link #intersection} of <i>coll1</i> and <i>coll2</i> is not empty.\n     *\n     * @param coll1 the first collection\n     * @param coll2 the first collection\n     * @return <code>true</code> iff the intersection of the collections is non-empty\n     * @see #intersection\n     */\n    public static boolean containsAny(final Collection coll1, final Collection coll2) {\n        if (coll1 == null || coll2 == null) return false;\n        if (coll1.size() < coll2.size()) {\n            for (Object o : coll1) {\n                if (coll2.contains(o)) {\n                    return true;\n                }\n            }\n        } else {\n            for (Object o : coll2) {\n                if (coll1.contains(o)) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n\n    /**\n     * Returns a {@link Map} mapping each unique element in the given\n     * {@link Collection} to an {@link Integer} representing the number\n     * of occurrences of that element in the {@link Collection}.\n     * <p>\n     * Only those elements present in the collection will appear as\n     * keys in the map.\n     *\n     * @param coll the collection to get the cardinality map for, must not be null\n     * @return the populated cardinality map\n     */\n    public static Map<Object, Integer> getCardinalityMap(final Collection coll) {\n        Map<Object, Integer> count = new HashMap<>();\n        if (coll == null) return count;\n        for (Object obj : coll) {\n            Integer c = count.get(obj);\n            if (c == null) {\n                count.put(obj, 1);\n            } else {\n                count.put(obj, c + 1);\n            }\n        }\n        return count;\n    }\n\n    /**\n     * Returns <tt>true</tt> iff <i>a</i> is a sub-collection of <i>b</i>,\n     * that is, iff the cardinality of <i>e</i> in <i>a</i> is less\n     * than or equal to the cardinality of <i>e</i> in <i>b</i>,\n     * for each element <i>e</i> in <i>a</i>.\n     *\n     * @param a the first (sub?) collection\n     * @param b the second (super?) collection\n     * @return <code>true</code> iff <i>a</i> is a sub-collection of <i>b</i>\n     * @see #isProperSubCollection\n     * @see Collection#containsAll\n     */\n    public static boolean isSubCollection(final Collection a, final Collection b) {\n        if (a == null || b == null) return false;\n        Map mapA = getCardinalityMap(a);\n        Map mapB = getCardinalityMap(b);\n        for (Object obj : a) {\n            if (getFreq(obj, mapA) > getFreq(obj, mapB)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Returns <tt>true</tt> iff <i>a</i> is a <i>proper</i> sub-collection of <i>b</i>,\n     * that is, iff the cardinality of <i>e</i> in <i>a</i> is less\n     * than or equal to the cardinality of <i>e</i> in <i>b</i>,\n     * for each element <i>e</i> in <i>a</i>, and there is at least one\n     * element <i>f</i> such that the cardinality of <i>f</i> in <i>b</i>\n     * is strictly greater than the cardinality of <i>f</i> in <i>a</i>.\n     * <p>\n     * The implementation assumes\n     * <ul>\n     * <li><code>a.size()</code> and <code>b.size()</code> represent the\n     * total cardinality of <i>a</i> and <i>b</i>, resp. </li>\n     * <li><code>a.size() < Integer.MAXVALUE</code></li>\n     * </ul>\n     *\n     * @param a the first (sub?) collection\n     * @param b the second (super?) collection\n     * @return <code>true</code> iff <i>a</i> is a <i>proper</i> sub-collection of <i>b</i>\n     * @see #isSubCollection\n     * @see Collection#containsAll\n     */\n    public static boolean isProperSubCollection(final Collection a, final Collection b) {\n        if (a == null || b == null) return false;\n        return a.size() < b.size() && isSubCollection(a, b);\n    }\n\n    /**\n     * Returns <tt>true</tt> iff the given {@link Collection}s contain\n     * exactly the same elements with exactly the same cardinalities.\n     * <p>\n     * That is, iff the cardinality of <i>e</i> in <i>a</i> is\n     * equal to the cardinality of <i>e</i> in <i>b</i>,\n     * for each element <i>e</i> in <i>a</i> or <i>b</i>.\n     *\n     * @param a the first collection\n     * @param b the second collection\n     * @return <code>true</code> iff the collections contain the same elements with the same cardinalities.\n     */\n    public static boolean isEqualCollection(final Collection a, final Collection b) {\n        if (a == null || b == null) return false;\n        if (a.size() != b.size()) {\n            return false;\n        } else {\n            Map mapA = getCardinalityMap(a);\n            Map mapB = getCardinalityMap(b);\n            if (mapA.size() != mapB.size()) {\n                return false;\n            } else {\n                for (Object obj : mapA.keySet()) {\n                    if (getFreq(obj, mapA) != getFreq(obj, mapB)) {\n                        return false;\n                    }\n                }\n                return true;\n            }\n        }\n    }\n\n    /**\n     * Returns the number of occurrences of <i>obj</i> in <i>coll</i>.\n     *\n     * @param obj  the object to find the cardinality of\n     * @param coll the collection to search\n     * @return the the number of occurrences of obj in coll\n     */\n    public static <E> int cardinality(E obj, final Collection<E> coll) {\n        if (coll == null) return 0;\n        if (coll instanceof Set) {\n            return (coll.contains(obj) ? 1 : 0);\n        }\n        int count = 0;\n        if (obj == null) {\n            for (E e : coll) {\n                if (e == null) {\n                    count++;\n                }\n            }\n        } else {\n            for (E e : coll) {\n                if (obj.equals(e)) {\n                    count++;\n                }\n            }\n        }\n        return count;\n    }\n\n    /**\n     * Finds the first element in the given collection which matches the given predicate.\n     * <p>\n     * If the input collection or predicate is null, or no element of the collection\n     * matches the predicate, null is returned.\n     *\n     * @param collection the collection to search, may be null\n     * @param predicate  the predicate to use, may be null\n     * @return the first element of the collection which matches the predicate or null if none could be found\n     */\n    public static <E> E find(Collection<E> collection, Predicate<E> predicate) {\n        if (collection == null || predicate == null) return null;\n        for (E item : collection) {\n            if (predicate.evaluate(item)) {\n                return item;\n            }\n        }\n        return null;\n    }\n\n    /**\n     * Executes the given closure on each element in the collection.\n     * <p>\n     * If the input collection or closure is null, there is no change made.\n     *\n     * @param collection the collection to get the input from, may be null\n     * @param closure    the closure to perform, may be null\n     */\n    public static <E> void forAllDo(Collection<E> collection, Closure<E> closure) {\n        if (collection == null || closure == null) return;\n        int index = 0;\n        for (E e : collection) {\n            closure.execute(index++, e);\n        }\n    }\n\n    /**\n     * Filter the collection by applying a Predicate to each element. If the\n     * predicate returns false, remove the element.\n     * <p>\n     * If the input collection or predicate is null, there is no change made.\n     *\n     * @param collection the collection to get the input from, may be null\n     * @param predicate  the predicate to use as a filter, may be null\n     */\n    public static <E> void filter(Collection<E> collection, Predicate<E> predicate) {\n        if (collection == null || predicate == null) return;\n        for (Iterator it = collection.iterator(); it.hasNext(); ) {\n            if (!predicate.evaluate((E) it.next())) {\n                it.remove();\n            }\n        }\n    }\n\n    /**\n     * Selects all elements from input collection which match the given predicate\n     * into an output collection.\n     * <p>\n     * A <code>null</code> predicate matches no elements.\n     *\n     * @param inputCollection the collection to get the input from, may not be null\n     * @param predicate       the predicate to use, may be null\n     * @return the elements matching the predicate (new list)\n     * @throws NullPointerException if the input collection is null\n     */\n    public static <E> Collection<E> select(Collection<E> inputCollection, Predicate<E> predicate) {\n        if (inputCollection == null || predicate == null) return new ArrayList<>();\n        ArrayList<E> answer = new ArrayList<>(inputCollection.size());\n        for (E o : inputCollection) {\n            if (predicate.evaluate(o)) {\n                answer.add(o);\n            }\n        }\n        return answer;\n    }\n\n    /**\n     * Selects all elements from inputCollection which don't match the given predicate\n     * into an output collection.\n     * <p>\n     * If the input predicate is <code>null</code>, the result is an empty list.\n     *\n     * @param inputCollection the collection to get the input from, may not be null\n     * @param predicate       the predicate to use, may be null\n     * @return the elements <b>not</b> matching the predicate (new list)\n     * @throws NullPointerException if the input collection is null\n     */\n    public static <E> Collection<E> selectRejected(Collection<E> inputCollection, Predicate<E> predicate) {\n        if (inputCollection == null || predicate == null) return new ArrayList<>();\n        ArrayList<E> answer = new ArrayList<>(inputCollection.size());\n        for (E o : inputCollection) {\n            if (!predicate.evaluate(o)) {\n                answer.add(o);\n            }\n        }\n        return answer;\n    }\n\n    /**\n     * Transform the collection by applying a Transformer to each element.\n     * <p>\n     * If the input collection or transformer is null, there is no change made.\n     * <p>\n     * This routine is best for Lists, for which set() is used to do the\n     * transformations \"in place.\"  For other Collections, clear() and addAll()\n     * are used to replace elements.\n     * <p>\n     * If the input collection controls its input, such as a Set, and the\n     * Transformer creates duplicates (or are otherwise invalid), the\n     * collection may reduce in size due to calling this method.\n     *\n     * @param collection  the collection to get the input from, may be null\n     * @param transformer the transformer to perform, may be null\n     */\n    public static <E1, E2> void transform(Collection<E1> collection, Transformer<E1, E2> transformer) {\n        if (collection == null || transformer == null) return;\n        if (collection instanceof List) {\n            List list = (List) collection;\n            for (ListIterator it = list.listIterator(); it.hasNext(); ) {\n                it.set(transformer.transform((E1) it.next()));\n            }\n        } else {\n            Collection resultCollection = collect(collection, transformer);\n            collection.clear();\n            collection.addAll(resultCollection);\n        }\n    }\n\n\n    /**\n     * Returns a new Collection consisting of the elements of inputCollection transformed\n     * by the given transformer.\n     * <p>\n     * If the input transformer is null, the result is an empty list.\n     *\n     * @param inputCollection the collection to get the input from, may be null\n     * @param transformer     the transformer to use, may be null\n     * @return the transformed result (new list)\n     */\n    public static <E1, E2> Collection<E2> collect(final Collection<E1> inputCollection,\n                                                  final Transformer<E1, E2> transformer) {\n        List<E2> answer = new ArrayList<>();\n        if (inputCollection == null || transformer == null) return answer;\n        for (E1 e1 : inputCollection) {\n            answer.add(transformer.transform(e1));\n        }\n        return answer;\n    }\n\n    /**\n     * Counts the number of elements in the input collection that match the predicate.\n     * <p>\n     * A <code>null</code> collection or predicate matches no elements.\n     *\n     * @param collection the collection to get the input from, may be null\n     * @param predicate  the predicate to use, may be null\n     * @return the number of matches for the predicate in the collection\n     */\n    public static <E> int countMatches(Collection<E> collection, Predicate<E> predicate) {\n        if (collection == null || predicate == null) return 0;\n        int count = 0;\n        for (E o : collection) {\n            if (predicate.evaluate(o)) {\n                count++;\n            }\n        }\n        return count;\n    }\n\n    /**\n     * Answers true if a predicate is true for at least one element of a collection.\n     * <p>\n     * A <code>null</code> collection or predicate returns false.\n     *\n     * @param collection the collection to get the input from, may be null\n     * @param predicate  the predicate to use, may be null\n     * @return true if at least one element of the collection matches the predicate\n     */\n    public static <E> boolean exists(Collection<E> collection, Predicate<E> predicate) {\n        if (collection == null || predicate == null) return false;\n        for (E o : collection) {\n            if (predicate.evaluate(o)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Adds an element to the collection unless the element is null.\n     *\n     * @param collection the collection to add to, may be null\n     * @param object     the object to add, if null it will not be added\n     * @return true if the collection changed\n     */\n    public static <E> boolean addIgnoreNull(Collection<E> collection, E object) {\n        if (collection == null) return false;\n        return (object != null && collection.add(object));\n    }\n\n    /**\n     * Adds all elements in the iteration to the given collection.\n     *\n     * @param collection the collection to add to, may be null\n     * @param iterator   the iterator of elements to add, may be null\n     */\n    public static <E> void addAll(Collection<E> collection, Iterator<E> iterator) {\n        if (collection == null || iterator == null) return;\n        while (iterator.hasNext()) {\n            collection.add(iterator.next());\n        }\n    }\n\n    /**\n     * Adds all elements in the enumeration to the given collection.\n     *\n     * @param collection  the collection to add to, may be null\n     * @param enumeration the enumeration of elements to add, may be null\n     */\n    public static <E> void addAll(Collection<E> collection, Enumeration<E> enumeration) {\n        if (collection == null || enumeration == null) return;\n        while (enumeration.hasMoreElements()) {\n            collection.add(enumeration.nextElement());\n        }\n    }\n\n    /**\n     * Adds all elements in the array to the given collection.\n     *\n     * @param collection the collection to add to, may be null\n     * @param elements   the array of elements to add, may be null\n     */\n    public static <E> void addAll(Collection<E> collection, E[] elements) {\n        if (collection == null || elements == null || elements.length == 0) return;\n        collection.addAll(Arrays.asList(elements));\n    }\n\n    /**\n     * Returns the <code>index</code>-th value in <code>object</code>, throwing\n     * <code>IndexOutOfBoundsException</code> if there is no such element or\n     * <code>IllegalArgumentException</code> if <code>object</code> is not an\n     * instance of one of the supported types.\n     * <p>\n     * The supported types, and associated semantics are:\n     * <ul>\n     * <li> Map -- the value returned is the <code>Map.Entry</code> in position\n     * <code>index</code> in the map's <code>entrySet</code> iterator,\n     * if there is such an entry.</li>\n     * <li> List -- this method is equivalent to the list's get method.</li>\n     * <li> Array -- the <code>index</code>-th array entry is returned,\n     * if there is such an entry; otherwise an <code>IndexOutOfBoundsException</code>\n     * is thrown.</li>\n     * <li> Collection -- the value returned is the <code>index</code>-th object\n     * returned by the collection's default iterator, if there is such an element.</li>\n     * <li> Iterator or Enumeration -- the value returned is the\n     * <code>index</code>-th object in the Iterator/Enumeration, if there\n     * is such an element.  The Iterator/Enumeration is advanced to\n     * <code>index</code> (or to the end, if <code>index</code> exceeds the\n     * number of entries) as a side effect of this method.</li>\n     * </ul>\n     *\n     * @param object the object to get a value from\n     * @param index  the index to get\n     * @return the object at the specified index\n     * @throws IndexOutOfBoundsException if the index is invalid\n     * @throws IllegalArgumentException  if the object type is invalid\n     */\n    public static Object get(Object object, int index) {\n        if (object == null) return null;\n        if (index < 0) {\n            throw new IndexOutOfBoundsException(\"Index cannot be negative: \" + index);\n        }\n        if (object instanceof Map) {\n            Map map = (Map) object;\n            Iterator iterator = map.entrySet().iterator();\n            return get(iterator, index);\n        } else if (object instanceof List) {\n            return ((List) object).get(index);\n        } else if (object instanceof Object[]) {\n            return ((Object[]) object)[index];\n        } else if (object instanceof Iterator) {\n            Iterator it = (Iterator) object;\n            while (it.hasNext()) {\n                index--;\n                if (index == -1) {\n                    return it.next();\n                } else {\n                    it.next();\n                }\n            }\n            throw new IndexOutOfBoundsException(\"Entry does not exist: \" + index);\n        } else if (object instanceof Collection) {\n            Iterator iterator = ((Collection) object).iterator();\n            return get(iterator, index);\n        } else if (object instanceof Enumeration) {\n            Enumeration it = (Enumeration) object;\n            while (it.hasMoreElements()) {\n                index--;\n                if (index == -1) {\n                    return it.nextElement();\n                } else {\n                    it.nextElement();\n                }\n            }\n            throw new IndexOutOfBoundsException(\"Entry does not exist: \" + index);\n        } else {\n            try {\n                return Array.get(object, index);\n            } catch (IllegalArgumentException ex) {\n                throw new IllegalArgumentException(\"Unsupported object type: \" + object.getClass().getName());\n            }\n        }\n    }\n\n    /**\n     * Gets the size of the collection/iterator specified.\n     * <p>\n     * This method can handles objects as follows\n     * <ul>\n     * <li>Collection - the collection size\n     * <li>Map - the map size\n     * <li>Array - the array size\n     * <li>Iterator - the number of elements remaining in the iterator\n     * <li>Enumeration - the number of elements remaining in the enumeration\n     * </ul>\n     *\n     * @param object the object to get the size of\n     * @return the size of the specified collection\n     * @throws IllegalArgumentException thrown if object is not recognised or null\n     */\n    public static int size(Object object) {\n        if (object == null) return 0;\n        int total = 0;\n        if (object instanceof Map) {\n            total = ((Map) object).size();\n        } else if (object instanceof Collection) {\n            total = ((Collection) object).size();\n        } else if (object instanceof Object[]) {\n            total = ((Object[]) object).length;\n        } else if (object instanceof Iterator) {\n            Iterator it = (Iterator) object;\n            while (it.hasNext()) {\n                total++;\n                it.next();\n            }\n        } else if (object instanceof Enumeration) {\n            Enumeration it = (Enumeration) object;\n            while (it.hasMoreElements()) {\n                total++;\n                it.nextElement();\n            }\n        } else {\n            try {\n                total = Array.getLength(object);\n            } catch (IllegalArgumentException ex) {\n                throw new IllegalArgumentException(\"Unsupported object type: \" + object.getClass().getName());\n            }\n        }\n        return total;\n    }\n\n    /**\n     * Checks if the specified collection/array/iterator is empty.\n     * <p>\n     * This method can handles objects as follows\n     * <ul>\n     * <li>Collection - via collection isEmpty\n     * <li>Map - via map isEmpty\n     * <li>Array - using array size\n     * <li>Iterator - via hasNext\n     * <li>Enumeration - via hasMoreElements\n     * </ul>\n     * <p>\n     * Note: This method is named to avoid clashing with\n     * {@link #isEmpty(Collection)}.\n     *\n     * @param object the object to get the size of, not null\n     * @return true if empty\n     * @throws IllegalArgumentException thrown if object is not recognised or null\n     */\n    public static boolean sizeIsEmpty(Object object) {\n        if (object == null) return true;\n        if (object instanceof Collection) {\n            return ((Collection) object).isEmpty();\n        } else if (object instanceof Map) {\n            return ((Map) object).isEmpty();\n        } else if (object instanceof Object[]) {\n            return ((Object[]) object).length == 0;\n        } else if (object instanceof Iterator) {\n            return !((Iterator) object).hasNext();\n        } else if (object instanceof Enumeration) {\n            return !((Enumeration) object).hasMoreElements();\n        } else {\n            try {\n                return Array.getLength(object) == 0;\n            } catch (IllegalArgumentException ex) {\n                throw new IllegalArgumentException(\"Unsupported object type: \" + object.getClass().getName());\n            }\n        }\n    }\n\n    /**\n     * Null-safe check if the specified collection is empty.\n     * <p>\n     * Null returns true.\n     *\n     * @param coll the collection to check, may be null\n     * @return true if empty or null\n     */\n    public static boolean isEmpty(Collection coll) {\n        return coll == null || coll.size() == 0;\n    }\n\n    /**\n     * Null-safe check if the specified collection is not empty.\n     * <p>\n     * Null returns false.\n     *\n     * @param coll the collection to check, may be null\n     * @return true if non-null and non-empty\n     */\n    public static boolean isNotEmpty(Collection coll) {\n        return !isEmpty(coll);\n    }\n\n\n    /**\n     * Returns a collection containing all the elements in <code>collection</code>\n     * that are also in <code>retain</code>. The cardinality of an element <code>e</code>\n     * in the returned collection is the same as the cardinality of <code>e</code>\n     * in <code>collection</code> unless <code>retain</code> does not contain <code>e</code>, in which\n     * case the cardinality is zero. This method is useful if you do not wish to modify\n     * the collection <code>c</code> and thus cannot call <code>c.retainAll(retain);</code>.\n     *\n     * @param collection the collection whose contents are the target of the #retailAll operation\n     * @param retain     the collection containing the elements to be retained in the returned collection\n     * @return a <code>Collection</code> containing all the elements of <code>collection</code>\n     * that occur at least once in <code>retain</code>.\n     */\n    public static <E> Collection<E> retainAll(Collection<E> collection, Collection<E> retain) {\n        if (collection == null || retain == null) return new ArrayList<>();\n        List<E> list = new ArrayList<>();\n        for (E item : collection) {\n            if (retain.contains(item)) {\n                list.add(item);\n            }\n        }\n        return list;\n    }\n\n    /**\n     * Removes the elements in <code>remove</code> from <code>collection</code>. That is, this\n     * method returns a collection containing all the elements in <code>c</code>\n     * that are not in <code>remove</code>. The cardinality of an element <code>e</code>\n     * in the returned collection is the same as the cardinality of <code>e</code>\n     * in <code>collection</code> unless <code>remove</code> contains <code>e</code>, in which\n     * case the cardinality is zero. This method is useful if you do not wish to modify\n     * the collection <code>c</code> and thus cannot call <code>collection.removeAll(remove);</code>.\n     *\n     * @param collection the collection from which items are removed (in the returned collection)\n     * @param remove     the items to be removed from the returned <code>collection</code>\n     * @return a <code>Collection</code> containing all the elements of <code>collection</code> except\n     * any elements that also occur in <code>remove</code>.\n     */\n    public static <E> Collection<E> removeAll(Collection<E> collection, Collection<E> remove) {\n        if (collection == null) return new ArrayList<>();\n        if (remove == null) return new ArrayList<>(collection);\n        List<E> list = new ArrayList<>();\n        for (E obj : collection) {\n            if (!remove.contains(obj)) {\n                list.add(obj);\n            }\n        }\n        return list;\n    }\n\n    /**\n     * Randomly permutes the specified list using a default source of randomness.\n     *\n     * @param list the list to be shuffled.\n     * @throws UnsupportedOperationException if the specified list or\n     *                                       its list-iterator does not support the <tt>set</tt> operation.\n     */\n    public static <T> void shuffle(List<T> list) {\n        Collections.shuffle(list);\n    }\n\n    /**\n     * Return the string of collection.\n     *\n     * @param collection The collection.\n     * @return the string of collection\n     */\n    public static String toString(Collection collection) {\n        if (collection == null) return \"null\";\n        return collection.toString();\n    }\n\n    public interface Closure<E> {\n        void execute(int index, E item);\n    }\n\n    public interface Transformer<E1, E2> {\n        E2 transform(E1 input);\n    }\n\n    public interface Predicate<E> {\n        boolean evaluate(E item);\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ColorUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.graphics.Color;\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.ColorRes;\nimport androidx.annotation.FloatRange;\nimport androidx.annotation.IntRange;\nimport androidx.annotation.NonNull;\nimport androidx.core.content.ContextCompat;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/01/15\n *     desc  : utils about color\n * </pre>\n */\npublic final class ColorUtils {\n\n    private ColorUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Returns a color associated with a particular resource ID.\n     *\n     * @param id The desired resource identifier.\n     * @return a color associated with a particular resource ID\n     */\n    public static int getColor(@ColorRes int id) {\n        return ContextCompat.getColor(Utils.getApp(), id);\n    }\n\n    /**\n     * Set the alpha component of {@code color} to be {@code alpha}.\n     *\n     * @param color The color.\n     * @param alpha Alpha component \\([0..255]\\) of the color.\n     * @return the {@code color} with {@code alpha} component\n     */\n    public static int setAlphaComponent(@ColorInt final int color,\n                                        @IntRange(from = 0x0, to = 0xFF) int alpha) {\n        return (color & 0x00ffffff) | (alpha << 24);\n    }\n\n    /**\n     * Set the alpha component of {@code color} to be {@code alpha}.\n     *\n     * @param color The color.\n     * @param alpha Alpha component \\([0..1]\\) of the color.\n     * @return the {@code color} with {@code alpha} component\n     */\n    public static int setAlphaComponent(@ColorInt int color,\n                                        @FloatRange(from = 0, to = 1) float alpha) {\n        return (color & 0x00ffffff) | ((int) (alpha * 255.0f + 0.5f) << 24);\n    }\n\n    /**\n     * Set the red component of {@code color} to be {@code red}.\n     *\n     * @param color The color.\n     * @param red   Red component \\([0..255]\\) of the color.\n     * @return the {@code color} with {@code red} component\n     */\n    public static int setRedComponent(@ColorInt int color,\n                                      @IntRange(from = 0x0, to = 0xFF) int red) {\n        return (color & 0xff00ffff) | (red << 16);\n    }\n\n    /**\n     * Set the red component of {@code color} to be {@code red}.\n     *\n     * @param color The color.\n     * @param red   Red component \\([0..1]\\) of the color.\n     * @return the {@code color} with {@code red} component\n     */\n    public static int setRedComponent(@ColorInt int color,\n                                      @FloatRange(from = 0, to = 1) float red) {\n        return (color & 0xff00ffff) | ((int) (red * 255.0f + 0.5f) << 16);\n    }\n\n    /**\n     * Set the green component of {@code color} to be {@code green}.\n     *\n     * @param color The color.\n     * @param green Green component \\([0..255]\\) of the color.\n     * @return the {@code color} with {@code green} component\n     */\n    public static int setGreenComponent(@ColorInt int color,\n                                        @IntRange(from = 0x0, to = 0xFF) int green) {\n        return (color & 0xffff00ff) | (green << 8);\n    }\n\n    /**\n     * Set the green component of {@code color} to be {@code green}.\n     *\n     * @param color The color.\n     * @param green Green component \\([0..1]\\) of the color.\n     * @return the {@code color} with {@code green} component\n     */\n    public static int setGreenComponent(@ColorInt int color,\n                                        @FloatRange(from = 0, to = 1) float green) {\n        return (color & 0xffff00ff) | ((int) (green * 255.0f + 0.5f) << 8);\n    }\n\n    /**\n     * Set the blue component of {@code color} to be {@code blue}.\n     *\n     * @param color The color.\n     * @param blue  Blue component \\([0..255]\\) of the color.\n     * @return the {@code color} with {@code blue} component\n     */\n    public static int setBlueComponent(@ColorInt int color,\n                                       @IntRange(from = 0x0, to = 0xFF) int blue) {\n        return (color & 0xffffff00) | blue;\n    }\n\n    /**\n     * Set the blue component of {@code color} to be {@code blue}.\n     *\n     * @param color The color.\n     * @param blue  Blue component \\([0..1]\\) of the color.\n     * @return the {@code color} with {@code blue} component\n     */\n    public static int setBlueComponent(@ColorInt int color,\n                                       @FloatRange(from = 0, to = 1) float blue) {\n        return (color & 0xffffff00) | (int) (blue * 255.0f + 0.5f);\n    }\n\n    /**\n     * Color-string to color-int.\n     * <p>Supported formats are:</p>\n     *\n     * <ul>\n     * <li><code>#RRGGBB</code></li>\n     * <li><code>#AARRGGBB</code></li>\n     * </ul>\n     *\n     * <p>The following names are also accepted: <code>red</code>, <code>blue</code>,\n     * <code>green</code>, <code>black</code>, <code>white</code>, <code>gray</code>,\n     * <code>cyan</code>, <code>magenta</code>, <code>yellow</code>, <code>lightgray</code>,\n     * <code>darkgray</code>, <code>grey</code>, <code>lightgrey</code>, <code>darkgrey</code>,\n     * <code>aqua</code>, <code>fuchsia</code>, <code>lime</code>, <code>maroon</code>,\n     * <code>navy</code>, <code>olive</code>, <code>purple</code>, <code>silver</code>,\n     * and <code>teal</code>.</p>\n     *\n     * @param colorString The color-string.\n     * @return color-int\n     * @throws IllegalArgumentException The string cannot be parsed.\n     */\n    public static int string2Int(@NonNull String colorString) {\n        return Color.parseColor(colorString);\n    }\n\n    /**\n     * Color-int to color-string.\n     *\n     * @param colorInt The color-int.\n     * @return color-string\n     */\n    public static String int2RgbString(@ColorInt int colorInt) {\n        colorInt = colorInt & 0x00ffffff;\n        String color = Integer.toHexString(colorInt);\n        while (color.length() < 6) {\n            color = \"0\" + color;\n        }\n        return \"#\" + color;\n    }\n\n    /**\n     * Color-int to color-string.\n     *\n     * @param colorInt The color-int.\n     * @return color-string\n     */\n    public static String int2ArgbString(@ColorInt final int colorInt) {\n        String color = Integer.toHexString(colorInt);\n        while (color.length() < 6) {\n            color = \"0\" + color;\n        }\n        while (color.length() < 8) {\n            color = \"f\" + color;\n        }\n        return \"#\" + color;\n    }\n\n    /**\n     * Return the random color.\n     *\n     * @return the random color\n     */\n    public static int getRandomColor() {\n        return getRandomColor(true);\n    }\n\n    /**\n     * Return the random color.\n     *\n     * @param supportAlpha True to support alpha, false otherwise.\n     * @return the random color\n     */\n    public static int getRandomColor(final boolean supportAlpha) {\n        int high = supportAlpha ? (int) (Math.random() * 0x100) << 24 : 0xFF000000;\n        return high | (int) (Math.random() * 0x1000000);\n    }\n\n    /**\n     * Return whether the color is light.\n     *\n     * @param color The color.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isLightColor(@ColorInt int color) {\n        return 0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color) >= 127.5;\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ConvertUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.annotation.SuppressLint;\nimport android.graphics.Bitmap;\nimport android.graphics.drawable.Drawable;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.view.View;\n\nimport com.blankj.utilcode.constant.MemoryConstants;\nimport com.blankj.utilcode.constant.TimeConstants;\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport java.io.BufferedReader;\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.ObjectInputStream;\nimport java.io.ObjectOutputStream;\nimport java.io.OutputStream;\nimport java.io.Serializable;\nimport java.io.UnsupportedEncodingException;\nimport java.nio.charset.Charset;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/13\n *     desc  : utils about convert\n * </pre>\n */\npublic final class ConvertUtils {\n\n    private static final int    BUFFER_SIZE      = 8192;\n    private static final char[] HEX_DIGITS_UPPER =\n            {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};\n    private static final char[] HEX_DIGITS_LOWER =\n            {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};\n\n    private ConvertUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Int to hex string.\n     *\n     * @param num The int number.\n     * @return the hex string\n     */\n    public static String int2HexString(int num) {\n        return Integer.toHexString(num);\n    }\n\n    /**\n     * Hex string to int.\n     *\n     * @param hexString The hex string.\n     * @return the int\n     */\n    public static int hexString2Int(String hexString) {\n        return Integer.parseInt(hexString, 16);\n    }\n\n    /**\n     * Bytes to bits.\n     *\n     * @param bytes The bytes.\n     * @return bits\n     */\n    public static String bytes2Bits(final byte[] bytes) {\n        if (bytes == null || bytes.length == 0) return \"\";\n        StringBuilder sb = new StringBuilder();\n        for (byte aByte : bytes) {\n            for (int j = 7; j >= 0; --j) {\n                sb.append(((aByte >> j) & 0x01) == 0 ? '0' : '1');\n            }\n        }\n        return sb.toString();\n    }\n\n    /**\n     * Bits to bytes.\n     *\n     * @param bits The bits.\n     * @return bytes\n     */\n    public static byte[] bits2Bytes(String bits) {\n        int lenMod = bits.length() % 8;\n        int byteLen = bits.length() / 8;\n        // add \"0\" until length to 8 times\n        if (lenMod != 0) {\n            for (int i = lenMod; i < 8; i++) {\n                bits = \"0\" + bits;\n            }\n            byteLen++;\n        }\n        byte[] bytes = new byte[byteLen];\n        for (int i = 0; i < byteLen; ++i) {\n            for (int j = 0; j < 8; ++j) {\n                bytes[i] <<= 1;\n                bytes[i] |= bits.charAt(i * 8 + j) - '0';\n            }\n        }\n        return bytes;\n    }\n\n    /**\n     * Bytes to chars.\n     *\n     * @param bytes The bytes.\n     * @return chars\n     */\n    public static char[] bytes2Chars(final byte[] bytes) {\n        if (bytes == null) return null;\n        int len = bytes.length;\n        if (len <= 0) return null;\n        char[] chars = new char[len];\n        for (int i = 0; i < len; i++) {\n            chars[i] = (char) (bytes[i] & 0xff);\n        }\n        return chars;\n    }\n\n    /**\n     * Chars to bytes.\n     *\n     * @param chars The chars.\n     * @return bytes\n     */\n    public static byte[] chars2Bytes(final char[] chars) {\n        if (chars == null || chars.length <= 0) return null;\n        int len = chars.length;\n        byte[] bytes = new byte[len];\n        for (int i = 0; i < len; i++) {\n            bytes[i] = (byte) (chars[i]);\n        }\n        return bytes;\n    }\n\n    /**\n     * Bytes to hex string.\n     * <p>e.g. bytes2HexString(new byte[] { 0, (byte) 0xa8 }) returns \"00A8\"</p>\n     *\n     * @param bytes The bytes.\n     * @return hex string\n     */\n    public static String bytes2HexString(final byte[] bytes) {\n        return bytes2HexString(bytes, true);\n    }\n\n    /**\n     * Bytes to hex string.\n     * <p>e.g. bytes2HexString(new byte[] { 0, (byte) 0xa8 }, true) returns \"00A8\"</p>\n     *\n     * @param bytes       The bytes.\n     * @param isUpperCase True to use upper case, false otherwise.\n     * @return hex string\n     */\n    public static String bytes2HexString(final byte[] bytes, boolean isUpperCase) {\n        if (bytes == null) return \"\";\n        char[] hexDigits = isUpperCase ? HEX_DIGITS_UPPER : HEX_DIGITS_LOWER;\n        int len = bytes.length;\n        if (len <= 0) return \"\";\n        char[] ret = new char[len << 1];\n        for (int i = 0, j = 0; i < len; i++) {\n            ret[j++] = hexDigits[bytes[i] >> 4 & 0x0f];\n            ret[j++] = hexDigits[bytes[i] & 0x0f];\n        }\n        return new String(ret);\n    }\n\n    /**\n     * Hex string to bytes.\n     * <p>e.g. hexString2Bytes(\"00A8\") returns { 0, (byte) 0xA8 }</p>\n     *\n     * @param hexString The hex string.\n     * @return the bytes\n     */\n    public static byte[] hexString2Bytes(String hexString) {\n        if (UtilsBridge.isSpace(hexString)) return new byte[0];\n        int len = hexString.length();\n        if (len % 2 != 0) {\n            hexString = \"0\" + hexString;\n            len = len + 1;\n        }\n        char[] hexBytes = hexString.toUpperCase().toCharArray();\n        byte[] ret = new byte[len >> 1];\n        for (int i = 0; i < len; i += 2) {\n            ret[i >> 1] = (byte) (hex2Dec(hexBytes[i]) << 4 | hex2Dec(hexBytes[i + 1]));\n        }\n        return ret;\n    }\n\n    private static int hex2Dec(final char hexChar) {\n        if (hexChar >= '0' && hexChar <= '9') {\n            return hexChar - '0';\n        } else if (hexChar >= 'A' && hexChar <= 'F') {\n            return hexChar - 'A' + 10;\n        } else {\n            throw new IllegalArgumentException();\n        }\n    }\n\n    /**\n     * Bytes to string.\n     */\n    public static String bytes2String(final byte[] bytes) {\n        return bytes2String(bytes, \"\");\n    }\n\n    /**\n     * Bytes to string.\n     */\n    public static String bytes2String(final byte[] bytes, final String charsetName) {\n        if (bytes == null) return null;\n        try {\n            return new String(bytes, getSafeCharset(charsetName));\n        } catch (UnsupportedEncodingException e) {\n            e.printStackTrace();\n            return new String(bytes);\n        }\n    }\n\n    /**\n     * String to bytes.\n     */\n    public static byte[] string2Bytes(final String string) {\n        return string2Bytes(string, \"\");\n    }\n\n    /**\n     * String to bytes.\n     */\n    public static byte[] string2Bytes(final String string, final String charsetName) {\n        if (string == null) return null;\n        try {\n            return string.getBytes(getSafeCharset(charsetName));\n        } catch (UnsupportedEncodingException e) {\n            e.printStackTrace();\n            return string.getBytes();\n        }\n    }\n\n    /**\n     * Bytes to JSONObject.\n     */\n    public static JSONObject bytes2JSONObject(final byte[] bytes) {\n        if (bytes == null) return null;\n        try {\n            return new JSONObject(new String(bytes));\n        } catch (Exception e) {\n            e.printStackTrace();\n            return null;\n        }\n    }\n\n    /**\n     * JSONObject to bytes.\n     */\n    public static byte[] jsonObject2Bytes(final JSONObject jsonObject) {\n        if (jsonObject == null) return null;\n        return jsonObject.toString().getBytes();\n    }\n\n    /**\n     * Bytes to JSONArray.\n     */\n    public static JSONArray bytes2JSONArray(final byte[] bytes) {\n        if (bytes == null) return null;\n        try {\n            return new JSONArray(new String(bytes));\n        } catch (Exception e) {\n            e.printStackTrace();\n            return null;\n        }\n    }\n\n    /**\n     * JSONArray to bytes.\n     */\n    public static byte[] jsonArray2Bytes(final JSONArray jsonArray) {\n        if (jsonArray == null) return null;\n        return jsonArray.toString().getBytes();\n    }\n\n    /**\n     * Bytes to Parcelable\n     */\n    public static <T> T bytes2Parcelable(final byte[] bytes,\n                                         final Parcelable.Creator<T> creator) {\n        if (bytes == null) return null;\n        Parcel parcel = Parcel.obtain();\n        parcel.unmarshall(bytes, 0, bytes.length);\n        parcel.setDataPosition(0);\n        T result = creator.createFromParcel(parcel);\n        parcel.recycle();\n        return result;\n    }\n\n    /**\n     * Parcelable to bytes.\n     */\n    public static byte[] parcelable2Bytes(final Parcelable parcelable) {\n        if (parcelable == null) return null;\n        Parcel parcel = Parcel.obtain();\n        parcelable.writeToParcel(parcel, 0);\n        byte[] bytes = parcel.marshall();\n        parcel.recycle();\n        return bytes;\n    }\n\n    /**\n     * Bytes to Serializable.\n     */\n    public static Object bytes2Object(final byte[] bytes) {\n        if (bytes == null) return null;\n        ObjectInputStream ois = null;\n        try {\n            ois = new ObjectInputStream(new ByteArrayInputStream(bytes));\n            return ois.readObject();\n        } catch (Exception e) {\n            e.printStackTrace();\n            return null;\n        } finally {\n            try {\n                if (ois != null) {\n                    ois.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    /**\n     * Serializable to bytes.\n     */\n    public static byte[] serializable2Bytes(final Serializable serializable) {\n        if (serializable == null) return null;\n        ByteArrayOutputStream baos;\n        ObjectOutputStream oos = null;\n        try {\n            oos = new ObjectOutputStream(baos = new ByteArrayOutputStream());\n            oos.writeObject(serializable);\n            return baos.toByteArray();\n        } catch (Exception e) {\n            e.printStackTrace();\n            return null;\n        } finally {\n            try {\n                if (oos != null) {\n                    oos.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    /**\n     * Bytes to bitmap.\n     */\n    public static Bitmap bytes2Bitmap(final byte[] bytes) {\n        return UtilsBridge.bytes2Bitmap(bytes);\n    }\n\n    /**\n     * Bitmap to bytes.\n     */\n    public static byte[] bitmap2Bytes(final Bitmap bitmap) {\n        return UtilsBridge.bitmap2Bytes(bitmap);\n    }\n\n    /**\n     * Bitmap to bytes.\n     */\n    public static byte[] bitmap2Bytes(final Bitmap bitmap, final Bitmap.CompressFormat format, int quality) {\n        return UtilsBridge.bitmap2Bytes(bitmap, format, quality);\n    }\n\n    /**\n     * Bytes to drawable.\n     */\n    public static Drawable bytes2Drawable(final byte[] bytes) {\n        return UtilsBridge.bytes2Drawable(bytes);\n    }\n\n    /**\n     * Drawable to bytes.\n     */\n    public static byte[] drawable2Bytes(final Drawable drawable) {\n        return UtilsBridge.drawable2Bytes(drawable);\n    }\n\n    /**\n     * Drawable to bytes.\n     */\n    public static byte[] drawable2Bytes(final Drawable drawable, final Bitmap.CompressFormat format, int quality) {\n        return UtilsBridge.drawable2Bytes(drawable, format, quality);\n    }\n\n    /**\n     * Size of memory in unit to size of byte.\n     *\n     * @param memorySize Size of memory.\n     * @param unit       The unit of memory size.\n     *                   <ul>\n     *                   <li>{@link MemoryConstants#BYTE}</li>\n     *                   <li>{@link MemoryConstants#KB}</li>\n     *                   <li>{@link MemoryConstants#MB}</li>\n     *                   <li>{@link MemoryConstants#GB}</li>\n     *                   </ul>\n     * @return size of byte\n     */\n    public static long memorySize2Byte(final long memorySize,\n                                       @MemoryConstants.Unit final int unit) {\n        if (memorySize < 0) return -1;\n        return memorySize * unit;\n    }\n\n    /**\n     * Size of byte to size of memory in unit.\n     *\n     * @param byteSize Size of byte.\n     * @param unit     The unit of memory size.\n     *                 <ul>\n     *                 <li>{@link MemoryConstants#BYTE}</li>\n     *                 <li>{@link MemoryConstants#KB}</li>\n     *                 <li>{@link MemoryConstants#MB}</li>\n     *                 <li>{@link MemoryConstants#GB}</li>\n     *                 </ul>\n     * @return size of memory in unit\n     */\n    public static double byte2MemorySize(final long byteSize,\n                                         @MemoryConstants.Unit final int unit) {\n        if (byteSize < 0) return -1;\n        return (double) byteSize / unit;\n    }\n\n    /**\n     * Size of byte to fit size of memory.\n     * <p>to three decimal places</p>\n     *\n     * @param byteSize Size of byte.\n     * @return fit size of memory\n     */\n    @SuppressLint(\"DefaultLocale\")\n    public static String byte2FitMemorySize(final long byteSize) {\n        return byte2FitMemorySize(byteSize, 3);\n    }\n\n    /**\n     * Size of byte to fit size of memory.\n     * <p>to three decimal places</p>\n     *\n     * @param byteSize  Size of byte.\n     * @param precision The precision\n     * @return fit size of memory\n     */\n    @SuppressLint(\"DefaultLocale\")\n    public static String byte2FitMemorySize(final long byteSize, int precision) {\n        if (precision < 0) {\n            throw new IllegalArgumentException(\"precision shouldn't be less than zero!\");\n        }\n        if (byteSize < 0) {\n            throw new IllegalArgumentException(\"byteSize shouldn't be less than zero!\");\n        } else if (byteSize < MemoryConstants.KB) {\n            return String.format(\"%.\" + precision + \"fB\", (double) byteSize);\n        } else if (byteSize < MemoryConstants.MB) {\n            return String.format(\"%.\" + precision + \"fKB\", (double) byteSize / MemoryConstants.KB);\n        } else if (byteSize < MemoryConstants.GB) {\n            return String.format(\"%.\" + precision + \"fMB\", (double) byteSize / MemoryConstants.MB);\n        } else {\n            return String.format(\"%.\" + precision + \"fGB\", (double) byteSize / MemoryConstants.GB);\n        }\n    }\n\n    /**\n     * Time span in unit to milliseconds.\n     *\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return milliseconds\n     */\n    public static long timeSpan2Millis(final long timeSpan, @TimeConstants.Unit final int unit) {\n        return timeSpan * unit;\n    }\n\n    /**\n     * Milliseconds to time span in unit.\n     *\n     * @param millis The milliseconds.\n     * @param unit   The unit of time span.\n     *               <ul>\n     *               <li>{@link TimeConstants#MSEC}</li>\n     *               <li>{@link TimeConstants#SEC }</li>\n     *               <li>{@link TimeConstants#MIN }</li>\n     *               <li>{@link TimeConstants#HOUR}</li>\n     *               <li>{@link TimeConstants#DAY }</li>\n     *               </ul>\n     * @return time span in unit\n     */\n    public static long millis2TimeSpan(final long millis, @TimeConstants.Unit final int unit) {\n        return millis / unit;\n    }\n\n    /**\n     * Milliseconds to fit time span.\n     *\n     * @param millis    The milliseconds.\n     *                  <p>millis &lt;= 0, return null</p>\n     * @param precision The precision of time span.\n     *                  <ul>\n     *                  <li>precision = 0, return null</li>\n     *                  <li>precision = 1, return 天</li>\n     *                  <li>precision = 2, return 天, 小时</li>\n     *                  <li>precision = 3, return 天, 小时, 分钟</li>\n     *                  <li>precision = 4, return 天, 小时, 分钟, 秒</li>\n     *                  <li>precision &gt;= 5，return 天, 小时, 分钟, 秒, 毫秒</li>\n     *                  </ul>\n     * @return fit time span\n     */\n    public static String millis2FitTimeSpan(long millis, int precision) {\n        return UtilsBridge.millis2FitTimeSpan(millis, precision);\n    }\n\n    /**\n     * Input stream to output stream.\n     */\n    public static ByteArrayOutputStream input2OutputStream(final InputStream is) {\n        if (is == null) return null;\n        try {\n            ByteArrayOutputStream os = new ByteArrayOutputStream();\n            byte[] b = new byte[BUFFER_SIZE];\n            int len;\n            while ((len = is.read(b, 0, BUFFER_SIZE)) != -1) {\n                os.write(b, 0, len);\n            }\n            return os;\n        } catch (IOException e) {\n            e.printStackTrace();\n            return null;\n        } finally {\n            try {\n                is.close();\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    /**\n     * Output stream to input stream.\n     */\n    public static ByteArrayInputStream output2InputStream(final OutputStream out) {\n        if (out == null) return null;\n        return new ByteArrayInputStream(((ByteArrayOutputStream) out).toByteArray());\n    }\n\n    /**\n     * Input stream to bytes.\n     */\n    public static byte[] inputStream2Bytes(final InputStream is) {\n        if (is == null) return null;\n        return input2OutputStream(is).toByteArray();\n    }\n\n    /**\n     * Bytes to input stream.\n     */\n    public static InputStream bytes2InputStream(final byte[] bytes) {\n        if (bytes == null || bytes.length <= 0) return null;\n        return new ByteArrayInputStream(bytes);\n    }\n\n    /**\n     * Output stream to bytes.\n     */\n    public static byte[] outputStream2Bytes(final OutputStream out) {\n        if (out == null) return null;\n        return ((ByteArrayOutputStream) out).toByteArray();\n    }\n\n    /**\n     * Bytes to output stream.\n     */\n    public static OutputStream bytes2OutputStream(final byte[] bytes) {\n        if (bytes == null || bytes.length <= 0) return null;\n        ByteArrayOutputStream os = null;\n        try {\n            os = new ByteArrayOutputStream();\n            os.write(bytes);\n            return os;\n        } catch (IOException e) {\n            e.printStackTrace();\n            return null;\n        } finally {\n            try {\n                if (os != null) {\n                    os.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    /**\n     * Input stream to string.\n     */\n    public static String inputStream2String(final InputStream is, final String charsetName) {\n        if (is == null) return \"\";\n        try {\n            ByteArrayOutputStream baos = input2OutputStream(is);\n            if (baos == null) return \"\";\n            return baos.toString(getSafeCharset(charsetName));\n        } catch (UnsupportedEncodingException e) {\n            e.printStackTrace();\n            return \"\";\n        }\n    }\n\n    /**\n     * String to input stream.\n     */\n    public static InputStream string2InputStream(final String string, final String charsetName) {\n        if (string == null) return null;\n        try {\n            return new ByteArrayInputStream(string.getBytes(getSafeCharset(charsetName)));\n        } catch (UnsupportedEncodingException e) {\n            e.printStackTrace();\n            return null;\n        }\n    }\n\n    /**\n     * Output stream to string.\n     */\n    public static String outputStream2String(final OutputStream out, final String charsetName) {\n        if (out == null) return \"\";\n        try {\n            return new String(outputStream2Bytes(out), getSafeCharset(charsetName));\n        } catch (UnsupportedEncodingException e) {\n            e.printStackTrace();\n            return \"\";\n        }\n    }\n\n    /**\n     * String to output stream.\n     */\n    public static OutputStream string2OutputStream(final String string, final String charsetName) {\n        if (string == null) return null;\n        try {\n            return bytes2OutputStream(string.getBytes(getSafeCharset(charsetName)));\n        } catch (UnsupportedEncodingException e) {\n            e.printStackTrace();\n            return null;\n        }\n    }\n\n    public static List<String> inputStream2Lines(final InputStream is) {\n        return inputStream2Lines(is, \"\");\n    }\n\n    public static List<String> inputStream2Lines(final InputStream is,\n                                                 final String charsetName) {\n        BufferedReader reader = null;\n        try {\n            List<String> list = new ArrayList<>();\n            reader = new BufferedReader(new InputStreamReader(is, getSafeCharset(charsetName)));\n            String line;\n            while ((line = reader.readLine()) != null) {\n                list.add(line);\n            }\n            return list;\n        } catch (IOException e) {\n            e.printStackTrace();\n            return null;\n        } finally {\n            try {\n                if (reader != null) {\n                    reader.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    /**\n     * Drawable to bitmap.\n     */\n    public static Bitmap drawable2Bitmap(final Drawable drawable) {\n        return UtilsBridge.drawable2Bitmap(drawable);\n    }\n\n    /**\n     * Bitmap to drawable.\n     */\n    public static Drawable bitmap2Drawable(final Bitmap bitmap) {\n        return UtilsBridge.bitmap2Drawable(bitmap);\n    }\n\n    /**\n     * View to bitmap.\n     */\n    public static Bitmap view2Bitmap(final View view) {\n        return UtilsBridge.view2Bitmap(view);\n    }\n\n    /**\n     * Value of dp to value of px.\n     */\n    public static int dp2px(final float dpValue) {\n        return UtilsBridge.dp2px(dpValue);\n    }\n\n    /**\n     * Value of px to value of dp.\n     */\n    public static int px2dp(final float pxValue) {\n        return UtilsBridge.px2dp(pxValue);\n    }\n\n    /**\n     * Value of sp to value of px.\n     */\n    public static int sp2px(final float spValue) {\n        return UtilsBridge.sp2px(spValue);\n    }\n\n    /**\n     * Value of px to value of sp.\n     */\n    public static int px2sp(final float pxValue) {\n        return UtilsBridge.px2sp(pxValue);\n    }\n\n    private static String getSafeCharset(String charsetName) {\n        String cn = charsetName;\n        if (UtilsBridge.isSpace(charsetName) || !Charset.isSupported(charsetName)) {\n            cn = \"UTF-8\";\n        }\n        return cn;\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/CrashUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport androidx.annotation.NonNull;\nimport java.io.File;\nimport java.lang.Thread.UncaughtExceptionHandler;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.Map;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/09/27\n *     desc  : utils about crash\n * </pre>\n */\npublic final class CrashUtils {\n\n    private static final String FILE_SEP = System.getProperty(\"file.separator\");\n\n    private static final UncaughtExceptionHandler DEFAULT_UNCAUGHT_EXCEPTION_HANDLER =\n        Thread.getDefaultUncaughtExceptionHandler();\n\n    private CrashUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Initialization.\n     */\n    public static void init() {\n        init(\"\");\n    }\n\n    /**\n     * Initialization\n     *\n     * @param crashDir The directory of saving crash information.\n     */\n    public static void init(@NonNull final File crashDir) {\n        init(crashDir.getAbsolutePath(), null);\n    }\n\n    /**\n     * Initialization\n     *\n     * @param crashDirPath The directory's path of saving crash information.\n     */\n    public static void init(final String crashDirPath) {\n        init(crashDirPath, null);\n    }\n\n    /**\n     * Initialization\n     *\n     * @param onCrashListener The crash listener.\n     */\n    public static void init(final OnCrashListener onCrashListener) {\n        init(\"\", onCrashListener);\n    }\n\n    /**\n     * Initialization\n     *\n     * @param crashDir The directory of saving crash information.\n     * @param onCrashListener The crash listener.\n     */\n    public static void init(@NonNull final File crashDir, final OnCrashListener onCrashListener) {\n        init(crashDir.getAbsolutePath(), onCrashListener);\n    }\n\n    /**\n     * Initialization\n     *\n     * @param crashDirPath The directory's path of saving crash information.\n     * @param onCrashListener The crash listener.\n     */\n    public static void init(final String crashDirPath, final OnCrashListener onCrashListener) {\n        String dirPath;\n        if (UtilsBridge.isSpace(crashDirPath)) {\n            if (UtilsBridge.isSDCardEnableByEnvironment()\n                && Utils.getApp().getExternalFilesDir(null) != null) {\n                dirPath = Utils.getApp().getExternalFilesDir(null) + FILE_SEP + \"crash\" + FILE_SEP;\n            } else {\n                dirPath = Utils.getApp().getFilesDir() + FILE_SEP + \"crash\" + FILE_SEP;\n            }\n        } else {\n            dirPath = crashDirPath.endsWith(FILE_SEP) ? crashDirPath : crashDirPath + FILE_SEP;\n        }\n        Thread.setDefaultUncaughtExceptionHandler(\n            getUncaughtExceptionHandler(dirPath, onCrashListener));\n    }\n\n    private static UncaughtExceptionHandler getUncaughtExceptionHandler(final String dirPath,\n                                                                        final OnCrashListener onCrashListener) {\n        return new UncaughtExceptionHandler() {\n            @Override\n            public void uncaughtException(@NonNull final Thread t, @NonNull final Throwable e) {\n                final String time = new SimpleDateFormat(\"yyyy_MM_dd-HH_mm_ss\").format(new Date());\n                CrashInfo info = new CrashInfo(time, e);\n                final String crashFile = dirPath + time + \".txt\";\n                UtilsBridge.writeFileFromString(crashFile, info.toString(), true);\n\n                if (DEFAULT_UNCAUGHT_EXCEPTION_HANDLER != null) {\n                    DEFAULT_UNCAUGHT_EXCEPTION_HANDLER.uncaughtException(t, e);\n                }\n                if (onCrashListener != null) {\n                    onCrashListener.onCrash(info);\n                }\n            }\n        };\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // interface\n    ///////////////////////////////////////////////////////////////////////////\n\n    public interface OnCrashListener {\n        void onCrash(CrashInfo crashInfo);\n    }\n\n    public static final class CrashInfo {\n        private UtilsBridge.FileHead mFileHeadProvider;\n        private Throwable mThrowable;\n\n        private CrashInfo(String time, Throwable throwable) {\n            mThrowable = throwable;\n            mFileHeadProvider = new UtilsBridge.FileHead(\"Crash\");\n            mFileHeadProvider.addFirst(\"Time Of Crash\", time);\n        }\n\n        public final void addExtraHead(Map<String, String> extraHead) {\n            mFileHeadProvider.append(extraHead);\n        }\n\n        public final void addExtraHead(String key, String value) {\n            mFileHeadProvider.append(key, value);\n        }\n\n        public final Throwable getThrowable() {\n            return mThrowable;\n        }\n\n        @Override\n        public String toString() {\n            return mFileHeadProvider.toString() + UtilsBridge.getFullStackTrace(mThrowable);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/DebouncingUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.os.SystemClock;\nimport android.text.TextUtils;\nimport android.view.View;\n\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport androidx.annotation.NonNull;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2020/09/01\n *     desc  : utils about debouncing\n * </pre>\n */\npublic class DebouncingUtils {\n\n    private static final int               CACHE_SIZE               = 64;\n    private static final Map<String, Long> KEY_MILLIS_MAP           = new ConcurrentHashMap<>(CACHE_SIZE);\n    private static final long              DEBOUNCING_DEFAULT_VALUE = 1000;\n\n    private DebouncingUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Return whether the view is not in a jitter state.\n     *\n     * @param view The view.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isValid(@NonNull final View view) {\n        return isValid(view, DEBOUNCING_DEFAULT_VALUE);\n    }\n\n    /**\n     * Return whether the view is not in a jitter state.\n     *\n     * @param view     The view.\n     * @param duration The duration.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isValid(@NonNull final View view, final long duration) {\n        return isValid(String.valueOf(view.hashCode()), duration);\n    }\n\n    /**\n     * Return whether the key is not in a jitter state.\n     *\n     * @param key      The key.\n     * @param duration The duration.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isValid(@NonNull String key, final long duration) {\n        if (TextUtils.isEmpty(key)) {\n            throw new IllegalArgumentException(\"The key is null.\");\n        }\n        if (duration < 0) {\n            throw new IllegalArgumentException(\"The duration is less than 0.\");\n        }\n        long curTime = SystemClock.elapsedRealtime();\n        clearIfNecessary(curTime);\n        Long validTime = KEY_MILLIS_MAP.get(key);\n        if (validTime == null || curTime >= validTime) {\n            KEY_MILLIS_MAP.put(key, curTime + duration);\n            return true;\n        }\n        return false;\n    }\n\n    private static void clearIfNecessary(long curTime) {\n        if (KEY_MILLIS_MAP.size() < CACHE_SIZE) return;\n        for (Iterator<Map.Entry<String, Long>> it = KEY_MILLIS_MAP.entrySet().iterator(); it.hasNext(); ) {\n            Map.Entry<String, Long> entry = it.next();\n            Long validTime = entry.getValue();\n            if (curTime >= validTime) {\n                it.remove();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/DeviceUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.res.Configuration;\nimport android.content.res.Resources;\nimport android.net.Uri;\nimport android.net.wifi.WifiInfo;\nimport android.net.wifi.WifiManager;\nimport android.os.Build;\nimport android.provider.Settings;\nimport android.telephony.TelephonyManager;\nimport android.text.TextUtils;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.net.InetAddress;\nimport java.net.NetworkInterface;\nimport java.net.SocketException;\nimport java.util.Enumeration;\nimport java.util.UUID;\n\nimport androidx.annotation.RequiresApi;\nimport androidx.annotation.RequiresPermission;\n\nimport static android.Manifest.permission.ACCESS_WIFI_STATE;\nimport static android.Manifest.permission.CHANGE_WIFI_STATE;\nimport static android.Manifest.permission.INTERNET;\nimport static android.content.Context.WIFI_SERVICE;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/8/1\n *     desc  : utils about device\n * </pre>\n */\npublic final class DeviceUtils {\n\n    private DeviceUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Return whether device is rooted.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isDeviceRooted() {\n        String su = \"su\";\n        String[] locations = {\"/system/bin/\", \"/system/xbin/\", \"/sbin/\", \"/system/sd/xbin/\",\n                \"/system/bin/failsafe/\", \"/data/local/xbin/\", \"/data/local/bin/\", \"/data/local/\",\n                \"/system/sbin/\", \"/usr/bin/\", \"/vendor/bin/\"};\n        for (String location : locations) {\n            if (new File(location + su).exists()) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Return whether ADB is enabled.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)\n    public static boolean isAdbEnabled() {\n        return Settings.Secure.getInt(\n                Utils.getApp().getContentResolver(),\n                Settings.Global.ADB_ENABLED, 0\n        ) > 0;\n    }\n\n    /**\n     * Return the version name of device's system.\n     *\n     * @return the version name of device's system\n     */\n    public static String getSDKVersionName() {\n        return android.os.Build.VERSION.RELEASE;\n    }\n\n    /**\n     * Return version code of device's system.\n     *\n     * @return version code of device's system\n     */\n    public static int getSDKVersionCode() {\n        return android.os.Build.VERSION.SDK_INT;\n    }\n\n    /**\n     * Return the android id of device.\n     *\n     * @return the android id of device\n     */\n    @SuppressLint(\"HardwareIds\")\n    public static String getAndroidID() {\n        String id = Settings.Secure.getString(\n                Utils.getApp().getContentResolver(),\n                Settings.Secure.ANDROID_ID\n        );\n        if (\"9774d56d682e549c\".equals(id)) return \"\";\n        return id == null ? \"\" : id;\n    }\n\n    /**\n     * Return the MAC address.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />},\n     * {@code <uses-permission android:name=\"android.permission.INTERNET\" />},\n     * {@code <uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\" />}</p>\n     *\n     * @return the MAC address\n     */\n    @RequiresPermission(allOf = {ACCESS_WIFI_STATE, CHANGE_WIFI_STATE})\n    public static String getMacAddress() {\n        String macAddress = getMacAddress((String[]) null);\n        if (!TextUtils.isEmpty(macAddress) || getWifiEnabled()) return macAddress;\n        setWifiEnabled(true);\n        setWifiEnabled(false);\n        return getMacAddress((String[]) null);\n    }\n\n    private static boolean getWifiEnabled() {\n        @SuppressLint(\"WifiManagerLeak\")\n        WifiManager manager = (WifiManager) Utils.getApp().getSystemService(WIFI_SERVICE);\n        if (manager == null) return false;\n        return manager.isWifiEnabled();\n    }\n\n    /**\n     * Enable or disable wifi.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\" />}</p>\n     *\n     * @param enabled True to enabled, false otherwise.\n     */\n    @RequiresPermission(CHANGE_WIFI_STATE)\n    private static void setWifiEnabled(final boolean enabled) {\n        @SuppressLint(\"WifiManagerLeak\")\n        WifiManager manager = (WifiManager) Utils.getApp().getSystemService(WIFI_SERVICE);\n        if (manager == null) return;\n        if (enabled == manager.isWifiEnabled()) return;\n        manager.setWifiEnabled(enabled);\n    }\n\n    /**\n     * Return the MAC address.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />},\n     * {@code <uses-permission android:name=\"android.permission.INTERNET\" />}</p>\n     *\n     * @return the MAC address\n     */\n    @RequiresPermission(allOf = {ACCESS_WIFI_STATE})\n    public static String getMacAddress(final String... excepts) {\n        String macAddress = getMacAddressByNetworkInterface();\n        if (isAddressNotInExcepts(macAddress, excepts)) {\n            return macAddress;\n        }\n        macAddress = getMacAddressByInetAddress();\n        if (isAddressNotInExcepts(macAddress, excepts)) {\n            return macAddress;\n        }\n        macAddress = getMacAddressByWifiInfo();\n        if (isAddressNotInExcepts(macAddress, excepts)) {\n            return macAddress;\n        }\n        macAddress = getMacAddressByFile();\n        if (isAddressNotInExcepts(macAddress, excepts)) {\n            return macAddress;\n        }\n        return \"\";\n    }\n\n    private static boolean isAddressNotInExcepts(final String address, final String... excepts) {\n        if (TextUtils.isEmpty(address)) {\n            return false;\n        }\n        if (\"02:00:00:00:00:00\".equals(address)) {\n            return false;\n        }\n        if (excepts == null || excepts.length == 0) {\n            return true;\n        }\n        for (String filter : excepts) {\n            if (filter != null && filter.equals(address)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    @RequiresPermission(ACCESS_WIFI_STATE)\n    private static String getMacAddressByWifiInfo() {\n        try {\n            final WifiManager wifi = (WifiManager) Utils.getApp()\n                    .getApplicationContext().getSystemService(WIFI_SERVICE);\n            if (wifi != null) {\n                final WifiInfo info = wifi.getConnectionInfo();\n                if (info != null) {\n                    @SuppressLint(\"HardwareIds\")\n                    String macAddress = info.getMacAddress();\n                    if (!TextUtils.isEmpty(macAddress)) {\n                        return macAddress;\n                    }\n                }\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return \"02:00:00:00:00:00\";\n    }\n\n    private static String getMacAddressByNetworkInterface() {\n        try {\n            Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();\n            while (nis.hasMoreElements()) {\n                NetworkInterface ni = nis.nextElement();\n                if (ni == null || !ni.getName().equalsIgnoreCase(\"wlan0\")) continue;\n                byte[] macBytes = ni.getHardwareAddress();\n                if (macBytes != null && macBytes.length > 0) {\n                    StringBuilder sb = new StringBuilder();\n                    for (byte b : macBytes) {\n                        sb.append(String.format(\"%02x:\", b));\n                    }\n                    return sb.substring(0, sb.length() - 1);\n                }\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return \"02:00:00:00:00:00\";\n    }\n\n    private static String getMacAddressByInetAddress() {\n        try {\n            InetAddress inetAddress = getInetAddress();\n            if (inetAddress != null) {\n                NetworkInterface ni = NetworkInterface.getByInetAddress(inetAddress);\n                if (ni != null) {\n                    byte[] macBytes = ni.getHardwareAddress();\n                    if (macBytes != null && macBytes.length > 0) {\n                        StringBuilder sb = new StringBuilder();\n                        for (byte b : macBytes) {\n                            sb.append(String.format(\"%02x:\", b));\n                        }\n                        return sb.substring(0, sb.length() - 1);\n                    }\n                }\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return \"02:00:00:00:00:00\";\n    }\n\n    private static InetAddress getInetAddress() {\n        try {\n            Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();\n            while (nis.hasMoreElements()) {\n                NetworkInterface ni = nis.nextElement();\n                // To prevent phone of xiaomi return \"10.0.2.15\"\n                if (!ni.isUp()) continue;\n                Enumeration<InetAddress> addresses = ni.getInetAddresses();\n                while (addresses.hasMoreElements()) {\n                    InetAddress inetAddress = addresses.nextElement();\n                    if (!inetAddress.isLoopbackAddress()) {\n                        String hostAddress = inetAddress.getHostAddress();\n                        if (hostAddress.indexOf(':') < 0) return inetAddress;\n                    }\n                }\n            }\n        } catch (SocketException e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    private static String getMacAddressByFile() {\n        ShellUtils.CommandResult result = UtilsBridge.execCmd(\"getprop wifi.interface\", false);\n        if (result.result == 0) {\n            String name = result.successMsg;\n            if (name != null) {\n                result = UtilsBridge.execCmd(\"cat /sys/class/net/\" + name + \"/address\", false);\n                if (result.result == 0) {\n                    String address = result.successMsg;\n                    if (address != null && address.length() > 0) {\n                        return address;\n                    }\n                }\n            }\n        }\n        return \"02:00:00:00:00:00\";\n    }\n\n    /**\n     * Return the manufacturer of the product/hardware.\n     * <p>e.g. Xiaomi</p>\n     *\n     * @return the manufacturer of the product/hardware\n     */\n    public static String getManufacturer() {\n        return Build.MANUFACTURER;\n    }\n\n    /**\n     * Return the model of device.\n     * <p>e.g. MI2SC</p>\n     *\n     * @return the model of device\n     */\n    public static String getModel() {\n        String model = Build.MODEL;\n        if (model != null) {\n            model = model.trim().replaceAll(\"\\\\s*\", \"\");\n        } else {\n            model = \"\";\n        }\n        return model;\n    }\n\n    /**\n     * Return an ordered list of ABIs supported by this device. The most preferred ABI is the first\n     * element in the list.\n     *\n     * @return an ordered list of ABIs supported by this device\n     */\n    public static String[] getABIs() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            return Build.SUPPORTED_ABIS;\n        } else {\n            if (!TextUtils.isEmpty(Build.CPU_ABI2)) {\n                return new String[]{Build.CPU_ABI, Build.CPU_ABI2};\n            }\n            return new String[]{Build.CPU_ABI};\n        }\n    }\n\n    /**\n     * Return whether device is tablet.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isTablet() {\n        return (Resources.getSystem().getConfiguration().screenLayout\n                & Configuration.SCREENLAYOUT_SIZE_MASK)\n                >= Configuration.SCREENLAYOUT_SIZE_LARGE;\n    }\n\n    /**\n     * Return whether device is emulator.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isEmulator() {\n        boolean checkProperty = Build.FINGERPRINT.startsWith(\"generic\")\n                || Build.FINGERPRINT.toLowerCase().contains(\"vbox\")\n                || Build.FINGERPRINT.toLowerCase().contains(\"test-keys\")\n                || Build.MODEL.contains(\"google_sdk\")\n                || Build.MODEL.contains(\"Emulator\")\n                || Build.MODEL.contains(\"Android SDK built for x86\")\n                || Build.MANUFACTURER.contains(\"Genymotion\")\n                || (Build.BRAND.startsWith(\"generic\") && Build.DEVICE.startsWith(\"generic\"))\n                || \"google_sdk\".equals(Build.PRODUCT);\n        if (checkProperty) return true;\n\n        String operatorName = \"\";\n        TelephonyManager tm = (TelephonyManager) Utils.getApp().getSystemService(Context.TELEPHONY_SERVICE);\n        if (tm != null) {\n            String name = tm.getNetworkOperatorName();\n            if (name != null) {\n                operatorName = name;\n            }\n        }\n        boolean checkOperatorName = operatorName.toLowerCase().equals(\"android\");\n        if (checkOperatorName) return true;\n\n        String url = \"tel:\" + \"123456\";\n        Intent intent = new Intent();\n        intent.setData(Uri.parse(url));\n        intent.setAction(Intent.ACTION_DIAL);\n        boolean checkDial = intent.resolveActivity(Utils.getApp().getPackageManager()) == null;\n        if (checkDial) return true;\n        if (isEmulatorByCpu()) return true;\n\n//        boolean checkDebuggerConnected = Debug.isDebuggerConnected();\n//        if (checkDebuggerConnected) return true;\n\n        return false;\n    }\n\n    /**\n     * Returns whether is emulator by check cpu info.\n     * by function of {@link #readCpuInfo}, obtain the device cpu information.\n     * then compare whether it is intel or amd (because intel and amd are generally not mobile phone cpu), to determine whether it is a real mobile phone\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    private static boolean isEmulatorByCpu() {\n        String cpuInfo = readCpuInfo();\n        return cpuInfo.contains(\"intel\") || cpuInfo.contains(\"amd\");\n    }\n\n    /**\n     * Return Cpu information\n     *\n     * @return Cpu info\n     */\n    private static String readCpuInfo() {\n        String result = \"\";\n        try {\n            String[] args = {\"/system/bin/cat\", \"/proc/cpuinfo\"};\n            ProcessBuilder cmd = new ProcessBuilder(args);\n            Process process = cmd.start();\n            StringBuilder sb = new StringBuilder();\n            String readLine;\n            BufferedReader responseReader = new BufferedReader(new InputStreamReader(process.getInputStream(), \"utf-8\"));\n            while ((readLine = responseReader.readLine()) != null) {\n                sb.append(readLine);\n            }\n            responseReader.close();\n            result = sb.toString().toLowerCase();\n        } catch (IOException ignored) {\n        }\n        return result;\n    }\n\n    /**\n     * Whether user has enabled development settings.\n     *\n     * @return whether user has enabled development settings.\n     */\n    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR1)\n    public static boolean isDevelopmentSettingsEnabled() {\n        return Settings.Global.getInt(\n                Utils.getApp().getContentResolver(),\n                Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0\n        ) > 0;\n    }\n\n\n    private static final    String KEY_UDID = \"KEY_UDID\";\n    private volatile static String udid;\n\n    /**\n     * Return the unique device id.\n     * <pre>{1}{UUID(macAddress)}</pre>\n     * <pre>{2}{UUID(androidId )}</pre>\n     * <pre>{9}{UUID(random    )}</pre>\n     *\n     * @return the unique device id\n     */\n    public static String getUniqueDeviceId() {\n        return getUniqueDeviceId(\"\", true);\n    }\n\n    /**\n     * Return the unique device id.\n     * <pre>android 10 deprecated {prefix}{1}{UUID(macAddress)}</pre>\n     * <pre>{prefix}{2}{UUID(androidId )}</pre>\n     * <pre>{prefix}{9}{UUID(random    )}</pre>\n     *\n     * @param prefix The prefix of the unique device id.\n     * @return the unique device id\n     */\n    public static String getUniqueDeviceId(String prefix) {\n        return getUniqueDeviceId(prefix, true);\n    }\n\n    /**\n     * Return the unique device id.\n     * <pre>{1}{UUID(macAddress)}</pre>\n     * <pre>{2}{UUID(androidId )}</pre>\n     * <pre>{9}{UUID(random    )}</pre>\n     *\n     * @param useCache True to use cache, false otherwise.\n     * @return the unique device id\n     */\n    public static String getUniqueDeviceId(boolean useCache) {\n        return getUniqueDeviceId(\"\", useCache);\n    }\n\n    /**\n     * Return the unique device id.\n     * <pre>android 10 deprecated {prefix}{1}{UUID(macAddress)}</pre>\n     * <pre>{prefix}{2}{UUID(androidId )}</pre>\n     * <pre>{prefix}{9}{UUID(random    )}</pre>\n     *\n     * @param prefix   The prefix of the unique device id.\n     * @param useCache True to use cache, false otherwise.\n     * @return the unique device id\n     */\n    public static String getUniqueDeviceId(String prefix, boolean useCache) {\n        if (!useCache) {\n            return getUniqueDeviceIdReal(prefix);\n        }\n        if (udid == null) {\n            synchronized (DeviceUtils.class) {\n                if (udid == null) {\n                    final String id = UtilsBridge.getSpUtils4Utils().getString(KEY_UDID, null);\n                    if (id != null) {\n                        udid = id;\n                        return udid;\n                    }\n                    return getUniqueDeviceIdReal(prefix);\n                }\n            }\n        }\n        return udid;\n    }\n\n    private static String getUniqueDeviceIdReal(String prefix) {\n        try {\n            final String androidId = getAndroidID();\n            if (!TextUtils.isEmpty(androidId)) {\n                return saveUdid(prefix + 2, androidId);\n            }\n\n        } catch (Exception ignore) {/**/}\n        return saveUdid(prefix + 9, \"\");\n    }\n\n    @RequiresPermission(allOf = {ACCESS_WIFI_STATE, INTERNET, CHANGE_WIFI_STATE})\n    public static boolean isSameDevice(final String uniqueDeviceId) {\n        // {prefix}{type}{32id}\n        if (TextUtils.isEmpty(uniqueDeviceId) && uniqueDeviceId.length() < 33) return false;\n        if (uniqueDeviceId.equals(udid)) return true;\n        final String cachedId = UtilsBridge.getSpUtils4Utils().getString(KEY_UDID, null);\n        if (uniqueDeviceId.equals(cachedId)) return true;\n        int st = uniqueDeviceId.length() - 33;\n        String type = uniqueDeviceId.substring(st, st + 1);\n        if (type.startsWith(\"1\")) {\n            String macAddress = getMacAddress();\n            if (macAddress.equals(\"\")) {\n                return false;\n            }\n            return uniqueDeviceId.substring(st + 1).equals(getUdid(\"\", macAddress));\n        } else if (type.startsWith(\"2\")) {\n            final String androidId = getAndroidID();\n            if (TextUtils.isEmpty(androidId)) {\n                return false;\n            }\n            return uniqueDeviceId.substring(st + 1).equals(getUdid(\"\", androidId));\n        }\n        return false;\n    }\n\n    private static String saveUdid(String prefix, String id) {\n        udid = getUdid(prefix, id);\n        UtilsBridge.getSpUtils4Utils().put(KEY_UDID, udid);\n        return udid;\n    }\n\n    private static String getUdid(String prefix, String id) {\n        if (id.equals(\"\")) {\n            return prefix + UUID.randomUUID().toString().replace(\"-\", \"\");\n        }\n        return prefix + UUID.nameUUIDFromBytes(id.getBytes()).toString().replace(\"-\", \"\");\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/DialogUtils.java",
    "content": "//package com.blankj.utilcode.util;\n//\n//import android.app.Activity;\n//import android.app.Dialog;\n//import android.content.Context;\n//import android.content.ContextWrapper;\n//import android.graphics.drawable.ColorDrawable;\n//import android.os.Build;\n//import android.support.annotation.LayoutRes;\n//import android.support.annotation.NonNull;\n//import android.util.Log;\n//import android.view.LayoutInflater;\n//import android.view.View;\n//import android.view.Window;\n//\n//import java.util.HashMap;\n//import java.util.TreeSet;\n//\n///**\n// * <pre>\n// *     author: blankj\n// *     blog  : http://blankj.com\n// *     time  : 2019/08/26\n// *     desc  : utils about dialog\n// * </pre>\n// */\n//public class DialogUtils {\n//\n//    private DialogUtils() {\n//        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n//    }\n//\n//    public static void show(final Dialog dialog) {\n//        if (dialog == null) return;\n//        Utils.runOnUiThread(new Runnable() {\n//            @Override\n//            public void run() {\n//                Activity activity = getActivityByContext(dialog.getContext());\n//                if (!isActivityAlive(activity)) return;\n//                dialog.show();\n//            }\n//        });\n//    }\n//\n//    public static void dismiss(final Dialog dialog) {\n//        if (dialog == null) return;\n//        Utils.runOnUiThread(new Runnable() {\n//            @Override\n//            public void run() {\n//                dialog.dismiss();\n//            }\n//        });\n//    }\n//\n//    public static void show(final Utils.TransActivityDelegate delegate) {\n//        Utils.TransActivity.start(null, delegate);\n//    }\n//\n//    public static Dialog create(Activity activity, @LayoutRes int layoutId) {\n//        Dialog dialog = new Dialog(activity);\n//        View dialogContent = LayoutInflater.from(activity).inflate(layoutId, null);\n//\n//        dialog.setContentView(dialogContent);\n//        Window window = dialog.getWindow();\n//        if (window != null) {\n//            window.setBackgroundDrawable(new ColorDrawable(0));\n//        }\n//\n//        return dialog;\n//    }\n//\n//    private static boolean isActivityAlive(final Activity activity) {\n//        return activity != null && !activity.isFinishing()\n//                && (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1 || !activity.isDestroyed());\n//    }\n//\n//    private static Activity getActivityByContext(Context context) {\n//        if (context instanceof Activity) return (Activity) context;\n//        while (context instanceof ContextWrapper) {\n//            if (context instanceof Activity) {\n//                return (Activity) context;\n//            }\n//            context = ((ContextWrapper) context).getBaseContext();\n//        }\n//        return null;\n//    }\n//\n//    public static final class UtilsDialog extends Dialog {\n//\n//        private int mPriority = 5;\n//\n//        public UtilsDialog(@NonNull Context context) {\n//            this(context, 0);\n//        }\n//\n//        public UtilsDialog(@NonNull Context context, int themeResId) {\n//            super(context, themeResId);\n//        }\n//\n//        @Override\n//        public void show() {\n//            Utils.runOnUiThread(new Runnable() {\n//                @Override\n//                public void run() {\n//                    Activity activity = getActivityByContext(getContext());\n//                    if (!isActivityAlive(activity)) {\n//                        Log.w(\"DialogUtils\", \"Activity is not alive.\");\n//                        return;\n//                    }\n//                    UtilsDialog.super.show();\n//                }\n//            });\n//        }\n//\n//        @Override\n//        public void dismiss() {\n//            Utils.runOnUiThread(new Runnable() {\n//                @Override\n//                public void run() {\n//                    UtilsDialog.super.dismiss();\n//                }\n//            });\n//        }\n//\n//        public void show(int priority) {\n//            mPriority = priority;\n//            show();\n//        }\n//    }\n//}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/EncodeUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.os.Build;\nimport android.text.Html;\nimport android.util.Base64;\n\nimport java.io.UnsupportedEncodingException;\nimport java.net.URLDecoder;\nimport java.net.URLEncoder;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/07\n *     desc  : utils about encode\n * </pre>\n */\npublic final class EncodeUtils {\n\n    private EncodeUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Return the urlencoded string.\n     *\n     * @param input The input.\n     * @return the urlencoded string\n     */\n    public static String urlEncode(final String input) {\n        return urlEncode(input, \"UTF-8\");\n    }\n\n    /**\n     * Return the urlencoded string.\n     *\n     * @param input       The input.\n     * @param charsetName The name of charset.\n     * @return the urlencoded string\n     */\n    public static String urlEncode(final String input, final String charsetName) {\n        if (input == null || input.length() == 0) return \"\";\n        try {\n            return URLEncoder.encode(input, charsetName);\n        } catch (UnsupportedEncodingException e) {\n            throw new AssertionError(e);\n        }\n    }\n\n    /**\n     * Return the string of decode urlencoded string.\n     *\n     * @param input The input.\n     * @return the string of decode urlencoded string\n     */\n    public static String urlDecode(final String input) {\n        return urlDecode(input, \"UTF-8\");\n    }\n\n    /**\n     * Return the string of decode urlencoded string.\n     *\n     * @param input       The input.\n     * @param charsetName The name of charset.\n     * @return the string of decode urlencoded string\n     */\n    public static String urlDecode(final String input, final String charsetName) {\n        if (input == null || input.length() == 0) return \"\";\n        try {\n            String safeInput = input.replaceAll(\"%(?![0-9a-fA-F]{2})\", \"%25\").replaceAll(\"\\\\+\", \"%2B\");\n            return URLDecoder.decode(safeInput, charsetName);\n        } catch (UnsupportedEncodingException e) {\n            throw new AssertionError(e);\n        }\n    }\n\n    /**\n     * Return Base64-encode bytes.\n     *\n     * @param input The input.\n     * @return Base64-encode bytes\n     */\n    public static byte[] base64Encode(final String input) {\n        return base64Encode(input.getBytes());\n    }\n\n    /**\n     * Return Base64-encode bytes.\n     *\n     * @param input The input.\n     * @return Base64-encode bytes\n     */\n    public static byte[] base64Encode(final byte[] input) {\n        if (input == null || input.length == 0) return new byte[0];\n        return Base64.encode(input, Base64.NO_WRAP);\n    }\n\n    /**\n     * Return Base64-encode string.\n     *\n     * @param input The input.\n     * @return Base64-encode string\n     */\n    public static String base64Encode2String(final byte[] input) {\n        if (input == null || input.length == 0) return \"\";\n        return Base64.encodeToString(input, Base64.NO_WRAP);\n    }\n\n    /**\n     * Return the bytes of decode Base64-encode string.\n     *\n     * @param input The input.\n     * @return the string of decode Base64-encode string\n     */\n    public static byte[] base64Decode(final String input) {\n        if (input == null || input.length() == 0) return new byte[0];\n        return Base64.decode(input, Base64.NO_WRAP);\n    }\n\n    /**\n     * Return the bytes of decode Base64-encode bytes.\n     *\n     * @param input The input.\n     * @return the bytes of decode Base64-encode bytes\n     */\n    public static byte[] base64Decode(final byte[] input) {\n        if (input == null || input.length == 0) return new byte[0];\n        return Base64.decode(input, Base64.NO_WRAP);\n    }\n\n    /**\n     * Return html-encode string.\n     *\n     * @param input The input.\n     * @return html-encode string\n     */\n    public static String htmlEncode(final CharSequence input) {\n        if (input == null || input.length() == 0) return \"\";\n        StringBuilder sb = new StringBuilder();\n        char c;\n        for (int i = 0, len = input.length(); i < len; i++) {\n            c = input.charAt(i);\n            switch (c) {\n                case '<':\n                    sb.append(\"&lt;\"); //$NON-NLS-1$\n                    break;\n                case '>':\n                    sb.append(\"&gt;\"); //$NON-NLS-1$\n                    break;\n                case '&':\n                    sb.append(\"&amp;\"); //$NON-NLS-1$\n                    break;\n                case '\\'':\n                    //http://www.w3.org/TR/xhtml1\n                    // The named character reference &apos; (the apostrophe, U+0027) was\n                    // introduced in XML 1.0 but does not appear in HTML. Authors should\n                    // therefore use &#39; instead of &apos; to work as expected in HTML 4\n                    // user agents.\n                    sb.append(\"&#39;\"); //$NON-NLS-1$\n                    break;\n                case '\"':\n                    sb.append(\"&quot;\"); //$NON-NLS-1$\n                    break;\n                default:\n                    sb.append(c);\n            }\n        }\n        return sb.toString();\n    }\n\n    /**\n     * Return the string of decode html-encode string.\n     *\n     * @param input The input.\n     * @return the string of decode html-encode string\n     */\n    public static CharSequence htmlDecode(final String input) {\n        if (input == null || input.length() == 0) return \"\";\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n            return Html.fromHtml(input, Html.FROM_HTML_MODE_LEGACY);\n        } else {\n            return Html.fromHtml(input);\n        }\n    }\n\n    /**\n     * Return the binary encoded string padded with one space\n     *\n     * @param input The input.\n     * @return binary string\n     */\n    public static String binaryEncode(final String input) {\n        if (input == null || input.length() == 0) return \"\";\n        StringBuilder sb = new StringBuilder();\n        for (char i : input.toCharArray()) {\n            sb.append(Integer.toBinaryString(i)).append(\" \");\n        }\n        return sb.deleteCharAt(sb.length() - 1).toString();\n    }\n\n    /**\n     * Return UTF-8 String from binary\n     *\n     * @param input binary string\n     * @return UTF-8 String\n     */\n    public static String binaryDecode(final String input) {\n        if (input == null || input.length() == 0) return \"\";\n        String[] splits = input.split(\" \");\n        StringBuilder sb = new StringBuilder();\n        for (String split : splits) {\n            sb.append(((char) Integer.parseInt(split, 2)));\n        }\n        return sb.toString();\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/EncryptUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.os.Build;\n\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.security.DigestInputStream;\nimport java.security.InvalidKeyException;\nimport java.security.Key;\nimport java.security.KeyFactory;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.spec.AlgorithmParameterSpec;\nimport java.security.spec.PKCS8EncodedKeySpec;\nimport java.security.spec.X509EncodedKeySpec;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.Mac;\nimport javax.crypto.SecretKey;\nimport javax.crypto.SecretKeyFactory;\nimport javax.crypto.spec.DESKeySpec;\nimport javax.crypto.spec.IvParameterSpec;\nimport javax.crypto.spec.SecretKeySpec;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/02\n *     desc  : utils about encrypt\n * </pre>\n */\npublic final class EncryptUtils {\n\n    private EncryptUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // hash encryption\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Return the hex string of MD2 encryption.\n     *\n     * @param data The data.\n     * @return the hex string of MD2 encryption\n     */\n    public static String encryptMD2ToString(final String data) {\n        if (data == null || data.length() == 0) return \"\";\n        return encryptMD2ToString(data.getBytes());\n    }\n\n    /**\n     * Return the hex string of MD2 encryption.\n     *\n     * @param data The data.\n     * @return the hex string of MD2 encryption\n     */\n    public static String encryptMD2ToString(final byte[] data) {\n        return UtilsBridge.bytes2HexString(encryptMD2(data));\n    }\n\n    /**\n     * Return the bytes of MD2 encryption.\n     *\n     * @param data The data.\n     * @return the bytes of MD2 encryption\n     */\n    public static byte[] encryptMD2(final byte[] data) {\n        return hashTemplate(data, \"MD2\");\n    }\n\n    /**\n     * Return the hex string of MD5 encryption.\n     *\n     * @param data The data.\n     * @return the hex string of MD5 encryption\n     */\n    public static String encryptMD5ToString(final String data) {\n        if (data == null || data.length() == 0) return \"\";\n        return encryptMD5ToString(data.getBytes());\n    }\n\n    /**\n     * Return the hex string of MD5 encryption.\n     *\n     * @param data The data.\n     * @param salt The salt.\n     * @return the hex string of MD5 encryption\n     */\n    public static String encryptMD5ToString(final String data, final String salt) {\n        if (data == null && salt == null) return \"\";\n        if (salt == null) return UtilsBridge.bytes2HexString(encryptMD5(data.getBytes()));\n        if (data == null) return UtilsBridge.bytes2HexString(encryptMD5(salt.getBytes()));\n        return UtilsBridge.bytes2HexString(encryptMD5((data + salt).getBytes()));\n    }\n\n    /**\n     * Return the hex string of MD5 encryption.\n     *\n     * @param data The data.\n     * @return the hex string of MD5 encryption\n     */\n    public static String encryptMD5ToString(final byte[] data) {\n        return UtilsBridge.bytes2HexString(encryptMD5(data));\n    }\n\n    /**\n     * Return the hex string of MD5 encryption.\n     *\n     * @param data The data.\n     * @param salt The salt.\n     * @return the hex string of MD5 encryption\n     */\n    public static String encryptMD5ToString(final byte[] data, final byte[] salt) {\n        if (data == null && salt == null) return \"\";\n        if (salt == null) return UtilsBridge.bytes2HexString(encryptMD5(data));\n        if (data == null) return UtilsBridge.bytes2HexString(encryptMD5(salt));\n        byte[] dataSalt = new byte[data.length + salt.length];\n        System.arraycopy(data, 0, dataSalt, 0, data.length);\n        System.arraycopy(salt, 0, dataSalt, data.length, salt.length);\n        return UtilsBridge.bytes2HexString(encryptMD5(dataSalt));\n    }\n\n    /**\n     * Return the bytes of MD5 encryption.\n     *\n     * @param data The data.\n     * @return the bytes of MD5 encryption\n     */\n    public static byte[] encryptMD5(final byte[] data) {\n        return hashTemplate(data, \"MD5\");\n    }\n\n    /**\n     * Return the hex string of file's MD5 encryption.\n     *\n     * @param filePath The path of file.\n     * @return the hex string of file's MD5 encryption\n     */\n    public static String encryptMD5File2String(final String filePath) {\n        File file = UtilsBridge.isSpace(filePath) ? null : new File(filePath);\n        return encryptMD5File2String(file);\n    }\n\n    /**\n     * Return the bytes of file's MD5 encryption.\n     *\n     * @param filePath The path of file.\n     * @return the bytes of file's MD5 encryption\n     */\n    public static byte[] encryptMD5File(final String filePath) {\n        File file = UtilsBridge.isSpace(filePath) ? null : new File(filePath);\n        return encryptMD5File(file);\n    }\n\n    /**\n     * Return the hex string of file's MD5 encryption.\n     *\n     * @param file The file.\n     * @return the hex string of file's MD5 encryption\n     */\n    public static String encryptMD5File2String(final File file) {\n        return UtilsBridge.bytes2HexString(encryptMD5File(file));\n    }\n\n    /**\n     * Return the bytes of file's MD5 encryption.\n     *\n     * @param file The file.\n     * @return the bytes of file's MD5 encryption\n     */\n    public static byte[] encryptMD5File(final File file) {\n        if (file == null) return null;\n        FileInputStream fis = null;\n        DigestInputStream digestInputStream;\n        try {\n            fis = new FileInputStream(file);\n            MessageDigest md = MessageDigest.getInstance(\"MD5\");\n            digestInputStream = new DigestInputStream(fis, md);\n            byte[] buffer = new byte[256 * 1024];\n            while (true) {\n                if (!(digestInputStream.read(buffer) > 0)) break;\n            }\n            md = digestInputStream.getMessageDigest();\n            return md.digest();\n        } catch (NoSuchAlgorithmException | IOException e) {\n            e.printStackTrace();\n            return null;\n        } finally {\n            try {\n                if (fis != null) {\n                    fis.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    /**\n     * Return the hex string of SHA1 encryption.\n     *\n     * @param data The data.\n     * @return the hex string of SHA1 encryption\n     */\n    public static String encryptSHA1ToString(final String data) {\n        if (data == null || data.length() == 0) return \"\";\n        return encryptSHA1ToString(data.getBytes());\n    }\n\n    /**\n     * Return the hex string of SHA1 encryption.\n     *\n     * @param data The data.\n     * @return the hex string of SHA1 encryption\n     */\n    public static String encryptSHA1ToString(final byte[] data) {\n        return UtilsBridge.bytes2HexString(encryptSHA1(data));\n    }\n\n    /**\n     * Return the bytes of SHA1 encryption.\n     *\n     * @param data The data.\n     * @return the bytes of SHA1 encryption\n     */\n    public static byte[] encryptSHA1(final byte[] data) {\n        return hashTemplate(data, \"SHA-1\");\n    }\n\n    /**\n     * Return the hex string of SHA224 encryption.\n     *\n     * @param data The data.\n     * @return the hex string of SHA224 encryption\n     */\n    public static String encryptSHA224ToString(final String data) {\n        if (data == null || data.length() == 0) return \"\";\n        return encryptSHA224ToString(data.getBytes());\n    }\n\n    /**\n     * Return the hex string of SHA224 encryption.\n     *\n     * @param data The data.\n     * @return the hex string of SHA224 encryption\n     */\n    public static String encryptSHA224ToString(final byte[] data) {\n        return UtilsBridge.bytes2HexString(encryptSHA224(data));\n    }\n\n    /**\n     * Return the bytes of SHA224 encryption.\n     *\n     * @param data The data.\n     * @return the bytes of SHA224 encryption\n     */\n    public static byte[] encryptSHA224(final byte[] data) {\n        return hashTemplate(data, \"SHA224\");\n    }\n\n    /**\n     * Return the hex string of SHA256 encryption.\n     *\n     * @param data The data.\n     * @return the hex string of SHA256 encryption\n     */\n    public static String encryptSHA256ToString(final String data) {\n        if (data == null || data.length() == 0) return \"\";\n        return encryptSHA256ToString(data.getBytes());\n    }\n\n    /**\n     * Return the hex string of SHA256 encryption.\n     *\n     * @param data The data.\n     * @return the hex string of SHA256 encryption\n     */\n    public static String encryptSHA256ToString(final byte[] data) {\n        return UtilsBridge.bytes2HexString(encryptSHA256(data));\n    }\n\n    /**\n     * Return the bytes of SHA256 encryption.\n     *\n     * @param data The data.\n     * @return the bytes of SHA256 encryption\n     */\n    public static byte[] encryptSHA256(final byte[] data) {\n        return hashTemplate(data, \"SHA-256\");\n    }\n\n    /**\n     * Return the hex string of SHA384 encryption.\n     *\n     * @param data The data.\n     * @return the hex string of SHA384 encryption\n     */\n    public static String encryptSHA384ToString(final String data) {\n        if (data == null || data.length() == 0) return \"\";\n        return encryptSHA384ToString(data.getBytes());\n    }\n\n    /**\n     * Return the hex string of SHA384 encryption.\n     *\n     * @param data The data.\n     * @return the hex string of SHA384 encryption\n     */\n    public static String encryptSHA384ToString(final byte[] data) {\n        return UtilsBridge.bytes2HexString(encryptSHA384(data));\n    }\n\n    /**\n     * Return the bytes of SHA384 encryption.\n     *\n     * @param data The data.\n     * @return the bytes of SHA384 encryption\n     */\n    public static byte[] encryptSHA384(final byte[] data) {\n        return hashTemplate(data, \"SHA-384\");\n    }\n\n    /**\n     * Return the hex string of SHA512 encryption.\n     *\n     * @param data The data.\n     * @return the hex string of SHA512 encryption\n     */\n    public static String encryptSHA512ToString(final String data) {\n        if (data == null || data.length() == 0) return \"\";\n        return encryptSHA512ToString(data.getBytes());\n    }\n\n    /**\n     * Return the hex string of SHA512 encryption.\n     *\n     * @param data The data.\n     * @return the hex string of SHA512 encryption\n     */\n    public static String encryptSHA512ToString(final byte[] data) {\n        return UtilsBridge.bytes2HexString(encryptSHA512(data));\n    }\n\n    /**\n     * Return the bytes of SHA512 encryption.\n     *\n     * @param data The data.\n     * @return the bytes of SHA512 encryption\n     */\n    public static byte[] encryptSHA512(final byte[] data) {\n        return hashTemplate(data, \"SHA-512\");\n    }\n\n    /**\n     * Return the bytes of hash encryption.\n     *\n     * @param data      The data.\n     * @param algorithm The name of hash encryption.\n     * @return the bytes of hash encryption\n     */\n    static byte[] hashTemplate(final byte[] data, final String algorithm) {\n        if (data == null || data.length <= 0) return null;\n        try {\n            MessageDigest md = MessageDigest.getInstance(algorithm);\n            md.update(data);\n            return md.digest();\n        } catch (NoSuchAlgorithmException e) {\n            e.printStackTrace();\n            return null;\n        }\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // hmac encryption\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Return the hex string of HmacMD5 encryption.\n     *\n     * @param data The data.\n     * @param key  The key.\n     * @return the hex string of HmacMD5 encryption\n     */\n    public static String encryptHmacMD5ToString(final String data, final String key) {\n        if (data == null || data.length() == 0 || key == null || key.length() == 0) return \"\";\n        return encryptHmacMD5ToString(data.getBytes(), key.getBytes());\n    }\n\n    /**\n     * Return the hex string of HmacMD5 encryption.\n     *\n     * @param data The data.\n     * @param key  The key.\n     * @return the hex string of HmacMD5 encryption\n     */\n    public static String encryptHmacMD5ToString(final byte[] data, final byte[] key) {\n        return UtilsBridge.bytes2HexString(encryptHmacMD5(data, key));\n    }\n\n    /**\n     * Return the bytes of HmacMD5 encryption.\n     *\n     * @param data The data.\n     * @param key  The key.\n     * @return the bytes of HmacMD5 encryption\n     */\n    public static byte[] encryptHmacMD5(final byte[] data, final byte[] key) {\n        return hmacTemplate(data, key, \"HmacMD5\");\n    }\n\n    /**\n     * Return the hex string of HmacSHA1 encryption.\n     *\n     * @param data The data.\n     * @param key  The key.\n     * @return the hex string of HmacSHA1 encryption\n     */\n    public static String encryptHmacSHA1ToString(final String data, final String key) {\n        if (data == null || data.length() == 0 || key == null || key.length() == 0) return \"\";\n        return encryptHmacSHA1ToString(data.getBytes(), key.getBytes());\n    }\n\n    /**\n     * Return the hex string of HmacSHA1 encryption.\n     *\n     * @param data The data.\n     * @param key  The key.\n     * @return the hex string of HmacSHA1 encryption\n     */\n    public static String encryptHmacSHA1ToString(final byte[] data, final byte[] key) {\n        return UtilsBridge.bytes2HexString(encryptHmacSHA1(data, key));\n    }\n\n    /**\n     * Return the bytes of HmacSHA1 encryption.\n     *\n     * @param data The data.\n     * @param key  The key.\n     * @return the bytes of HmacSHA1 encryption\n     */\n    public static byte[] encryptHmacSHA1(final byte[] data, final byte[] key) {\n        return hmacTemplate(data, key, \"HmacSHA1\");\n    }\n\n    /**\n     * Return the hex string of HmacSHA224 encryption.\n     *\n     * @param data The data.\n     * @param key  The key.\n     * @return the hex string of HmacSHA224 encryption\n     */\n    public static String encryptHmacSHA224ToString(final String data, final String key) {\n        if (data == null || data.length() == 0 || key == null || key.length() == 0) return \"\";\n        return encryptHmacSHA224ToString(data.getBytes(), key.getBytes());\n    }\n\n    /**\n     * Return the hex string of HmacSHA224 encryption.\n     *\n     * @param data The data.\n     * @param key  The key.\n     * @return the hex string of HmacSHA224 encryption\n     */\n    public static String encryptHmacSHA224ToString(final byte[] data, final byte[] key) {\n        return UtilsBridge.bytes2HexString(encryptHmacSHA224(data, key));\n    }\n\n    /**\n     * Return the bytes of HmacSHA224 encryption.\n     *\n     * @param data The data.\n     * @param key  The key.\n     * @return the bytes of HmacSHA224 encryption\n     */\n    public static byte[] encryptHmacSHA224(final byte[] data, final byte[] key) {\n        return hmacTemplate(data, key, \"HmacSHA224\");\n    }\n\n    /**\n     * Return the hex string of HmacSHA256 encryption.\n     *\n     * @param data The data.\n     * @param key  The key.\n     * @return the hex string of HmacSHA256 encryption\n     */\n    public static String encryptHmacSHA256ToString(final String data, final String key) {\n        if (data == null || data.length() == 0 || key == null || key.length() == 0) return \"\";\n        return encryptHmacSHA256ToString(data.getBytes(), key.getBytes());\n    }\n\n    /**\n     * Return the hex string of HmacSHA256 encryption.\n     *\n     * @param data The data.\n     * @param key  The key.\n     * @return the hex string of HmacSHA256 encryption\n     */\n    public static String encryptHmacSHA256ToString(final byte[] data, final byte[] key) {\n        return UtilsBridge.bytes2HexString(encryptHmacSHA256(data, key));\n    }\n\n    /**\n     * Return the bytes of HmacSHA256 encryption.\n     *\n     * @param data The data.\n     * @param key  The key.\n     * @return the bytes of HmacSHA256 encryption\n     */\n    public static byte[] encryptHmacSHA256(final byte[] data, final byte[] key) {\n        return hmacTemplate(data, key, \"HmacSHA256\");\n    }\n\n    /**\n     * Return the hex string of HmacSHA384 encryption.\n     *\n     * @param data The data.\n     * @param key  The key.\n     * @return the hex string of HmacSHA384 encryption\n     */\n    public static String encryptHmacSHA384ToString(final String data, final String key) {\n        if (data == null || data.length() == 0 || key == null || key.length() == 0) return \"\";\n        return encryptHmacSHA384ToString(data.getBytes(), key.getBytes());\n    }\n\n    /**\n     * Return the hex string of HmacSHA384 encryption.\n     *\n     * @param data The data.\n     * @param key  The key.\n     * @return the hex string of HmacSHA384 encryption\n     */\n    public static String encryptHmacSHA384ToString(final byte[] data, final byte[] key) {\n        return UtilsBridge.bytes2HexString(encryptHmacSHA384(data, key));\n    }\n\n    /**\n     * Return the bytes of HmacSHA384 encryption.\n     *\n     * @param data The data.\n     * @param key  The key.\n     * @return the bytes of HmacSHA384 encryption\n     */\n    public static byte[] encryptHmacSHA384(final byte[] data, final byte[] key) {\n        return hmacTemplate(data, key, \"HmacSHA384\");\n    }\n\n    /**\n     * Return the hex string of HmacSHA512 encryption.\n     *\n     * @param data The data.\n     * @param key  The key.\n     * @return the hex string of HmacSHA512 encryption\n     */\n    public static String encryptHmacSHA512ToString(final String data, final String key) {\n        if (data == null || data.length() == 0 || key == null || key.length() == 0) return \"\";\n        return encryptHmacSHA512ToString(data.getBytes(), key.getBytes());\n    }\n\n    /**\n     * Return the hex string of HmacSHA512 encryption.\n     *\n     * @param data The data.\n     * @param key  The key.\n     * @return the hex string of HmacSHA512 encryption\n     */\n    public static String encryptHmacSHA512ToString(final byte[] data, final byte[] key) {\n        return UtilsBridge.bytes2HexString(encryptHmacSHA512(data, key));\n    }\n\n    /**\n     * Return the bytes of HmacSHA512 encryption.\n     *\n     * @param data The data.\n     * @param key  The key.\n     * @return the bytes of HmacSHA512 encryption\n     */\n    public static byte[] encryptHmacSHA512(final byte[] data, final byte[] key) {\n        return hmacTemplate(data, key, \"HmacSHA512\");\n    }\n\n    /**\n     * Return the bytes of hmac encryption.\n     *\n     * @param data      The data.\n     * @param key       The key.\n     * @param algorithm The name of hmac encryption.\n     * @return the bytes of hmac encryption\n     */\n    private static byte[] hmacTemplate(final byte[] data,\n                                       final byte[] key,\n                                       final String algorithm) {\n        if (data == null || data.length == 0 || key == null || key.length == 0) return null;\n        try {\n            SecretKeySpec secretKey = new SecretKeySpec(key, algorithm);\n            Mac mac = Mac.getInstance(algorithm);\n            mac.init(secretKey);\n            return mac.doFinal(data);\n        } catch (InvalidKeyException | NoSuchAlgorithmException e) {\n            e.printStackTrace();\n            return null;\n        }\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // DES encryption\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Return the Base64-encode bytes of DES encryption.\n     *\n     * @param data           The data.\n     * @param key            The key.\n     * @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS5Padding</i>.\n     * @param iv             The buffer with the IV. The contents of the\n     *                       buffer are copied to protect against subsequent modification.\n     * @return the Base64-encode bytes of DES encryption\n     */\n    public static byte[] encryptDES2Base64(final byte[] data,\n                                           final byte[] key,\n                                           final String transformation,\n                                           final byte[] iv) {\n        return UtilsBridge.base64Encode(encryptDES(data, key, transformation, iv));\n    }\n\n    /**\n     * Return the hex string of DES encryption.\n     *\n     * @param data           The data.\n     * @param key            The key.\n     * @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS5Padding</i>.\n     * @param iv             The buffer with the IV. The contents of the\n     *                       buffer are copied to protect against subsequent modification.\n     * @return the hex string of DES encryption\n     */\n    public static String encryptDES2HexString(final byte[] data,\n                                              final byte[] key,\n                                              final String transformation,\n                                              final byte[] iv) {\n        return UtilsBridge.bytes2HexString(encryptDES(data, key, transformation, iv));\n    }\n\n    /**\n     * Return the bytes of DES encryption.\n     *\n     * @param data           The data.\n     * @param key            The key.\n     * @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS5Padding</i>.\n     * @param iv             The buffer with the IV. The contents of the\n     *                       buffer are copied to protect against subsequent modification.\n     * @return the bytes of DES encryption\n     */\n    public static byte[] encryptDES(final byte[] data,\n                                    final byte[] key,\n                                    final String transformation,\n                                    final byte[] iv) {\n        return symmetricTemplate(data, key, \"DES\", transformation, iv, true);\n    }\n\n    /**\n     * Return the bytes of DES decryption for Base64-encode bytes.\n     *\n     * @param data           The data.\n     * @param key            The key.\n     * @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS5Padding</i>.\n     * @param iv             The buffer with the IV. The contents of the\n     *                       buffer are copied to protect against subsequent modification.\n     * @return the bytes of DES decryption for Base64-encode bytes\n     */\n    public static byte[] decryptBase64DES(final byte[] data,\n                                          final byte[] key,\n                                          final String transformation,\n                                          final byte[] iv) {\n        return decryptDES(UtilsBridge.base64Decode(data), key, transformation, iv);\n    }\n\n    /**\n     * Return the bytes of DES decryption for hex string.\n     *\n     * @param data           The data.\n     * @param key            The key.\n     * @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS5Padding</i>.\n     * @param iv             The buffer with the IV. The contents of the\n     *                       buffer are copied to protect against subsequent modification.\n     * @return the bytes of DES decryption for hex string\n     */\n    public static byte[] decryptHexStringDES(final String data,\n                                             final byte[] key,\n                                             final String transformation,\n                                             final byte[] iv) {\n        return decryptDES(UtilsBridge.hexString2Bytes(data), key, transformation, iv);\n    }\n\n    /**\n     * Return the bytes of DES decryption.\n     *\n     * @param data           The data.\n     * @param key            The key.\n     * @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS5Padding</i>.\n     * @param iv             The buffer with the IV. The contents of the\n     *                       buffer are copied to protect against subsequent modification.\n     * @return the bytes of DES decryption\n     */\n    public static byte[] decryptDES(final byte[] data,\n                                    final byte[] key,\n                                    final String transformation,\n                                    final byte[] iv) {\n        return symmetricTemplate(data, key, \"DES\", transformation, iv, false);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // 3DES encryption\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Return the Base64-encode bytes of 3DES encryption.\n     *\n     * @param data           The data.\n     * @param key            The key.\n     * @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS5Padding</i>.\n     * @param iv             The buffer with the IV. The contents of the\n     *                       buffer are copied to protect against subsequent modification.\n     * @return the Base64-encode bytes of 3DES encryption\n     */\n    public static byte[] encrypt3DES2Base64(final byte[] data,\n                                            final byte[] key,\n                                            final String transformation,\n                                            final byte[] iv) {\n        return UtilsBridge.base64Encode(encrypt3DES(data, key, transformation, iv));\n    }\n\n    /**\n     * Return the hex string of 3DES encryption.\n     *\n     * @param data           The data.\n     * @param key            The key.\n     * @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS5Padding</i>.\n     * @param iv             The buffer with the IV. The contents of the\n     *                       buffer are copied to protect against subsequent modification.\n     * @return the hex string of 3DES encryption\n     */\n    public static String encrypt3DES2HexString(final byte[] data,\n                                               final byte[] key,\n                                               final String transformation,\n                                               final byte[] iv) {\n        return UtilsBridge.bytes2HexString(encrypt3DES(data, key, transformation, iv));\n    }\n\n    /**\n     * Return the bytes of 3DES encryption.\n     *\n     * @param data           The data.\n     * @param key            The key.\n     * @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS5Padding</i>.\n     * @param iv             The buffer with the IV. The contents of the\n     *                       buffer are copied to protect against subsequent modification.\n     * @return the bytes of 3DES encryption\n     */\n    public static byte[] encrypt3DES(final byte[] data,\n                                     final byte[] key,\n                                     final String transformation,\n                                     final byte[] iv) {\n        return symmetricTemplate(data, key, \"DESede\", transformation, iv, true);\n    }\n\n    /**\n     * Return the bytes of 3DES decryption for Base64-encode bytes.\n     *\n     * @param data           The data.\n     * @param key            The key.\n     * @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS5Padding</i>.\n     * @param iv             The buffer with the IV. The contents of the\n     *                       buffer are copied to protect against subsequent modification.\n     * @return the bytes of 3DES decryption for Base64-encode bytes\n     */\n    public static byte[] decryptBase64_3DES(final byte[] data,\n                                            final byte[] key,\n                                            final String transformation,\n                                            final byte[] iv) {\n        return decrypt3DES(UtilsBridge.base64Decode(data), key, transformation, iv);\n    }\n\n    /**\n     * Return the bytes of 3DES decryption for hex string.\n     *\n     * @param data           The data.\n     * @param key            The key.\n     * @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS5Padding</i>.\n     * @param iv             The buffer with the IV. The contents of the\n     *                       buffer are copied to protect against subsequent modification.\n     * @return the bytes of 3DES decryption for hex string\n     */\n    public static byte[] decryptHexString3DES(final String data,\n                                              final byte[] key,\n                                              final String transformation,\n                                              final byte[] iv) {\n        return decrypt3DES(UtilsBridge.hexString2Bytes(data), key, transformation, iv);\n    }\n\n    /**\n     * Return the bytes of 3DES decryption.\n     *\n     * @param data           The data.\n     * @param key            The key.\n     * @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS5Padding</i>.\n     * @param iv             The buffer with the IV. The contents of the\n     *                       buffer are copied to protect against subsequent modification.\n     * @return the bytes of 3DES decryption\n     */\n    public static byte[] decrypt3DES(final byte[] data,\n                                     final byte[] key,\n                                     final String transformation,\n                                     final byte[] iv) {\n        return symmetricTemplate(data, key, \"DESede\", transformation, iv, false);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // AES encryption\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Return the Base64-encode bytes of AES encryption.\n     *\n     * @param data           The data.\n     * @param key            The key.\n     * @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS5Padding</i>.\n     * @param iv             The buffer with the IV. The contents of the\n     *                       buffer are copied to protect against subsequent modification.\n     * @return the Base64-encode bytes of AES encryption\n     */\n    public static byte[] encryptAES2Base64(final byte[] data,\n                                           final byte[] key,\n                                           final String transformation,\n                                           final byte[] iv) {\n        return UtilsBridge.base64Encode(encryptAES(data, key, transformation, iv));\n    }\n\n    /**\n     * Return the hex string of AES encryption.\n     *\n     * @param data           The data.\n     * @param key            The key.\n     * @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS5Padding</i>.\n     * @param iv             The buffer with the IV. The contents of the\n     *                       buffer are copied to protect against subsequent modification.\n     * @return the hex string of AES encryption\n     */\n    public static String encryptAES2HexString(final byte[] data,\n                                              final byte[] key,\n                                              final String transformation,\n                                              final byte[] iv) {\n        return UtilsBridge.bytes2HexString(encryptAES(data, key, transformation, iv));\n    }\n\n    /**\n     * Return the bytes of AES encryption.\n     *\n     * @param data           The data.\n     * @param key            The key.\n     * @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS5Padding</i>.\n     * @param iv             The buffer with the IV. The contents of the\n     *                       buffer are copied to protect against subsequent modification.\n     * @return the bytes of AES encryption\n     */\n    public static byte[] encryptAES(final byte[] data,\n                                    final byte[] key,\n                                    final String transformation,\n                                    final byte[] iv) {\n        return symmetricTemplate(data, key, \"AES\", transformation, iv, true);\n    }\n\n    /**\n     * Return the bytes of AES decryption for Base64-encode bytes.\n     *\n     * @param data           The data.\n     * @param key            The key.\n     * @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS5Padding</i>.\n     * @param iv             The buffer with the IV. The contents of the\n     *                       buffer are copied to protect against subsequent modification.\n     * @return the bytes of AES decryption for Base64-encode bytes\n     */\n    public static byte[] decryptBase64AES(final byte[] data,\n                                          final byte[] key,\n                                          final String transformation,\n                                          final byte[] iv) {\n        return decryptAES(UtilsBridge.base64Decode(data), key, transformation, iv);\n    }\n\n    /**\n     * Return the bytes of AES decryption for hex string.\n     *\n     * @param data           The data.\n     * @param key            The key.\n     * @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS5Padding</i>.\n     * @param iv             The buffer with the IV. The contents of the\n     *                       buffer are copied to protect against subsequent modification.\n     * @return the bytes of AES decryption for hex string\n     */\n    public static byte[] decryptHexStringAES(final String data,\n                                             final byte[] key,\n                                             final String transformation,\n                                             final byte[] iv) {\n        return decryptAES(UtilsBridge.hexString2Bytes(data), key, transformation, iv);\n    }\n\n    /**\n     * Return the bytes of AES decryption.\n     *\n     * @param data           The data.\n     * @param key            The key.\n     * @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS5Padding</i>.\n     * @param iv             The buffer with the IV. The contents of the\n     *                       buffer are copied to protect against subsequent modification.\n     * @return the bytes of AES decryption\n     */\n    public static byte[] decryptAES(final byte[] data,\n                                    final byte[] key,\n                                    final String transformation,\n                                    final byte[] iv) {\n        return symmetricTemplate(data, key, \"AES\", transformation, iv, false);\n    }\n\n    /**\n     * Return the bytes of symmetric encryption or decryption.\n     *\n     * @param data           The data.\n     * @param key            The key.\n     * @param algorithm      The name of algorithm.\n     * @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS5Padding</i>.\n     * @param isEncrypt      True to encrypt, false otherwise.\n     * @return the bytes of symmetric encryption or decryption\n     */\n    private static byte[] symmetricTemplate(final byte[] data,\n                                            final byte[] key,\n                                            final String algorithm,\n                                            final String transformation,\n                                            final byte[] iv,\n                                            final boolean isEncrypt) {\n        if (data == null || data.length == 0 || key == null || key.length == 0) return null;\n        try {\n            SecretKey secretKey;\n            if (\"DES\".equals(algorithm)) {\n                DESKeySpec desKey = new DESKeySpec(key);\n                SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(algorithm);\n                secretKey = keyFactory.generateSecret(desKey);\n            } else {\n                secretKey = new SecretKeySpec(key, algorithm);\n            }\n            Cipher cipher = Cipher.getInstance(transformation);\n            if (iv == null || iv.length == 0) {\n                cipher.init(isEncrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, secretKey);\n            } else {\n                AlgorithmParameterSpec params = new IvParameterSpec(iv);\n                cipher.init(isEncrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, secretKey, params);\n            }\n            return cipher.doFinal(data);\n        } catch (Throwable e) {\n            e.printStackTrace();\n            return null;\n        }\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // RSA encryption\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Return the Base64-encode bytes of RSA encryption.\n     *\n     * @param data           The data.\n     * @param publicKey      The public key.\n     * @param keySize        The size of key, e.g. 1024, 2048...\n     * @param transformation The name of the transformation, e.g., <i>RSA/CBC/PKCS1Padding</i>.\n     * @return the Base64-encode bytes of RSA encryption\n     */\n    public static byte[] encryptRSA2Base64(final byte[] data,\n                                           final byte[] publicKey,\n                                           final int keySize,\n                                           final String transformation) {\n        return UtilsBridge.base64Encode(encryptRSA(data, publicKey, keySize, transformation));\n    }\n\n    /**\n     * Return the hex string of RSA encryption.\n     *\n     * @param data           The data.\n     * @param publicKey      The public key.\n     * @param keySize        The size of key, e.g. 1024, 2048...\n     * @param transformation The name of the transformation, e.g., <i>RSA/CBC/PKCS1Padding</i>.\n     * @return the hex string of RSA encryption\n     */\n    public static String encryptRSA2HexString(final byte[] data,\n                                              final byte[] publicKey,\n                                              final int keySize,\n                                              final String transformation) {\n        return UtilsBridge.bytes2HexString(encryptRSA(data, publicKey, keySize, transformation));\n    }\n\n    /**\n     * Return the bytes of RSA encryption.\n     *\n     * @param data           The data.\n     * @param publicKey      The public key.\n     * @param keySize        The size of key, e.g. 1024, 2048...\n     * @param transformation The name of the transformation, e.g., <i>RSA/CBC/PKCS1Padding</i>.\n     * @return the bytes of RSA encryption\n     */\n    public static byte[] encryptRSA(final byte[] data,\n                                    final byte[] publicKey,\n                                    final int keySize,\n                                    final String transformation) {\n        return rsaTemplate(data, publicKey, keySize, transformation, true);\n    }\n\n    /**\n     * Return the bytes of RSA decryption for Base64-encode bytes.\n     *\n     * @param data           The data.\n     * @param privateKey     The private key.\n     * @param keySize        The size of key, e.g. 1024, 2048...\n     * @param transformation The name of the transformation, e.g., <i>RSA/CBC/PKCS1Padding</i>.\n     * @return the bytes of RSA decryption for Base64-encode bytes\n     */\n    public static byte[] decryptBase64RSA(final byte[] data,\n                                          final byte[] privateKey,\n                                          final int keySize,\n                                          final String transformation) {\n        return decryptRSA(UtilsBridge.base64Decode(data), privateKey, keySize, transformation);\n    }\n\n    /**\n     * Return the bytes of RSA decryption for hex string.\n     *\n     * @param data           The data.\n     * @param privateKey     The private key.\n     * @param keySize        The size of key, e.g. 1024, 2048...\n     * @param transformation The name of the transformation, e.g., <i>RSA/CBC/PKCS1Padding</i>.\n     * @return the bytes of RSA decryption for hex string\n     */\n    public static byte[] decryptHexStringRSA(final String data,\n                                             final byte[] privateKey,\n                                             final int keySize,\n                                             final String transformation) {\n        return decryptRSA(UtilsBridge.hexString2Bytes(data), privateKey, keySize, transformation);\n    }\n\n    /**\n     * Return the bytes of RSA decryption.\n     *\n     * @param data           The data.\n     * @param privateKey     The private key.\n     * @param keySize        The size of key, e.g. 1024, 2048...\n     * @param transformation The name of the transformation, e.g., <i>RSA/CBC/PKCS1Padding</i>.\n     * @return the bytes of RSA decryption\n     */\n    public static byte[] decryptRSA(final byte[] data,\n                                    final byte[] privateKey,\n                                    final int keySize,\n                                    final String transformation) {\n        return rsaTemplate(data, privateKey, keySize, transformation, false);\n    }\n\n    /**\n     * Return the bytes of RSA encryption or decryption.\n     *\n     * @param data           The data.\n     * @param key            The key.\n     * @param keySize        The size of key, e.g. 1024, 2048...\n     * @param transformation The name of the transformation, e.g., <i>DES/CBC/PKCS1Padding</i>.\n     * @param isEncrypt      True to encrypt, false otherwise.\n     * @return the bytes of RSA encryption or decryption\n     */\n    private static byte[] rsaTemplate(final byte[] data,\n                                      final byte[] key,\n                                      final int keySize,\n                                      final String transformation,\n                                      final boolean isEncrypt) {\n        if (data == null || data.length == 0 || key == null || key.length == 0) {\n            return null;\n        }\n        try {\n            Key rsaKey;\n            KeyFactory keyFactory;\n            if (Build.VERSION.SDK_INT < 28) {\n                keyFactory = KeyFactory.getInstance(\"RSA\", \"BC\");\n            } else {\n                keyFactory = KeyFactory.getInstance(\"RSA\");\n            }\n            if (isEncrypt) {\n                X509EncodedKeySpec keySpec = new X509EncodedKeySpec(key);\n                rsaKey = keyFactory.generatePublic(keySpec);\n            } else {\n                PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(key);\n                rsaKey = keyFactory.generatePrivate(keySpec);\n            }\n            if (rsaKey == null) return null;\n            Cipher cipher = Cipher.getInstance(transformation);\n            cipher.init(isEncrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE, rsaKey);\n            int len = data.length;\n            int maxLen = keySize / 8;\n            if (isEncrypt) {\n                String lowerTrans = transformation.toLowerCase();\n                if (lowerTrans.endsWith(\"pkcs1padding\")) {\n                    maxLen -= 11;\n                }\n            }\n            int count = len / maxLen;\n            if (count > 0) {\n                byte[] ret = new byte[0];\n                byte[] buff = new byte[maxLen];\n                int index = 0;\n                for (int i = 0; i < count; i++) {\n                    System.arraycopy(data, index, buff, 0, maxLen);\n                    ret = joins(ret, cipher.doFinal(buff));\n                    index += maxLen;\n                }\n                if (index != len) {\n                    int restLen = len - index;\n                    buff = new byte[restLen];\n                    System.arraycopy(data, index, buff, 0, restLen);\n                    ret = joins(ret, cipher.doFinal(buff));\n                }\n                return ret;\n            } else {\n                return cipher.doFinal(data);\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    /**\n     * Return the bytes of RC4 encryption/decryption.\n     *\n     * @param data The data.\n     * @param key  The key.\n     */\n    public static byte[] rc4(byte[] data, byte[] key) {\n        if (data == null || data.length == 0 || key == null) return null;\n        if (key.length < 1 || key.length > 256) {\n            throw new IllegalArgumentException(\"key must be between 1 and 256 bytes\");\n        }\n        final byte[] iS = new byte[256];\n        final byte[] iK = new byte[256];\n        int keyLen = key.length;\n        for (int i = 0; i < 256; i++) {\n            iS[i] = (byte) i;\n            iK[i] = key[i % keyLen];\n        }\n        int j = 0;\n        byte tmp;\n        for (int i = 0; i < 256; i++) {\n            j = (j + iS[i] + iK[i]) & 0xFF;\n            tmp = iS[j];\n            iS[j] = iS[i];\n            iS[i] = tmp;\n        }\n\n        final byte[] ret = new byte[data.length];\n        int i = 0, k, t;\n        for (int counter = 0; counter < data.length; counter++) {\n            i = (i + 1) & 0xFF;\n            j = (j + iS[i]) & 0xFF;\n            tmp = iS[j];\n            iS[j] = iS[i];\n            iS[i] = tmp;\n            t = (iS[i] + iS[j]) & 0xFF;\n            k = iS[t];\n            ret[counter] = (byte) (data[counter] ^ k);\n        }\n        return ret;\n    }\n\n    private static byte[] joins(final byte[] prefix, final byte[] suffix) {\n        byte[] ret = new byte[prefix.length + suffix.length];\n        System.arraycopy(prefix, 0, ret, 0, prefix.length);\n        System.arraycopy(suffix, 0, ret, prefix.length, suffix.length);\n        return ret;\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/FileIOUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.util.Log;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedOutputStream;\nimport java.io.BufferedReader;\nimport java.io.BufferedWriter;\nimport java.io.ByteArrayInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.FileOutputStream;\nimport java.io.FileWriter;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.OutputStream;\nimport java.io.RandomAccessFile;\nimport java.io.UnsupportedEncodingException;\nimport java.nio.ByteBuffer;\nimport java.nio.MappedByteBuffer;\nimport java.nio.channels.FileChannel;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/06/22\n *     desc  : utils about file io\n * </pre>\n */\npublic final class FileIOUtils {\n\n    private static int sBufferSize = 524288;\n\n    private FileIOUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // writeFileFromIS without progress\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Write file from input stream.\n     *\n     * @param filePath The path of file.\n     * @param is       The input stream.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromIS(final String filePath, final InputStream is) {\n        return writeFileFromIS(UtilsBridge.getFileByPath(filePath), is, false, null);\n    }\n\n    /**\n     * Write file from input stream.\n     *\n     * @param filePath The path of file.\n     * @param is       The input stream.\n     * @param append   True to append, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromIS(final String filePath,\n                                          final InputStream is,\n                                          final boolean append) {\n        return writeFileFromIS(UtilsBridge.getFileByPath(filePath), is, append, null);\n    }\n\n    /**\n     * Write file from input stream.\n     *\n     * @param file The file.\n     * @param is   The input stream.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromIS(final File file, final InputStream is) {\n        return writeFileFromIS(file, is, false, null);\n    }\n\n    /**\n     * Write file from input stream.\n     *\n     * @param file   The file.\n     * @param is     The input stream.\n     * @param append True to append, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromIS(final File file,\n                                          final InputStream is,\n                                          final boolean append) {\n        return writeFileFromIS(file, is, append, null);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // writeFileFromIS with progress\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Write file from input stream.\n     *\n     * @param filePath The path of file.\n     * @param is       The input stream.\n     * @param listener The progress update listener.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromIS(final String filePath,\n                                          final InputStream is,\n                                          final OnProgressUpdateListener listener) {\n        return writeFileFromIS(UtilsBridge.getFileByPath(filePath), is, false, listener);\n    }\n\n    /**\n     * Write file from input stream.\n     *\n     * @param filePath The path of file.\n     * @param is       The input stream.\n     * @param append   True to append, false otherwise.\n     * @param listener The progress update listener.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromIS(final String filePath,\n                                          final InputStream is,\n                                          final boolean append,\n                                          final OnProgressUpdateListener listener) {\n        return writeFileFromIS(UtilsBridge.getFileByPath(filePath), is, append, listener);\n    }\n\n    /**\n     * Write file from input stream.\n     *\n     * @param file     The file.\n     * @param is       The input stream.\n     * @param listener The progress update listener.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromIS(final File file,\n                                          final InputStream is,\n                                          final OnProgressUpdateListener listener) {\n        return writeFileFromIS(file, is, false, listener);\n    }\n\n    /**\n     * Write file from input stream.\n     *\n     * @param file     The file.\n     * @param is       The input stream.\n     * @param append   True to append, false otherwise.\n     * @param listener The progress update listener.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromIS(final File file,\n                                          final InputStream is,\n                                          final boolean append,\n                                          final OnProgressUpdateListener listener) {\n        if (is == null || !UtilsBridge.createOrExistsFile(file)) {\n            Log.e(\"FileIOUtils\", \"create file <\" + file + \"> failed.\");\n            return false;\n        }\n        OutputStream os = null;\n        try {\n            os = new BufferedOutputStream(new FileOutputStream(file, append), sBufferSize);\n            if (listener == null) {\n                byte[] data = new byte[sBufferSize];\n                for (int len; (len = is.read(data)) != -1; ) {\n                    os.write(data, 0, len);\n                }\n            } else {\n                double totalSize = is.available();\n                int curSize = 0;\n                listener.onProgressUpdate(0);\n                byte[] data = new byte[sBufferSize];\n                for (int len; (len = is.read(data)) != -1; ) {\n                    os.write(data, 0, len);\n                    curSize += len;\n                    listener.onProgressUpdate(curSize / totalSize);\n                }\n            }\n            return true;\n        } catch (IOException e) {\n            e.printStackTrace();\n            return false;\n        } finally {\n            try {\n                is.close();\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n            try {\n                if (os != null) {\n                    os.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n\n    ///////////////////////////////////////////////////////////////////////////\n    // writeFileFromBytesByStream without progress\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Write file from bytes by stream.\n     *\n     * @param filePath The path of file.\n     * @param bytes    The bytes.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromBytesByStream(final String filePath, final byte[] bytes) {\n        return writeFileFromBytesByStream(UtilsBridge.getFileByPath(filePath), bytes, false, null);\n    }\n\n    /**\n     * Write file from bytes by stream.\n     *\n     * @param filePath The path of file.\n     * @param bytes    The bytes.\n     * @param append   True to append, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromBytesByStream(final String filePath,\n                                                     final byte[] bytes,\n                                                     final boolean append) {\n        return writeFileFromBytesByStream(UtilsBridge.getFileByPath(filePath), bytes, append, null);\n    }\n\n    /**\n     * Write file from bytes by stream.\n     *\n     * @param file  The file.\n     * @param bytes The bytes.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromBytesByStream(final File file, final byte[] bytes) {\n        return writeFileFromBytesByStream(file, bytes, false, null);\n    }\n\n    /**\n     * Write file from bytes by stream.\n     *\n     * @param file   The file.\n     * @param bytes  The bytes.\n     * @param append True to append, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromBytesByStream(final File file,\n                                                     final byte[] bytes,\n                                                     final boolean append) {\n        return writeFileFromBytesByStream(file, bytes, append, null);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // writeFileFromBytesByStream with progress\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Write file from bytes by stream.\n     *\n     * @param filePath The path of file.\n     * @param bytes    The bytes.\n     * @param listener The progress update listener.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromBytesByStream(final String filePath,\n                                                     final byte[] bytes,\n                                                     final OnProgressUpdateListener listener) {\n        return writeFileFromBytesByStream(UtilsBridge.getFileByPath(filePath), bytes, false, listener);\n    }\n\n    /**\n     * Write file from bytes by stream.\n     *\n     * @param filePath The path of file.\n     * @param bytes    The bytes.\n     * @param append   True to append, false otherwise.\n     * @param listener The progress update listener.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromBytesByStream(final String filePath,\n                                                     final byte[] bytes,\n                                                     final boolean append,\n                                                     final OnProgressUpdateListener listener) {\n        return writeFileFromBytesByStream(UtilsBridge.getFileByPath(filePath), bytes, append, listener);\n    }\n\n    /**\n     * Write file from bytes by stream.\n     *\n     * @param file     The file.\n     * @param bytes    The bytes.\n     * @param listener The progress update listener.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromBytesByStream(final File file,\n                                                     final byte[] bytes,\n                                                     final OnProgressUpdateListener listener) {\n        return writeFileFromBytesByStream(file, bytes, false, listener);\n    }\n\n    /**\n     * Write file from bytes by stream.\n     *\n     * @param file     The file.\n     * @param bytes    The bytes.\n     * @param append   True to append, false otherwise.\n     * @param listener The progress update listener.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromBytesByStream(final File file,\n                                                     final byte[] bytes,\n                                                     final boolean append,\n                                                     final OnProgressUpdateListener listener) {\n        if (bytes == null) return false;\n        return writeFileFromIS(file, new ByteArrayInputStream(bytes), append, listener);\n    }\n\n    /**\n     * Write file from bytes by channel.\n     *\n     * @param filePath The path of file.\n     * @param bytes    The bytes.\n     * @param isForce  是否写入文件\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromBytesByChannel(final String filePath,\n                                                      final byte[] bytes,\n                                                      final boolean isForce) {\n        return writeFileFromBytesByChannel(UtilsBridge.getFileByPath(filePath), bytes, false, isForce);\n    }\n\n    /**\n     * Write file from bytes by channel.\n     *\n     * @param filePath The path of file.\n     * @param bytes    The bytes.\n     * @param append   True to append, false otherwise.\n     * @param isForce  True to force write file, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromBytesByChannel(final String filePath,\n                                                      final byte[] bytes,\n                                                      final boolean append,\n                                                      final boolean isForce) {\n        return writeFileFromBytesByChannel(UtilsBridge.getFileByPath(filePath), bytes, append, isForce);\n    }\n\n    /**\n     * Write file from bytes by channel.\n     *\n     * @param file    The file.\n     * @param bytes   The bytes.\n     * @param isForce True to force write file, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromBytesByChannel(final File file,\n                                                      final byte[] bytes,\n                                                      final boolean isForce) {\n        return writeFileFromBytesByChannel(file, bytes, false, isForce);\n    }\n\n    /**\n     * Write file from bytes by channel.\n     *\n     * @param file    The file.\n     * @param bytes   The bytes.\n     * @param append  True to append, false otherwise.\n     * @param isForce True to force write file, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromBytesByChannel(final File file,\n                                                      final byte[] bytes,\n                                                      final boolean append,\n                                                      final boolean isForce) {\n        if (bytes == null) {\n            Log.e(\"FileIOUtils\", \"bytes is null.\");\n            return false;\n        }\n        if (!UtilsBridge.createOrExistsFile(file)) {\n            Log.e(\"FileIOUtils\", \"create file <\" + file + \"> failed.\");\n            return false;\n        }\n        FileChannel fc = null;\n        try {\n            fc = new FileOutputStream(file, append).getChannel();\n            if (fc == null) {\n                Log.e(\"FileIOUtils\", \"fc is null.\");\n                return false;\n            }\n            fc.position(fc.size());\n            fc.write(ByteBuffer.wrap(bytes));\n            if (isForce) fc.force(true);\n            return true;\n        } catch (IOException e) {\n            e.printStackTrace();\n            return false;\n        } finally {\n            try {\n                if (fc != null) {\n                    fc.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    /**\n     * Write file from bytes by map.\n     *\n     * @param filePath The path of file.\n     * @param bytes    The bytes.\n     * @param isForce  True to force write file, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromBytesByMap(final String filePath,\n                                                  final byte[] bytes,\n                                                  final boolean isForce) {\n        return writeFileFromBytesByMap(filePath, bytes, false, isForce);\n    }\n\n    /**\n     * Write file from bytes by map.\n     *\n     * @param filePath The path of file.\n     * @param bytes    The bytes.\n     * @param append   True to append, false otherwise.\n     * @param isForce  True to force write file, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromBytesByMap(final String filePath,\n                                                  final byte[] bytes,\n                                                  final boolean append,\n                                                  final boolean isForce) {\n        return writeFileFromBytesByMap(UtilsBridge.getFileByPath(filePath), bytes, append, isForce);\n    }\n\n    /**\n     * Write file from bytes by map.\n     *\n     * @param file    The file.\n     * @param bytes   The bytes.\n     * @param isForce True to force write file, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromBytesByMap(final File file,\n                                                  final byte[] bytes,\n                                                  final boolean isForce) {\n        return writeFileFromBytesByMap(file, bytes, false, isForce);\n    }\n\n    /**\n     * Write file from bytes by map.\n     *\n     * @param file    The file.\n     * @param bytes   The bytes.\n     * @param append  True to append, false otherwise.\n     * @param isForce True to force write file, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromBytesByMap(final File file,\n                                                  final byte[] bytes,\n                                                  final boolean append,\n                                                  final boolean isForce) {\n        if (bytes == null || !UtilsBridge.createOrExistsFile(file)) {\n            Log.e(\"FileIOUtils\", \"create file <\" + file + \"> failed.\");\n            return false;\n        }\n        FileChannel fc = null;\n        try {\n            fc = new FileOutputStream(file, append).getChannel();\n            if (fc == null) {\n                Log.e(\"FileIOUtils\", \"fc is null.\");\n                return false;\n            }\n            MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_WRITE, fc.size(), bytes.length);\n            mbb.put(bytes);\n            if (isForce) mbb.force();\n            return true;\n        } catch (IOException e) {\n            e.printStackTrace();\n            return false;\n        } finally {\n            try {\n                if (fc != null) {\n                    fc.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    /**\n     * Write file from string.\n     *\n     * @param filePath The path of file.\n     * @param content  The string of content.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromString(final String filePath, final String content) {\n        return writeFileFromString(UtilsBridge.getFileByPath(filePath), content, false);\n    }\n\n    /**\n     * Write file from string.\n     *\n     * @param filePath The path of file.\n     * @param content  The string of content.\n     * @param append   True to append, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromString(final String filePath,\n                                              final String content,\n                                              final boolean append) {\n        return writeFileFromString(UtilsBridge.getFileByPath(filePath), content, append);\n    }\n\n    /**\n     * Write file from string.\n     *\n     * @param file    The file.\n     * @param content The string of content.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromString(final File file, final String content) {\n        return writeFileFromString(file, content, false);\n    }\n\n    /**\n     * Write file from string.\n     *\n     * @param file    The file.\n     * @param content The string of content.\n     * @param append  True to append, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean writeFileFromString(final File file,\n                                              final String content,\n                                              final boolean append) {\n        if (file == null || content == null) return false;\n        if (!UtilsBridge.createOrExistsFile(file)) {\n            Log.e(\"FileIOUtils\", \"create file <\" + file + \"> failed.\");\n            return false;\n        }\n        BufferedWriter bw = null;\n        try {\n            bw = new BufferedWriter(new FileWriter(file, append));\n            bw.write(content);\n            return true;\n        } catch (IOException e) {\n            e.printStackTrace();\n            return false;\n        } finally {\n            try {\n                if (bw != null) {\n                    bw.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // the divide line of write and read\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Return the lines in file.\n     *\n     * @param filePath The path of file.\n     * @return the lines in file\n     */\n    public static List<String> readFile2List(final String filePath) {\n        return readFile2List(UtilsBridge.getFileByPath(filePath), null);\n    }\n\n    /**\n     * Return the lines in file.\n     *\n     * @param filePath    The path of file.\n     * @param charsetName The name of charset.\n     * @return the lines in file\n     */\n    public static List<String> readFile2List(final String filePath, final String charsetName) {\n        return readFile2List(UtilsBridge.getFileByPath(filePath), charsetName);\n    }\n\n    /**\n     * Return the lines in file.\n     *\n     * @param file The file.\n     * @return the lines in file\n     */\n    public static List<String> readFile2List(final File file) {\n        return readFile2List(file, 0, 0x7FFFFFFF, null);\n    }\n\n    /**\n     * Return the lines in file.\n     *\n     * @param file        The file.\n     * @param charsetName The name of charset.\n     * @return the lines in file\n     */\n    public static List<String> readFile2List(final File file, final String charsetName) {\n        return readFile2List(file, 0, 0x7FFFFFFF, charsetName);\n    }\n\n    /**\n     * Return the lines in file.\n     *\n     * @param filePath The path of file.\n     * @param st       The line's index of start.\n     * @param end      The line's index of end.\n     * @return the lines in file\n     */\n    public static List<String> readFile2List(final String filePath, final int st, final int end) {\n        return readFile2List(UtilsBridge.getFileByPath(filePath), st, end, null);\n    }\n\n    /**\n     * Return the lines in file.\n     *\n     * @param filePath    The path of file.\n     * @param st          The line's index of start.\n     * @param end         The line's index of end.\n     * @param charsetName The name of charset.\n     * @return the lines in file\n     */\n    public static List<String> readFile2List(final String filePath,\n                                             final int st,\n                                             final int end,\n                                             final String charsetName) {\n        return readFile2List(UtilsBridge.getFileByPath(filePath), st, end, charsetName);\n    }\n\n    /**\n     * Return the lines in file.\n     *\n     * @param file The file.\n     * @param st   The line's index of start.\n     * @param end  The line's index of end.\n     * @return the lines in file\n     */\n    public static List<String> readFile2List(final File file, final int st, final int end) {\n        return readFile2List(file, st, end, null);\n    }\n\n    /**\n     * Return the lines in file.\n     *\n     * @param file        The file.\n     * @param st          The line's index of start.\n     * @param end         The line's index of end.\n     * @param charsetName The name of charset.\n     * @return the lines in file\n     */\n    public static List<String> readFile2List(final File file,\n                                             final int st,\n                                             final int end,\n                                             final String charsetName) {\n        if (!UtilsBridge.isFileExists(file)) return null;\n        if (st > end) return null;\n        BufferedReader reader = null;\n        try {\n            String line;\n            int curLine = 1;\n            List<String> list = new ArrayList<>();\n            if (UtilsBridge.isSpace(charsetName)) {\n                reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));\n            } else {\n                reader = new BufferedReader(\n                        new InputStreamReader(new FileInputStream(file), charsetName)\n                );\n            }\n            while ((line = reader.readLine()) != null) {\n                if (curLine > end) break;\n                if (st <= curLine && curLine <= end) list.add(line);\n                ++curLine;\n            }\n            return list;\n        } catch (IOException e) {\n            e.printStackTrace();\n            return null;\n        } finally {\n            try {\n                if (reader != null) {\n                    reader.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    /**\n     * Return the string in file.\n     *\n     * @param filePath The path of file.\n     * @return the string in file\n     */\n    public static String readFile2String(final String filePath) {\n        return readFile2String(UtilsBridge.getFileByPath(filePath), null);\n    }\n\n    /**\n     * Return the string in file.\n     *\n     * @param filePath    The path of file.\n     * @param charsetName The name of charset.\n     * @return the string in file\n     */\n    public static String readFile2String(final String filePath, final String charsetName) {\n        return readFile2String(UtilsBridge.getFileByPath(filePath), charsetName);\n    }\n\n    /**\n     * Return the string in file.\n     *\n     * @param file The file.\n     * @return the string in file\n     */\n    public static String readFile2String(final File file) {\n        return readFile2String(file, null);\n    }\n\n    /**\n     * Return the string in file.\n     *\n     * @param file        The file.\n     * @param charsetName The name of charset.\n     * @return the string in file\n     */\n    public static String readFile2String(final File file, final String charsetName) {\n        byte[] bytes = readFile2BytesByStream(file);\n        if (bytes == null) return null;\n        if (UtilsBridge.isSpace(charsetName)) {\n            return new String(bytes);\n        } else {\n            try {\n                return new String(bytes, charsetName);\n            } catch (UnsupportedEncodingException e) {\n                e.printStackTrace();\n                return \"\";\n            }\n        }\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // readFile2BytesByStream without progress\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Return the bytes in file by stream.\n     *\n     * @param filePath The path of file.\n     * @return the bytes in file\n     */\n    public static byte[] readFile2BytesByStream(final String filePath) {\n        return readFile2BytesByStream(UtilsBridge.getFileByPath(filePath), null);\n    }\n\n    /**\n     * Return the bytes in file by stream.\n     *\n     * @param file The file.\n     * @return the bytes in file\n     */\n    public static byte[] readFile2BytesByStream(final File file) {\n        return readFile2BytesByStream(file, null);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // readFile2BytesByStream with progress\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Return the bytes in file by stream.\n     *\n     * @param filePath The path of file.\n     * @param listener The progress update listener.\n     * @return the bytes in file\n     */\n    public static byte[] readFile2BytesByStream(final String filePath,\n                                                final OnProgressUpdateListener listener) {\n        return readFile2BytesByStream(UtilsBridge.getFileByPath(filePath), listener);\n    }\n\n    /**\n     * Return the bytes in file by stream.\n     *\n     * @param file     The file.\n     * @param listener The progress update listener.\n     * @return the bytes in file\n     */\n    public static byte[] readFile2BytesByStream(final File file,\n                                                final OnProgressUpdateListener listener) {\n        if (!UtilsBridge.isFileExists(file)) return null;\n        try {\n            ByteArrayOutputStream os = null;\n            InputStream is = new BufferedInputStream(new FileInputStream(file), sBufferSize);\n            try {\n                os = new ByteArrayOutputStream();\n                byte[] b = new byte[sBufferSize];\n                int len;\n                if (listener == null) {\n                    while ((len = is.read(b, 0, sBufferSize)) != -1) {\n                        os.write(b, 0, len);\n                    }\n                } else {\n                    double totalSize = is.available();\n                    int curSize = 0;\n                    listener.onProgressUpdate(0);\n                    while ((len = is.read(b, 0, sBufferSize)) != -1) {\n                        os.write(b, 0, len);\n                        curSize += len;\n                        listener.onProgressUpdate(curSize / totalSize);\n                    }\n                }\n                return os.toByteArray();\n            } catch (IOException e) {\n                e.printStackTrace();\n                return null;\n            } finally {\n                try {\n                    is.close();\n                } catch (IOException e) {\n                    e.printStackTrace();\n                }\n                try {\n                    if (os != null) {\n                        os.close();\n                    }\n                } catch (IOException e) {\n                    e.printStackTrace();\n                }\n            }\n        } catch (FileNotFoundException e) {\n            e.printStackTrace();\n            return null;\n        }\n    }\n\n    /**\n     * Return the bytes in file by channel.\n     *\n     * @param filePath The path of file.\n     * @return the bytes in file\n     */\n    public static byte[] readFile2BytesByChannel(final String filePath) {\n        return readFile2BytesByChannel(UtilsBridge.getFileByPath(filePath));\n    }\n\n    /**\n     * Return the bytes in file by channel.\n     *\n     * @param file The file.\n     * @return the bytes in file\n     */\n    public static byte[] readFile2BytesByChannel(final File file) {\n        if (!UtilsBridge.isFileExists(file)) return null;\n        FileChannel fc = null;\n        try {\n            fc = new RandomAccessFile(file, \"r\").getChannel();\n            if (fc == null) {\n                Log.e(\"FileIOUtils\", \"fc is null.\");\n                return new byte[0];\n            }\n            ByteBuffer byteBuffer = ByteBuffer.allocate((int) fc.size());\n            while (true) {\n                if (!((fc.read(byteBuffer)) > 0)) break;\n            }\n            return byteBuffer.array();\n        } catch (IOException e) {\n            e.printStackTrace();\n            return null;\n        } finally {\n            try {\n                if (fc != null) {\n                    fc.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    /**\n     * Return the bytes in file by map.\n     *\n     * @param filePath The path of file.\n     * @return the bytes in file\n     */\n    public static byte[] readFile2BytesByMap(final String filePath) {\n        return readFile2BytesByMap(UtilsBridge.getFileByPath(filePath));\n    }\n\n    /**\n     * Return the bytes in file by map.\n     *\n     * @param file The file.\n     * @return the bytes in file\n     */\n    public static byte[] readFile2BytesByMap(final File file) {\n        if (!UtilsBridge.isFileExists(file)) return null;\n        FileChannel fc = null;\n        try {\n            fc = new RandomAccessFile(file, \"r\").getChannel();\n            if (fc == null) {\n                Log.e(\"FileIOUtils\", \"fc is null.\");\n                return new byte[0];\n            }\n            int size = (int) fc.size();\n            MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, size).load();\n            byte[] result = new byte[size];\n            mbb.get(result, 0, size);\n            return result;\n        } catch (IOException e) {\n            e.printStackTrace();\n            return null;\n        } finally {\n            try {\n                if (fc != null) {\n                    fc.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    /**\n     * Set the buffer's size.\n     * <p>Default size equals 8192 bytes.</p>\n     *\n     * @param bufferSize The buffer's size.\n     */\n    public static void setBufferSize(final int bufferSize) {\n        sBufferSize = bufferSize;\n    }\n\n    public interface OnProgressUpdateListener {\n        void onProgressUpdate(double progress);\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/FileUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.content.ContentResolver;\nimport android.content.Intent;\nimport android.content.res.AssetFileDescriptor;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.StatFs;\nimport android.text.TextUtils;\n\nimport java.io.BufferedInputStream;\nimport java.io.File;\nimport java.io.FileFilter;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.net.URL;\nimport java.security.DigestInputStream;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\n\nimport javax.net.ssl.HttpsURLConnection;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/05/03\n *     desc  : utils about file\n * </pre>\n */\npublic final class FileUtils {\n\n    private static final String LINE_SEP = System.getProperty(\"line.separator\");\n\n    private FileUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Return the file by path.\n     *\n     * @param filePath The path of file.\n     * @return the file\n     */\n    public static File getFileByPath(final String filePath) {\n        return UtilsBridge.isSpace(filePath) ? null : new File(filePath);\n    }\n\n    /**\n     * Return whether the file exists.\n     *\n     * @param file The file.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isFileExists(final File file) {\n        if (file == null) return false;\n        if (file.exists()) {\n            return true;\n        }\n        return isFileExists(file.getAbsolutePath());\n    }\n\n    /**\n     * Return whether the file exists.\n     *\n     * @param filePath The path of file.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isFileExists(final String filePath) {\n        File file = getFileByPath(filePath);\n        if (file == null) return false;\n        if (file.exists()) {\n            return true;\n        }\n        return isFileExistsApi29(filePath);\n    }\n\n    private static boolean isFileExistsApi29(String filePath) {\n        if (Build.VERSION.SDK_INT >= 29) {\n            try {\n                Uri uri = Uri.parse(filePath);\n                ContentResolver cr = Utils.getApp().getContentResolver();\n                AssetFileDescriptor afd = cr.openAssetFileDescriptor(uri, \"r\");\n                if (afd == null) return false;\n                try {\n                    afd.close();\n                } catch (IOException ignore) {\n                }\n            } catch (FileNotFoundException e) {\n                return false;\n            }\n            return true;\n        }\n        return false;\n    }\n\n    /**\n     * Rename the file.\n     *\n     * @param filePath The path of file.\n     * @param newName  The new name of file.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean rename(final String filePath, final String newName) {\n        return rename(getFileByPath(filePath), newName);\n    }\n\n    /**\n     * Rename the file.\n     *\n     * @param file    The file.\n     * @param newName The new name of file.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean rename(final File file, final String newName) {\n        // file is null then return false\n        if (file == null) return false;\n        // file doesn't exist then return false\n        if (!file.exists()) return false;\n        // the new name is space then return false\n        if (UtilsBridge.isSpace(newName)) return false;\n        // the new name equals old name then return true\n        if (newName.equals(file.getName())) return true;\n        File newFile = new File(file.getParent() + File.separator + newName);\n        // the new name of file exists then return false\n        return !newFile.exists()\n                && file.renameTo(newFile);\n    }\n\n    /**\n     * Return whether it is a directory.\n     *\n     * @param dirPath The path of directory.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isDir(final String dirPath) {\n        return isDir(getFileByPath(dirPath));\n    }\n\n    /**\n     * Return whether it is a directory.\n     *\n     * @param file The file.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isDir(final File file) {\n        return file != null && file.exists() && file.isDirectory();\n    }\n\n    /**\n     * Return whether it is a file.\n     *\n     * @param filePath The path of file.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isFile(final String filePath) {\n        return isFile(getFileByPath(filePath));\n    }\n\n    /**\n     * Return whether it is a file.\n     *\n     * @param file The file.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isFile(final File file) {\n        return file != null && file.exists() && file.isFile();\n    }\n\n    /**\n     * Create a directory if it doesn't exist, otherwise do nothing.\n     *\n     * @param dirPath The path of directory.\n     * @return {@code true}: exists or creates successfully<br>{@code false}: otherwise\n     */\n    public static boolean createOrExistsDir(final String dirPath) {\n        return createOrExistsDir(getFileByPath(dirPath));\n    }\n\n    /**\n     * Create a directory if it doesn't exist, otherwise do nothing.\n     *\n     * @param file The file.\n     * @return {@code true}: exists or creates successfully<br>{@code false}: otherwise\n     */\n    public static boolean createOrExistsDir(final File file) {\n        return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());\n    }\n\n    /**\n     * Create a file if it doesn't exist, otherwise do nothing.\n     *\n     * @param filePath The path of file.\n     * @return {@code true}: exists or creates successfully<br>{@code false}: otherwise\n     */\n    public static boolean createOrExistsFile(final String filePath) {\n        return createOrExistsFile(getFileByPath(filePath));\n    }\n\n    /**\n     * Create a file if it doesn't exist, otherwise do nothing.\n     *\n     * @param file The file.\n     * @return {@code true}: exists or creates successfully<br>{@code false}: otherwise\n     */\n    public static boolean createOrExistsFile(final File file) {\n        if (file == null) return false;\n        if (file.exists()) return file.isFile();\n        if (!createOrExistsDir(file.getParentFile())) return false;\n        try {\n            return file.createNewFile();\n        } catch (IOException e) {\n            e.printStackTrace();\n            return false;\n        }\n    }\n\n    /**\n     * Create a file if it doesn't exist, otherwise delete old file before creating.\n     *\n     * @param filePath The path of file.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean createFileByDeleteOldFile(final String filePath) {\n        return createFileByDeleteOldFile(getFileByPath(filePath));\n    }\n\n    /**\n     * Create a file if it doesn't exist, otherwise delete old file before creating.\n     *\n     * @param file The file.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean createFileByDeleteOldFile(final File file) {\n        if (file == null) return false;\n        // file exists and unsuccessfully delete then return false\n        if (file.exists() && !file.delete()) return false;\n        if (!createOrExistsDir(file.getParentFile())) return false;\n        try {\n            return file.createNewFile();\n        } catch (IOException e) {\n            e.printStackTrace();\n            return false;\n        }\n    }\n\n    /**\n     * Copy the directory or file.\n     *\n     * @param srcPath  The path of source.\n     * @param destPath The path of destination.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean copy(final String srcPath,\n                               final String destPath) {\n        return copy(getFileByPath(srcPath), getFileByPath(destPath), null);\n    }\n\n    /**\n     * Copy the directory or file.\n     *\n     * @param srcPath  The path of source.\n     * @param destPath The path of destination.\n     * @param listener The replace listener.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean copy(final String srcPath,\n                               final String destPath,\n                               final OnReplaceListener listener) {\n        return copy(getFileByPath(srcPath), getFileByPath(destPath), listener);\n    }\n\n    /**\n     * Copy the directory or file.\n     *\n     * @param src  The source.\n     * @param dest The destination.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean copy(final File src,\n                               final File dest) {\n        return copy(src, dest, null);\n    }\n\n    /**\n     * Copy the directory or file.\n     *\n     * @param src      The source.\n     * @param dest     The destination.\n     * @param listener The replace listener.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean copy(final File src,\n                               final File dest,\n                               final OnReplaceListener listener) {\n        if (src == null) return false;\n        if (src.isDirectory()) {\n            return copyDir(src, dest, listener);\n        }\n        return copyFile(src, dest, listener);\n    }\n\n    /**\n     * Copy the directory.\n     *\n     * @param srcDir   The source directory.\n     * @param destDir  The destination directory.\n     * @param listener The replace listener.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    private static boolean copyDir(final File srcDir,\n                                   final File destDir,\n                                   final OnReplaceListener listener) {\n        return copyOrMoveDir(srcDir, destDir, listener, false);\n    }\n\n    /**\n     * Copy the file.\n     *\n     * @param srcFile  The source file.\n     * @param destFile The destination file.\n     * @param listener The replace listener.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    private static boolean copyFile(final File srcFile,\n                                    final File destFile,\n                                    final OnReplaceListener listener) {\n        return copyOrMoveFile(srcFile, destFile, listener, false);\n    }\n\n    /**\n     * Move the directory or file.\n     *\n     * @param srcPath  The path of source.\n     * @param destPath The path of destination.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean move(final String srcPath,\n                               final String destPath) {\n        return move(getFileByPath(srcPath), getFileByPath(destPath), null);\n    }\n\n    /**\n     * Move the directory or file.\n     *\n     * @param srcPath  The path of source.\n     * @param destPath The path of destination.\n     * @param listener The replace listener.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean move(final String srcPath,\n                               final String destPath,\n                               final OnReplaceListener listener) {\n        return move(getFileByPath(srcPath), getFileByPath(destPath), listener);\n    }\n\n    /**\n     * Move the directory or file.\n     *\n     * @param src  The source.\n     * @param dest The destination.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean move(final File src,\n                               final File dest) {\n        return move(src, dest, null);\n    }\n\n    /**\n     * Move the directory or file.\n     *\n     * @param src      The source.\n     * @param dest     The destination.\n     * @param listener The replace listener.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean move(final File src,\n                               final File dest,\n                               final OnReplaceListener listener) {\n        if (src == null) return false;\n        if (src.isDirectory()) {\n            return moveDir(src, dest, listener);\n        }\n        return moveFile(src, dest, listener);\n    }\n\n    /**\n     * Move the directory.\n     *\n     * @param srcDir   The source directory.\n     * @param destDir  The destination directory.\n     * @param listener The replace listener.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean moveDir(final File srcDir,\n                                  final File destDir,\n                                  final OnReplaceListener listener) {\n        return copyOrMoveDir(srcDir, destDir, listener, true);\n    }\n\n    /**\n     * Move the file.\n     *\n     * @param srcFile  The source file.\n     * @param destFile The destination file.\n     * @param listener The replace listener.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean moveFile(final File srcFile,\n                                   final File destFile,\n                                   final OnReplaceListener listener) {\n        return copyOrMoveFile(srcFile, destFile, listener, true);\n    }\n\n    private static boolean copyOrMoveDir(final File srcDir,\n                                         final File destDir,\n                                         final OnReplaceListener listener,\n                                         final boolean isMove) {\n        if (srcDir == null || destDir == null) return false;\n        // destDir's path locate in srcDir's path then return false\n        String srcPath = srcDir.getPath() + File.separator;\n        String destPath = destDir.getPath() + File.separator;\n        if (destPath.contains(srcPath)) return false;\n        if (!srcDir.exists() || !srcDir.isDirectory()) return false;\n        if (!createOrExistsDir(destDir)) return false;\n        File[] files = srcDir.listFiles();\n        if (files != null && files.length > 0) {\n            for (File file : files) {\n                File oneDestFile = new File(destPath + file.getName());\n                if (file.isFile()) {\n                    if (!copyOrMoveFile(file, oneDestFile, listener, isMove)) return false;\n                } else if (file.isDirectory()) {\n                    if (!copyOrMoveDir(file, oneDestFile, listener, isMove)) return false;\n                }\n            }\n        }\n        return !isMove || deleteDir(srcDir);\n    }\n\n    private static boolean copyOrMoveFile(final File srcFile,\n                                          final File destFile,\n                                          final OnReplaceListener listener,\n                                          final boolean isMove) {\n        if (srcFile == null || destFile == null) return false;\n        // srcFile equals destFile then return false\n        if (srcFile.equals(destFile)) return false;\n        // srcFile doesn't exist or isn't a file then return false\n        if (!srcFile.exists() || !srcFile.isFile()) return false;\n        if (destFile.exists()) {\n            if (listener == null || listener.onReplace(srcFile, destFile)) {// require delete the old file\n                if (!destFile.delete()) {// unsuccessfully delete then return false\n                    return false;\n                }\n            } else {\n                return true;\n            }\n        }\n        if (!createOrExistsDir(destFile.getParentFile())) return false;\n        try {\n            return UtilsBridge.writeFileFromIS(destFile.getAbsolutePath(), new FileInputStream(srcFile))\n                    && !(isMove && !deleteFile(srcFile));\n        } catch (FileNotFoundException e) {\n            e.printStackTrace();\n            return false;\n        }\n    }\n\n    /**\n     * Delete the directory.\n     *\n     * @param filePath The path of file.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean delete(final String filePath) {\n        return delete(getFileByPath(filePath));\n    }\n\n    /**\n     * Delete the directory.\n     *\n     * @param file The file.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean delete(final File file) {\n        if (file == null) return false;\n        if (file.isDirectory()) {\n            return deleteDir(file);\n        }\n        return deleteFile(file);\n    }\n\n    /**\n     * Delete the directory.\n     *\n     * @param dir The directory.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    private static boolean deleteDir(final File dir) {\n        if (dir == null) return false;\n        // dir doesn't exist then return true\n        if (!dir.exists()) return true;\n        // dir isn't a directory then return false\n        if (!dir.isDirectory()) return false;\n        File[] files = dir.listFiles();\n        if (files != null && files.length > 0) {\n            for (File file : files) {\n                if (file.isFile()) {\n                    if (!file.delete()) return false;\n                } else if (file.isDirectory()) {\n                    if (!deleteDir(file)) return false;\n                }\n            }\n        }\n        return dir.delete();\n    }\n\n    /**\n     * Delete the file.\n     *\n     * @param file The file.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    private static boolean deleteFile(final File file) {\n        return file != null && (!file.exists() || file.isFile() && file.delete());\n    }\n\n    /**\n     * Delete the all in directory.\n     *\n     * @param dirPath The path of directory.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean deleteAllInDir(final String dirPath) {\n        return deleteAllInDir(getFileByPath(dirPath));\n    }\n\n    /**\n     * Delete the all in directory.\n     *\n     * @param dir The directory.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean deleteAllInDir(final File dir) {\n        return deleteFilesInDirWithFilter(dir, new FileFilter() {\n            @Override\n            public boolean accept(File pathname) {\n                return true;\n            }\n        });\n    }\n\n    /**\n     * Delete all files in directory.\n     *\n     * @param dirPath The path of directory.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean deleteFilesInDir(final String dirPath) {\n        return deleteFilesInDir(getFileByPath(dirPath));\n    }\n\n    /**\n     * Delete all files in directory.\n     *\n     * @param dir The directory.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean deleteFilesInDir(final File dir) {\n        return deleteFilesInDirWithFilter(dir, new FileFilter() {\n            @Override\n            public boolean accept(File pathname) {\n                return pathname.isFile();\n            }\n        });\n    }\n\n    /**\n     * Delete all files that satisfy the filter in directory.\n     *\n     * @param dirPath The path of directory.\n     * @param filter  The filter.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean deleteFilesInDirWithFilter(final String dirPath,\n                                                     final FileFilter filter) {\n        return deleteFilesInDirWithFilter(getFileByPath(dirPath), filter);\n    }\n\n    /**\n     * Delete all files that satisfy the filter in directory.\n     *\n     * @param dir    The directory.\n     * @param filter The filter.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean deleteFilesInDirWithFilter(final File dir, final FileFilter filter) {\n        if (dir == null || filter == null) return false;\n        // dir doesn't exist then return true\n        if (!dir.exists()) return true;\n        // dir isn't a directory then return false\n        if (!dir.isDirectory()) return false;\n        File[] files = dir.listFiles();\n        if (files != null && files.length != 0) {\n            for (File file : files) {\n                if (filter.accept(file)) {\n                    if (file.isFile()) {\n                        if (!file.delete()) return false;\n                    } else if (file.isDirectory()) {\n                        if (!deleteDir(file)) return false;\n                    }\n                }\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Return the files in directory.\n     * <p>Doesn't traverse subdirectories</p>\n     *\n     * @param dirPath The path of directory.\n     * @return the files in directory\n     */\n    public static List<File> listFilesInDir(final String dirPath) {\n        return listFilesInDir(dirPath, null);\n    }\n\n    /**\n     * Return the files in directory.\n     * <p>Doesn't traverse subdirectories</p>\n     *\n     * @param dir The directory.\n     * @return the files in directory\n     */\n    public static List<File> listFilesInDir(final File dir) {\n        return listFilesInDir(dir, null);\n    }\n\n    /**\n     * Return the files in directory.\n     * <p>Doesn't traverse subdirectories</p>\n     *\n     * @param dirPath    The path of directory.\n     * @param comparator The comparator to determine the order of the list.\n     * @return the files in directory\n     */\n    public static List<File> listFilesInDir(final String dirPath, Comparator<File> comparator) {\n        return listFilesInDir(getFileByPath(dirPath), false, comparator);\n    }\n\n    /**\n     * Return the files in directory.\n     * <p>Doesn't traverse subdirectories</p>\n     *\n     * @param dir        The directory.\n     * @param comparator The comparator to determine the order of the list.\n     * @return the files in directory\n     */\n    public static List<File> listFilesInDir(final File dir, Comparator<File> comparator) {\n        return listFilesInDir(dir, false, comparator);\n    }\n\n    /**\n     * Return the files in directory.\n     *\n     * @param dirPath     The path of directory.\n     * @param isRecursive True to traverse subdirectories, false otherwise.\n     * @return the files in directory\n     */\n    public static List<File> listFilesInDir(final String dirPath, final boolean isRecursive) {\n        return listFilesInDir(getFileByPath(dirPath), isRecursive);\n    }\n\n    /**\n     * Return the files in directory.\n     *\n     * @param dir         The directory.\n     * @param isRecursive True to traverse subdirectories, false otherwise.\n     * @return the files in directory\n     */\n    public static List<File> listFilesInDir(final File dir, final boolean isRecursive) {\n        return listFilesInDir(dir, isRecursive, null);\n    }\n\n    /**\n     * Return the files in directory.\n     *\n     * @param dirPath     The path of directory.\n     * @param isRecursive True to traverse subdirectories, false otherwise.\n     * @param comparator  The comparator to determine the order of the list.\n     * @return the files in directory\n     */\n    public static List<File> listFilesInDir(final String dirPath,\n                                            final boolean isRecursive,\n                                            final Comparator<File> comparator) {\n        return listFilesInDir(getFileByPath(dirPath), isRecursive, comparator);\n    }\n\n    /**\n     * Return the files in directory.\n     *\n     * @param dir         The directory.\n     * @param isRecursive True to traverse subdirectories, false otherwise.\n     * @param comparator  The comparator to determine the order of the list.\n     * @return the files in directory\n     */\n    public static List<File> listFilesInDir(final File dir,\n                                            final boolean isRecursive,\n                                            final Comparator<File> comparator) {\n        return listFilesInDirWithFilter(dir, new FileFilter() {\n            @Override\n            public boolean accept(File pathname) {\n                return true;\n            }\n        }, isRecursive, comparator);\n    }\n\n    /**\n     * Return the files that satisfy the filter in directory.\n     * <p>Doesn't traverse subdirectories</p>\n     *\n     * @param dirPath The path of directory.\n     * @param filter  The filter.\n     * @return the files that satisfy the filter in directory\n     */\n    public static List<File> listFilesInDirWithFilter(final String dirPath,\n                                                      final FileFilter filter) {\n        return listFilesInDirWithFilter(getFileByPath(dirPath), filter);\n    }\n\n    /**\n     * Return the files that satisfy the filter in directory.\n     * <p>Doesn't traverse subdirectories</p>\n     *\n     * @param dir    The directory.\n     * @param filter The filter.\n     * @return the files that satisfy the filter in directory\n     */\n    public static List<File> listFilesInDirWithFilter(final File dir,\n                                                      final FileFilter filter) {\n        return listFilesInDirWithFilter(dir, filter, false, null);\n    }\n\n    /**\n     * Return the files that satisfy the filter in directory.\n     * <p>Doesn't traverse subdirectories</p>\n     *\n     * @param dirPath    The path of directory.\n     * @param filter     The filter.\n     * @param comparator The comparator to determine the order of the list.\n     * @return the files that satisfy the filter in directory\n     */\n    public static List<File> listFilesInDirWithFilter(final String dirPath,\n                                                      final FileFilter filter,\n                                                      final Comparator<File> comparator) {\n        return listFilesInDirWithFilter(getFileByPath(dirPath), filter, comparator);\n    }\n\n    /**\n     * Return the files that satisfy the filter in directory.\n     * <p>Doesn't traverse subdirectories</p>\n     *\n     * @param dir        The directory.\n     * @param filter     The filter.\n     * @param comparator The comparator to determine the order of the list.\n     * @return the files that satisfy the filter in directory\n     */\n    public static List<File> listFilesInDirWithFilter(final File dir,\n                                                      final FileFilter filter,\n                                                      final Comparator<File> comparator) {\n        return listFilesInDirWithFilter(dir, filter, false, comparator);\n    }\n\n    /**\n     * Return the files that satisfy the filter in directory.\n     *\n     * @param dirPath     The path of directory.\n     * @param filter      The filter.\n     * @param isRecursive True to traverse subdirectories, false otherwise.\n     * @return the files that satisfy the filter in directory\n     */\n    public static List<File> listFilesInDirWithFilter(final String dirPath,\n                                                      final FileFilter filter,\n                                                      final boolean isRecursive) {\n        return listFilesInDirWithFilter(getFileByPath(dirPath), filter, isRecursive);\n    }\n\n    /**\n     * Return the files that satisfy the filter in directory.\n     *\n     * @param dir         The directory.\n     * @param filter      The filter.\n     * @param isRecursive True to traverse subdirectories, false otherwise.\n     * @return the files that satisfy the filter in directory\n     */\n    public static List<File> listFilesInDirWithFilter(final File dir,\n                                                      final FileFilter filter,\n                                                      final boolean isRecursive) {\n        return listFilesInDirWithFilter(dir, filter, isRecursive, null);\n    }\n\n\n    /**\n     * Return the files that satisfy the filter in directory.\n     *\n     * @param dirPath     The path of directory.\n     * @param filter      The filter.\n     * @param isRecursive True to traverse subdirectories, false otherwise.\n     * @param comparator  The comparator to determine the order of the list.\n     * @return the files that satisfy the filter in directory\n     */\n    public static List<File> listFilesInDirWithFilter(final String dirPath,\n                                                      final FileFilter filter,\n                                                      final boolean isRecursive,\n                                                      final Comparator<File> comparator) {\n        return listFilesInDirWithFilter(getFileByPath(dirPath), filter, isRecursive, comparator);\n    }\n\n    /**\n     * Return the files that satisfy the filter in directory.\n     *\n     * @param dir         The directory.\n     * @param filter      The filter.\n     * @param isRecursive True to traverse subdirectories, false otherwise.\n     * @param comparator  The comparator to determine the order of the list.\n     * @return the files that satisfy the filter in directory\n     */\n    public static List<File> listFilesInDirWithFilter(final File dir,\n                                                      final FileFilter filter,\n                                                      final boolean isRecursive,\n                                                      final Comparator<File> comparator) {\n        List<File> files = listFilesInDirWithFilterInner(dir, filter, isRecursive);\n        if (comparator != null) {\n            Collections.sort(files, comparator);\n        }\n        return files;\n    }\n\n    private static List<File> listFilesInDirWithFilterInner(final File dir,\n                                                            final FileFilter filter,\n                                                            final boolean isRecursive) {\n        List<File> list = new ArrayList<>();\n        if (!isDir(dir)) return list;\n        File[] files = dir.listFiles();\n        if (files != null && files.length > 0) {\n            for (File file : files) {\n                if (filter.accept(file)) {\n                    list.add(file);\n                }\n                if (isRecursive && file.isDirectory()) {\n                    list.addAll(listFilesInDirWithFilterInner(file, filter, true));\n                }\n            }\n        }\n        return list;\n    }\n\n    /**\n     * Return the time that the file was last modified.\n     *\n     * @param filePath The path of file.\n     * @return the time that the file was last modified\n     */\n\n    public static long getFileLastModified(final String filePath) {\n        return getFileLastModified(getFileByPath(filePath));\n    }\n\n    /**\n     * Return the time that the file was last modified.\n     *\n     * @param file The file.\n     * @return the time that the file was last modified\n     */\n    public static long getFileLastModified(final File file) {\n        if (file == null) return -1;\n        return file.lastModified();\n    }\n\n    /**\n     * Return the charset of file simply.\n     *\n     * @param filePath The path of file.\n     * @return the charset of file simply\n     */\n    public static String getFileCharsetSimple(final String filePath) {\n        return getFileCharsetSimple(getFileByPath(filePath));\n    }\n\n    /**\n     * Return the charset of file simply.\n     *\n     * @param file The file.\n     * @return the charset of file simply\n     */\n    public static String getFileCharsetSimple(final File file) {\n        if (file == null) return \"\";\n        if (isUtf8(file)) return \"UTF-8\";\n        int p = 0;\n        InputStream is = null;\n        try {\n            is = new BufferedInputStream(new FileInputStream(file));\n            p = (is.read() << 8) + is.read();\n        } catch (IOException e) {\n            e.printStackTrace();\n        } finally {\n            try {\n                if (is != null) {\n                    is.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n        switch (p) {\n            case 0xfffe:\n                return \"Unicode\";\n            case 0xfeff:\n                return \"UTF-16BE\";\n            default:\n                return \"GBK\";\n        }\n    }\n\n    /**\n     * Return whether the charset of file is utf8.\n     *\n     * @param filePath The path of file.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isUtf8(final String filePath) {\n        return isUtf8(getFileByPath(filePath));\n    }\n\n    /**\n     * Return whether the charset of file is utf8.\n     *\n     * @param file The file.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isUtf8(final File file) {\n        if (file == null) return false;\n        InputStream is = null;\n        try {\n            byte[] bytes = new byte[24];\n            is = new BufferedInputStream(new FileInputStream(file));\n            int read = is.read(bytes);\n            if (read != -1) {\n                byte[] readArr = new byte[read];\n                System.arraycopy(bytes, 0, readArr, 0, read);\n                return isUtf8(readArr) == 100;\n            } else {\n                return false;\n            }\n        } catch (IOException e) {\n            e.printStackTrace();\n        } finally {\n            try {\n                if (is != null) {\n                    is.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n        return false;\n    }\n\n    /**\n     * UTF-8编码方式\n     * ----------------------------------------------\n     * 0xxxxxxx\n     * 110xxxxx 10xxxxxx\n     * 1110xxxx 10xxxxxx 10xxxxxx\n     * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx\n     */\n    private static int isUtf8(byte[] raw) {\n        int i, len;\n        int utf8 = 0, ascii = 0;\n        if (raw.length > 3) {\n            if ((raw[0] == (byte) 0xEF) && (raw[1] == (byte) 0xBB) && (raw[2] == (byte) 0xBF)) {\n                return 100;\n            }\n        }\n        len = raw.length;\n        int child = 0;\n        for (i = 0; i < len; ) {\n            // UTF-8 byte shouldn't be FF and FE\n            if ((raw[i] & (byte) 0xFF) == (byte) 0xFF || (raw[i] & (byte) 0xFE) == (byte) 0xFE) {\n                return 0;\n            }\n            if (child == 0) {\n                // ASCII format is 0x0*******\n                if ((raw[i] & (byte) 0x7F) == raw[i] && raw[i] != 0) {\n                    ascii++;\n                } else if ((raw[i] & (byte) 0xC0) == (byte) 0xC0) {\n                    // 0x11****** maybe is UTF-8\n                    for (int bit = 0; bit < 8; bit++) {\n                        if ((((byte) (0x80 >> bit)) & raw[i]) == ((byte) (0x80 >> bit))) {\n                            child = bit;\n                        } else {\n                            break;\n                        }\n                    }\n                    utf8++;\n                }\n                i++;\n            } else {\n                child = (raw.length - i > child) ? child : (raw.length - i);\n                boolean currentNotUtf8 = false;\n                for (int children = 0; children < child; children++) {\n                    // format must is 0x10******\n                    if ((raw[i + children] & ((byte) 0x80)) != ((byte) 0x80)) {\n                        if ((raw[i + children] & (byte) 0x7F) == raw[i + children] && raw[i] != 0) {\n                            // ASCII format is 0x0*******\n                            ascii++;\n                        }\n                        currentNotUtf8 = true;\n                    }\n                }\n                if (currentNotUtf8) {\n                    utf8--;\n                    i++;\n                } else {\n                    utf8 += child;\n                    i += child;\n                }\n                child = 0;\n            }\n        }\n        // UTF-8 contains ASCII\n        if (ascii == len) {\n            return 100;\n        }\n        return (int) (100 * ((float) (utf8 + ascii) / (float) len));\n    }\n\n    /**\n     * Return the number of lines of file.\n     *\n     * @param filePath The path of file.\n     * @return the number of lines of file\n     */\n    public static int getFileLines(final String filePath) {\n        return getFileLines(getFileByPath(filePath));\n    }\n\n    /**\n     * Return the number of lines of file.\n     *\n     * @param file The file.\n     * @return the number of lines of file\n     */\n    public static int getFileLines(final File file) {\n        int count = 1;\n        InputStream is = null;\n        try {\n            is = new BufferedInputStream(new FileInputStream(file));\n            byte[] buffer = new byte[1024];\n            int readChars;\n            if (LINE_SEP.endsWith(\"\\n\")) {\n                while ((readChars = is.read(buffer, 0, 1024)) != -1) {\n                    for (int i = 0; i < readChars; ++i) {\n                        if (buffer[i] == '\\n') ++count;\n                    }\n                }\n            } else {\n                while ((readChars = is.read(buffer, 0, 1024)) != -1) {\n                    for (int i = 0; i < readChars; ++i) {\n                        if (buffer[i] == '\\r') ++count;\n                    }\n                }\n            }\n        } catch (IOException e) {\n            e.printStackTrace();\n        } finally {\n            try {\n                if (is != null) {\n                    is.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n        return count;\n    }\n\n    /**\n     * Return the size.\n     *\n     * @param filePath The path of file.\n     * @return the size\n     */\n    public static String getSize(final String filePath) {\n        return getSize(getFileByPath(filePath));\n    }\n\n    /**\n     * Return the size.\n     *\n     * @param file The directory.\n     * @return the size\n     */\n    public static String getSize(final File file) {\n        if (file == null) return \"\";\n        if (file.isDirectory()) {\n            return getDirSize(file);\n        }\n        return getFileSize(file);\n    }\n\n    /**\n     * Return the size of directory.\n     *\n     * @param dir The directory.\n     * @return the size of directory\n     */\n    private static String getDirSize(final File dir) {\n        long len = getDirLength(dir);\n        return len == -1 ? \"\" : UtilsBridge.byte2FitMemorySize(len);\n    }\n\n    /**\n     * Return the size of file.\n     *\n     * @param file The file.\n     * @return the length of file\n     */\n    private static String getFileSize(final File file) {\n        long len = getFileLength(file);\n        return len == -1 ? \"\" : UtilsBridge.byte2FitMemorySize(len);\n    }\n\n    /**\n     * Return the length.\n     *\n     * @param filePath The path of file.\n     * @return the length\n     */\n    public static long getLength(final String filePath) {\n        return getLength(getFileByPath(filePath));\n    }\n\n    /**\n     * Return the length.\n     *\n     * @param file The file.\n     * @return the length\n     */\n    public static long getLength(final File file) {\n        if (file == null) return 0;\n        if (file.isDirectory()) {\n            return getDirLength(file);\n        }\n        return getFileLength(file);\n    }\n\n    /**\n     * Return the length of directory.\n     *\n     * @param dir The directory.\n     * @return the length of directory\n     */\n    private static long getDirLength(final File dir) {\n        if (!isDir(dir)) return 0;\n        long len = 0;\n        File[] files = dir.listFiles();\n        if (files != null && files.length > 0) {\n            for (File file : files) {\n                if (file.isDirectory()) {\n                    len += getDirLength(file);\n                } else {\n                    len += file.length();\n                }\n            }\n        }\n        return len;\n    }\n\n    /**\n     * Return the length of file.\n     *\n     * @param filePath The path of file.\n     * @return the length of file\n     */\n    public static long getFileLength(final String filePath) {\n        boolean isURL = filePath.matches(\"[a-zA-z]+://[^\\\\s]*\");\n        if (isURL) {\n            try {\n                HttpsURLConnection conn = (HttpsURLConnection) new URL(filePath).openConnection();\n                conn.setRequestProperty(\"Accept-Encoding\", \"identity\");\n                conn.connect();\n                if (conn.getResponseCode() == 200) {\n                    return conn.getContentLength();\n                }\n                return -1;\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n        return getFileLength(getFileByPath(filePath));\n    }\n\n    /**\n     * Return the length of file.\n     *\n     * @param file The file.\n     * @return the length of file\n     */\n    private static long getFileLength(final File file) {\n        if (!isFile(file)) return -1;\n        return file.length();\n    }\n\n    /**\n     * Return the MD5 of file.\n     *\n     * @param filePath The path of file.\n     * @return the md5 of file\n     */\n    public static String getFileMD5ToString(final String filePath) {\n        File file = UtilsBridge.isSpace(filePath) ? null : new File(filePath);\n        return getFileMD5ToString(file);\n    }\n\n    /**\n     * Return the MD5 of file.\n     *\n     * @param file The file.\n     * @return the md5 of file\n     */\n    public static String getFileMD5ToString(final File file) {\n        return UtilsBridge.bytes2HexString(getFileMD5(file));\n    }\n\n    /**\n     * Return the MD5 of file.\n     *\n     * @param filePath The path of file.\n     * @return the md5 of file\n     */\n    public static byte[] getFileMD5(final String filePath) {\n        return getFileMD5(getFileByPath(filePath));\n    }\n\n    /**\n     * Return the MD5 of file.\n     *\n     * @param file The file.\n     * @return the md5 of file\n     */\n    public static byte[] getFileMD5(final File file) {\n        if (file == null) return null;\n        DigestInputStream dis = null;\n        try {\n            FileInputStream fis = new FileInputStream(file);\n            MessageDigest md = MessageDigest.getInstance(\"MD5\");\n            dis = new DigestInputStream(fis, md);\n            byte[] buffer = new byte[1024 * 256];\n            while (true) {\n                if (!(dis.read(buffer) > 0)) break;\n            }\n            md = dis.getMessageDigest();\n            return md.digest();\n        } catch (NoSuchAlgorithmException | IOException e) {\n            e.printStackTrace();\n        } finally {\n            try {\n                if (dis != null) {\n                    dis.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n        return null;\n    }\n\n    /**\n     * Return the file's path of directory.\n     *\n     * @param file The file.\n     * @return the file's path of directory\n     */\n    public static String getDirName(final File file) {\n        if (file == null) return \"\";\n        return getDirName(file.getAbsolutePath());\n    }\n\n    /**\n     * Return the file's path of directory.\n     *\n     * @param filePath The path of file.\n     * @return the file's path of directory\n     */\n    public static String getDirName(final String filePath) {\n        if (UtilsBridge.isSpace(filePath)) return \"\";\n        int lastSep = filePath.lastIndexOf(File.separator);\n        return lastSep == -1 ? \"\" : filePath.substring(0, lastSep + 1);\n    }\n\n    /**\n     * Return the name of file.\n     *\n     * @param file The file.\n     * @return the name of file\n     */\n    public static String getFileName(final File file) {\n        if (file == null) return \"\";\n        return getFileName(file.getAbsolutePath());\n    }\n\n    /**\n     * Return the name of file.\n     *\n     * @param filePath The path of file.\n     * @return the name of file\n     */\n    public static String getFileName(final String filePath) {\n        if (UtilsBridge.isSpace(filePath)) return \"\";\n        int lastSep = filePath.lastIndexOf(File.separator);\n        return lastSep == -1 ? filePath : filePath.substring(lastSep + 1);\n    }\n\n    /**\n     * Return the name of file without extension.\n     *\n     * @param file The file.\n     * @return the name of file without extension\n     */\n    public static String getFileNameNoExtension(final File file) {\n        if (file == null) return \"\";\n        return getFileNameNoExtension(file.getPath());\n    }\n\n    /**\n     * Return the name of file without extension.\n     *\n     * @param filePath The path of file.\n     * @return the name of file without extension\n     */\n    public static String getFileNameNoExtension(final String filePath) {\n        if (UtilsBridge.isSpace(filePath)) return \"\";\n        int lastPoi = filePath.lastIndexOf('.');\n        int lastSep = filePath.lastIndexOf(File.separator);\n        if (lastSep == -1) {\n            return (lastPoi == -1 ? filePath : filePath.substring(0, lastPoi));\n        }\n        if (lastPoi == -1 || lastSep > lastPoi) {\n            return filePath.substring(lastSep + 1);\n        }\n        return filePath.substring(lastSep + 1, lastPoi);\n    }\n\n    /**\n     * Return the extension of file.\n     *\n     * @param file The file.\n     * @return the extension of file\n     */\n    public static String getFileExtension(final File file) {\n        if (file == null) return \"\";\n        return getFileExtension(file.getPath());\n    }\n\n    /**\n     * Return the extension of file.\n     *\n     * @param filePath The path of file.\n     * @return the extension of file\n     */\n    public static String getFileExtension(final String filePath) {\n        if (UtilsBridge.isSpace(filePath)) return \"\";\n        int lastPoi = filePath.lastIndexOf('.');\n        int lastSep = filePath.lastIndexOf(File.separator);\n        if (lastPoi == -1 || lastSep >= lastPoi) return \"\";\n        return filePath.substring(lastPoi + 1);\n    }\n\n    /**\n     * Notify system to scan the file.\n     *\n     * @param filePath The path of file.\n     */\n    public static void notifySystemToScan(final String filePath) {\n        notifySystemToScan(getFileByPath(filePath));\n    }\n\n    /**\n     * Notify system to scan the file.\n     *\n     * @param file The file.\n     */\n    public static void notifySystemToScan(final File file) {\n        if (file == null || !file.exists()) return;\n        Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);\n        intent.setData(Uri.parse(\"file://\" + file.getAbsolutePath()));\n        Utils.getApp().sendBroadcast(intent);\n    }\n\n    /**\n     * Return the total size of file system.\n     *\n     * @param anyPathInFs Any path in file system.\n     * @return the total size of file system\n     */\n    public static long getFsTotalSize(String anyPathInFs) {\n        if (TextUtils.isEmpty(anyPathInFs)) return 0;\n        StatFs statFs = new StatFs(anyPathInFs);\n        long blockSize;\n        long totalSize;\n        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {\n            blockSize = statFs.getBlockSizeLong();\n            totalSize = statFs.getBlockCountLong();\n        } else {\n            blockSize = statFs.getBlockSize();\n            totalSize = statFs.getBlockCount();\n        }\n        return blockSize * totalSize;\n    }\n\n    /**\n     * Return the available size of file system.\n     *\n     * @param anyPathInFs Any path in file system.\n     * @return the available size of file system\n     */\n    public static long getFsAvailableSize(final String anyPathInFs) {\n        if (TextUtils.isEmpty(anyPathInFs)) return 0;\n        StatFs statFs = new StatFs(anyPathInFs);\n        long blockSize;\n        long availableSize;\n        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR2) {\n            blockSize = statFs.getBlockSizeLong();\n            availableSize = statFs.getAvailableBlocksLong();\n        } else {\n            blockSize = statFs.getBlockSize();\n            availableSize = statFs.getAvailableBlocks();\n        }\n        return blockSize * availableSize;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // interface\n    ///////////////////////////////////////////////////////////////////////////\n\n    public interface OnReplaceListener {\n        boolean onReplace(File srcFile, File destFile);\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/FlashlightUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.content.pm.PackageManager;\nimport android.graphics.SurfaceTexture;\nimport android.hardware.Camera;\nimport android.util.Log;\n\nimport java.io.IOException;\n\nimport static android.hardware.Camera.Parameters.FLASH_MODE_OFF;\nimport static android.hardware.Camera.Parameters.FLASH_MODE_TORCH;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/04/27\n *     desc  : utils about flashlight\n * </pre>\n */\npublic final class FlashlightUtils {\n\n    private static Camera         mCamera;\n    private static SurfaceTexture mSurfaceTexture;\n\n    private FlashlightUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Return whether the device supports flashlight.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isFlashlightEnable() {\n        return Utils.getApp()\n                .getPackageManager()\n                .hasSystemFeature(PackageManager.FEATURE_CAMERA_FLASH);\n    }\n\n    /**\n     * Return whether the flashlight is working.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isFlashlightOn() {\n        if (!init()) return false;\n        Camera.Parameters parameters = mCamera.getParameters();\n        return FLASH_MODE_TORCH.equals(parameters.getFlashMode());\n    }\n\n    /**\n     * Turn on or turn off the flashlight.\n     *\n     * @param isOn True to turn on the flashlight, false otherwise.\n     */\n    public static void setFlashlightStatus(final boolean isOn) {\n        if (!init()) return;\n        final Camera.Parameters parameters = mCamera.getParameters();\n        if (isOn) {\n            if (!FLASH_MODE_TORCH.equals(parameters.getFlashMode())) {\n                try {\n                    mCamera.setPreviewTexture(mSurfaceTexture);\n                    mCamera.startPreview();\n                    parameters.setFlashMode(FLASH_MODE_TORCH);\n                    mCamera.setParameters(parameters);\n                } catch (IOException e) {\n                    e.printStackTrace();\n                }\n            }\n        } else {\n            if (!FLASH_MODE_OFF.equals(parameters.getFlashMode())) {\n                parameters.setFlashMode(FLASH_MODE_OFF);\n                mCamera.setParameters(parameters);\n            }\n        }\n    }\n\n    /**\n     * Destroy the flashlight.\n     */\n    public static void destroy() {\n        if (mCamera == null) return;\n        mCamera.release();\n        mSurfaceTexture = null;\n        mCamera = null;\n    }\n\n    private static boolean init() {\n        if (mCamera == null) {\n            try {\n                mCamera = Camera.open(0);\n                mSurfaceTexture = new SurfaceTexture(0);\n            } catch (Throwable t) {\n                Log.e(\"FlashlightUtils\", \"init failed: \", t);\n                return false;\n            }\n        }\n        if (mCamera == null) {\n            Log.e(\"FlashlightUtils\", \"init failed.\");\n            return false;\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/FragmentUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.graphics.drawable.Drawable;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.View;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\nimport androidx.annotation.AnimRes;\nimport androidx.annotation.AnimatorRes;\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.DrawableRes;\nimport androidx.annotation.IdRes;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.fragment.app.Fragment;\nimport androidx.fragment.app.FragmentManager;\nimport androidx.fragment.app.FragmentTransaction;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/01/17\n *     desc  : utils about fragment\n * </pre>\n */\npublic final class FragmentUtils {\n\n    private static final int TYPE_ADD_FRAGMENT       = 0x01;\n    private static final int TYPE_SHOW_FRAGMENT      = 0x01 << 1;\n    private static final int TYPE_HIDE_FRAGMENT      = 0x01 << 2;\n    private static final int TYPE_SHOW_HIDE_FRAGMENT = 0x01 << 3;\n    private static final int TYPE_REPLACE_FRAGMENT   = 0x01 << 4;\n    private static final int TYPE_REMOVE_FRAGMENT    = 0x01 << 5;\n    private static final int TYPE_REMOVE_TO_FRAGMENT = 0x01 << 6;\n\n    private static final String ARGS_ID           = \"args_id\";\n    private static final String ARGS_IS_HIDE      = \"args_is_hide\";\n    private static final String ARGS_IS_ADD_STACK = \"args_is_add_stack\";\n    private static final String ARGS_TAG          = \"args_tag\";\n\n    private FragmentUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param add         The fragment will be add.\n     * @param containerId The id of container.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final Fragment add,\n                           @IdRes final int containerId) {\n        add(fm, add, containerId, null, false, false);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param add         The fragment will be add.\n     * @param containerId The id of container.\n     * @param isHide      True to hide, false otherwise.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final Fragment add,\n                           @IdRes final int containerId,\n                           final boolean isHide) {\n        add(fm, add, containerId, null, isHide, false);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param add         The fragment will be add.\n     * @param containerId The id of container.\n     * @param isHide      True to hide, false otherwise.\n     * @param isAddStack  True to add fragment in stack, false otherwise.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final Fragment add,\n                           @IdRes final int containerId,\n                           final boolean isHide,\n                           final boolean isAddStack) {\n        add(fm, add, containerId, null, isHide, isAddStack);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param add         The fragment will be add.\n     * @param containerId The id of container.\n     * @param enterAnim   An animation or animator resource ID used for the enter animation on the\n     *                    view of the fragment being added or attached.\n     * @param exitAnim    An animation or animator resource ID used for the exit animation on the\n     *                    view of the fragment being removed or detached.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final Fragment add,\n                           @IdRes final int containerId,\n                           @AnimatorRes @AnimRes final int enterAnim,\n                           @AnimatorRes @AnimRes final int exitAnim) {\n        add(fm, add, containerId, null, false, enterAnim, exitAnim, 0, 0);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param containerId The id of container.\n     * @param add         The fragment will be add.\n     * @param isAddStack  True to add fragment in stack, false otherwise.\n     * @param enterAnim   An animation or animator resource ID used for the enter animation on the\n     *                    view of the fragment being added or attached.\n     * @param exitAnim    An animation or animator resource ID used for the exit animation on the\n     *                    view of the fragment being removed or detached.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final Fragment add,\n                           @IdRes final int containerId,\n                           final boolean isAddStack,\n                           @AnimatorRes @AnimRes final int enterAnim,\n                           @AnimatorRes @AnimRes final int exitAnim) {\n        add(fm, add, containerId, null, isAddStack, enterAnim, exitAnim, 0, 0);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm           The manager of fragment.\n     * @param containerId  The id of container.\n     * @param add          The fragment will be add.\n     * @param enterAnim    An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being added or attached.\n     * @param exitAnim     An animation or animator resource ID used for the exit animation on the\n     *                     view of the fragment being removed or detached.\n     * @param popEnterAnim An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being readded or reattached caused by\n     *                     popBackStack() or similar methods.\n     * @param popExitAnim  An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being removed or detached caused by\n     *                     popBackStack() or similar methods.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final Fragment add,\n                           @IdRes final int containerId,\n                           @AnimatorRes @AnimRes final int enterAnim,\n                           @AnimatorRes @AnimRes final int exitAnim,\n                           @AnimatorRes @AnimRes final int popEnterAnim,\n                           @AnimatorRes @AnimRes final int popExitAnim) {\n        add(fm, add, containerId, null, false, enterAnim, exitAnim, popEnterAnim, popExitAnim);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm           The manager of fragment.\n     * @param containerId  The id of container.\n     * @param add          The fragment will be add.\n     * @param isAddStack   True to add fragment in stack, false otherwise.\n     * @param enterAnim    An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being added or attached.\n     * @param exitAnim     An animation or animator resource ID used for the exit animation on the\n     *                     view of the fragment being removed or detached.\n     * @param popEnterAnim An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being readded or reattached caused by\n     *                     popBackStack() or similar methods.\n     * @param popExitAnim  An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being removed or detached caused by\n     *                     popBackStack() or similar methods.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final Fragment add,\n                           @IdRes final int containerId,\n                           final boolean isAddStack,\n                           @AnimatorRes @AnimRes final int enterAnim,\n                           @AnimatorRes @AnimRes final int exitAnim,\n                           @AnimatorRes @AnimRes final int popEnterAnim,\n                           @AnimatorRes @AnimRes final int popExitAnim) {\n        add(fm, add, containerId, null, isAddStack, enterAnim, exitAnim, popEnterAnim, popExitAnim);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm             The manager of fragment.\n     * @param add            The fragment will be add.\n     * @param containerId    The id of container.\n     * @param sharedElements A View in a disappearing Fragment to match with a View in an\n     *                       appearing Fragment.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final Fragment add,\n                           @IdRes final int containerId,\n                           @NonNull final View... sharedElements) {\n        add(fm, add, containerId, null, false, sharedElements);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm             The manager of fragment.\n     * @param add            The fragment will be add.\n     * @param containerId    The id of container.\n     * @param isAddStack     True to add fragment in stack, false otherwise.\n     * @param sharedElements A View in a disappearing Fragment to match with a View in an\n     *                       appearing Fragment.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final Fragment add,\n                           @IdRes final int containerId,\n                           final boolean isAddStack,\n                           @NonNull final View... sharedElements) {\n        add(fm, add, containerId, null, isAddStack, sharedElements);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param adds        The fragments will be add.\n     * @param containerId The id of container.\n     * @param showIndex   The index of fragment will be shown.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final List<Fragment> adds,\n                           @IdRes final int containerId,\n                           final int showIndex) {\n        add(fm, adds.toArray(new Fragment[0]), containerId, null, showIndex);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param adds        The fragments will be add.\n     * @param containerId The id of container.\n     * @param showIndex   The index of fragment will be shown.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final Fragment[] adds,\n                           @IdRes final int containerId,\n                           final int showIndex) {\n        add(fm, adds, containerId, null, showIndex);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param add         The fragment will be add.\n     * @param containerId The id of container.\n     * @param tag         The tag of fragment.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final Fragment add,\n                           @IdRes final int containerId,\n                           final String tag) {\n        add(fm, add, containerId, tag, false, false);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param add         The fragment will be add.\n     * @param containerId The id of container.\n     * @param tag         The tag of fragment.\n     * @param isHide      True to hide, false otherwise.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final Fragment add,\n                           @IdRes final int containerId,\n                           final String tag,\n                           final boolean isHide) {\n        add(fm, add, containerId, tag, isHide, false);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param add         The fragment will be add.\n     * @param containerId The id of container.\n     * @param tag         The tag of fragment.\n     * @param isHide      True to hide, false otherwise.\n     * @param isAddStack  True to add fragment in stack, false otherwise.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final Fragment add,\n                           @IdRes final int containerId,\n                           final String tag,\n                           final boolean isHide,\n                           final boolean isAddStack) {\n        putArgs(add, new Args(containerId, tag, isHide, isAddStack));\n        operateNoAnim(TYPE_ADD_FRAGMENT, fm, null, add);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param add         The fragment will be add.\n     * @param containerId The id of container.\n     * @param tag         The tag of fragment.\n     * @param enterAnim   An animation or animator resource ID used for the enter animation on the\n     *                    view of the fragment being added or attached.\n     * @param exitAnim    An animation or animator resource ID used for the exit animation on the\n     *                    view of the fragment being removed or detached.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final Fragment add,\n                           @IdRes final int containerId,\n                           final String tag,\n                           @AnimatorRes @AnimRes final int enterAnim,\n                           @AnimatorRes @AnimRes final int exitAnim) {\n        add(fm, add, containerId, tag, false, enterAnim, exitAnim, 0, 0);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param add         The fragment will be add.\n     * @param containerId The id of container.\n     * @param tag         The tag of fragment.\n     * @param isAddStack  True to add fragment in stack, false otherwise.\n     * @param enterAnim   An animation or animator resource ID used for the enter animation on the\n     *                    view of the fragment being added or attached.\n     * @param exitAnim    An animation or animator resource ID used for the exit animation on the\n     *                    view of the fragment being removed or detached.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final Fragment add,\n                           @IdRes final int containerId,\n                           final String tag,\n                           final boolean isAddStack,\n                           @AnimatorRes @AnimRes final int enterAnim,\n                           @AnimatorRes @AnimRes final int exitAnim) {\n        add(fm, add, containerId, tag, isAddStack, enterAnim, exitAnim, 0, 0);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm           The manager of fragment.\n     * @param add          The fragment will be add.\n     * @param containerId  The id of container.\n     * @param tag          The tag of fragment.\n     * @param enterAnim    An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being added or attached.\n     * @param exitAnim     An animation or animator resource ID used for the exit animation on the\n     *                     view of the fragment being removed or detached.\n     * @param popEnterAnim An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being readded or reattached caused by\n     *                     popBackStack() or similar methods.\n     * @param popExitAnim  An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being removed or detached caused by\n     *                     popBackStack() or similar methods.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final Fragment add,\n                           @IdRes final int containerId,\n                           final String tag,\n                           @AnimatorRes @AnimRes final int enterAnim,\n                           @AnimatorRes @AnimRes final int exitAnim,\n                           @AnimatorRes @AnimRes final int popEnterAnim,\n                           @AnimatorRes @AnimRes final int popExitAnim) {\n        add(fm, add, containerId, tag, false, enterAnim, exitAnim, popEnterAnim, popExitAnim);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm           The manager of fragment.\n     * @param add          The fragment will be add.\n     * @param containerId  The id of container.\n     * @param tag          The tag of fragment.\n     * @param isAddStack   True to add fragment in stack, false otherwise.\n     * @param enterAnim    An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being added or attached.\n     * @param exitAnim     An animation or animator resource ID used for the exit animation on the\n     *                     view of the fragment being removed or detached.\n     * @param popEnterAnim An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being readded or reattached caused by\n     *                     popBackStack() or similar methods.\n     * @param popExitAnim  An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being removed or detached caused by\n     *                     popBackStack() or similar methods.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final Fragment add,\n                           @IdRes final int containerId,\n                           final String tag,\n                           final boolean isAddStack,\n                           @AnimatorRes @AnimRes final int enterAnim,\n                           @AnimatorRes @AnimRes final int exitAnim,\n                           @AnimatorRes @AnimRes final int popEnterAnim,\n                           @AnimatorRes @AnimRes final int popExitAnim) {\n        FragmentTransaction ft = fm.beginTransaction();\n        putArgs(add, new Args(containerId, tag, false, isAddStack));\n        addAnim(ft, enterAnim, exitAnim, popEnterAnim, popExitAnim);\n        operate(TYPE_ADD_FRAGMENT, fm, ft, null, add);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm             The manager of fragment.\n     * @param add            The fragment will be add.\n     * @param tag            The tag of fragment.\n     * @param containerId    The id of container.\n     * @param sharedElements A View in a disappearing Fragment to match with a View in an\n     *                       appearing Fragment.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final Fragment add,\n                           @IdRes final int containerId,\n                           final String tag,\n                           @NonNull final View... sharedElements) {\n        add(fm, add, containerId, tag, false, sharedElements);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm             The manager of fragment.\n     * @param add            The fragment will be add.\n     * @param containerId    The id of container.\n     * @param isAddStack     True to add fragment in stack, false otherwise.\n     * @param sharedElements A View in a disappearing Fragment to match with a View in an\n     *                       appearing Fragment.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final Fragment add,\n                           @IdRes final int containerId,\n                           final String tag,\n                           final boolean isAddStack,\n                           @NonNull final View... sharedElements) {\n        FragmentTransaction ft = fm.beginTransaction();\n        putArgs(add, new Args(containerId, tag, false, isAddStack));\n        addSharedElement(ft, sharedElements);\n        operate(TYPE_ADD_FRAGMENT, fm, ft, null, add);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param adds        The fragments will be add.\n     * @param containerId The id of container.\n     * @param showIndex   The index of fragment will be shown.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final List<Fragment> adds,\n                           @IdRes final int containerId,\n                           final String[] tags,\n                           final int showIndex) {\n        add(fm, adds.toArray(new Fragment[0]), containerId, tags, showIndex);\n    }\n\n    /**\n     * Add fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param adds        The fragments will be add.\n     * @param containerId The id of container.\n     * @param showIndex   The index of fragment will be shown.\n     */\n    public static void add(@NonNull final FragmentManager fm,\n                           @NonNull final Fragment[] adds,\n                           @IdRes final int containerId,\n                           final String[] tags,\n                           final int showIndex) {\n        if (tags == null) {\n            for (int i = 0, len = adds.length; i < len; ++i) {\n                putArgs(adds[i], new Args(containerId, null, showIndex != i, false));\n            }\n        } else {\n            for (int i = 0, len = adds.length; i < len; ++i) {\n                putArgs(adds[i], new Args(containerId, tags[i], showIndex != i, false));\n            }\n        }\n        operateNoAnim(TYPE_ADD_FRAGMENT, fm, null, adds);\n    }\n\n    /**\n     * Show fragment.\n     *\n     * @param show The fragment will be show.\n     */\n    public static void show(@NonNull final Fragment show) {\n        putArgs(show, false);\n        operateNoAnim(TYPE_SHOW_FRAGMENT, show.getFragmentManager(), null, show);\n    }\n\n    /**\n     * Show fragment.\n     *\n     * @param fm The manager of fragment.\n     */\n    public static void show(@NonNull final FragmentManager fm) {\n        List<Fragment> fragments = getFragments(fm);\n        for (Fragment show : fragments) {\n            putArgs(show, false);\n        }\n        operateNoAnim(TYPE_SHOW_FRAGMENT, fm, null, fragments.toArray(new Fragment[0]));\n    }\n\n    /**\n     * Hide fragment.\n     *\n     * @param hide The fragment will be hide.\n     */\n    public static void hide(@NonNull final Fragment hide) {\n        putArgs(hide, true);\n        operateNoAnim(TYPE_HIDE_FRAGMENT, hide.getFragmentManager(), null, hide);\n    }\n\n    /**\n     * Hide fragment.\n     *\n     * @param fm The manager of fragment.\n     */\n    public static void hide(@NonNull final FragmentManager fm) {\n        List<Fragment> fragments = getFragments(fm);\n        for (Fragment hide : fragments) {\n            putArgs(hide, true);\n        }\n        operateNoAnim(TYPE_HIDE_FRAGMENT, fm, null, fragments.toArray(new Fragment[0]));\n    }\n\n    /**\n     * Show fragment then hide other fragment.\n     *\n     * @param show The fragment will be show.\n     * @param hide The fragment will be hide.\n     */\n    public static void showHide(@NonNull final Fragment show,\n                                @NonNull final Fragment hide) {\n        showHide(show, Collections.singletonList(hide));\n    }\n\n    /**\n     * Show fragment then hide other fragment.\n     *\n     * @param showIndex The index of fragment will be shown.\n     * @param fragments The fragment will be hide.\n     */\n    public static void showHide(final int showIndex, @NonNull final Fragment... fragments) {\n        showHide(fragments[showIndex], fragments);\n    }\n\n    /**\n     * Show fragment then hide other fragment.\n     *\n     * @param show The fragment will be show.\n     * @param hide The fragment will be hide.\n     */\n    public static void showHide(@NonNull final Fragment show, @NonNull final Fragment... hide) {\n        showHide(show, Arrays.asList(hide));\n    }\n\n    /**\n     * Show fragment then hide other fragment.\n     *\n     * @param showIndex The index of fragment will be shown.\n     * @param fragments The fragments will be hide.\n     */\n    public static void showHide(final int showIndex, @NonNull final List<Fragment> fragments) {\n        showHide(fragments.get(showIndex), fragments);\n    }\n\n    /**\n     * Show fragment then hide other fragment.\n     *\n     * @param show The fragment will be show.\n     * @param hide The fragment will be hide.\n     */\n    public static void showHide(@NonNull final Fragment show, @NonNull final List<Fragment> hide) {\n        for (Fragment fragment : hide) {\n            putArgs(fragment, fragment != show);\n        }\n        operateNoAnim(TYPE_SHOW_HIDE_FRAGMENT, show.getFragmentManager(), show, hide.toArray(new Fragment[0]));\n    }\n\n\n    /**\n     * Show fragment then hide other fragment.\n     *\n     * @param show The fragment will be show.\n     * @param hide The fragment will be hide.\n     */\n    public static void showHide(@NonNull final Fragment show,\n                                @NonNull final Fragment hide, @AnimatorRes @AnimRes final int enterAnim,\n                                @AnimatorRes @AnimRes final int exitAnim,\n                                @AnimatorRes @AnimRes final int popEnterAnim,\n                                @AnimatorRes @AnimRes final int popExitAnim) {\n        showHide(show, Collections.singletonList(hide), enterAnim, exitAnim, popEnterAnim, popExitAnim);\n    }\n\n    /**\n     * Show fragment then hide other fragment.\n     *\n     * @param showIndex The index of fragment will be shown.\n     * @param fragments The fragments will be hide.\n     */\n    public static void showHide(final int showIndex, @NonNull final List<Fragment> fragments,\n                                @AnimatorRes @AnimRes final int enterAnim,\n                                @AnimatorRes @AnimRes final int exitAnim,\n                                @AnimatorRes @AnimRes final int popEnterAnim,\n                                @AnimatorRes @AnimRes final int popExitAnim) {\n        showHide(fragments.get(showIndex), fragments, enterAnim, exitAnim, popEnterAnim, popExitAnim);\n    }\n\n    /**\n     * Show fragment then hide other fragment.\n     *\n     * @param show The fragment will be show.\n     * @param hide The fragment will be hide.\n     */\n    public static void showHide(@NonNull final Fragment show, @NonNull final List<Fragment> hide,\n                                @AnimatorRes @AnimRes final int enterAnim,\n                                @AnimatorRes @AnimRes final int exitAnim,\n                                @AnimatorRes @AnimRes final int popEnterAnim,\n                                @AnimatorRes @AnimRes final int popExitAnim) {\n        for (Fragment fragment : hide) {\n            putArgs(fragment, fragment != show);\n        }\n        FragmentManager fm = show.getFragmentManager();\n        if (fm != null) {\n            FragmentTransaction ft = fm.beginTransaction();\n            addAnim(ft, enterAnim, exitAnim, popEnterAnim, popExitAnim);\n            operate(TYPE_SHOW_HIDE_FRAGMENT, fm, ft, show, hide.toArray(new Fragment[0]));\n        }\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param srcFragment  The source of fragment.\n     * @param destFragment The destination of fragment.\n     */\n    public static void replace(@NonNull final Fragment srcFragment,\n                               @NonNull final Fragment destFragment) {\n        replace(srcFragment, destFragment, null, false);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param srcFragment  The source of fragment.\n     * @param destFragment The destination of fragment.\n     * @param isAddStack   True to add fragment in stack, false otherwise.\n     */\n    public static void replace(@NonNull final Fragment srcFragment,\n                               @NonNull final Fragment destFragment,\n                               final boolean isAddStack) {\n        replace(srcFragment, destFragment, null, isAddStack);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param srcFragment  The source of fragment.\n     * @param destFragment The destination of fragment.\n     * @param enterAnim    An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being added or attached.\n     * @param exitAnim     An animation or animator resource ID used for the exit animation on the\n     *                     view of the fragment being removed or detached.\n     */\n    public static void replace(@NonNull final Fragment srcFragment,\n                               @NonNull final Fragment destFragment,\n                               @AnimatorRes @AnimRes final int enterAnim,\n                               @AnimatorRes @AnimRes final int exitAnim) {\n        replace(srcFragment, destFragment, null, false, enterAnim, exitAnim, 0, 0);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param srcFragment  The source of fragment.\n     * @param destFragment The destination of fragment.\n     * @param isAddStack   True to add fragment in stack, false otherwise.\n     * @param enterAnim    An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being added or attached.\n     * @param exitAnim     An animation or animator resource ID used for the exit animation on the\n     *                     view of the fragment being removed or detached.\n     */\n    public static void replace(@NonNull final Fragment srcFragment,\n                               @NonNull final Fragment destFragment,\n                               final boolean isAddStack,\n                               @AnimatorRes @AnimRes final int enterAnim,\n                               @AnimatorRes @AnimRes final int exitAnim) {\n        replace(srcFragment, destFragment, null, isAddStack, enterAnim, exitAnim, 0, 0);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param srcFragment  The source of fragment.\n     * @param destFragment The destination of fragment.\n     * @param enterAnim    An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being added or attached.\n     * @param exitAnim     An animation or animator resource ID used for the exit animation on the\n     *                     view of the fragment being removed or detached.\n     * @param popEnterAnim An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being readded or reattached caused by\n     *                     popBackStack() or similar methods.\n     * @param popExitAnim  An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being removed or detached caused by\n     *                     popBackStack() or similar methods.\n     */\n    public static void replace(@NonNull final Fragment srcFragment,\n                               @NonNull final Fragment destFragment,\n                               @AnimatorRes @AnimRes final int enterAnim,\n                               @AnimatorRes @AnimRes final int exitAnim,\n                               @AnimatorRes @AnimRes final int popEnterAnim,\n                               @AnimatorRes @AnimRes final int popExitAnim) {\n        replace(srcFragment, destFragment, null, false,\n                enterAnim, exitAnim, popEnterAnim, popExitAnim);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param srcFragment  The source of fragment.\n     * @param destFragment The destination of fragment.\n     * @param isAddStack   True to add fragment in stack, false otherwise.\n     * @param enterAnim    An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being added or attached.\n     * @param exitAnim     An animation or animator resource ID used for the exit animation on the\n     *                     view of the fragment being removed or detached.\n     * @param popEnterAnim An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being readded or reattached caused by\n     *                     popBackStack() or similar methods.\n     * @param popExitAnim  An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being removed or detached caused by\n     *                     popBackStack() or similar methods.\n     */\n    public static void replace(@NonNull final Fragment srcFragment,\n                               @NonNull final Fragment destFragment,\n                               final boolean isAddStack,\n                               @AnimatorRes @AnimRes final int enterAnim,\n                               @AnimatorRes @AnimRes final int exitAnim,\n                               @AnimatorRes @AnimRes final int popEnterAnim,\n                               @AnimatorRes @AnimRes final int popExitAnim) {\n        replace(srcFragment, destFragment, null, isAddStack,\n                enterAnim, exitAnim, popEnterAnim, popExitAnim);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param srcFragment    The source of fragment.\n     * @param destFragment   The destination of fragment.\n     * @param sharedElements A View in a disappearing Fragment to match with a View in an\n     *                       appearing Fragment.\n     */\n    public static void replace(@NonNull final Fragment srcFragment,\n                               @NonNull final Fragment destFragment,\n                               final View... sharedElements) {\n        replace(srcFragment, destFragment, null, false, sharedElements);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param srcFragment    The source of fragment.\n     * @param destFragment   The destination of fragment.\n     * @param isAddStack     True to add fragment in stack, false otherwise.\n     * @param sharedElements A View in a disappearing Fragment to match with a View in an\n     *                       appearing Fragment.\n     */\n    public static void replace(@NonNull final Fragment srcFragment,\n                               @NonNull final Fragment destFragment,\n                               final boolean isAddStack,\n                               final View... sharedElements) {\n        replace(srcFragment, destFragment, null, isAddStack, sharedElements);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param fragment    The new fragment to place in the container.\n     * @param containerId The id of container.\n     */\n    public static void replace(@NonNull final FragmentManager fm,\n                               @NonNull final Fragment fragment,\n                               @IdRes final int containerId) {\n        replace(fm, fragment, containerId, null, false);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param containerId The id of container.\n     * @param fragment    The new fragment to place in the container.\n     * @param isAddStack  True to add fragment in stack, false otherwise.\n     */\n    public static void replace(@NonNull final FragmentManager fm,\n                               @NonNull final Fragment fragment,\n                               @IdRes final int containerId,\n                               final boolean isAddStack) {\n        replace(fm, fragment, containerId, null, isAddStack);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param containerId The id of container.\n     * @param fragment    The new fragment to place in the container.\n     * @param enterAnim   An animation or animator resource ID used for the enter animation on the\n     *                    view of the fragment being added or attached.\n     * @param exitAnim    An animation or animator resource ID used for the exit animation on the\n     *                    view of the fragment being removed or detached.\n     */\n    public static void replace(@NonNull final FragmentManager fm,\n                               @NonNull final Fragment fragment,\n                               @IdRes final int containerId,\n                               @AnimatorRes @AnimRes final int enterAnim,\n                               @AnimatorRes @AnimRes final int exitAnim) {\n        replace(fm, fragment, containerId, null, false, enterAnim, exitAnim, 0, 0);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param containerId The id of container.\n     * @param fragment    The new fragment to place in the container.\n     * @param isAddStack  True to add fragment in stack, false otherwise.\n     * @param enterAnim   An animation or animator resource ID used for the enter animation on the\n     *                    view of the fragment being added or attached.\n     * @param exitAnim    An animation or animator resource ID used for the exit animation on the\n     *                    view of the fragment being removed or detached.\n     */\n    public static void replace(@NonNull final FragmentManager fm,\n                               @NonNull final Fragment fragment,\n                               @IdRes final int containerId,\n                               final boolean isAddStack,\n                               @AnimatorRes @AnimRes final int enterAnim,\n                               @AnimatorRes @AnimRes final int exitAnim) {\n        replace(fm, fragment, containerId, null, isAddStack, enterAnim, exitAnim, 0, 0);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param fm           The manager of fragment.\n     * @param containerId  The id of container.\n     * @param fragment     The new fragment to place in the container.\n     * @param enterAnim    An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being added or attached.\n     * @param exitAnim     An animation or animator resource ID used for the exit animation on the\n     *                     view of the fragment being removed or detached.\n     * @param popEnterAnim An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being readded or reattached caused by\n     *                     popBackStack() or similar methods.\n     * @param popExitAnim  An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being removed or detached caused by\n     *                     popBackStack() or similar methods.\n     */\n    public static void replace(@NonNull final FragmentManager fm,\n                               @NonNull final Fragment fragment,\n                               @IdRes final int containerId,\n                               @AnimatorRes @AnimRes final int enterAnim,\n                               @AnimatorRes @AnimRes final int exitAnim,\n                               @AnimatorRes @AnimRes final int popEnterAnim,\n                               @AnimatorRes @AnimRes final int popExitAnim) {\n        replace(fm, fragment, containerId, null, false,\n                enterAnim, exitAnim, popEnterAnim, popExitAnim);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param fm           The manager of fragment.\n     * @param containerId  The id of container.\n     * @param fragment     The new fragment to place in the container.\n     * @param isAddStack   True to add fragment in stack, false otherwise.\n     * @param enterAnim    An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being added or attached.\n     * @param exitAnim     An animation or animator resource ID used for the exit animation on the\n     *                     view of the fragment being removed or detached.\n     * @param popEnterAnim An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being readded or reattached caused by\n     *                     popBackStack() or similar methods.\n     * @param popExitAnim  An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being removed or detached caused by\n     *                     popBackStack() or similar methods.\n     */\n    public static void replace(@NonNull final FragmentManager fm,\n                               @NonNull final Fragment fragment,\n                               @IdRes final int containerId,\n                               final boolean isAddStack,\n                               @AnimatorRes @AnimRes final int enterAnim,\n                               @AnimatorRes @AnimRes final int exitAnim,\n                               @AnimatorRes @AnimRes final int popEnterAnim,\n                               @AnimatorRes @AnimRes final int popExitAnim) {\n        replace(fm, fragment, containerId, null, isAddStack,\n                enterAnim, exitAnim, popEnterAnim, popExitAnim);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param fm             The manager of fragment.\n     * @param containerId    The id of container.\n     * @param fragment       The new fragment to place in the container.\n     * @param sharedElements A View in a disappearing Fragment to match with a View in an\n     *                       appearing Fragment.\n     */\n    public static void replace(@NonNull final FragmentManager fm,\n                               @NonNull final Fragment fragment,\n                               @IdRes final int containerId,\n                               final View... sharedElements) {\n        replace(fm, fragment, containerId, null, false, sharedElements);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param fm             The manager of fragment.\n     * @param containerId    The id of container.\n     * @param fragment       The new fragment to place in the container.\n     * @param isAddStack     True to add fragment in stack, false otherwise.\n     * @param sharedElements A View in a disappearing Fragment to match with a View in an\n     *                       appearing Fragment.\n     */\n    public static void replace(@NonNull final FragmentManager fm,\n                               @NonNull final Fragment fragment,\n                               @IdRes final int containerId,\n                               final boolean isAddStack,\n                               final View... sharedElements) {\n        replace(fm, fragment, containerId, null, isAddStack, sharedElements);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param srcFragment  The source of fragment.\n     * @param destFragment The destination of fragment.\n     * @param destTag      The destination of fragment's tag.\n     */\n    public static void replace(@NonNull final Fragment srcFragment,\n                               @NonNull final Fragment destFragment,\n                               final String destTag) {\n        replace(srcFragment, destFragment, destTag, false);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param srcFragment  The source of fragment.\n     * @param destFragment The destination of fragment.\n     * @param destTag      The destination of fragment's tag.\n     * @param isAddStack   True to add fragment in stack, false otherwise.\n     */\n    public static void replace(@NonNull final Fragment srcFragment,\n                               @NonNull final Fragment destFragment,\n                               final String destTag,\n                               final boolean isAddStack) {\n        FragmentManager fm = srcFragment.getFragmentManager();\n        if (fm == null) return;\n        Args args = getArgs(srcFragment);\n        replace(fm, destFragment, args.id, destTag, isAddStack);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param srcFragment  The source of fragment.\n     * @param destFragment The destination of fragment.\n     * @param destTag      The destination of fragment's tag.\n     * @param enterAnim    An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being added or attached.\n     * @param exitAnim     An animation or animator resource ID used for the exit animation on the\n     *                     view of the fragment being removed or detached.\n     */\n    public static void replace(@NonNull final Fragment srcFragment,\n                               @NonNull final Fragment destFragment,\n                               final String destTag,\n                               @AnimatorRes @AnimRes final int enterAnim,\n                               @AnimatorRes @AnimRes final int exitAnim) {\n        replace(srcFragment, destFragment, destTag, false, enterAnim, exitAnim, 0, 0);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param srcFragment  The source of fragment.\n     * @param destFragment The destination of fragment.\n     * @param destTag      The destination of fragment's tag.\n     * @param isAddStack   True to add fragment in stack, false otherwise.\n     * @param enterAnim    An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being added or attached.\n     * @param exitAnim     An animation or animator resource ID used for the exit animation on the\n     *                     view of the fragment being removed or detached.\n     */\n    public static void replace(@NonNull final Fragment srcFragment,\n                               @NonNull final Fragment destFragment,\n                               final String destTag,\n                               final boolean isAddStack,\n                               @AnimatorRes @AnimRes final int enterAnim,\n                               @AnimatorRes @AnimRes final int exitAnim) {\n        replace(srcFragment, destFragment, destTag, isAddStack, enterAnim, exitAnim, 0, 0);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param srcFragment  The source of fragment.\n     * @param destFragment The destination of fragment.\n     * @param destTag      The destination of fragment's tag.\n     * @param enterAnim    An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being added or attached.\n     * @param exitAnim     An animation or animator resource ID used for the exit animation on the\n     *                     view of the fragment being removed or detached.\n     * @param popEnterAnim An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being readded or reattached caused by\n     *                     popBackStack() or similar methods.\n     * @param popExitAnim  An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being removed or detached caused by\n     *                     popBackStack() or similar methods.\n     */\n    public static void replace(@NonNull final Fragment srcFragment,\n                               @NonNull final Fragment destFragment,\n                               final String destTag,\n                               @AnimatorRes @AnimRes final int enterAnim,\n                               @AnimatorRes @AnimRes final int exitAnim,\n                               @AnimatorRes @AnimRes final int popEnterAnim,\n                               @AnimatorRes @AnimRes final int popExitAnim) {\n        replace(srcFragment, destFragment, destTag, false,\n                enterAnim, exitAnim, popEnterAnim, popExitAnim);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param srcFragment  The source of fragment.\n     * @param destFragment The destination of fragment.\n     * @param destTag      The destination of fragment's tag.\n     * @param isAddStack   True to add fragment in stack, false otherwise.\n     * @param enterAnim    An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being added or attached.\n     * @param exitAnim     An animation or animator resource ID used for the exit animation on the\n     *                     view of the fragment being removed or detached.\n     * @param popEnterAnim An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being readded or reattached caused by\n     *                     popBackStack() or similar methods.\n     * @param popExitAnim  An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being removed or detached caused by\n     *                     popBackStack() or similar methods.\n     */\n    public static void replace(@NonNull final Fragment srcFragment,\n                               @NonNull final Fragment destFragment,\n                               final String destTag,\n                               final boolean isAddStack,\n                               @AnimatorRes @AnimRes final int enterAnim,\n                               @AnimatorRes @AnimRes final int exitAnim,\n                               @AnimatorRes @AnimRes final int popEnterAnim,\n                               @AnimatorRes @AnimRes final int popExitAnim) {\n        FragmentManager fm = srcFragment.getFragmentManager();\n        if (fm == null) return;\n        Args args = getArgs(srcFragment);\n        replace(fm, destFragment, args.id, destTag, isAddStack,\n                enterAnim, exitAnim, popEnterAnim, popExitAnim);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param srcFragment    The source of fragment.\n     * @param destFragment   The destination of fragment.\n     * @param destTag        The destination of fragment's tag.\n     * @param sharedElements A View in a disappearing Fragment to match with a View in an\n     *                       appearing Fragment.\n     */\n    public static void replace(@NonNull final Fragment srcFragment,\n                               @NonNull final Fragment destFragment,\n                               final String destTag,\n                               final View... sharedElements) {\n        replace(srcFragment, destFragment, destTag, false, sharedElements);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param srcFragment    The source of fragment.\n     * @param destFragment   The destination of fragment.\n     * @param destTag        The destination of fragment's tag.\n     * @param isAddStack     True to add fragment in stack, false otherwise.\n     * @param sharedElements A View in a disappearing Fragment to match with a View in an\n     *                       appearing Fragment.\n     */\n    public static void replace(@NonNull final Fragment srcFragment,\n                               @NonNull final Fragment destFragment,\n                               final String destTag,\n                               final boolean isAddStack,\n                               final View... sharedElements) {\n        FragmentManager fm = srcFragment.getFragmentManager();\n        if (fm == null) return;\n        Args args = getArgs(srcFragment);\n        replace(fm,\n                destFragment,\n                args.id,\n                destTag,\n                isAddStack,\n                sharedElements\n        );\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param fragment    The new fragment to place in the container.\n     * @param containerId The id of container.\n     * @param destTag     The destination of fragment's tag.\n     */\n    public static void replace(@NonNull final FragmentManager fm,\n                               @NonNull final Fragment fragment,\n                               @IdRes final int containerId,\n                               final String destTag) {\n        replace(fm, fragment, containerId, destTag, false);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param fragment    The new fragment to place in the container.\n     * @param containerId The id of container.\n     * @param destTag     The destination of fragment's tag.\n     * @param isAddStack  True to add fragment in stack, false otherwise.\n     */\n    public static void replace(@NonNull final FragmentManager fm,\n                               @NonNull final Fragment fragment,\n                               @IdRes final int containerId,\n                               final String destTag,\n                               final boolean isAddStack) {\n        FragmentTransaction ft = fm.beginTransaction();\n        putArgs(fragment, new Args(containerId, destTag, false, isAddStack));\n        operate(TYPE_REPLACE_FRAGMENT, fm, ft, null, fragment);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param fragment    The new fragment to place in the container.\n     * @param containerId The id of container.\n     * @param destTag     The destination of fragment's tag.\n     * @param enterAnim   An animation or animator resource ID used for the enter animation on the\n     *                    view of the fragment being added or attached.\n     * @param exitAnim    An animation or animator resource ID used for the exit animation on the\n     *                    view of the fragment being removed or detached.\n     */\n    public static void replace(@NonNull final FragmentManager fm,\n                               @NonNull final Fragment fragment,\n                               @IdRes final int containerId,\n                               final String destTag,\n                               @AnimatorRes @AnimRes final int enterAnim,\n                               @AnimatorRes @AnimRes final int exitAnim) {\n        replace(fm, fragment, containerId, destTag, false, enterAnim, exitAnim, 0, 0);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param fragment    The new fragment to place in the container.\n     * @param containerId The id of container.\n     * @param destTag     The destination of fragment's tag.\n     * @param isAddStack  True to add fragment in stack, false otherwise.\n     * @param enterAnim   An animation or animator resource ID used for the enter animation on the\n     *                    view of the fragment being added or attached.\n     * @param exitAnim    An animation or animator resource ID used for the exit animation on the\n     *                    view of the fragment being removed or detached.\n     */\n    public static void replace(@NonNull final FragmentManager fm,\n                               @NonNull final Fragment fragment,\n                               @IdRes final int containerId,\n                               final String destTag,\n                               final boolean isAddStack,\n                               @AnimatorRes @AnimRes final int enterAnim,\n                               @AnimatorRes @AnimRes final int exitAnim) {\n        replace(fm, fragment, containerId, destTag, isAddStack, enterAnim, exitAnim, 0, 0);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param fm           The manager of fragment.\n     * @param fragment     The new fragment to place in the container.\n     * @param containerId  The id of container.\n     * @param destTag      The destination of fragment's tag.\n     * @param enterAnim    An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being added or attached.\n     * @param exitAnim     An animation or animator resource ID used for the exit animation on the\n     *                     view of the fragment being removed or detached.\n     * @param popEnterAnim An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being readded or reattached caused by\n     *                     popBackStack() or similar methods.\n     * @param popExitAnim  An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being removed or detached caused by\n     *                     popBackStack() or similar methods.\n     */\n    public static void replace(@NonNull final FragmentManager fm,\n                               @NonNull final Fragment fragment,\n                               @IdRes final int containerId,\n                               final String destTag,\n                               @AnimatorRes @AnimRes final int enterAnim,\n                               @AnimatorRes @AnimRes final int exitAnim,\n                               @AnimatorRes @AnimRes final int popEnterAnim,\n                               @AnimatorRes @AnimRes final int popExitAnim) {\n        replace(fm, fragment, containerId, destTag, false,\n                enterAnim, exitAnim, popEnterAnim, popExitAnim);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param fm           The manager of fragment.\n     * @param fragment     The new fragment to place in the container.\n     * @param containerId  The id of container.\n     * @param destTag      The destination of fragment's tag.\n     * @param isAddStack   True to add fragment in stack, false otherwise.\n     * @param enterAnim    An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being added or attached.\n     * @param exitAnim     An animation or animator resource ID used for the exit animation on the\n     *                     view of the fragment being removed or detached.\n     * @param popEnterAnim An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being readded or reattached caused by\n     *                     popBackStack() or similar methods.\n     * @param popExitAnim  An animation or animator resource ID used for the enter animation on the\n     *                     view of the fragment being removed or detached caused by\n     *                     popBackStack() or similar methods.\n     */\n    public static void replace(@NonNull final FragmentManager fm,\n                               @NonNull final Fragment fragment,\n                               @IdRes final int containerId,\n                               final String destTag,\n                               final boolean isAddStack,\n                               @AnimatorRes @AnimRes final int enterAnim,\n                               @AnimatorRes @AnimRes final int exitAnim,\n                               @AnimatorRes @AnimRes final int popEnterAnim,\n                               @AnimatorRes @AnimRes final int popExitAnim) {\n        FragmentTransaction ft = fm.beginTransaction();\n        putArgs(fragment, new Args(containerId, destTag, false, isAddStack));\n        addAnim(ft, enterAnim, exitAnim, popEnterAnim, popExitAnim);\n        operate(TYPE_REPLACE_FRAGMENT, fm, ft, null, fragment);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param fm             The manager of fragment.\n     * @param fragment       The new fragment to place in the container.\n     * @param containerId    The id of container.\n     * @param destTag        The destination of fragment's tag.\n     * @param sharedElements A View in a disappearing Fragment to match with a View in an\n     *                       appearing Fragment.\n     */\n    public static void replace(@NonNull final FragmentManager fm,\n                               @NonNull final Fragment fragment,\n                               @IdRes final int containerId,\n                               final String destTag,\n                               final View... sharedElements) {\n        replace(fm, fragment, containerId, destTag, false, sharedElements);\n    }\n\n    /**\n     * Replace fragment.\n     *\n     * @param fm             The manager of fragment.\n     * @param fragment       The new fragment to place in the container.\n     * @param containerId    The id of container.\n     * @param destTag        The destination of fragment's tag.\n     * @param isAddStack     True to add fragment in stack, false otherwise.\n     * @param sharedElements A View in a disappearing Fragment to match with a View in an\n     *                       appearing Fragment.\n     */\n    public static void replace(@NonNull final FragmentManager fm,\n                               @NonNull final Fragment fragment,\n                               @IdRes final int containerId,\n                               final String destTag,\n                               final boolean isAddStack,\n                               final View... sharedElements) {\n        FragmentTransaction ft = fm.beginTransaction();\n        putArgs(fragment, new Args(containerId, destTag, false, isAddStack));\n        addSharedElement(ft, sharedElements);\n        operate(TYPE_REPLACE_FRAGMENT, fm, ft, null, fragment);\n    }\n\n    /**\n     * Pop fragment.\n     *\n     * @param fm The manager of fragment.\n     */\n    public static void pop(@NonNull final FragmentManager fm) {\n        pop(fm, true);\n    }\n\n    /**\n     * Pop fragment.\n     *\n     * @param fm          The manager of fragment.\n     * @param isImmediate True to pop immediately, false otherwise.\n     */\n    public static void pop(@NonNull final FragmentManager fm,\n                           final boolean isImmediate) {\n        if (isImmediate) {\n            fm.popBackStackImmediate();\n        } else {\n            fm.popBackStack();\n        }\n    }\n\n    /**\n     * Pop to fragment.\n     *\n     * @param fm            The manager of fragment.\n     * @param popClz        The class of fragment will be popped to.\n     * @param isIncludeSelf True to include the fragment, false otherwise.\n     */\n    public static void popTo(@NonNull final FragmentManager fm,\n                             final Class<? extends Fragment> popClz,\n                             final boolean isIncludeSelf) {\n        popTo(fm, popClz, isIncludeSelf, true);\n    }\n\n    /**\n     * Pop to fragment.\n     *\n     * @param fm            The manager of fragment.\n     * @param popClz        The class of fragment will be popped to.\n     * @param isIncludeSelf True to include the fragment, false otherwise.\n     * @param isImmediate   True to pop immediately, false otherwise.\n     */\n    public static void popTo(@NonNull final FragmentManager fm,\n                             final Class<? extends Fragment> popClz,\n                             final boolean isIncludeSelf,\n                             final boolean isImmediate) {\n        if (isImmediate) {\n            fm.popBackStackImmediate(popClz.getName(),\n                    isIncludeSelf ? FragmentManager.POP_BACK_STACK_INCLUSIVE : 0);\n        } else {\n            fm.popBackStack(popClz.getName(),\n                    isIncludeSelf ? FragmentManager.POP_BACK_STACK_INCLUSIVE : 0);\n        }\n    }\n\n    /**\n     * Pop all fragments.\n     *\n     * @param fm The manager of fragment.\n     */\n    public static void popAll(@NonNull final FragmentManager fm) {\n        popAll(fm, true);\n    }\n\n    /**\n     * Pop all fragments.\n     *\n     * @param fm The manager of fragment.\n     */\n    public static void popAll(@NonNull final FragmentManager fm, final boolean isImmediate) {\n        if (fm.getBackStackEntryCount() > 0) {\n            FragmentManager.BackStackEntry entry = fm.getBackStackEntryAt(0);\n            if (isImmediate) {\n                fm.popBackStackImmediate(entry.getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);\n            } else {\n                fm.popBackStack(entry.getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);\n            }\n        }\n    }\n\n    /**\n     * Remove fragment.\n     *\n     * @param remove The fragment will be removed.\n     */\n    public static void remove(@NonNull final Fragment remove) {\n        operateNoAnim(TYPE_REMOVE_FRAGMENT, remove.getFragmentManager(), null, remove);\n    }\n\n    /**\n     * Remove to fragment.\n     *\n     * @param removeTo      The fragment will be removed to.\n     * @param isIncludeSelf True to include the fragment, false otherwise.\n     */\n    public static void removeTo(@NonNull final Fragment removeTo, final boolean isIncludeSelf) {\n        operateNoAnim(TYPE_REMOVE_TO_FRAGMENT, removeTo.getFragmentManager(), isIncludeSelf ? removeTo : null, removeTo);\n    }\n\n    /**\n     * Remove all fragments.\n     *\n     * @param fm The manager of fragment.\n     */\n    public static void removeAll(@NonNull final FragmentManager fm) {\n        List<Fragment> fragments = getFragments(fm);\n        operateNoAnim(TYPE_REMOVE_FRAGMENT, fm, null, fragments.toArray(new Fragment[0]));\n    }\n\n    private static void putArgs(final Fragment fragment, final Args args) {\n        Bundle bundle = fragment.getArguments();\n        if (bundle == null) {\n            bundle = new Bundle();\n            fragment.setArguments(bundle);\n        }\n        bundle.putInt(ARGS_ID, args.id);\n        bundle.putBoolean(ARGS_IS_HIDE, args.isHide);\n        bundle.putBoolean(ARGS_IS_ADD_STACK, args.isAddStack);\n        bundle.putString(ARGS_TAG, args.tag);\n    }\n\n    private static void putArgs(final Fragment fragment, final boolean isHide) {\n        Bundle bundle = fragment.getArguments();\n        if (bundle == null) {\n            bundle = new Bundle();\n            fragment.setArguments(bundle);\n        }\n        bundle.putBoolean(ARGS_IS_HIDE, isHide);\n    }\n\n    private static Args getArgs(final Fragment fragment) {\n        Bundle bundle = fragment.getArguments();\n        if (bundle == null) bundle = Bundle.EMPTY;\n        return new Args(bundle.getInt(ARGS_ID, fragment.getId()),\n                bundle.getBoolean(ARGS_IS_HIDE),\n                bundle.getBoolean(ARGS_IS_ADD_STACK));\n    }\n\n    private static void operateNoAnim(final int type, @Nullable final FragmentManager fm,\n                                      final Fragment src,\n                                      Fragment... dest) {\n        if (fm == null) return;\n        FragmentTransaction ft = fm.beginTransaction();\n        operate(type, fm, ft, src, dest);\n    }\n\n    private static void operate(final int type,\n                                @NonNull final FragmentManager fm,\n                                final FragmentTransaction ft,\n                                final Fragment src,\n                                final Fragment... dest) {\n        if (src != null && src.isRemoving()) {\n            Log.e(\"FragmentUtils\", src.getClass().getName() + \" is isRemoving\");\n            return;\n        }\n        String name;\n        Bundle args;\n        switch (type) {\n            case TYPE_ADD_FRAGMENT:\n                for (Fragment fragment : dest) {\n                    args = fragment.getArguments();\n                    if (args == null) return;\n                    name = args.getString(ARGS_TAG, fragment.getClass().getName());\n                    Fragment fragmentByTag = fm.findFragmentByTag(name);\n                    if (fragmentByTag != null && fragmentByTag.isAdded()) {\n                        ft.remove(fragmentByTag);\n                    }\n                    ft.add(args.getInt(ARGS_ID), fragment, name);\n                    if (args.getBoolean(ARGS_IS_HIDE)) ft.hide(fragment);\n                    if (args.getBoolean(ARGS_IS_ADD_STACK)) ft.addToBackStack(name);\n                }\n                break;\n            case TYPE_HIDE_FRAGMENT:\n                for (Fragment fragment : dest) {\n                    ft.hide(fragment);\n                }\n                break;\n            case TYPE_SHOW_FRAGMENT:\n                for (Fragment fragment : dest) {\n                    ft.show(fragment);\n                }\n                break;\n            case TYPE_SHOW_HIDE_FRAGMENT:\n                ft.show(src);\n                for (Fragment fragment : dest) {\n                    if (fragment != src) {\n                        ft.hide(fragment);\n                    }\n                }\n                break;\n            case TYPE_REPLACE_FRAGMENT:\n                args = dest[0].getArguments();\n                if (args == null) return;\n                name = args.getString(ARGS_TAG, dest[0].getClass().getName());\n                ft.replace(args.getInt(ARGS_ID), dest[0], name);\n                if (args.getBoolean(ARGS_IS_ADD_STACK)) ft.addToBackStack(name);\n                break;\n            case TYPE_REMOVE_FRAGMENT:\n                for (Fragment fragment : dest) {\n                    if (fragment != src) {\n                        ft.remove(fragment);\n                    }\n                }\n                break;\n            case TYPE_REMOVE_TO_FRAGMENT:\n                for (int i = dest.length - 1; i >= 0; --i) {\n                    Fragment fragment = dest[i];\n                    if (fragment == dest[0]) {\n                        if (src != null) ft.remove(fragment);\n                        break;\n                    }\n                    ft.remove(fragment);\n                }\n                break;\n        }\n        ft.commitAllowingStateLoss();\n        fm.executePendingTransactions();\n    }\n\n    private static void addAnim(final FragmentTransaction ft,\n                                final int enter,\n                                final int exit,\n                                final int popEnter,\n                                final int popExit) {\n        ft.setCustomAnimations(enter, exit, popEnter, popExit);\n    }\n\n    private static void addSharedElement(final FragmentTransaction ft,\n                                         final View... views) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            for (View view : views) {\n                ft.addSharedElement(view, view.getTransitionName());\n            }\n        }\n    }\n\n    /**\n     * Return the top fragment.\n     *\n     * @param fm The manager of fragment.\n     * @return the top fragment\n     */\n    public static Fragment getTop(@NonNull final FragmentManager fm) {\n        return getTopIsInStack(fm, null, false);\n    }\n\n    /**\n     * Return the top fragment in stack.\n     *\n     * @param fm The manager of fragment.\n     * @return the top fragment in stack\n     */\n    public static Fragment getTopInStack(@NonNull final FragmentManager fm) {\n        return getTopIsInStack(fm, null, true);\n    }\n\n    private static Fragment getTopIsInStack(@NonNull final FragmentManager fm,\n                                            Fragment parentFragment,\n                                            final boolean isInStack) {\n        List<Fragment> fragments = getFragments(fm);\n        for (int i = fragments.size() - 1; i >= 0; --i) {\n            Fragment fragment = fragments.get(i);\n            if (fragment != null) {\n                if (isInStack) {\n                    Bundle args = fragment.getArguments();\n                    if (args != null && args.getBoolean(ARGS_IS_ADD_STACK)) {\n                        return getTopIsInStack(fragment.getChildFragmentManager(), fragment, true);\n                    }\n                } else {\n                    return getTopIsInStack(fragment.getChildFragmentManager(), fragment, false);\n                }\n            }\n        }\n        return parentFragment;\n    }\n\n    /**\n     * Return the top fragment which is shown.\n     *\n     * @param fm The manager of fragment.\n     * @return the top fragment which is shown\n     */\n    public static Fragment getTopShow(@NonNull final FragmentManager fm) {\n        return getTopShowIsInStack(fm, null, false);\n    }\n\n    /**\n     * Return the top fragment which is shown in stack.\n     *\n     * @param fm The manager of fragment.\n     * @return the top fragment which is shown in stack\n     */\n    public static Fragment getTopShowInStack(@NonNull final FragmentManager fm) {\n        return getTopShowIsInStack(fm, null, true);\n    }\n\n    private static Fragment getTopShowIsInStack(@NonNull final FragmentManager fm,\n                                                Fragment parentFragment,\n                                                final boolean isInStack) {\n        List<Fragment> fragments = getFragments(fm);\n        for (int i = fragments.size() - 1; i >= 0; --i) {\n            Fragment fragment = fragments.get(i);\n            if (fragment != null\n                    && fragment.isResumed()\n                    && fragment.isVisible()\n                    && fragment.getUserVisibleHint()) {\n                if (isInStack) {\n                    Bundle args = fragment.getArguments();\n                    if (args != null && args.getBoolean(ARGS_IS_ADD_STACK)) {\n                        return getTopShowIsInStack(fragment.getChildFragmentManager(), fragment, true);\n                    }\n                } else {\n                    return getTopShowIsInStack(fragment.getChildFragmentManager(), fragment, false);\n                }\n            }\n        }\n        return parentFragment;\n    }\n\n    /**\n     * Return the fragments in manager.\n     *\n     * @param fm The manager of fragment.\n     * @return the fragments in manager\n     */\n    public static List<Fragment> getFragments(@NonNull final FragmentManager fm) {\n        List<Fragment> fragments = fm.getFragments();\n        if (fragments == null || fragments.isEmpty()) return Collections.emptyList();\n        return fragments;\n    }\n\n    /**\n     * Return the fragments in stack in manager.\n     *\n     * @param fm The manager of fragment.\n     * @return the fragments in stack in manager\n     */\n    public static List<Fragment> getFragmentsInStack(@NonNull final FragmentManager fm) {\n        List<Fragment> fragments = getFragments(fm);\n        List<Fragment> result = new ArrayList<>();\n        for (Fragment fragment : fragments) {\n            if (fragment != null) {\n                Bundle args = fragment.getArguments();\n                if (args != null && args.getBoolean(ARGS_IS_ADD_STACK)) {\n                    result.add(fragment);\n                }\n            }\n        }\n        return result;\n    }\n\n    /**\n     * Return all fragments in manager.\n     *\n     * @param fm The manager of fragment.\n     * @return all fragments in manager\n     */\n    public static List<FragmentNode> getAllFragments(@NonNull final FragmentManager fm) {\n        return getAllFragments(fm, new ArrayList<FragmentNode>());\n    }\n\n    private static List<FragmentNode> getAllFragments(@NonNull final FragmentManager fm,\n                                                      final List<FragmentNode> result) {\n        List<Fragment> fragments = getFragments(fm);\n        for (int i = fragments.size() - 1; i >= 0; --i) {\n            Fragment fragment = fragments.get(i);\n            if (fragment != null) {\n                result.add(new FragmentNode(fragment,\n                        getAllFragments(fragment.getChildFragmentManager(),\n                                new ArrayList<FragmentNode>())));\n            }\n        }\n        return result;\n    }\n\n    /**\n     * Return all fragments in stack in manager.\n     *\n     * @param fm The manager of fragment.\n     * @return all fragments in stack in manager\n     */\n    public static List<FragmentNode> getAllFragmentsInStack(@NonNull final FragmentManager fm) {\n        return getAllFragmentsInStack(fm, new ArrayList<FragmentNode>());\n    }\n\n    private static List<FragmentNode> getAllFragmentsInStack(@NonNull final FragmentManager fm,\n                                                             final List<FragmentNode> result) {\n        List<Fragment> fragments = getFragments(fm);\n        for (int i = fragments.size() - 1; i >= 0; --i) {\n            Fragment fragment = fragments.get(i);\n            if (fragment != null) {\n                Bundle args = fragment.getArguments();\n                if (args != null && args.getBoolean(ARGS_IS_ADD_STACK)) {\n                    result.add(new FragmentNode(fragment,\n                            getAllFragmentsInStack(fragment.getChildFragmentManager(),\n                                    new ArrayList<FragmentNode>())));\n                }\n            }\n        }\n        return result;\n    }\n\n    /**\n     * Find fragment.\n     *\n     * @param fm      The manager of fragment.\n     * @param findClz The class of fragment will be found.\n     * @return the fragment matches class\n     */\n    public static Fragment findFragment(@NonNull final FragmentManager fm,\n                                        final Class<? extends Fragment> findClz) {\n        return fm.findFragmentByTag(findClz.getName());\n    }\n\n    /**\n     * Find fragment.\n     *\n     * @param fm  The manager of fragment.\n     * @param tag The tag of fragment will be found.\n     * @return the fragment matches class\n     */\n    public static Fragment findFragment(@NonNull final FragmentManager fm,\n                                        @NonNull final String tag) {\n        return fm.findFragmentByTag(tag);\n    }\n\n    /**\n     * Dispatch the back press for fragment.\n     *\n     * @param fragment The fragment.\n     * @return {@code true}: the fragment consumes the back press<br>{@code false}: otherwise\n     */\n    public static boolean dispatchBackPress(@NonNull final Fragment fragment) {\n        return fragment.isResumed()\n                && fragment.isVisible()\n                && fragment.getUserVisibleHint()\n                && fragment instanceof OnBackClickListener\n                && ((OnBackClickListener) fragment).onBackClick();\n    }\n\n    /**\n     * Dispatch the back press for fragment.\n     *\n     * @param fm The manager of fragment.\n     * @return {@code true}: the fragment consumes the back press<br>{@code false}: otherwise\n     */\n    public static boolean dispatchBackPress(@NonNull final FragmentManager fm) {\n        List<Fragment> fragments = getFragments(fm);\n        if (fragments == null || fragments.isEmpty()) return false;\n        for (int i = fragments.size() - 1; i >= 0; --i) {\n            Fragment fragment = fragments.get(i);\n            if (fragment != null\n                    && fragment.isResumed()\n                    && fragment.isVisible()\n                    && fragment.getUserVisibleHint()\n                    && fragment instanceof OnBackClickListener\n                    && ((OnBackClickListener) fragment).onBackClick()) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Set background color for fragment.\n     *\n     * @param fragment The fragment.\n     * @param color    The background color.\n     */\n    public static void setBackgroundColor(@NonNull final Fragment fragment,\n                                          @ColorInt final int color) {\n        View view = fragment.getView();\n        if (view != null) {\n            view.setBackgroundColor(color);\n        }\n    }\n\n    /**\n     * Set background resource for fragment.\n     *\n     * @param fragment The fragment.\n     * @param resId    The resource id.\n     */\n    public static void setBackgroundResource(@NonNull final Fragment fragment,\n                                             @DrawableRes final int resId) {\n        View view = fragment.getView();\n        if (view != null) {\n            view.setBackgroundResource(resId);\n        }\n    }\n\n    /**\n     * Set background color for fragment.\n     *\n     * @param fragment   The fragment.\n     * @param background The background.\n     */\n    public static void setBackground(@NonNull final Fragment fragment, final Drawable background) {\n        View view = fragment.getView();\n        if (view == null) return;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n            view.setBackground(background);\n        } else {\n            view.setBackgroundDrawable(background);\n        }\n    }\n\n    /**\n     * Return the simple name of fragment.\n     *\n     * @param fragment The fragment.\n     * @return the simple name of fragment\n     */\n    public static String getSimpleName(final Fragment fragment) {\n        return fragment == null ? \"null\" : fragment.getClass().getSimpleName();\n    }\n\n    private static class Args {\n        final int     id;\n        final boolean isHide;\n        final boolean isAddStack;\n        final String  tag;\n\n        Args(final int id, final boolean isHide, final boolean isAddStack) {\n            this(id, null, isHide, isAddStack);\n        }\n\n        Args(final int id, final String tag,\n             final boolean isHide, final boolean isAddStack) {\n            this.id = id;\n            this.tag = tag;\n            this.isHide = isHide;\n            this.isAddStack = isAddStack;\n        }\n    }\n\n    public static class FragmentNode {\n        final Fragment           fragment;\n        final List<FragmentNode> next;\n\n        public FragmentNode(final Fragment fragment, final List<FragmentNode> next) {\n            this.fragment = fragment;\n            this.next = next;\n        }\n\n        public Fragment getFragment() {\n            return fragment;\n        }\n\n        public List<FragmentNode> getNext() {\n            return next;\n        }\n\n        @Override\n        public String toString() {\n            return fragment.getClass().getSimpleName()\n                    + \"->\"\n                    + ((next == null || next.isEmpty()) ? \"no child\" : next.toString());\n        }\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // interface\n    ///////////////////////////////////////////////////////////////////////////\n\n    public interface OnBackClickListener {\n        boolean onBackClick();\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/GsonUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.text.TextUtils;\n\nimport com.google.gson.Gson;\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.reflect.TypeToken;\n\nimport java.io.Reader;\nimport java.lang.reflect.Type;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport androidx.annotation.NonNull;\n\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/04/05\n *     desc  : utils about gson\n * </pre>\n */\npublic final class GsonUtils {\n\n    private static final String KEY_DEFAULT   = \"defaultGson\";\n    private static final String KEY_DELEGATE  = \"delegateGson\";\n    private static final String KEY_LOG_UTILS = \"logUtilsGson\";\n\n    private static final Map<String, Gson> GSONS = new ConcurrentHashMap<>();\n\n    private GsonUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Set the delegate of {@link Gson}.\n     *\n     * @param delegate The delegate of {@link Gson}.\n     */\n    public static void setGsonDelegate(Gson delegate) {\n        if (delegate == null) return;\n        GSONS.put(KEY_DELEGATE, delegate);\n    }\n\n    /**\n     * Set the {@link Gson} with key.\n     *\n     * @param key  The key.\n     * @param gson The {@link Gson}.\n     */\n    public static void setGson(final String key, final Gson gson) {\n        if (TextUtils.isEmpty(key) || gson == null) return;\n        GSONS.put(key, gson);\n    }\n\n    /**\n     * Return the {@link Gson} with key.\n     *\n     * @param key The key.\n     * @return the {@link Gson} with key\n     */\n    public static Gson getGson(final String key) {\n        return GSONS.get(key);\n    }\n\n    public static Gson getGson() {\n        Gson gsonDelegate = GSONS.get(KEY_DELEGATE);\n        if (gsonDelegate != null) {\n            return gsonDelegate;\n        }\n        Gson gsonDefault = GSONS.get(KEY_DEFAULT);\n        if (gsonDefault == null) {\n            gsonDefault = createGson();\n            GSONS.put(KEY_DEFAULT, gsonDefault);\n        }\n        return gsonDefault;\n    }\n\n    /**\n     * Serializes an object into json.\n     *\n     * @param object The object to serialize.\n     * @return object serialized into json.\n     */\n    public static String toJson(final Object object) {\n        return toJson(getGson(), object);\n    }\n\n    /**\n     * Serializes an object into json.\n     *\n     * @param src       The object to serialize.\n     * @param typeOfSrc The specific genericized type of src.\n     * @return object serialized into json.\n     */\n    public static String toJson(final Object src, @NonNull final Type typeOfSrc) {\n        return toJson(getGson(), src, typeOfSrc);\n    }\n\n    /**\n     * Serializes an object into json.\n     *\n     * @param gson   The gson.\n     * @param object The object to serialize.\n     * @return object serialized into json.\n     */\n    public static String toJson(@NonNull final Gson gson, final Object object) {\n        return gson.toJson(object);\n    }\n\n    /**\n     * Serializes an object into json.\n     *\n     * @param gson      The gson.\n     * @param src       The object to serialize.\n     * @param typeOfSrc The specific genericized type of src.\n     * @return object serialized into json.\n     */\n    public static String toJson(@NonNull final Gson gson, final Object src, @NonNull final Type typeOfSrc) {\n        return gson.toJson(src, typeOfSrc);\n    }\n\n    /**\n     * Converts {@link String} to given type.\n     *\n     * @param json The json to convert.\n     * @param type Type json will be converted to.\n     * @return instance of type\n     */\n    public static <T> T fromJson(final String json, @NonNull final Class<T> type) {\n        return fromJson(getGson(), json, type);\n    }\n\n    /**\n     * Converts {@link String} to given type.\n     *\n     * @param json the json to convert.\n     * @param type type type json will be converted to.\n     * @return instance of type\n     */\n    public static <T> T fromJson(final String json, @NonNull final Type type) {\n        return fromJson(getGson(), json, type);\n    }\n\n    /**\n     * Converts {@link Reader} to given type.\n     *\n     * @param reader the reader to convert.\n     * @param type   type type json will be converted to.\n     * @return instance of type\n     */\n    public static <T> T fromJson(@NonNull final Reader reader, @NonNull final Class<T> type) {\n        return fromJson(getGson(), reader, type);\n    }\n\n    /**\n     * Converts {@link Reader} to given type.\n     *\n     * @param reader the reader to convert.\n     * @param type   type type json will be converted to.\n     * @return instance of type\n     */\n    public static <T> T fromJson(@NonNull final Reader reader, @NonNull final Type type) {\n        return fromJson(getGson(), reader, type);\n    }\n\n    /**\n     * Converts {@link String} to given type.\n     *\n     * @param gson The gson.\n     * @param json The json to convert.\n     * @param type Type json will be converted to.\n     * @return instance of type\n     */\n    public static <T> T fromJson(@NonNull final Gson gson, final String json, @NonNull final Class<T> type) {\n        return gson.fromJson(json, type);\n    }\n\n    /**\n     * Converts {@link String} to given type.\n     *\n     * @param gson The gson.\n     * @param json the json to convert.\n     * @param type type type json will be converted to.\n     * @return instance of type\n     */\n    public static <T> T fromJson(@NonNull final Gson gson, final String json, @NonNull final Type type) {\n        return gson.fromJson(json, type);\n    }\n\n    /**\n     * Converts {@link Reader} to given type.\n     *\n     * @param gson   The gson.\n     * @param reader the reader to convert.\n     * @param type   type type json will be converted to.\n     * @return instance of type\n     */\n    public static <T> T fromJson(@NonNull final Gson gson, final Reader reader, @NonNull final Class<T> type) {\n        return gson.fromJson(reader, type);\n    }\n\n    /**\n     * Converts {@link Reader} to given type.\n     *\n     * @param gson   The gson.\n     * @param reader the reader to convert.\n     * @param type   type type json will be converted to.\n     * @return instance of type\n     */\n    public static <T> T fromJson(@NonNull final Gson gson, final Reader reader, @NonNull final Type type) {\n        return gson.fromJson(reader, type);\n    }\n\n    /**\n     * Return the type of {@link List} with the {@code type}.\n     *\n     * @param type The type.\n     * @return the type of {@link List} with the {@code type}\n     */\n    public static Type getListType(@NonNull final Type type) {\n        return TypeToken.getParameterized(List.class, type).getType();\n    }\n\n    /**\n     * Return the type of {@link Set} with the {@code type}.\n     *\n     * @param type The type.\n     * @return the type of {@link Set} with the {@code type}\n     */\n    public static Type getSetType(@NonNull final Type type) {\n        return TypeToken.getParameterized(Set.class, type).getType();\n    }\n\n    /**\n     * Return the type of map with the {@code keyType} and {@code valueType}.\n     *\n     * @param keyType   The type of key.\n     * @param valueType The type of value.\n     * @return the type of map with the {@code keyType} and {@code valueType}\n     */\n    public static Type getMapType(@NonNull final Type keyType, @NonNull final Type valueType) {\n        return TypeToken.getParameterized(Map.class, keyType, valueType).getType();\n    }\n\n    /**\n     * Return the type of array with the {@code type}.\n     *\n     * @param type The type.\n     * @return the type of map with the {@code type}\n     */\n    public static Type getArrayType(@NonNull final Type type) {\n        return TypeToken.getArray(type).getType();\n    }\n\n    /**\n     * Return the type of {@code rawType} with the {@code typeArguments}.\n     *\n     * @param rawType       The raw type.\n     * @param typeArguments The type of arguments.\n     * @return the type of map with the {@code type}\n     */\n    public static Type getType(@NonNull final Type rawType, @NonNull final Type... typeArguments) {\n        return TypeToken.getParameterized(rawType, typeArguments).getType();\n    }\n\n    static Gson getGson4LogUtils() {\n        Gson gson4LogUtils = GSONS.get(KEY_LOG_UTILS);\n        if (gson4LogUtils == null) {\n            gson4LogUtils = new GsonBuilder().setPrettyPrinting().serializeNulls().create();\n            GSONS.put(KEY_LOG_UTILS, gson4LogUtils);\n        }\n        return gson4LogUtils;\n    }\n\n    private static Gson createGson() {\n        return new GsonBuilder().serializeNulls().disableHtmlEscaping().create();\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ImageUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.Manifest;\nimport android.content.ContentValues;\nimport android.content.res.Resources;\nimport android.graphics.Bitmap;\nimport android.graphics.Bitmap.CompressFormat;\nimport android.graphics.BitmapFactory;\nimport android.graphics.BitmapShader;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.ColorMatrix;\nimport android.graphics.ColorMatrixColorFilter;\nimport android.graphics.LinearGradient;\nimport android.graphics.Matrix;\nimport android.graphics.Paint;\nimport android.graphics.Path;\nimport android.graphics.PixelFormat;\nimport android.graphics.PorterDuff;\nimport android.graphics.PorterDuffColorFilter;\nimport android.graphics.PorterDuffXfermode;\nimport android.graphics.Rect;\nimport android.graphics.RectF;\nimport android.graphics.Shader;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.media.ExifInterface;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Environment;\nimport android.provider.MediaStore;\nimport android.renderscript.Allocation;\nimport android.renderscript.Element;\nimport android.renderscript.RenderScript;\nimport android.renderscript.ScriptIntrinsicBlur;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.view.View;\n\nimport java.io.BufferedOutputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.FileDescriptor;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.DrawableRes;\nimport androidx.annotation.FloatRange;\nimport androidx.annotation.IntRange;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.RequiresApi;\nimport androidx.core.content.ContextCompat;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/12\n *     desc  : utils about image\n * </pre>\n */\npublic final class ImageUtils {\n\n    private ImageUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Bitmap to bytes.\n     *\n     * @param bitmap The bitmap.\n     * @return bytes\n     */\n    public static byte[] bitmap2Bytes(final Bitmap bitmap) {\n        return bitmap2Bytes(bitmap, CompressFormat.PNG, 100);\n    }\n\n    /**\n     * Bitmap to bytes.\n     *\n     * @param bitmap  The bitmap.\n     * @param format  The format of bitmap.\n     * @param quality The quality.\n     * @return bytes\n     */\n    public static byte[] bitmap2Bytes(@Nullable final Bitmap bitmap, @NonNull final CompressFormat format, int quality) {\n        if (bitmap == null) return null;\n        ByteArrayOutputStream baos = new ByteArrayOutputStream();\n        bitmap.compress(format, quality, baos);\n        return baos.toByteArray();\n    }\n\n    /**\n     * Bytes to bitmap.\n     *\n     * @param bytes The bytes.\n     * @return bitmap\n     */\n    public static Bitmap bytes2Bitmap(@Nullable final byte[] bytes) {\n        return (bytes == null || bytes.length == 0)\n                ? null\n                : BitmapFactory.decodeByteArray(bytes, 0, bytes.length);\n    }\n\n    /**\n     * Drawable to bitmap.\n     *\n     * @param drawable The drawable.\n     * @return bitmap\n     */\n    public static Bitmap drawable2Bitmap(@Nullable final Drawable drawable) {\n        if (drawable == null) return null;\n        if (drawable instanceof BitmapDrawable) {\n            BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;\n            if (bitmapDrawable.getBitmap() != null) {\n                return bitmapDrawable.getBitmap();\n            }\n        }\n        Bitmap bitmap;\n        if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {\n            bitmap = Bitmap.createBitmap(1, 1,\n                    drawable.getOpacity() != PixelFormat.OPAQUE\n                            ? Bitmap.Config.ARGB_8888\n                            : Bitmap.Config.RGB_565);\n        } else {\n            bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),\n                    drawable.getIntrinsicHeight(),\n                    drawable.getOpacity() != PixelFormat.OPAQUE\n                            ? Bitmap.Config.ARGB_8888\n                            : Bitmap.Config.RGB_565);\n        }\n        Canvas canvas = new Canvas(bitmap);\n        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());\n        drawable.draw(canvas);\n        return bitmap;\n    }\n\n    /**\n     * Bitmap to drawable.\n     *\n     * @param bitmap The bitmap.\n     * @return drawable\n     */\n    public static Drawable bitmap2Drawable(@Nullable final Bitmap bitmap) {\n        return bitmap == null ? null : new BitmapDrawable(Utils.getApp().getResources(), bitmap);\n    }\n\n    /**\n     * Drawable to bytes.\n     *\n     * @param drawable The drawable.\n     * @return bytes\n     */\n    public static byte[] drawable2Bytes(@Nullable final Drawable drawable) {\n        return drawable == null ? null : bitmap2Bytes(drawable2Bitmap(drawable));\n    }\n\n    /**\n     * Drawable to bytes.\n     *\n     * @param drawable The drawable.\n     * @param format   The format of bitmap.\n     * @return bytes\n     */\n    public static byte[] drawable2Bytes(final Drawable drawable, final CompressFormat format, int quality) {\n        return drawable == null ? null : bitmap2Bytes(drawable2Bitmap(drawable), format, quality);\n    }\n\n    /**\n     * Bytes to drawable.\n     *\n     * @param bytes The bytes.\n     * @return drawable\n     */\n    public static Drawable bytes2Drawable(final byte[] bytes) {\n        return bitmap2Drawable(bytes2Bitmap(bytes));\n    }\n\n    /**\n     * View to bitmap.\n     *\n     * @param view The view.\n     * @return bitmap\n     */\n    public static Bitmap view2Bitmap(final View view) {\n        if (view == null) return null;\n        boolean drawingCacheEnabled = view.isDrawingCacheEnabled();\n        boolean willNotCacheDrawing = view.willNotCacheDrawing();\n        view.setDrawingCacheEnabled(true);\n        view.setWillNotCacheDrawing(false);\n        Bitmap drawingCache = view.getDrawingCache();\n        Bitmap bitmap;\n        if (null == drawingCache || drawingCache.isRecycled()) {\n            view.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),\n                    View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));\n            view.layout(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());\n            view.buildDrawingCache();\n            drawingCache = view.getDrawingCache();\n            if (null == drawingCache || drawingCache.isRecycled()) {\n                bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.RGB_565);\n                Canvas canvas = new Canvas(bitmap);\n                view.draw(canvas);\n            } else {\n                bitmap = Bitmap.createBitmap(drawingCache);\n            }\n        } else {\n            bitmap = Bitmap.createBitmap(drawingCache);\n        }\n        view.setWillNotCacheDrawing(willNotCacheDrawing);\n        view.setDrawingCacheEnabled(drawingCacheEnabled);\n        return bitmap;\n    }\n\n    /**\n     * Return bitmap.\n     *\n     * @param file The file.\n     * @return bitmap\n     */\n    public static Bitmap getBitmap(final File file) {\n        if (file == null) return null;\n        return BitmapFactory.decodeFile(file.getAbsolutePath());\n    }\n\n    /**\n     * Return bitmap.\n     *\n     * @param file      The file.\n     * @param maxWidth  The maximum width.\n     * @param maxHeight The maximum height.\n     * @return bitmap\n     */\n    public static Bitmap getBitmap(final File file, final int maxWidth, final int maxHeight) {\n        if (file == null) return null;\n        BitmapFactory.Options options = new BitmapFactory.Options();\n        options.inJustDecodeBounds = true;\n        BitmapFactory.decodeFile(file.getAbsolutePath(), options);\n        options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);\n        options.inJustDecodeBounds = false;\n        return BitmapFactory.decodeFile(file.getAbsolutePath(), options);\n    }\n\n    /**\n     * Return bitmap.\n     *\n     * @param filePath The path of file.\n     * @return bitmap\n     */\n    public static Bitmap getBitmap(final String filePath) {\n        if (UtilsBridge.isSpace(filePath)) return null;\n        return BitmapFactory.decodeFile(filePath);\n    }\n\n    /**\n     * Return bitmap.\n     *\n     * @param filePath  The path of file.\n     * @param maxWidth  The maximum width.\n     * @param maxHeight The maximum height.\n     * @return bitmap\n     */\n    public static Bitmap getBitmap(final String filePath, final int maxWidth, final int maxHeight) {\n        if (UtilsBridge.isSpace(filePath)) return null;\n        BitmapFactory.Options options = new BitmapFactory.Options();\n        options.inJustDecodeBounds = true;\n        BitmapFactory.decodeFile(filePath, options);\n        options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);\n        options.inJustDecodeBounds = false;\n        return BitmapFactory.decodeFile(filePath, options);\n    }\n\n    /**\n     * Return bitmap.\n     *\n     * @param is The input stream.\n     * @return bitmap\n     */\n    public static Bitmap getBitmap(final InputStream is) {\n        if (is == null) return null;\n        return BitmapFactory.decodeStream(is);\n    }\n\n    /**\n     * Return bitmap.\n     *\n     * @param is        The input stream.\n     * @param maxWidth  The maximum width.\n     * @param maxHeight The maximum height.\n     * @return bitmap\n     */\n    public static Bitmap getBitmap(final InputStream is, final int maxWidth, final int maxHeight) {\n        if (is == null) return null;\n        BitmapFactory.Options options = new BitmapFactory.Options();\n        options.inJustDecodeBounds = true;\n        BitmapFactory.decodeStream(is, null, options);\n        options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);\n        options.inJustDecodeBounds = false;\n        return BitmapFactory.decodeStream(is, null, options);\n    }\n\n    /**\n     * Return bitmap.\n     *\n     * @param data   The data.\n     * @param offset The offset.\n     * @return bitmap\n     */\n    public static Bitmap getBitmap(final byte[] data, final int offset) {\n        if (data.length == 0) return null;\n        return BitmapFactory.decodeByteArray(data, offset, data.length);\n    }\n\n    /**\n     * Return bitmap.\n     *\n     * @param data      The data.\n     * @param offset    The offset.\n     * @param maxWidth  The maximum width.\n     * @param maxHeight The maximum height.\n     * @return bitmap\n     */\n    public static Bitmap getBitmap(final byte[] data,\n                                   final int offset,\n                                   final int maxWidth,\n                                   final int maxHeight) {\n        if (data.length == 0) return null;\n        BitmapFactory.Options options = new BitmapFactory.Options();\n        options.inJustDecodeBounds = true;\n        BitmapFactory.decodeByteArray(data, offset, data.length, options);\n        options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);\n        options.inJustDecodeBounds = false;\n        return BitmapFactory.decodeByteArray(data, offset, data.length, options);\n    }\n\n    /**\n     * Return bitmap.\n     *\n     * @param resId The resource id.\n     * @return bitmap\n     */\n    public static Bitmap getBitmap(@DrawableRes final int resId) {\n        Drawable drawable = ContextCompat.getDrawable(Utils.getApp(), resId);\n        if (drawable == null) {\n            return null;\n        }\n        Canvas canvas = new Canvas();\n        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),\n                drawable.getIntrinsicHeight(),\n                Bitmap.Config.ARGB_8888);\n        canvas.setBitmap(bitmap);\n        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());\n        drawable.draw(canvas);\n        return bitmap;\n    }\n\n    /**\n     * Return bitmap.\n     *\n     * @param resId     The resource id.\n     * @param maxWidth  The maximum width.\n     * @param maxHeight The maximum height.\n     * @return bitmap\n     */\n    public static Bitmap getBitmap(@DrawableRes final int resId,\n                                   final int maxWidth,\n                                   final int maxHeight) {\n        BitmapFactory.Options options = new BitmapFactory.Options();\n        final Resources resources = Utils.getApp().getResources();\n        options.inJustDecodeBounds = true;\n        BitmapFactory.decodeResource(resources, resId, options);\n        options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);\n        options.inJustDecodeBounds = false;\n        return BitmapFactory.decodeResource(resources, resId, options);\n    }\n\n    /**\n     * Return bitmap.\n     *\n     * @param fd The file descriptor.\n     * @return bitmap\n     */\n    public static Bitmap getBitmap(final FileDescriptor fd) {\n        if (fd == null) return null;\n        return BitmapFactory.decodeFileDescriptor(fd);\n    }\n\n    /**\n     * Return bitmap.\n     *\n     * @param fd        The file descriptor\n     * @param maxWidth  The maximum width.\n     * @param maxHeight The maximum height.\n     * @return bitmap\n     */\n    public static Bitmap getBitmap(final FileDescriptor fd,\n                                   final int maxWidth,\n                                   final int maxHeight) {\n        if (fd == null) return null;\n        BitmapFactory.Options options = new BitmapFactory.Options();\n        options.inJustDecodeBounds = true;\n        BitmapFactory.decodeFileDescriptor(fd, null, options);\n        options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);\n        options.inJustDecodeBounds = false;\n        return BitmapFactory.decodeFileDescriptor(fd, null, options);\n    }\n\n    /**\n     * Return the bitmap with the specified color.\n     *\n     * @param src   The source of bitmap.\n     * @param color The color.\n     * @return the bitmap with the specified color\n     */\n    public static Bitmap drawColor(@NonNull final Bitmap src, @ColorInt final int color) {\n        return drawColor(src, color, false);\n    }\n\n    /**\n     * Return the bitmap with the specified color.\n     *\n     * @param src     The source of bitmap.\n     * @param color   The color.\n     * @param recycle True to recycle the source of bitmap, false otherwise.\n     * @return the bitmap with the specified color\n     */\n    public static Bitmap drawColor(@NonNull final Bitmap src,\n                                   @ColorInt final int color,\n                                   final boolean recycle) {\n        if (isEmptyBitmap(src)) return null;\n        Bitmap ret = recycle ? src : src.copy(src.getConfig(), true);\n        Canvas canvas = new Canvas(ret);\n        canvas.drawColor(color, PorterDuff.Mode.DARKEN);\n        return ret;\n    }\n\n    /**\n     * Return the scaled bitmap.\n     *\n     * @param src       The source of bitmap.\n     * @param newWidth  The new width.\n     * @param newHeight The new height.\n     * @return the scaled bitmap\n     */\n    public static Bitmap scale(final Bitmap src, final int newWidth, final int newHeight) {\n        return scale(src, newWidth, newHeight, false);\n    }\n\n    /**\n     * Return the scaled bitmap.\n     *\n     * @param src       The source of bitmap.\n     * @param newWidth  The new width.\n     * @param newHeight The new height.\n     * @param recycle   True to recycle the source of bitmap, false otherwise.\n     * @return the scaled bitmap\n     */\n    public static Bitmap scale(final Bitmap src,\n                               final int newWidth,\n                               final int newHeight,\n                               final boolean recycle) {\n        if (isEmptyBitmap(src)) return null;\n        Bitmap ret = Bitmap.createScaledBitmap(src, newWidth, newHeight, true);\n        if (recycle && !src.isRecycled() && ret != src) src.recycle();\n        return ret;\n    }\n\n    /**\n     * Return the scaled bitmap\n     *\n     * @param src         The source of bitmap.\n     * @param scaleWidth  The scale of width.\n     * @param scaleHeight The scale of height.\n     * @return the scaled bitmap\n     */\n    public static Bitmap scale(final Bitmap src, final float scaleWidth, final float scaleHeight) {\n        return scale(src, scaleWidth, scaleHeight, false);\n    }\n\n    /**\n     * Return the scaled bitmap\n     *\n     * @param src         The source of bitmap.\n     * @param scaleWidth  The scale of width.\n     * @param scaleHeight The scale of height.\n     * @param recycle     True to recycle the source of bitmap, false otherwise.\n     * @return the scaled bitmap\n     */\n    public static Bitmap scale(final Bitmap src,\n                               final float scaleWidth,\n                               final float scaleHeight,\n                               final boolean recycle) {\n        if (isEmptyBitmap(src)) return null;\n        Matrix matrix = new Matrix();\n        matrix.setScale(scaleWidth, scaleHeight);\n        Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);\n        if (recycle && !src.isRecycled() && ret != src) src.recycle();\n        return ret;\n    }\n\n    /**\n     * Return the clipped bitmap.\n     *\n     * @param src    The source of bitmap.\n     * @param x      The x coordinate of the first pixel.\n     * @param y      The y coordinate of the first pixel.\n     * @param width  The width.\n     * @param height The height.\n     * @return the clipped bitmap\n     */\n    public static Bitmap clip(final Bitmap src,\n                              final int x,\n                              final int y,\n                              final int width,\n                              final int height) {\n        return clip(src, x, y, width, height, false);\n    }\n\n    /**\n     * Return the clipped bitmap.\n     *\n     * @param src     The source of bitmap.\n     * @param x       The x coordinate of the first pixel.\n     * @param y       The y coordinate of the first pixel.\n     * @param width   The width.\n     * @param height  The height.\n     * @param recycle True to recycle the source of bitmap, false otherwise.\n     * @return the clipped bitmap\n     */\n    public static Bitmap clip(final Bitmap src,\n                              final int x,\n                              final int y,\n                              final int width,\n                              final int height,\n                              final boolean recycle) {\n        if (isEmptyBitmap(src)) return null;\n        Bitmap ret = Bitmap.createBitmap(src, x, y, width, height);\n        if (recycle && !src.isRecycled() && ret != src) src.recycle();\n        return ret;\n    }\n\n    /**\n     * Return the skewed bitmap.\n     *\n     * @param src The source of bitmap.\n     * @param kx  The skew factor of x.\n     * @param ky  The skew factor of y.\n     * @return the skewed bitmap\n     */\n    public static Bitmap skew(final Bitmap src, final float kx, final float ky) {\n        return skew(src, kx, ky, 0, 0, false);\n    }\n\n    /**\n     * Return the skewed bitmap.\n     *\n     * @param src     The source of bitmap.\n     * @param kx      The skew factor of x.\n     * @param ky      The skew factor of y.\n     * @param recycle True to recycle the source of bitmap, false otherwise.\n     * @return the skewed bitmap\n     */\n    public static Bitmap skew(final Bitmap src,\n                              final float kx,\n                              final float ky,\n                              final boolean recycle) {\n        return skew(src, kx, ky, 0, 0, recycle);\n    }\n\n    /**\n     * Return the skewed bitmap.\n     *\n     * @param src The source of bitmap.\n     * @param kx  The skew factor of x.\n     * @param ky  The skew factor of y.\n     * @param px  The x coordinate of the pivot point.\n     * @param py  The y coordinate of the pivot point.\n     * @return the skewed bitmap\n     */\n    public static Bitmap skew(final Bitmap src,\n                              final float kx,\n                              final float ky,\n                              final float px,\n                              final float py) {\n        return skew(src, kx, ky, px, py, false);\n    }\n\n    /**\n     * Return the skewed bitmap.\n     *\n     * @param src     The source of bitmap.\n     * @param kx      The skew factor of x.\n     * @param ky      The skew factor of y.\n     * @param px      The x coordinate of the pivot point.\n     * @param py      The y coordinate of the pivot point.\n     * @param recycle True to recycle the source of bitmap, false otherwise.\n     * @return the skewed bitmap\n     */\n    public static Bitmap skew(final Bitmap src,\n                              final float kx,\n                              final float ky,\n                              final float px,\n                              final float py,\n                              final boolean recycle) {\n        if (isEmptyBitmap(src)) return null;\n        Matrix matrix = new Matrix();\n        matrix.setSkew(kx, ky, px, py);\n        Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);\n        if (recycle && !src.isRecycled() && ret != src) src.recycle();\n        return ret;\n    }\n\n    /**\n     * Return the rotated bitmap.\n     *\n     * @param src     The source of bitmap.\n     * @param degrees The number of degrees.\n     * @param px      The x coordinate of the pivot point.\n     * @param py      The y coordinate of the pivot point.\n     * @return the rotated bitmap\n     */\n    public static Bitmap rotate(final Bitmap src,\n                                final int degrees,\n                                final float px,\n                                final float py) {\n        return rotate(src, degrees, px, py, false);\n    }\n\n    /**\n     * Return the rotated bitmap.\n     *\n     * @param src     The source of bitmap.\n     * @param degrees The number of degrees.\n     * @param px      The x coordinate of the pivot point.\n     * @param py      The y coordinate of the pivot point.\n     * @param recycle True to recycle the source of bitmap, false otherwise.\n     * @return the rotated bitmap\n     */\n    public static Bitmap rotate(final Bitmap src,\n                                final int degrees,\n                                final float px,\n                                final float py,\n                                final boolean recycle) {\n        if (isEmptyBitmap(src)) return null;\n        if (degrees == 0) return src;\n        Matrix matrix = new Matrix();\n        matrix.setRotate(degrees, px, py);\n        Bitmap ret = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);\n        if (recycle && !src.isRecycled() && ret != src) src.recycle();\n        return ret;\n    }\n\n    /**\n     * Return the rotated degree.\n     *\n     * @param filePath The path of file.\n     * @return the rotated degree\n     */\n    public static int getRotateDegree(final String filePath) {\n        try {\n            ExifInterface exifInterface = new ExifInterface(filePath);\n            int orientation = exifInterface.getAttributeInt(\n                    ExifInterface.TAG_ORIENTATION,\n                    ExifInterface.ORIENTATION_NORMAL\n            );\n            switch (orientation) {\n                case ExifInterface.ORIENTATION_ROTATE_90:\n                    return 90;\n                case ExifInterface.ORIENTATION_ROTATE_180:\n                    return 180;\n                case ExifInterface.ORIENTATION_ROTATE_270:\n                    return 270;\n                default:\n                    return 0;\n            }\n        } catch (IOException e) {\n            e.printStackTrace();\n            return -1;\n        }\n    }\n\n    /**\n     * Return the round bitmap.\n     *\n     * @param src The source of bitmap.\n     * @return the round bitmap\n     */\n    public static Bitmap toRound(final Bitmap src) {\n        return toRound(src, 0, 0, false);\n    }\n\n    /**\n     * Return the round bitmap.\n     *\n     * @param src     The source of bitmap.\n     * @param recycle True to recycle the source of bitmap, false otherwise.\n     * @return the round bitmap\n     */\n    public static Bitmap toRound(final Bitmap src, final boolean recycle) {\n        return toRound(src, 0, 0, recycle);\n    }\n\n    /**\n     * Return the round bitmap.\n     *\n     * @param src         The source of bitmap.\n     * @param borderSize  The size of border.\n     * @param borderColor The color of border.\n     * @return the round bitmap\n     */\n    public static Bitmap toRound(final Bitmap src,\n                                 @IntRange(from = 0) int borderSize,\n                                 @ColorInt int borderColor) {\n        return toRound(src, borderSize, borderColor, false);\n    }\n\n    /**\n     * Return the round bitmap.\n     *\n     * @param src         The source of bitmap.\n     * @param recycle     True to recycle the source of bitmap, false otherwise.\n     * @param borderSize  The size of border.\n     * @param borderColor The color of border.\n     * @return the round bitmap\n     */\n    public static Bitmap toRound(final Bitmap src,\n                                 @IntRange(from = 0) int borderSize,\n                                 @ColorInt int borderColor,\n                                 final boolean recycle) {\n        if (isEmptyBitmap(src)) return null;\n        int width = src.getWidth();\n        int height = src.getHeight();\n        int size = Math.min(width, height);\n        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);\n        Bitmap ret = Bitmap.createBitmap(width, height, src.getConfig());\n        float center = size / 2f;\n        RectF rectF = new RectF(0, 0, width, height);\n        rectF.inset((width - size) / 2f, (height - size) / 2f);\n        Matrix matrix = new Matrix();\n        matrix.setTranslate(rectF.left, rectF.top);\n        if (width != height) {\n            matrix.preScale((float) size / width, (float) size / height);\n        }\n        BitmapShader shader = new BitmapShader(src, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);\n        shader.setLocalMatrix(matrix);\n        paint.setShader(shader);\n        Canvas canvas = new Canvas(ret);\n        canvas.drawRoundRect(rectF, center, center, paint);\n        if (borderSize > 0) {\n            paint.setShader(null);\n            paint.setColor(borderColor);\n            paint.setStyle(Paint.Style.STROKE);\n            paint.setStrokeWidth(borderSize);\n            float radius = center - borderSize / 2f;\n            canvas.drawCircle(width / 2f, height / 2f, radius, paint);\n        }\n        if (recycle && !src.isRecycled() && ret != src) src.recycle();\n        return ret;\n    }\n\n    /**\n     * Return the round corner bitmap.\n     *\n     * @param src    The source of bitmap.\n     * @param radius The radius of corner.\n     * @return the round corner bitmap\n     */\n    public static Bitmap toRoundCorner(final Bitmap src, final float radius) {\n        return toRoundCorner(src, radius, 0, 0, false);\n    }\n\n    /**\n     * Return the round corner bitmap.\n     *\n     * @param src     The source of bitmap.\n     * @param radius  The radius of corner.\n     * @param recycle True to recycle the source of bitmap, false otherwise.\n     * @return the round corner bitmap\n     */\n    public static Bitmap toRoundCorner(final Bitmap src,\n                                       final float radius,\n                                       final boolean recycle) {\n        return toRoundCorner(src, radius, 0, 0, recycle);\n    }\n\n    /**\n     * Return the round corner bitmap.\n     *\n     * @param src         The source of bitmap.\n     * @param radius      The radius of corner.\n     * @param borderSize  The size of border.\n     * @param borderColor The color of border.\n     * @return the round corner bitmap\n     */\n    public static Bitmap toRoundCorner(final Bitmap src,\n                                       final float radius,\n                                       @FloatRange(from = 0) float borderSize,\n                                       @ColorInt int borderColor) {\n        return toRoundCorner(src, radius, borderSize, borderColor, false);\n    }\n\n    /**\n     * Return the round corner bitmap.\n     *\n     * @param src         The source of bitmap.\n     * @param radii       Array of 8 values, 4 pairs of [X,Y] radii\n     * @param borderSize  The size of border.\n     * @param borderColor The color of border.\n     * @return the round corner bitmap\n     */\n    public static Bitmap toRoundCorner(final Bitmap src,\n                                       final float[] radii,\n                                       @FloatRange(from = 0) float borderSize,\n                                       @ColorInt int borderColor) {\n        return toRoundCorner(src, radii, borderSize, borderColor, false);\n    }\n\n    /**\n     * Return the round corner bitmap.\n     *\n     * @param src         The source of bitmap.\n     * @param radius      The radius of corner.\n     * @param borderSize  The size of border.\n     * @param borderColor The color of border.\n     * @param recycle     True to recycle the source of bitmap, false otherwise.\n     * @return the round corner bitmap\n     */\n    public static Bitmap toRoundCorner(final Bitmap src,\n                                       final float radius,\n                                       @FloatRange(from = 0) float borderSize,\n                                       @ColorInt int borderColor,\n                                       final boolean recycle) {\n        float[] radii = {radius, radius, radius, radius, radius, radius, radius, radius};\n        return toRoundCorner(src, radii, borderSize, borderColor, recycle);\n    }\n\n    /**\n     * Return the round corner bitmap.\n     *\n     * @param src         The source of bitmap.\n     * @param radii       Array of 8 values, 4 pairs of [X,Y] radii\n     * @param borderSize  The size of border.\n     * @param borderColor The color of border.\n     * @param recycle     True to recycle the source of bitmap, false otherwise.\n     * @return the round corner bitmap\n     */\n    public static Bitmap toRoundCorner(final Bitmap src,\n                                       final float[] radii,\n                                       @FloatRange(from = 0) float borderSize,\n                                       @ColorInt int borderColor,\n                                       final boolean recycle) {\n        if (isEmptyBitmap(src)) return null;\n        int width = src.getWidth();\n        int height = src.getHeight();\n        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);\n        Bitmap ret = Bitmap.createBitmap(width, height, src.getConfig());\n        BitmapShader shader = new BitmapShader(src, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);\n        paint.setShader(shader);\n        Canvas canvas = new Canvas(ret);\n        RectF rectF = new RectF(0, 0, width, height);\n        float halfBorderSize = borderSize / 2f;\n        rectF.inset(halfBorderSize, halfBorderSize);\n        Path path = new Path();\n        path.addRoundRect(rectF, radii, Path.Direction.CW);\n        canvas.drawPath(path, paint);\n        if (borderSize > 0) {\n            paint.setShader(null);\n            paint.setColor(borderColor);\n            paint.setStyle(Paint.Style.STROKE);\n            paint.setStrokeWidth(borderSize);\n            paint.setStrokeCap(Paint.Cap.ROUND);\n            canvas.drawPath(path, paint);\n        }\n        if (recycle && !src.isRecycled() && ret != src) src.recycle();\n        return ret;\n    }\n\n    /**\n     * Return the round corner bitmap with border.\n     *\n     * @param src          The source of bitmap.\n     * @param borderSize   The size of border.\n     * @param color        The color of border.\n     * @param cornerRadius The radius of corner.\n     * @return the round corner bitmap with border\n     */\n    public static Bitmap addCornerBorder(final Bitmap src,\n                                         @FloatRange(from = 1) final float borderSize,\n                                         @ColorInt final int color,\n                                         @FloatRange(from = 0) final float cornerRadius) {\n        return addBorder(src, borderSize, color, false, cornerRadius, false);\n    }\n\n    /**\n     * Return the round corner bitmap with border.\n     *\n     * @param src        The source of bitmap.\n     * @param borderSize The size of border.\n     * @param color      The color of border.\n     * @param radii      Array of 8 values, 4 pairs of [X,Y] radii\n     * @return the round corner bitmap with border\n     */\n    public static Bitmap addCornerBorder(final Bitmap src,\n                                         @FloatRange(from = 1) final float borderSize,\n                                         @ColorInt final int color,\n                                         final float[] radii) {\n        return addBorder(src, borderSize, color, false, radii, false);\n    }\n\n    /**\n     * Return the round corner bitmap with border.\n     *\n     * @param src        The source of bitmap.\n     * @param borderSize The size of border.\n     * @param color      The color of border.\n     * @param radii      Array of 8 values, 4 pairs of [X,Y] radii\n     * @param recycle    True to recycle the source of bitmap, false otherwise.\n     * @return the round corner bitmap with border\n     */\n    public static Bitmap addCornerBorder(final Bitmap src,\n                                         @FloatRange(from = 1) final float borderSize,\n                                         @ColorInt final int color,\n                                         final float[] radii,\n                                         final boolean recycle) {\n        return addBorder(src, borderSize, color, false, radii, recycle);\n    }\n\n    /**\n     * Return the round corner bitmap with border.\n     *\n     * @param src          The source of bitmap.\n     * @param borderSize   The size of border.\n     * @param color        The color of border.\n     * @param cornerRadius The radius of corner.\n     * @param recycle      True to recycle the source of bitmap, false otherwise.\n     * @return the round corner bitmap with border\n     */\n    public static Bitmap addCornerBorder(final Bitmap src,\n                                         @FloatRange(from = 1) final float borderSize,\n                                         @ColorInt final int color,\n                                         @FloatRange(from = 0) final float cornerRadius,\n                                         final boolean recycle) {\n        return addBorder(src, borderSize, color, false, cornerRadius, recycle);\n    }\n\n    /**\n     * Return the round bitmap with border.\n     *\n     * @param src        The source of bitmap.\n     * @param borderSize The size of border.\n     * @param color      The color of border.\n     * @return the round bitmap with border\n     */\n    public static Bitmap addCircleBorder(final Bitmap src,\n                                         @FloatRange(from = 1) final float borderSize,\n                                         @ColorInt final int color) {\n        return addBorder(src, borderSize, color, true, 0, false);\n    }\n\n    /**\n     * Return the round bitmap with border.\n     *\n     * @param src        The source of bitmap.\n     * @param borderSize The size of border.\n     * @param color      The color of border.\n     * @param recycle    True to recycle the source of bitmap, false otherwise.\n     * @return the round bitmap with border\n     */\n    public static Bitmap addCircleBorder(final Bitmap src,\n                                         @FloatRange(from = 1) final float borderSize,\n                                         @ColorInt final int color,\n                                         final boolean recycle) {\n        return addBorder(src, borderSize, color, true, 0, recycle);\n    }\n\n    /**\n     * Return the bitmap with border.\n     *\n     * @param src          The source of bitmap.\n     * @param borderSize   The size of border.\n     * @param color        The color of border.\n     * @param isCircle     True to draw circle, false to draw corner.\n     * @param cornerRadius The radius of corner.\n     * @param recycle      True to recycle the source of bitmap, false otherwise.\n     * @return the bitmap with border\n     */\n    private static Bitmap addBorder(final Bitmap src,\n                                    @FloatRange(from = 1) final float borderSize,\n                                    @ColorInt final int color,\n                                    final boolean isCircle,\n                                    final float cornerRadius,\n                                    final boolean recycle) {\n        float[] radii = {cornerRadius, cornerRadius, cornerRadius, cornerRadius,\n                cornerRadius, cornerRadius, cornerRadius, cornerRadius};\n        return addBorder(src, borderSize, color, isCircle, radii, recycle);\n    }\n\n    /**\n     * Return the bitmap with border.\n     *\n     * @param src        The source of bitmap.\n     * @param borderSize The size of border.\n     * @param color      The color of border.\n     * @param isCircle   True to draw circle, false to draw corner.\n     * @param radii      Array of 8 values, 4 pairs of [X,Y] radii\n     * @param recycle    True to recycle the source of bitmap, false otherwise.\n     * @return the bitmap with border\n     */\n    private static Bitmap addBorder(final Bitmap src,\n                                    @FloatRange(from = 1) final float borderSize,\n                                    @ColorInt final int color,\n                                    final boolean isCircle,\n                                    final float[] radii,\n                                    final boolean recycle) {\n        if (isEmptyBitmap(src)) return null;\n        Bitmap ret = recycle ? src : src.copy(src.getConfig(), true);\n        int width = ret.getWidth();\n        int height = ret.getHeight();\n        Canvas canvas = new Canvas(ret);\n        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);\n        paint.setColor(color);\n        paint.setStyle(Paint.Style.STROKE);\n        paint.setStrokeWidth(borderSize);\n        if (isCircle) {\n            float radius = Math.min(width, height) / 2f - borderSize / 2f;\n            canvas.drawCircle(width / 2f, height / 2f, radius, paint);\n        } else {\n            RectF rectF = new RectF(0, 0, width, height);\n            float halfBorderSize = borderSize / 2f;\n            rectF.inset(halfBorderSize, halfBorderSize);\n            Path path = new Path();\n            path.addRoundRect(rectF, radii, Path.Direction.CW);\n            canvas.drawPath(path, paint);\n        }\n        return ret;\n    }\n\n    /**\n     * Return the bitmap with reflection.\n     *\n     * @param src              The source of bitmap.\n     * @param reflectionHeight The height of reflection.\n     * @return the bitmap with reflection\n     */\n    public static Bitmap addReflection(final Bitmap src, final int reflectionHeight) {\n        return addReflection(src, reflectionHeight, false);\n    }\n\n    /**\n     * Return the bitmap with reflection.\n     *\n     * @param src              The source of bitmap.\n     * @param reflectionHeight The height of reflection.\n     * @param recycle          True to recycle the source of bitmap, false otherwise.\n     * @return the bitmap with reflection\n     */\n    public static Bitmap addReflection(final Bitmap src,\n                                       final int reflectionHeight,\n                                       final boolean recycle) {\n        if (isEmptyBitmap(src)) return null;\n        final int REFLECTION_GAP = 0;\n        int srcWidth = src.getWidth();\n        int srcHeight = src.getHeight();\n        Matrix matrix = new Matrix();\n        matrix.preScale(1, -1);\n        Bitmap reflectionBitmap = Bitmap.createBitmap(src, 0, srcHeight - reflectionHeight,\n                srcWidth, reflectionHeight, matrix, false);\n        Bitmap ret = Bitmap.createBitmap(srcWidth, srcHeight + reflectionHeight, src.getConfig());\n        Canvas canvas = new Canvas(ret);\n        canvas.drawBitmap(src, 0, 0, null);\n        canvas.drawBitmap(reflectionBitmap, 0, srcHeight + REFLECTION_GAP, null);\n        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);\n        LinearGradient shader = new LinearGradient(\n                0, srcHeight,\n                0, ret.getHeight() + REFLECTION_GAP,\n                0x70FFFFFF,\n                0x00FFFFFF,\n                Shader.TileMode.MIRROR);\n        paint.setShader(shader);\n        paint.setXfermode(new PorterDuffXfermode(android.graphics.PorterDuff.Mode.DST_IN));\n        canvas.drawRect(0, srcHeight + REFLECTION_GAP, srcWidth, ret.getHeight(), paint);\n        if (!reflectionBitmap.isRecycled()) reflectionBitmap.recycle();\n        if (recycle && !src.isRecycled() && ret != src) src.recycle();\n        return ret;\n    }\n\n    /**\n     * Return the bitmap with text watermarking.\n     *\n     * @param src      The source of bitmap.\n     * @param content  The content of text.\n     * @param textSize The size of text.\n     * @param color    The color of text.\n     * @param x        The x coordinate of the first pixel.\n     * @param y        The y coordinate of the first pixel.\n     * @return the bitmap with text watermarking\n     */\n    public static Bitmap addTextWatermark(final Bitmap src,\n                                          final String content,\n                                          final int textSize,\n                                          @ColorInt final int color,\n                                          final float x,\n                                          final float y) {\n        return addTextWatermark(src, content, textSize, color, x, y, false);\n    }\n\n    /**\n     * Return the bitmap with text watermarking.\n     *\n     * @param src      The source of bitmap.\n     * @param content  The content of text.\n     * @param textSize The size of text.\n     * @param color    The color of text.\n     * @param x        The x coordinate of the first pixel.\n     * @param y        The y coordinate of the first pixel.\n     * @param recycle  True to recycle the source of bitmap, false otherwise.\n     * @return the bitmap with text watermarking\n     */\n    public static Bitmap addTextWatermark(final Bitmap src,\n                                          final String content,\n                                          final float textSize,\n                                          @ColorInt final int color,\n                                          final float x,\n                                          final float y,\n                                          final boolean recycle) {\n        if (isEmptyBitmap(src) || content == null) return null;\n        Bitmap ret = src.copy(src.getConfig(), true);\n        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);\n        Canvas canvas = new Canvas(ret);\n        paint.setColor(color);\n        paint.setTextSize(textSize);\n        Rect bounds = new Rect();\n        paint.getTextBounds(content, 0, content.length(), bounds);\n        canvas.drawText(content, x, y + textSize, paint);\n        if (recycle && !src.isRecycled() && ret != src) src.recycle();\n        return ret;\n    }\n\n    /**\n     * Return the bitmap with image watermarking.\n     *\n     * @param src       The source of bitmap.\n     * @param watermark The image watermarking.\n     * @param x         The x coordinate of the first pixel.\n     * @param y         The y coordinate of the first pixel.\n     * @param alpha     The alpha of watermark.\n     * @return the bitmap with image watermarking\n     */\n    public static Bitmap addImageWatermark(final Bitmap src,\n                                           final Bitmap watermark,\n                                           final int x, final int y,\n                                           final int alpha) {\n        return addImageWatermark(src, watermark, x, y, alpha, false);\n    }\n\n    /**\n     * Return the bitmap with image watermarking.\n     *\n     * @param src       The source of bitmap.\n     * @param watermark The image watermarking.\n     * @param x         The x coordinate of the first pixel.\n     * @param y         The y coordinate of the first pixel.\n     * @param alpha     The alpha of watermark.\n     * @param recycle   True to recycle the source of bitmap, false otherwise.\n     * @return the bitmap with image watermarking\n     */\n    public static Bitmap addImageWatermark(final Bitmap src,\n                                           final Bitmap watermark,\n                                           final int x,\n                                           final int y,\n                                           final int alpha,\n                                           final boolean recycle) {\n        if (isEmptyBitmap(src)) return null;\n        Bitmap ret = src.copy(src.getConfig(), true);\n        if (!isEmptyBitmap(watermark)) {\n            Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);\n            Canvas canvas = new Canvas(ret);\n            paint.setAlpha(alpha);\n            canvas.drawBitmap(watermark, x, y, paint);\n        }\n        if (recycle && !src.isRecycled() && ret != src) src.recycle();\n        return ret;\n    }\n\n    /**\n     * Return the alpha bitmap.\n     *\n     * @param src The source of bitmap.\n     * @return the alpha bitmap\n     */\n    public static Bitmap toAlpha(final Bitmap src) {\n        return toAlpha(src, false);\n    }\n\n    /**\n     * Return the alpha bitmap.\n     *\n     * @param src     The source of bitmap.\n     * @param recycle True to recycle the source of bitmap, false otherwise.\n     * @return the alpha bitmap\n     */\n    public static Bitmap toAlpha(final Bitmap src, final Boolean recycle) {\n        if (isEmptyBitmap(src)) return null;\n        Bitmap ret = src.extractAlpha();\n        if (recycle && !src.isRecycled() && ret != src) src.recycle();\n        return ret;\n    }\n\n    /**\n     * Return the gray bitmap.\n     *\n     * @param src The source of bitmap.\n     * @return the gray bitmap\n     */\n    public static Bitmap toGray(final Bitmap src) {\n        return toGray(src, false);\n    }\n\n    /**\n     * Return the gray bitmap.\n     *\n     * @param src     The source of bitmap.\n     * @param recycle True to recycle the source of bitmap, false otherwise.\n     * @return the gray bitmap\n     */\n    public static Bitmap toGray(final Bitmap src, final boolean recycle) {\n        if (isEmptyBitmap(src)) return null;\n        Bitmap ret = Bitmap.createBitmap(src.getWidth(), src.getHeight(), src.getConfig());\n        Canvas canvas = new Canvas(ret);\n        Paint paint = new Paint();\n        ColorMatrix colorMatrix = new ColorMatrix();\n        colorMatrix.setSaturation(0);\n        ColorMatrixColorFilter colorMatrixColorFilter = new ColorMatrixColorFilter(colorMatrix);\n        paint.setColorFilter(colorMatrixColorFilter);\n        canvas.drawBitmap(src, 0, 0, paint);\n        if (recycle && !src.isRecycled() && ret != src) src.recycle();\n        return ret;\n    }\n\n    /**\n     * Return the blur bitmap fast.\n     * <p>zoom out, blur, zoom in</p>\n     *\n     * @param src    The source of bitmap.\n     * @param scale  The scale(0...1).\n     * @param radius The radius(0...25).\n     * @return the blur bitmap\n     */\n    public static Bitmap fastBlur(final Bitmap src,\n                                  @FloatRange(\n                                          from = 0, to = 1, fromInclusive = false\n                                  ) final float scale,\n                                  @FloatRange(\n                                          from = 0, to = 25, fromInclusive = false\n                                  ) final float radius) {\n        return fastBlur(src, scale, radius, false, false);\n    }\n\n    /**\n     * Return the blur bitmap fast.\n     * <p>zoom out, blur, zoom in</p>\n     *\n     * @param src    The source of bitmap.\n     * @param scale  The scale(0...1).\n     * @param radius The radius(0...25).\n     * @return the blur bitmap\n     */\n    public static Bitmap fastBlur(final Bitmap src,\n                                  @FloatRange(\n                                          from = 0, to = 1, fromInclusive = false\n                                  ) final float scale,\n                                  @FloatRange(\n                                          from = 0, to = 25, fromInclusive = false\n                                  ) final float radius,\n                                  final boolean recycle) {\n        return fastBlur(src, scale, radius, recycle, false);\n    }\n\n    /**\n     * Return the blur bitmap fast.\n     * <p>zoom out, blur, zoom in</p>\n     *\n     * @param src           The source of bitmap.\n     * @param scale         The scale(0...1).\n     * @param radius        The radius(0...25).\n     * @param recycle       True to recycle the source of bitmap, false otherwise.\n     * @param isReturnScale True to return the scale blur bitmap, false otherwise.\n     * @return the blur bitmap\n     */\n    public static Bitmap fastBlur(final Bitmap src,\n                                  @FloatRange(\n                                          from = 0, to = 1, fromInclusive = false\n                                  ) final float scale,\n                                  @FloatRange(\n                                          from = 0, to = 25, fromInclusive = false\n                                  ) final float radius,\n                                  final boolean recycle,\n                                  final boolean isReturnScale) {\n        if (isEmptyBitmap(src)) return null;\n        int width = src.getWidth();\n        int height = src.getHeight();\n        Matrix matrix = new Matrix();\n        matrix.setScale(scale, scale);\n        Bitmap scaleBitmap =\n                Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);\n        Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);\n        Canvas canvas = new Canvas();\n        PorterDuffColorFilter filter = new PorterDuffColorFilter(\n                Color.TRANSPARENT, PorterDuff.Mode.SRC_ATOP);\n        paint.setColorFilter(filter);\n        canvas.scale(scale, scale);\n        canvas.drawBitmap(scaleBitmap, 0, 0, paint);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            scaleBitmap = renderScriptBlur(scaleBitmap, radius, recycle);\n        } else {\n            scaleBitmap = stackBlur(scaleBitmap, (int) radius, recycle);\n        }\n        if (scale == 1 || isReturnScale) {\n            if (recycle && !src.isRecycled() && scaleBitmap != src) src.recycle();\n            return scaleBitmap;\n        }\n        Bitmap ret = Bitmap.createScaledBitmap(scaleBitmap, width, height, true);\n        if (!scaleBitmap.isRecycled()) scaleBitmap.recycle();\n        if (recycle && !src.isRecycled() && ret != src) src.recycle();\n        return ret;\n    }\n\n    /**\n     * Return the blur bitmap using render script.\n     *\n     * @param src    The source of bitmap.\n     * @param radius The radius(0...25).\n     * @return the blur bitmap\n     */\n    @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\n    public static Bitmap renderScriptBlur(final Bitmap src,\n                                          @FloatRange(\n                                                  from = 0, to = 25, fromInclusive = false\n                                          ) final float radius) {\n        return renderScriptBlur(src, radius, false);\n    }\n\n    /**\n     * Return the blur bitmap using render script.\n     *\n     * @param src     The source of bitmap.\n     * @param radius  The radius(0...25).\n     * @param recycle True to recycle the source of bitmap, false otherwise.\n     * @return the blur bitmap\n     */\n    @RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1)\n    public static Bitmap renderScriptBlur(final Bitmap src,\n                                          @FloatRange(\n                                                  from = 0, to = 25, fromInclusive = false\n                                          ) final float radius,\n                                          final boolean recycle) {\n        RenderScript rs = null;\n        Bitmap ret = recycle ? src : src.copy(src.getConfig(), true);\n        try {\n            rs = RenderScript.create(Utils.getApp());\n            rs.setMessageHandler(new RenderScript.RSMessageHandler());\n            Allocation input = Allocation.createFromBitmap(rs,\n                    ret,\n                    Allocation.MipmapControl.MIPMAP_NONE,\n                    Allocation.USAGE_SCRIPT);\n            Allocation output = Allocation.createTyped(rs, input.getType());\n            ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));\n            blurScript.setInput(input);\n            blurScript.setRadius(radius);\n            blurScript.forEach(output);\n            output.copyTo(ret);\n        } finally {\n            if (rs != null) {\n                rs.destroy();\n            }\n        }\n        return ret;\n    }\n\n    /**\n     * Return the blur bitmap using stack.\n     *\n     * @param src    The source of bitmap.\n     * @param radius The radius(0...25).\n     * @return the blur bitmap\n     */\n    public static Bitmap stackBlur(final Bitmap src, final int radius) {\n        return stackBlur(src, radius, false);\n    }\n\n    /**\n     * Return the blur bitmap using stack.\n     *\n     * @param src     The source of bitmap.\n     * @param radius  The radius(0...25).\n     * @param recycle True to recycle the source of bitmap, false otherwise.\n     * @return the blur bitmap\n     */\n    public static Bitmap stackBlur(final Bitmap src, int radius, final boolean recycle) {\n        Bitmap ret = recycle ? src : src.copy(src.getConfig(), true);\n        if (radius < 1) {\n            radius = 1;\n        }\n        int w = ret.getWidth();\n        int h = ret.getHeight();\n\n        int[] pix = new int[w * h];\n        ret.getPixels(pix, 0, w, 0, 0, w, h);\n\n        int wm = w - 1;\n        int hm = h - 1;\n        int wh = w * h;\n        int div = radius + radius + 1;\n\n        int r[] = new int[wh];\n        int g[] = new int[wh];\n        int b[] = new int[wh];\n        int rsum, gsum, bsum, x, y, i, p, yp, yi, yw;\n        int vmin[] = new int[Math.max(w, h)];\n\n        int divsum = (div + 1) >> 1;\n        divsum *= divsum;\n        int dv[] = new int[256 * divsum];\n        for (i = 0; i < 256 * divsum; i++) {\n            dv[i] = (i / divsum);\n        }\n\n        yw = yi = 0;\n\n        int[][] stack = new int[div][3];\n        int stackpointer;\n        int stackstart;\n        int[] sir;\n        int rbs;\n        int r1 = radius + 1;\n        int routsum, goutsum, boutsum;\n        int rinsum, ginsum, binsum;\n\n        for (y = 0; y < h; y++) {\n            rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;\n            for (i = -radius; i <= radius; i++) {\n                p = pix[yi + Math.min(wm, Math.max(i, 0))];\n                sir = stack[i + radius];\n                sir[0] = (p & 0xff0000) >> 16;\n                sir[1] = (p & 0x00ff00) >> 8;\n                sir[2] = (p & 0x0000ff);\n                rbs = r1 - Math.abs(i);\n                rsum += sir[0] * rbs;\n                gsum += sir[1] * rbs;\n                bsum += sir[2] * rbs;\n                if (i > 0) {\n                    rinsum += sir[0];\n                    ginsum += sir[1];\n                    binsum += sir[2];\n                } else {\n                    routsum += sir[0];\n                    goutsum += sir[1];\n                    boutsum += sir[2];\n                }\n            }\n            stackpointer = radius;\n\n            for (x = 0; x < w; x++) {\n\n                r[yi] = dv[rsum];\n                g[yi] = dv[gsum];\n                b[yi] = dv[bsum];\n\n                rsum -= routsum;\n                gsum -= goutsum;\n                bsum -= boutsum;\n\n                stackstart = stackpointer - radius + div;\n                sir = stack[stackstart % div];\n\n                routsum -= sir[0];\n                goutsum -= sir[1];\n                boutsum -= sir[2];\n\n                if (y == 0) {\n                    vmin[x] = Math.min(x + radius + 1, wm);\n                }\n                p = pix[yw + vmin[x]];\n\n                sir[0] = (p & 0xff0000) >> 16;\n                sir[1] = (p & 0x00ff00) >> 8;\n                sir[2] = (p & 0x0000ff);\n\n                rinsum += sir[0];\n                ginsum += sir[1];\n                binsum += sir[2];\n\n                rsum += rinsum;\n                gsum += ginsum;\n                bsum += binsum;\n\n                stackpointer = (stackpointer + 1) % div;\n                sir = stack[(stackpointer) % div];\n\n                routsum += sir[0];\n                goutsum += sir[1];\n                boutsum += sir[2];\n\n                rinsum -= sir[0];\n                ginsum -= sir[1];\n                binsum -= sir[2];\n\n                yi++;\n            }\n            yw += w;\n        }\n        for (x = 0; x < w; x++) {\n            rinsum = ginsum = binsum = routsum = goutsum = boutsum = rsum = gsum = bsum = 0;\n            yp = -radius * w;\n            for (i = -radius; i <= radius; i++) {\n                yi = Math.max(0, yp) + x;\n\n                sir = stack[i + radius];\n\n                sir[0] = r[yi];\n                sir[1] = g[yi];\n                sir[2] = b[yi];\n\n                rbs = r1 - Math.abs(i);\n\n                rsum += r[yi] * rbs;\n                gsum += g[yi] * rbs;\n                bsum += b[yi] * rbs;\n\n                if (i > 0) {\n                    rinsum += sir[0];\n                    ginsum += sir[1];\n                    binsum += sir[2];\n                } else {\n                    routsum += sir[0];\n                    goutsum += sir[1];\n                    boutsum += sir[2];\n                }\n\n                if (i < hm) {\n                    yp += w;\n                }\n            }\n            yi = x;\n            stackpointer = radius;\n            for (y = 0; y < h; y++) {\n                // Preserve alpha channel: ( 0xff000000 & pix[yi] )\n                pix[yi] = (0xff000000 & pix[yi]) | (dv[rsum] << 16) | (dv[gsum] << 8) | dv[bsum];\n\n                rsum -= routsum;\n                gsum -= goutsum;\n                bsum -= boutsum;\n\n                stackstart = stackpointer - radius + div;\n                sir = stack[stackstart % div];\n\n                routsum -= sir[0];\n                goutsum -= sir[1];\n                boutsum -= sir[2];\n\n                if (x == 0) {\n                    vmin[y] = Math.min(y + r1, hm) * w;\n                }\n                p = x + vmin[y];\n\n                sir[0] = r[p];\n                sir[1] = g[p];\n                sir[2] = b[p];\n\n                rinsum += sir[0];\n                ginsum += sir[1];\n                binsum += sir[2];\n\n                rsum += rinsum;\n                gsum += ginsum;\n                bsum += binsum;\n\n                stackpointer = (stackpointer + 1) % div;\n                sir = stack[stackpointer];\n\n                routsum += sir[0];\n                goutsum += sir[1];\n                boutsum += sir[2];\n\n                rinsum -= sir[0];\n                ginsum -= sir[1];\n                binsum -= sir[2];\n\n                yi += w;\n            }\n        }\n        ret.setPixels(pix, 0, w, 0, 0, w, h);\n        return ret;\n    }\n\n    /**\n     * Save the bitmap.\n     *\n     * @param src      The source of bitmap.\n     * @param filePath The path of file.\n     * @param format   The format of the image.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean save(final Bitmap src,\n                               final String filePath,\n                               final CompressFormat format) {\n        return save(src, filePath, format, 100, false);\n    }\n\n    /**\n     * Save the bitmap.\n     *\n     * @param src    The source of bitmap.\n     * @param file   The file.\n     * @param format The format of the image.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean save(final Bitmap src, final File file, final CompressFormat format) {\n        return save(src, file, format, 100, false);\n    }\n\n    /**\n     * Save the bitmap.\n     *\n     * @param src      The source of bitmap.\n     * @param filePath The path of file.\n     * @param format   The format of the image.\n     * @param recycle  True to recycle the source of bitmap, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean save(final Bitmap src,\n                               final String filePath,\n                               final CompressFormat format,\n                               final boolean recycle) {\n        return save(src, filePath, format, 100, recycle);\n    }\n\n    /**\n     * Save the bitmap.\n     *\n     * @param src     The source of bitmap.\n     * @param file    The file.\n     * @param format  The format of the image.\n     * @param recycle True to recycle the source of bitmap, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean save(final Bitmap src,\n                               final File file,\n                               final CompressFormat format,\n                               final boolean recycle) {\n        return save(src, file, format, 100, recycle);\n    }\n\n    /**\n     * Save the bitmap.\n     *\n     * @param src      The source of bitmap.\n     * @param filePath The path of file.\n     * @param format   The format of the image.\n     * @param quality  Hint to the compressor, 0-100. 0 meaning compress for\n     *                 small size, 100 meaning compress for max quality. Some\n     *                 formats, like PNG which is lossless, will ignore the\n     *                 quality setting\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean save(final Bitmap src,\n                               final String filePath,\n                               final CompressFormat format,\n                               final int quality) {\n        return save(src, UtilsBridge.getFileByPath(filePath), format, quality, false);\n    }\n\n    /**\n     * Save the bitmap.\n     *\n     * @param src    The source of bitmap.\n     * @param file   The file.\n     * @param format The format of the image.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean save(final Bitmap src,\n                               final File file,\n                               final CompressFormat format,\n                               final int quality) {\n        return save(src, file, format, quality, false);\n    }\n\n    /**\n     * Save the bitmap.\n     *\n     * @param src      The source of bitmap.\n     * @param filePath The path of file.\n     * @param format   The format of the image.\n     * @param quality  Hint to the compressor, 0-100. 0 meaning compress for\n     *                 small size, 100 meaning compress for max quality. Some\n     *                 formats, like PNG which is lossless, will ignore the\n     *                 quality setting\n     * @param recycle  True to recycle the source of bitmap, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean save(final Bitmap src,\n                               final String filePath,\n                               final CompressFormat format,\n                               final int quality,\n                               final boolean recycle) {\n        return save(src, UtilsBridge.getFileByPath(filePath), format, quality, recycle);\n    }\n\n    /**\n     * Save the bitmap.\n     *\n     * @param src     The source of bitmap.\n     * @param file    The file.\n     * @param format  The format of the image.\n     * @param quality Hint to the compressor, 0-100. 0 meaning compress for\n     *                small size, 100 meaning compress for max quality. Some\n     *                formats, like PNG which is lossless, will ignore the\n     *                quality setting\n     * @param recycle True to recycle the source of bitmap, false otherwise.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean save(final Bitmap src,\n                               final File file,\n                               final CompressFormat format,\n                               final int quality,\n                               final boolean recycle) {\n        if (isEmptyBitmap(src)) {\n            Log.e(\"ImageUtils\", \"bitmap is empty.\");\n            return false;\n        }\n        if (src.isRecycled()) {\n            Log.e(\"ImageUtils\", \"bitmap is recycled.\");\n            return false;\n        }\n        if (!UtilsBridge.createFileByDeleteOldFile(file)) {\n            Log.e(\"ImageUtils\", \"create or delete file <\" + file + \"> failed.\");\n            return false;\n        }\n        OutputStream os = null;\n        boolean ret = false;\n        try {\n            os = new BufferedOutputStream(new FileOutputStream(file));\n            ret = src.compress(format, quality, os);\n            if (recycle && !src.isRecycled()) src.recycle();\n        } catch (IOException e) {\n            e.printStackTrace();\n        } finally {\n            try {\n                if (os != null) {\n                    os.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n        return ret;\n    }\n\n    /**\n     * @param src    The source of bitmap.\n     * @param format The format of the image.\n     * @return the file if save success, otherwise return null.\n     */\n    @Nullable\n    public static File save2Album(final Bitmap src,\n                                  final CompressFormat format) {\n        return save2Album(src, \"\", format, 100, false);\n    }\n\n    /**\n     * @param src     The source of bitmap.\n     * @param format  The format of the image.\n     * @param recycle True to recycle the source of bitmap, false otherwise.\n     * @return the file if save success, otherwise return null.\n     */\n    @Nullable\n    public static File save2Album(final Bitmap src,\n                                  final CompressFormat format,\n                                  final boolean recycle) {\n        return save2Album(src, \"\", format, 100, recycle);\n    }\n\n    /**\n     * @param src     The source of bitmap.\n     * @param format  The format of the image.\n     * @param quality Hint to the compressor, 0-100. 0 meaning compress for\n     *                small size, 100 meaning compress for max quality. Some\n     *                formats, like PNG which is lossless, will ignore the\n     *                quality setting\n     * @return the file if save success, otherwise return null.\n     */\n    @Nullable\n    public static File save2Album(final Bitmap src,\n                                  final CompressFormat format,\n                                  final int quality) {\n        return save2Album(src, \"\", format, quality, false);\n    }\n\n    /**\n     * @param src     The source of bitmap.\n     * @param format  The format of the image.\n     * @param quality Hint to the compressor, 0-100. 0 meaning compress for\n     *                small size, 100 meaning compress for max quality. Some\n     *                formats, like PNG which is lossless, will ignore the\n     *                quality setting\n     * @param recycle True to recycle the source of bitmap, false otherwise.\n     * @return the file if save success, otherwise return null.\n     */\n    @Nullable\n    public static File save2Album(final Bitmap src,\n                                  final CompressFormat format,\n                                  final int quality,\n                                  final boolean recycle) {\n        return save2Album(src, \"\", format, quality, recycle);\n    }\n\n    /**\n     * @param src     The source of bitmap.\n     * @param dirName The name of directory.\n     * @param format  The format of the image.\n     * @return the file if save success, otherwise return null.\n     */\n    @Nullable\n    public static File save2Album(final Bitmap src,\n                                  final String dirName,\n                                  final CompressFormat format) {\n        return save2Album(src, dirName, format, 100, false);\n    }\n\n    /**\n     * @param src     The source of bitmap.\n     * @param dirName The name of directory.\n     * @param format  The format of the image.\n     * @param recycle True to recycle the source of bitmap, false otherwise.\n     * @return the file if save success, otherwise return null.\n     */\n    @Nullable\n    public static File save2Album(final Bitmap src,\n                                  final String dirName,\n                                  final CompressFormat format,\n                                  final boolean recycle) {\n        return save2Album(src, dirName, format, 100, recycle);\n    }\n\n    /**\n     * @param src     The source of bitmap.\n     * @param dirName The name of directory.\n     * @param format  The format of the image.\n     * @param quality Hint to the compressor, 0-100. 0 meaning compress for\n     *                small size, 100 meaning compress for max quality. Some\n     *                formats, like PNG which is lossless, will ignore the\n     *                quality setting\n     * @return the file if save success, otherwise return null.\n     */\n    @Nullable\n    public static File save2Album(final Bitmap src,\n                                  final String dirName,\n                                  final CompressFormat format,\n                                  final int quality) {\n        return save2Album(src, dirName, format, quality, false);\n    }\n\n    /**\n     * @param src     The source of bitmap.\n     * @param dirName The name of directory.\n     * @param format  The format of the image.\n     * @param quality Hint to the compressor, 0-100. 0 meaning compress for\n     *                small size, 100 meaning compress for max quality. Some\n     *                formats, like PNG which is lossless, will ignore the\n     *                quality setting\n     * @param recycle True to recycle the source of bitmap, false otherwise.\n     * @return the file if save success, otherwise return null.\n     */\n    @Nullable\n    public static File save2Album(final Bitmap src,\n                                  final String dirName,\n                                  final CompressFormat format,\n                                  final int quality,\n                                  final boolean recycle) {\n        String safeDirName = TextUtils.isEmpty(dirName) ? Utils.getApp().getPackageName() : dirName;\n        String suffix = CompressFormat.JPEG.equals(format) ? \"JPG\" : format.name();\n        String fileName = System.currentTimeMillis() + \"_\" + quality + \".\" + suffix;\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {\n            if (!UtilsBridge.isGranted(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {\n                Log.e(\"ImageUtils\", \"save to album need storage permission\");\n                return null;\n            }\n            File picDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM);\n            File destFile = new File(picDir, safeDirName + \"/\" + fileName);\n            if (!save(src, destFile, format, quality, recycle)) {\n                return null;\n            }\n            UtilsBridge.notifySystemToScan(destFile);\n            return destFile;\n        } else {\n            ContentValues contentValues = new ContentValues();\n            contentValues.put(MediaStore.Images.Media.DISPLAY_NAME, fileName);\n            contentValues.put(MediaStore.Images.Media.MIME_TYPE, \"image/*\");\n            Uri contentUri;\n            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {\n                contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;\n            } else {\n                contentUri = MediaStore.Images.Media.INTERNAL_CONTENT_URI;\n            }\n            contentValues.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_DCIM + \"/\" + safeDirName);\n            contentValues.put(MediaStore.MediaColumns.IS_PENDING, 1);\n            Uri uri = Utils.getApp().getContentResolver().insert(contentUri, contentValues);\n            if (uri == null) {\n                return null;\n            }\n            OutputStream os = null;\n            try {\n                os = Utils.getApp().getContentResolver().openOutputStream(uri);\n                src.compress(format, quality, os);\n\n                contentValues.clear();\n                contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0);\n                Utils.getApp().getContentResolver().update(uri, contentValues, null, null);\n\n                return UtilsBridge.uri2File(uri);\n            } catch (Exception e) {\n                Utils.getApp().getContentResolver().delete(uri, null, null);\n                e.printStackTrace();\n                return null;\n            } finally {\n                try {\n                    if (os != null) {\n                        os.close();\n                    }\n                } catch (IOException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n    }\n\n    /**\n     * Return whether it is a image according to the file name.\n     *\n     * @param file The file.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isImage(final File file) {\n        if (file == null || !file.exists()) {\n            return false;\n        }\n        return isImage(file.getPath());\n    }\n\n    /**\n     * Return whether it is a image according to the file name.\n     *\n     * @param filePath The path of file.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isImage(final String filePath) {\n        try {\n            BitmapFactory.Options options = new BitmapFactory.Options();\n            options.inJustDecodeBounds = true;\n            BitmapFactory.decodeFile(filePath, options);\n            return options.outWidth > 0 && options.outHeight > 0;\n        } catch (Exception e) {\n            return false;\n        }\n    }\n\n    /**\n     * Return the type of image.\n     *\n     * @param filePath The path of file.\n     * @return the type of image\n     */\n    public static ImageType getImageType(final String filePath) {\n        return getImageType(UtilsBridge.getFileByPath(filePath));\n    }\n\n    /**\n     * Return the type of image.\n     *\n     * @param file The file.\n     * @return the type of image\n     */\n    public static ImageType getImageType(final File file) {\n        if (file == null) return null;\n        InputStream is = null;\n        try {\n            is = new FileInputStream(file);\n            ImageType type = getImageType(is);\n            if (type != null) {\n                return type;\n            }\n        } catch (IOException e) {\n            e.printStackTrace();\n        } finally {\n            try {\n                if (is != null) {\n                    is.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n        }\n        return null;\n    }\n\n    private static ImageType getImageType(final InputStream is) {\n        if (is == null) return null;\n        try {\n            byte[] bytes = new byte[12];\n            return is.read(bytes) != -1 ? getImageType(bytes) : null;\n        } catch (IOException e) {\n            e.printStackTrace();\n            return null;\n        }\n    }\n\n    private static ImageType getImageType(final byte[] bytes) {\n        String type = UtilsBridge.bytes2HexString(bytes).toUpperCase();\n        if (type.contains(\"FFD8FF\")) {\n            return ImageType.TYPE_JPG;\n        } else if (type.contains(\"89504E47\")) {\n            return ImageType.TYPE_PNG;\n        } else if (type.contains(\"47494638\")) {\n            return ImageType.TYPE_GIF;\n        } else if (type.contains(\"49492A00\") || type.contains(\"4D4D002A\")) {\n            return ImageType.TYPE_TIFF;\n        } else if (type.contains(\"424D\")) {\n            return ImageType.TYPE_BMP;\n        } else if (type.startsWith(\"52494646\") && type.endsWith(\"57454250\")) {//524946461c57000057454250-12个字节\n            return ImageType.TYPE_WEBP;\n        } else if (type.contains(\"00000100\") || type.contains(\"00000200\")) {\n            return ImageType.TYPE_ICO;\n        } else {\n            return ImageType.TYPE_UNKNOWN;\n        }\n    }\n\n    private static boolean isJPEG(final byte[] b) {\n        return b.length >= 2\n                && (b[0] == (byte) 0xFF) && (b[1] == (byte) 0xD8);\n    }\n\n    private static boolean isGIF(final byte[] b) {\n        return b.length >= 6\n                && b[0] == 'G' && b[1] == 'I'\n                && b[2] == 'F' && b[3] == '8'\n                && (b[4] == '7' || b[4] == '9') && b[5] == 'a';\n    }\n\n    private static boolean isPNG(final byte[] b) {\n        return b.length >= 8\n                && (b[0] == (byte) 137 && b[1] == (byte) 80\n                && b[2] == (byte) 78 && b[3] == (byte) 71\n                && b[4] == (byte) 13 && b[5] == (byte) 10\n                && b[6] == (byte) 26 && b[7] == (byte) 10);\n    }\n\n    private static boolean isBMP(final byte[] b) {\n        return b.length >= 2\n                && (b[0] == 0x42) && (b[1] == 0x4d);\n    }\n\n    private static boolean isEmptyBitmap(final Bitmap src) {\n        return src == null || src.getWidth() == 0 || src.getHeight() == 0;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // about compress\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Return the compressed bitmap using scale.\n     *\n     * @param src       The source of bitmap.\n     * @param newWidth  The new width.\n     * @param newHeight The new height.\n     * @return the compressed bitmap\n     */\n    public static Bitmap compressByScale(final Bitmap src,\n                                         final int newWidth,\n                                         final int newHeight) {\n        return scale(src, newWidth, newHeight, false);\n    }\n\n    /**\n     * Return the compressed bitmap using scale.\n     *\n     * @param src       The source of bitmap.\n     * @param newWidth  The new width.\n     * @param newHeight The new height.\n     * @param recycle   True to recycle the source of bitmap, false otherwise.\n     * @return the compressed bitmap\n     */\n    public static Bitmap compressByScale(final Bitmap src,\n                                         final int newWidth,\n                                         final int newHeight,\n                                         final boolean recycle) {\n        return scale(src, newWidth, newHeight, recycle);\n    }\n\n    /**\n     * Return the compressed bitmap using scale.\n     *\n     * @param src         The source of bitmap.\n     * @param scaleWidth  The scale of width.\n     * @param scaleHeight The scale of height.\n     * @return the compressed bitmap\n     */\n    public static Bitmap compressByScale(final Bitmap src,\n                                         final float scaleWidth,\n                                         final float scaleHeight) {\n        return scale(src, scaleWidth, scaleHeight, false);\n    }\n\n    /**\n     * Return the compressed bitmap using scale.\n     *\n     * @param src         The source of bitmap.\n     * @param scaleWidth  The scale of width.\n     * @param scaleHeight The scale of height.\n     * @param recycle     True to recycle the source of bitmap, false otherwise.\n     * @return he compressed bitmap\n     */\n    public static Bitmap compressByScale(final Bitmap src,\n                                         final float scaleWidth,\n                                         final float scaleHeight,\n                                         final boolean recycle) {\n        return scale(src, scaleWidth, scaleHeight, recycle);\n    }\n\n    /**\n     * Return the compressed data using quality.\n     *\n     * @param src     The source of bitmap.\n     * @param quality The quality.\n     * @return the compressed data using quality\n     */\n    public static byte[] compressByQuality(final Bitmap src,\n                                           @IntRange(from = 0, to = 100) final int quality) {\n        return compressByQuality(src, quality, false);\n    }\n\n    /**\n     * Return the compressed data using quality.\n     *\n     * @param src     The source of bitmap.\n     * @param quality The quality.\n     * @param recycle True to recycle the source of bitmap, false otherwise.\n     * @return the compressed data using quality\n     */\n    public static byte[] compressByQuality(final Bitmap src,\n                                           @IntRange(from = 0, to = 100) final int quality,\n                                           final boolean recycle) {\n        if (isEmptyBitmap(src)) return null;\n        ByteArrayOutputStream baos = new ByteArrayOutputStream();\n        src.compress(Bitmap.CompressFormat.JPEG, quality, baos);\n        byte[] bytes = baos.toByteArray();\n        if (recycle && !src.isRecycled()) src.recycle();\n        return bytes;\n    }\n\n    /**\n     * Return the compressed data using quality.\n     *\n     * @param src         The source of bitmap.\n     * @param maxByteSize The maximum size of byte.\n     * @return the compressed data using quality\n     */\n    public static byte[] compressByQuality(final Bitmap src, final long maxByteSize) {\n        return compressByQuality(src, maxByteSize, false);\n    }\n\n    /**\n     * Return the compressed data using quality.\n     *\n     * @param src         The source of bitmap.\n     * @param maxByteSize The maximum size of byte.\n     * @param recycle     True to recycle the source of bitmap, false otherwise.\n     * @return the compressed data using quality\n     */\n    public static byte[] compressByQuality(final Bitmap src,\n                                           final long maxByteSize,\n                                           final boolean recycle) {\n        if (isEmptyBitmap(src) || maxByteSize <= 0) return new byte[0];\n        ByteArrayOutputStream baos = new ByteArrayOutputStream();\n        src.compress(CompressFormat.JPEG, 100, baos);\n        byte[] bytes;\n        if (baos.size() <= maxByteSize) {\n            bytes = baos.toByteArray();\n        } else {\n            baos.reset();\n            src.compress(CompressFormat.JPEG, 0, baos);\n            if (baos.size() >= maxByteSize) {\n                bytes = baos.toByteArray();\n            } else {\n                // find the best quality using binary search\n                int st = 0;\n                int end = 100;\n                int mid = 0;\n                while (st < end) {\n                    mid = (st + end) / 2;\n                    baos.reset();\n                    src.compress(CompressFormat.JPEG, mid, baos);\n                    int len = baos.size();\n                    if (len == maxByteSize) {\n                        break;\n                    } else if (len > maxByteSize) {\n                        end = mid - 1;\n                    } else {\n                        st = mid + 1;\n                    }\n                }\n                if (end == mid - 1) {\n                    baos.reset();\n                    src.compress(CompressFormat.JPEG, st, baos);\n                }\n                bytes = baos.toByteArray();\n            }\n        }\n        if (recycle && !src.isRecycled()) src.recycle();\n        return bytes;\n    }\n\n    /**\n     * Return the compressed bitmap using sample size.\n     *\n     * @param src        The source of bitmap.\n     * @param sampleSize The sample size.\n     * @return the compressed bitmap\n     */\n\n    public static Bitmap compressBySampleSize(final Bitmap src, final int sampleSize) {\n        return compressBySampleSize(src, sampleSize, false);\n    }\n\n    /**\n     * Return the compressed bitmap using sample size.\n     *\n     * @param src        The source of bitmap.\n     * @param sampleSize The sample size.\n     * @param recycle    True to recycle the source of bitmap, false otherwise.\n     * @return the compressed bitmap\n     */\n    public static Bitmap compressBySampleSize(final Bitmap src,\n                                              final int sampleSize,\n                                              final boolean recycle) {\n        if (isEmptyBitmap(src)) return null;\n        BitmapFactory.Options options = new BitmapFactory.Options();\n        options.inSampleSize = sampleSize;\n        ByteArrayOutputStream baos = new ByteArrayOutputStream();\n        src.compress(Bitmap.CompressFormat.JPEG, 100, baos);\n        byte[] bytes = baos.toByteArray();\n        if (recycle && !src.isRecycled()) src.recycle();\n        return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);\n    }\n\n    /**\n     * Return the compressed bitmap using sample size.\n     *\n     * @param src       The source of bitmap.\n     * @param maxWidth  The maximum width.\n     * @param maxHeight The maximum height.\n     * @return the compressed bitmap\n     */\n    public static Bitmap compressBySampleSize(final Bitmap src,\n                                              final int maxWidth,\n                                              final int maxHeight) {\n        return compressBySampleSize(src, maxWidth, maxHeight, false);\n    }\n\n    /**\n     * Return the compressed bitmap using sample size.\n     *\n     * @param src       The source of bitmap.\n     * @param maxWidth  The maximum width.\n     * @param maxHeight The maximum height.\n     * @param recycle   True to recycle the source of bitmap, false otherwise.\n     * @return the compressed bitmap\n     */\n    public static Bitmap compressBySampleSize(final Bitmap src,\n                                              final int maxWidth,\n                                              final int maxHeight,\n                                              final boolean recycle) {\n        if (isEmptyBitmap(src)) return null;\n        BitmapFactory.Options options = new BitmapFactory.Options();\n        options.inJustDecodeBounds = true;\n        ByteArrayOutputStream baos = new ByteArrayOutputStream();\n        src.compress(Bitmap.CompressFormat.JPEG, 100, baos);\n        byte[] bytes = baos.toByteArray();\n        BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);\n        options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight);\n        options.inJustDecodeBounds = false;\n        if (recycle && !src.isRecycled()) src.recycle();\n        return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, options);\n    }\n\n    /**\n     * Return the size of bitmap.\n     *\n     * @param filePath The path of file.\n     * @return the size of bitmap\n     */\n    public static int[] getSize(String filePath) {\n        return getSize(UtilsBridge.getFileByPath(filePath));\n    }\n\n    /**\n     * Return the size of bitmap.\n     *\n     * @param file The file.\n     * @return the size of bitmap\n     */\n    public static int[] getSize(File file) {\n        if (file == null) return new int[]{0, 0};\n        BitmapFactory.Options opts = new BitmapFactory.Options();\n        opts.inJustDecodeBounds = true;\n        BitmapFactory.decodeFile(file.getAbsolutePath(), opts);\n        return new int[]{opts.outWidth, opts.outHeight};\n    }\n\n    /**\n     * Return the sample size.\n     *\n     * @param options   The options.\n     * @param maxWidth  The maximum width.\n     * @param maxHeight The maximum height.\n     * @return the sample size\n     */\n    public static int calculateInSampleSize(final BitmapFactory.Options options,\n                                            final int maxWidth,\n                                            final int maxHeight) {\n        int height = options.outHeight;\n        int width = options.outWidth;\n        int inSampleSize = 1;\n        while (height > maxHeight || width > maxWidth) {\n            height >>= 1;\n            width >>= 1;\n            inSampleSize <<= 1;\n        }\n        return inSampleSize;\n    }\n\n    public enum ImageType {\n        TYPE_JPG(\"jpg\"),\n\n        TYPE_PNG(\"png\"),\n\n        TYPE_GIF(\"gif\"),\n\n        TYPE_TIFF(\"tiff\"),\n\n        TYPE_BMP(\"bmp\"),\n\n        TYPE_WEBP(\"webp\"),\n\n        TYPE_ICO(\"ico\"),\n\n        TYPE_UNKNOWN(\"unknown\");\n\n        String value;\n\n        ImageType(String value) {\n            this.value = value;\n        }\n\n        public String getValue() {\n            return value;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/IntentUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.content.ComponentName;\nimport android.content.Intent;\nimport android.content.pm.PackageManager;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.provider.MediaStore;\nimport android.provider.Settings;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.LinkedList;\nimport java.util.List;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.RequiresPermission;\nimport androidx.core.content.FileProvider;\n\nimport static android.Manifest.permission.CALL_PHONE;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/09/23\n *     desc  : utils about intent\n * </pre>\n */\npublic final class IntentUtils {\n\n    private IntentUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Return whether the intent is available.\n     *\n     * @param intent The intent.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isIntentAvailable(final Intent intent) {\n        return Utils.getApp()\n                .getPackageManager()\n                .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)\n                .size() > 0;\n    }\n\n    /**\n     * Return the intent of install app.\n     * <p>Target APIs greater than 25 must hold\n     * {@code <uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\" />}</p>\n     *\n     * @param filePath The path of file.\n     * @return the intent of install app\n     */\n    public static Intent getInstallAppIntent(final String filePath) {\n        return getInstallAppIntent(UtilsBridge.getFileByPath(filePath));\n    }\n\n    /**\n     * Return the intent of install app.\n     * <p>Target APIs greater than 25 must hold\n     * {@code <uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\" />}</p>\n     *\n     * @param file The file.\n     * @return the intent of install app\n     */\n    public static Intent getInstallAppIntent(final File file) {\n        if (!UtilsBridge.isFileExists(file)) return null;\n        Uri uri;\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {\n            uri = Uri.fromFile(file);\n        } else {\n            String authority = Utils.getApp().getPackageName() + \".utilcode.fileprovider\";\n            uri = FileProvider.getUriForFile(Utils.getApp(), authority, file);\n        }\n        return getInstallAppIntent(uri);\n    }\n\n    /**\n     * Return the intent of install app.\n     * <p>Target APIs greater than 25 must hold\n     * {@code <uses-permission android:name=\"android.permission.REQUEST_INSTALL_PACKAGES\" />}</p>\n     *\n     * @param uri The uri.\n     * @return the intent of install app\n     */\n    public static Intent getInstallAppIntent(final Uri uri) {\n        if (uri == null) return null;\n        Intent intent = new Intent(Intent.ACTION_VIEW);\n        String type = \"application/vnd.android.package-archive\";\n        intent.setDataAndType(uri, type);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);\n        }\n        return intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n    }\n\n    /**\n     * Return the intent of uninstall app.\n     * <p>Target APIs greater than 25 must hold\n     * Must hold {@code <uses-permission android:name=\"android.permission.REQUEST_DELETE_PACKAGES\" />}</p>\n     *\n     * @param pkgName The name of the package.\n     * @return the intent of uninstall app\n     */\n    public static Intent getUninstallAppIntent(final String pkgName) {\n        Intent intent = new Intent(Intent.ACTION_DELETE);\n        intent.setData(Uri.parse(\"package:\" + pkgName));\n        return intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n    }\n\n    /**\n     * Return the intent of launch app.\n     *\n     * @param pkgName The name of the package.\n     * @return the intent of launch app\n     */\n    public static Intent getLaunchAppIntent(final String pkgName) {\n        String launcherActivity = UtilsBridge.getLauncherActivity(pkgName);\n        if (UtilsBridge.isSpace(launcherActivity)) return null;\n        Intent intent = new Intent(Intent.ACTION_MAIN);\n        intent.addCategory(Intent.CATEGORY_LAUNCHER);\n        intent.setClassName(pkgName, launcherActivity);\n        return intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n    }\n\n    /**\n     * Return the intent of launch app details settings.\n     *\n     * @param pkgName The name of the package.\n     * @return the intent of launch app details settings\n     */\n    public static Intent getLaunchAppDetailsSettingsIntent(final String pkgName) {\n        return getLaunchAppDetailsSettingsIntent(pkgName, false);\n    }\n\n    /**\n     * Return the intent of launch app details settings.\n     *\n     * @param pkgName The name of the package.\n     * @return the intent of launch app details settings\n     */\n    public static Intent getLaunchAppDetailsSettingsIntent(final String pkgName, final boolean isNewTask) {\n        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);\n        intent.setData(Uri.parse(\"package:\" + pkgName));\n        return getIntent(intent, isNewTask);\n    }\n\n    /**\n     * Return the intent of share text.\n     *\n     * @param content The content.\n     * @return the intent of share text\n     */\n    public static Intent getShareTextIntent(final String content) {\n        Intent intent = new Intent(Intent.ACTION_SEND);\n        intent.setType(\"text/plain\");\n        intent.putExtra(Intent.EXTRA_TEXT, content);\n        intent = Intent.createChooser(intent, \"\");\n        return getIntent(intent, true);\n    }\n\n    /**\n     * Return the intent of share image.\n     *\n     * @param imagePath The path of image.\n     * @return the intent of share image\n     */\n    public static Intent getShareImageIntent(final String imagePath) {\n        return getShareTextImageIntent(\"\", imagePath);\n    }\n\n    /**\n     * Return the intent of share image.\n     *\n     * @param imageFile The file of image.\n     * @return the intent of share image\n     */\n    public static Intent getShareImageIntent(final File imageFile) {\n        return getShareTextImageIntent(\"\", imageFile);\n    }\n\n    /**\n     * Return the intent of share image.\n     *\n     * @param imageUri The uri of image.\n     * @return the intent of share image\n     */\n    public static Intent getShareImageIntent(final Uri imageUri) {\n        return getShareTextImageIntent(\"\", imageUri);\n    }\n\n    /**\n     * Return the intent of share image.\n     *\n     * @param content   The content.\n     * @param imagePath The path of image.\n     * @return the intent of share image\n     */\n    public static Intent getShareTextImageIntent(@Nullable final String content, final String imagePath) {\n        return getShareTextImageIntent(content, UtilsBridge.getFileByPath(imagePath));\n    }\n\n    /**\n     * Return the intent of share image.\n     *\n     * @param content   The content.\n     * @param imageFile The file of image.\n     * @return the intent of share image\n     */\n    public static Intent getShareTextImageIntent(@Nullable final String content, final File imageFile) {\n        return getShareTextImageIntent(content, UtilsBridge.file2Uri(imageFile));\n    }\n\n    /**\n     * Return the intent of share image.\n     *\n     * @param content  The content.\n     * @param imageUri The uri of image.\n     * @return the intent of share image\n     */\n    public static Intent getShareTextImageIntent(@Nullable final String content, final Uri imageUri) {\n        Intent intent = new Intent(Intent.ACTION_SEND);\n        intent.putExtra(Intent.EXTRA_TEXT, content);\n        intent.putExtra(Intent.EXTRA_STREAM, imageUri);\n        intent.setType(\"image/*\");\n        intent = Intent.createChooser(intent, \"\");\n        return getIntent(intent, true);\n    }\n\n    /**\n     * Return the intent of share images.\n     *\n     * @param imagePaths The paths of images.\n     * @return the intent of share images\n     */\n    public static Intent getShareImageIntent(final LinkedList<String> imagePaths) {\n        return getShareTextImageIntent(\"\", imagePaths);\n    }\n\n    /**\n     * Return the intent of share images.\n     *\n     * @param images The files of images.\n     * @return the intent of share images\n     */\n    public static Intent getShareImageIntent(final List<File> images) {\n        return getShareTextImageIntent(\"\", images);\n    }\n\n    /**\n     * Return the intent of share images.\n     *\n     * @param uris The uris of image.\n     * @return the intent of share image\n     */\n    public static Intent getShareImageIntent(final ArrayList<Uri> uris) {\n        return getShareTextImageIntent(\"\", uris);\n    }\n\n    /**\n     * Return the intent of share images.\n     *\n     * @param content    The content.\n     * @param imagePaths The paths of images.\n     * @return the intent of share images\n     */\n    public static Intent getShareTextImageIntent(@Nullable final String content,\n                                                 final LinkedList<String> imagePaths) {\n        List<File> files = new ArrayList<>();\n        if (imagePaths != null) {\n            for (String imagePath : imagePaths) {\n                File file = UtilsBridge.getFileByPath(imagePath);\n                if (file != null) {\n                    files.add(file);\n                }\n            }\n        }\n        return getShareTextImageIntent(content, files);\n    }\n\n    /**\n     * Return the intent of share images.\n     *\n     * @param content The content.\n     * @param images  The files of images.\n     * @return the intent of share images\n     */\n    public static Intent getShareTextImageIntent(@Nullable final String content, final List<File> images) {\n        ArrayList<Uri> uris = new ArrayList<>();\n        if (images != null) {\n            for (File image : images) {\n                Uri uri = UtilsBridge.file2Uri(image);\n                if (uri != null) {\n                    uris.add(uri);\n                }\n            }\n        }\n        return getShareTextImageIntent(content, uris);\n    }\n\n    /**\n     * Return the intent of share images.\n     *\n     * @param content The content.\n     * @param uris    The uris of image.\n     * @return the intent of share image\n     */\n    public static Intent getShareTextImageIntent(@Nullable final String content, final ArrayList<Uri> uris) {\n        Intent intent = new Intent(Intent.ACTION_SEND_MULTIPLE);\n        intent.putExtra(Intent.EXTRA_TEXT, content);\n        intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);\n        intent.setType(\"image/*\");\n        intent = Intent.createChooser(intent, \"\");\n        return getIntent(intent, true);\n    }\n\n    /**\n     * Return the intent of component.\n     *\n     * @param pkgName   The name of the package.\n     * @param className The name of class.\n     * @return the intent of component\n     */\n    public static Intent getComponentIntent(final String pkgName, final String className) {\n        return getComponentIntent(pkgName, className, null, false);\n    }\n\n    /**\n     * Return the intent of component.\n     *\n     * @param pkgName   The name of the package.\n     * @param className The name of class.\n     * @param isNewTask True to add flag of new task, false otherwise.\n     * @return the intent of component\n     */\n    public static Intent getComponentIntent(final String pkgName,\n                                            final String className,\n                                            final boolean isNewTask) {\n        return getComponentIntent(pkgName, className, null, isNewTask);\n    }\n\n    /**\n     * Return the intent of component.\n     *\n     * @param pkgName   The name of the package.\n     * @param className The name of class.\n     * @param bundle    The Bundle of extras to add to this intent.\n     * @return the intent of component\n     */\n    public static Intent getComponentIntent(final String pkgName,\n                                            final String className,\n                                            final Bundle bundle) {\n        return getComponentIntent(pkgName, className, bundle, false);\n    }\n\n    /**\n     * Return the intent of component.\n     *\n     * @param pkgName   The name of the package.\n     * @param className The name of class.\n     * @param bundle    The Bundle of extras to add to this intent.\n     * @param isNewTask True to add flag of new task, false otherwise.\n     * @return the intent of component\n     */\n    public static Intent getComponentIntent(final String pkgName,\n                                            final String className,\n                                            final Bundle bundle,\n                                            final boolean isNewTask) {\n        Intent intent = new Intent();\n        if (bundle != null) intent.putExtras(bundle);\n        ComponentName cn = new ComponentName(pkgName, className);\n        intent.setComponent(cn);\n        return getIntent(intent, isNewTask);\n    }\n\n    /**\n     * Return the intent of shutdown.\n     * <p>Requires root permission\n     * or hold {@code android:sharedUserId=\"android.uid.system\"},\n     * {@code <uses-permission android:name=\"android.permission.SHUTDOWN\" />}\n     * in manifest.</p>\n     *\n     * @return the intent of shutdown\n     */\n    public static Intent getShutdownIntent() {\n        Intent intent;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n            intent = new Intent(\"com.android.internal.intent.action.REQUEST_SHUTDOWN\");\n        } else {\n            intent = new Intent(\"android.intent.action.ACTION_REQUEST_SHUTDOWN\");\n        }\n        intent.putExtra(\"android.intent.extra.KEY_CONFIRM\", false);\n        return intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n    }\n\n    /**\n     * Return the intent of dial.\n     *\n     * @param phoneNumber The phone number.\n     * @return the intent of dial\n     */\n    public static Intent getDialIntent(@NonNull final String phoneNumber) {\n        Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(\"tel:\" + Uri.encode(phoneNumber)));\n        return getIntent(intent, true);\n    }\n\n    /**\n     * Return the intent of call.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.CALL_PHONE\" />}</p>\n     *\n     * @param phoneNumber The phone number.\n     * @return the intent of call\n     */\n    @RequiresPermission(CALL_PHONE)\n    public static Intent getCallIntent(@NonNull final String phoneNumber) {\n        Intent intent = new Intent(\"android.intent.action.CALL\", Uri.parse(\"tel:\" + Uri.encode(phoneNumber)));\n        return getIntent(intent, true);\n    }\n\n    /**\n     * Return the intent of send SMS.\n     *\n     * @param phoneNumber The phone number.\n     * @param content     The content of SMS.\n     * @return the intent of send SMS\n     */\n    public static Intent getSendSmsIntent(@NonNull final String phoneNumber, final String content) {\n        Uri uri = Uri.parse(\"smsto:\" + Uri.encode(phoneNumber));\n        Intent intent = new Intent(Intent.ACTION_SENDTO, uri);\n        intent.putExtra(\"sms_body\", content);\n        return getIntent(intent, true);\n    }\n\n    /**\n     * Return the intent of capture.\n     *\n     * @param outUri The uri of output.\n     * @return the intent of capture\n     */\n    public static Intent getCaptureIntent(final Uri outUri) {\n        return getCaptureIntent(outUri, false);\n    }\n\n    /**\n     * Return the intent of capture.\n     *\n     * @param outUri    The uri of output.\n     * @param isNewTask True to add flag of new task, false otherwise.\n     * @return the intent of capture\n     */\n    public static Intent getCaptureIntent(final Uri outUri, final boolean isNewTask) {\n        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);\n        intent.putExtra(MediaStore.EXTRA_OUTPUT, outUri);\n        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);\n        return getIntent(intent, isNewTask);\n    }\n\n    private static Intent getIntent(final Intent intent, final boolean isNewTask) {\n        return isNewTask ? intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) : intent;\n    }\n\n//    /**\n//     * 获取选择照片的 Intent\n//     *\n//     * @return\n//     */\n//    public static Intent getPickIntentWithGallery() {\n//        Intent intent = new Intent(Intent.ACTION_PICK);\n//        return intent.setType(\"image*//*\");\n//    }\n//\n//    /**\n//     * 获取从文件中选择照片的 Intent\n//     *\n//     * @return\n//     */\n//    public static Intent getPickIntentWithDocuments() {\n//        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);\n//        return intent.setType(\"image*//*\");\n//    }\n//\n//\n//    public static Intent buildImageGetIntent(final Uri saveTo, final int outputX, final int outputY, final boolean returnData) {\n//        return buildImageGetIntent(saveTo, 1, 1, outputX, outputY, returnData);\n//    }\n//\n//    public static Intent buildImageGetIntent(Uri saveTo, int aspectX, int aspectY,\n//                                             int outputX, int outputY, boolean returnData) {\n//        Intent intent = new Intent();\n//        if (Build.VERSION.SDK_INT < 19) {\n//            intent.setAction(Intent.ACTION_GET_CONTENT);\n//        } else {\n//            intent.setAction(Intent.ACTION_OPEN_DOCUMENT);\n//            intent.addCategory(Intent.CATEGORY_OPENABLE);\n//        }\n//        intent.setType(\"image*//*\");\n//        intent.putExtra(\"output\", saveTo);\n//        intent.putExtra(\"aspectX\", aspectX);\n//        intent.putExtra(\"aspectY\", aspectY);\n//        intent.putExtra(\"outputX\", outputX);\n//        intent.putExtra(\"outputY\", outputY);\n//        intent.putExtra(\"scale\", true);\n//        intent.putExtra(\"return-data\", returnData);\n//        intent.putExtra(\"outputFormat\", Bitmap.CompressFormat.PNG.toString());\n//        return intent;\n//    }\n//\n//    public static Intent buildImageCropIntent(final Uri uriFrom, final Uri uriTo, final int outputX, final int outputY, final boolean returnData) {\n//        return buildImageCropIntent(uriFrom, uriTo, 1, 1, outputX, outputY, returnData);\n//    }\n//\n//    public static Intent buildImageCropIntent(Uri uriFrom, Uri uriTo, int aspectX, int aspectY,\n//                                              int outputX, int outputY, boolean returnData) {\n//        Intent intent = new Intent(\"com.android.camera.action.CROP\");\n//        intent.setDataAndType(uriFrom, \"image*//*\");\n//        intent.putExtra(\"crop\", \"true\");\n//        intent.putExtra(\"output\", uriTo);\n//        intent.putExtra(\"aspectX\", aspectX);\n//        intent.putExtra(\"aspectY\", aspectY);\n//        intent.putExtra(\"outputX\", outputX);\n//        intent.putExtra(\"outputY\", outputY);\n//        intent.putExtra(\"scale\", true);\n//        intent.putExtra(\"return-data\", returnData);\n//        intent.putExtra(\"outputFormat\", Bitmap.CompressFormat.PNG.toString());\n//        return intent;\n//    }\n//\n//    public static Intent buildImageCaptureIntent(final Uri uri) {\n//        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);\n//        intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);\n//        return intent;\n//    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/JsonUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/01/07\n *     desc  : utils about json\n * </pre>\n */\npublic final class JsonUtils {\n\n    private static final byte TYPE_BOOLEAN     = 0x00;\n    private static final byte TYPE_INT         = 0x01;\n    private static final byte TYPE_LONG        = 0x02;\n    private static final byte TYPE_DOUBLE      = 0x03;\n    private static final byte TYPE_STRING      = 0x04;\n    private static final byte TYPE_JSON_OBJECT = 0x05;\n    private static final byte TYPE_JSON_ARRAY  = 0x06;\n\n    private JsonUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    \n    /**\n     * Checks if a given input is a JSONObject.\n     *\n     * @param input Anything.\n     * @return true if it is a JSONObject.\n     */\n    public static <T> boolean isJSONObject(final T input) {\n        return input instanceof JSONObject;\n    }\n\n    /**\n     * Checks if a given input is a JSONArray\n     *\n     * @param input Anything.\n     * @return true if it is a JSONArray.\n     */\n    public static <T> boolean isJSONArray(final T input) {\n        return input instanceof JSONArray;\n    }\n    \n    public static boolean getBoolean(final JSONObject jsonObject,\n                                     final String key) {\n        return getBoolean(jsonObject, key, false);\n    }\n\n    public static boolean getBoolean(final JSONObject jsonObject,\n                                     final String key,\n                                     final boolean defaultValue) {\n        return getValueByType(jsonObject, key, defaultValue, TYPE_BOOLEAN);\n    }\n\n    public static boolean getBoolean(final String json,\n                                     final String key) {\n        return getBoolean(json, key, false);\n    }\n\n    public static boolean getBoolean(final String json,\n                                     final String key,\n                                     final boolean defaultValue) {\n        return getValueByType(json, key, defaultValue, TYPE_BOOLEAN);\n    }\n\n    public static int getInt(final JSONObject jsonObject,\n                             final String key) {\n        return getInt(jsonObject, key, -1);\n    }\n\n    public static int getInt(final JSONObject jsonObject,\n                             final String key,\n                             final int defaultValue) {\n        return getValueByType(jsonObject, key, defaultValue, TYPE_INT);\n    }\n\n    public static int getInt(final String json,\n                             final String key) {\n        return getInt(json, key, -1);\n    }\n\n    public static int getInt(final String json,\n                             final String key,\n                             final int defaultValue) {\n        return getValueByType(json, key, defaultValue, TYPE_INT);\n    }\n\n    public static long getLong(final JSONObject jsonObject,\n                               final String key) {\n        return getLong(jsonObject, key, -1);\n    }\n\n    public static long getLong(final JSONObject jsonObject,\n                               final String key,\n                               final long defaultValue) {\n        return getValueByType(jsonObject, key, defaultValue, TYPE_LONG);\n    }\n\n    public static long getLong(final String json,\n                               final String key) {\n        return getLong(json, key, -1);\n    }\n\n    public static long getLong(final String json,\n                               final String key,\n                               final long defaultValue) {\n        return getValueByType(json, key, defaultValue, TYPE_LONG);\n    }\n\n    public static double getDouble(final JSONObject jsonObject,\n                                   final String key) {\n        return getDouble(jsonObject, key, -1);\n    }\n\n    public static double getDouble(final JSONObject jsonObject,\n                                   final String key,\n                                   final double defaultValue) {\n        return getValueByType(jsonObject, key, defaultValue, TYPE_DOUBLE);\n    }\n\n    public static double getDouble(final String json,\n                                   final String key) {\n        return getDouble(json, key, -1);\n    }\n\n    public static double getDouble(final String json,\n                                   final String key,\n                                   final double defaultValue) {\n        return getValueByType(json, key, defaultValue, TYPE_DOUBLE);\n    }\n\n    public static String getString(final JSONObject jsonObject,\n                                   final String key) {\n        return getString(jsonObject, key, \"\");\n    }\n\n    public static String getString(final JSONObject jsonObject,\n                                   final String key,\n                                   final String defaultValue) {\n        return getValueByType(jsonObject, key, defaultValue, TYPE_STRING);\n    }\n\n    public static String getString(final String json,\n                                   final String key) {\n        return getString(json, key, \"\");\n    }\n\n    public static String getString(final String json,\n                                   final String key,\n                                   final String defaultValue) {\n        return getValueByType(json, key, defaultValue, TYPE_STRING);\n    }\n\n    public static JSONObject getJSONObject(final JSONObject jsonObject,\n                                           final String key,\n                                           final JSONObject defaultValue) {\n        return getValueByType(jsonObject, key, defaultValue, TYPE_JSON_OBJECT);\n    }\n\n    public static JSONObject getJSONObject(final String json,\n                                           final String key,\n                                           final JSONObject defaultValue) {\n        return getValueByType(json, key, defaultValue, TYPE_JSON_OBJECT);\n    }\n\n    public static JSONArray getJSONArray(final JSONObject jsonObject,\n                                         final String key,\n                                         final JSONArray defaultValue) {\n        return getValueByType(jsonObject, key, defaultValue, TYPE_JSON_ARRAY);\n    }\n\n    public static JSONArray getJSONArray(final String json,\n                                         final String key,\n                                         final JSONArray defaultValue) {\n        return getValueByType(json, key, defaultValue, TYPE_JSON_ARRAY);\n    }\n\n    private static <T> T getValueByType(final JSONObject jsonObject,\n                                        final String key,\n                                        final T defaultValue,\n                                        final byte type) {\n        if (jsonObject == null || key == null || key.length() == 0) {\n            return defaultValue;\n        }\n        try {\n            Object ret;\n            if (type == TYPE_BOOLEAN) {\n                ret = jsonObject.getBoolean(key);\n            } else if (type == TYPE_INT) {\n                ret = jsonObject.getInt(key);\n            } else if (type == TYPE_LONG) {\n                ret = jsonObject.getLong(key);\n            } else if (type == TYPE_DOUBLE) {\n                ret = jsonObject.getDouble(key);\n            } else if (type == TYPE_STRING) {\n                ret = jsonObject.getString(key);\n            } else if (type == TYPE_JSON_OBJECT) {\n                ret = jsonObject.getJSONObject(key);\n            } else if (type == TYPE_JSON_ARRAY) {\n                ret = jsonObject.getJSONArray(key);\n            } else {\n                return defaultValue;\n            }\n            //noinspection unchecked\n            return (T) ret;\n        } catch (JSONException e) {\n            e.printStackTrace();\n            return defaultValue;\n        }\n    }\n\n    private static <T> T getValueByType(final String json,\n                                        final String key,\n                                        final T defaultValue,\n                                        final byte type) {\n        if (json == null || json.length() == 0\n                || key == null || key.length() == 0) {\n            return defaultValue;\n        }\n        try {\n            return getValueByType(new JSONObject(json), key, defaultValue, type);\n        } catch (JSONException e) {\n            e.printStackTrace();\n            return defaultValue;\n        }\n    }\n\n    public static String formatJson(final String json) {\n        return formatJson(json, 4);\n    }\n\n    public static String formatJson(final String json, final int indentSpaces) {\n        try {\n            for (int i = 0, len = json.length(); i < len; i++) {\n                char c = json.charAt(i);\n                if (c == '{') {\n                    return new JSONObject(json).toString(indentSpaces);\n                } else if (c == '[') {\n                    return new JSONArray(json).toString(indentSpaces);\n                } else if (!Character.isWhitespace(c)) {\n                    return json;\n                }\n            }\n        } catch (JSONException e) {\n            e.printStackTrace();\n        }\n        return json;\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/KeyboardUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.graphics.Rect;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.ResultReceiver;\nimport android.os.SystemClock;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewTreeObserver.OnGlobalLayoutListener;\nimport android.view.Window;\nimport android.view.WindowManager;\nimport android.view.inputmethod.InputMethodManager;\nimport android.widget.EditText;\nimport android.widget.FrameLayout;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport java.lang.reflect.Field;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/02\n *     desc  : utils about keyboard\n * </pre>\n */\npublic final class KeyboardUtils {\n\n    private static final int TAG_ON_GLOBAL_LAYOUT_LISTENER = -8;\n\n    private KeyboardUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Show the soft input.\n     */\n    public static void showSoftInput() {\n        InputMethodManager imm =\n            (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE);\n        if (imm == null) {\n            return;\n        }\n        imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);\n    }\n\n    /**\n     * Show the soft input.\n     */\n    public static void showSoftInput(@Nullable Activity activity) {\n        if (activity == null) {\n            return;\n        }\n        if (!isSoftInputVisible(activity)) {\n            toggleSoftInput();\n        }\n    }\n\n    /**\n     * Show the soft input.\n     *\n     * @param view The view.\n     */\n    public static void showSoftInput(@NonNull final View view) {\n        showSoftInput(view, 0);\n    }\n\n    /**\n     * Show the soft input.\n     *\n     * @param view The view.\n     * @param flags Provides additional operating flags.  Currently may be\n     * 0 or have the {@link InputMethodManager#SHOW_IMPLICIT} bit set.\n     */\n    public static void showSoftInput(@NonNull final View view, final int flags) {\n        InputMethodManager imm =\n            (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE);\n        if (imm == null) {\n            return;\n        }\n        view.setFocusable(true);\n        view.setFocusableInTouchMode(true);\n        view.requestFocus();\n        imm.showSoftInput(view, flags, new ResultReceiver(new Handler()) {\n            @Override\n            protected void onReceiveResult(int resultCode, Bundle resultData) {\n                if (resultCode == InputMethodManager.RESULT_UNCHANGED_HIDDEN\n                    || resultCode == InputMethodManager.RESULT_HIDDEN) {\n                    toggleSoftInput();\n                }\n            }\n        });\n        imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, InputMethodManager.HIDE_IMPLICIT_ONLY);\n    }\n\n    /**\n     * Hide the soft input.\n     *\n     * @param activity The activity.\n     */\n    public static void hideSoftInput(@Nullable final Activity activity) {\n        if (activity == null) {\n            return;\n        }\n        hideSoftInput(activity.getWindow());\n    }\n\n    /**\n     * Hide the soft input.\n     *\n     * @param window The window.\n     */\n    public static void hideSoftInput(@Nullable final Window window) {\n        if (window == null) {\n            return;\n        }\n        View view = window.getCurrentFocus();\n        if (view == null) {\n            View decorView = window.getDecorView();\n            View focusView = decorView.findViewWithTag(\"keyboardTagView\");\n            if (focusView == null) {\n                view = new EditText(window.getContext());\n                view.setTag(\"keyboardTagView\");\n                ((ViewGroup) decorView).addView(view, 0, 0);\n            } else {\n                view = focusView;\n            }\n            view.requestFocus();\n        }\n        hideSoftInput(view);\n    }\n\n    /**\n     * Hide the soft input.\n     *\n     * @param view The view.\n     */\n    public static void hideSoftInput(@NonNull final View view) {\n        InputMethodManager imm =\n            (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE);\n        if (imm == null) {\n            return;\n        }\n        imm.hideSoftInputFromWindow(view.getWindowToken(), 0);\n    }\n\n    private static long millis;\n\n    /**\n     * Hide the soft input.\n     *\n     * @param activity The activity.\n     */\n    public static void hideSoftInputByToggle(@Nullable final Activity activity) {\n        if (activity == null) {\n            return;\n        }\n        long nowMillis = SystemClock.elapsedRealtime();\n        long delta = nowMillis - millis;\n        if (Math.abs(delta) > 500 && KeyboardUtils.isSoftInputVisible(activity)) {\n            KeyboardUtils.toggleSoftInput();\n        }\n        millis = nowMillis;\n    }\n\n    /**\n     * Toggle the soft input display or not.\n     */\n    public static void toggleSoftInput() {\n        InputMethodManager imm =\n            (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE);\n        if (imm == null) {\n            return;\n        }\n        imm.toggleSoftInput(0, 0);\n    }\n\n    private static int sDecorViewDelta = 0;\n\n    /**\n     * Return whether soft input is visible.\n     *\n     * @param activity The activity.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isSoftInputVisible(@NonNull final Activity activity) {\n        return getDecorViewInvisibleHeight(activity.getWindow()) > 0;\n    }\n\n    private static int getDecorViewInvisibleHeight(@NonNull final Window window) {\n        final View decorView = window.getDecorView();\n        final Rect outRect = new Rect();\n        decorView.getWindowVisibleDisplayFrame(outRect);\n        Log.d(\"KeyboardUtils\",\n            \"getDecorViewInvisibleHeight: \" + (decorView.getBottom() - outRect.bottom));\n        int delta = Math.abs(decorView.getBottom() - outRect.bottom);\n        if (delta <= UtilsBridge.getNavBarHeight() + UtilsBridge.getStatusBarHeight()) {\n            sDecorViewDelta = delta;\n            return 0;\n        }\n        return delta - sDecorViewDelta;\n    }\n\n    /**\n     * Register soft input changed listener.\n     *\n     * @param activity The activity.\n     * @param listener The soft input changed listener.\n     */\n    public static void registerSoftInputChangedListener(@NonNull final Activity activity,\n                                                        @NonNull\n                                                        final OnSoftInputChangedListener listener) {\n        registerSoftInputChangedListener(activity.getWindow(), listener);\n    }\n\n    /**\n     * Register soft input changed listener.\n     *\n     * @param window The window.\n     * @param listener The soft input changed listener.\n     */\n    public static void registerSoftInputChangedListener(@NonNull final Window window,\n                                                        @NonNull\n                                                        final OnSoftInputChangedListener listener) {\n        final int flags = window.getAttributes().flags;\n        if ((flags & WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS) != 0) {\n            window.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);\n        }\n        final FrameLayout contentView = window.findViewById(android.R.id.content);\n        final int[] decorViewInvisibleHeightPre = { getDecorViewInvisibleHeight(window) };\n        OnGlobalLayoutListener onGlobalLayoutListener = new OnGlobalLayoutListener() {\n            @Override\n            public void onGlobalLayout() {\n                int height = getDecorViewInvisibleHeight(window);\n                if (decorViewInvisibleHeightPre[0] != height) {\n                    listener.onSoftInputChanged(height);\n                    decorViewInvisibleHeightPre[0] = height;\n                }\n            }\n        };\n        contentView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);\n        contentView.setTag(TAG_ON_GLOBAL_LAYOUT_LISTENER, onGlobalLayoutListener);\n    }\n\n    /**\n     * Unregister soft input changed listener.\n     *\n     * @param window The window.\n     */\n    public static void unregisterSoftInputChangedListener(@NonNull final Window window) {\n        final View contentView = window.findViewById(android.R.id.content);\n        if (contentView == null) {\n            return;\n        }\n        Object tag = contentView.getTag(TAG_ON_GLOBAL_LAYOUT_LISTENER);\n        if (tag instanceof OnGlobalLayoutListener) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n                contentView.getViewTreeObserver().removeOnGlobalLayoutListener((OnGlobalLayoutListener) tag);\n                //这里会发生内存泄漏 如果不设置为null\n                contentView.setTag(TAG_ON_GLOBAL_LAYOUT_LISTENER, null);\n            }\n        }\n    }\n\n    /**\n     * Fix the bug of 5497 in Android.\n     * <p>Don't set adjustResize</p>\n     *\n     * @param activity The activity.\n     */\n    public static void fixAndroidBug5497(@NonNull final Activity activity) {\n        fixAndroidBug5497(activity.getWindow());\n    }\n\n    /**\n     * Fix the bug of 5497 in Android.\n     * <p>It will clean the adjustResize</p>\n     *\n     * @param window The window.\n     */\n    public static void fixAndroidBug5497(@NonNull final Window window) {\n        int softInputMode = window.getAttributes().softInputMode;\n        window.setSoftInputMode(\n            softInputMode & ~WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);\n        final FrameLayout contentView = window.findViewById(android.R.id.content);\n        final View contentViewChild = contentView.getChildAt(0);\n        final int paddingBottom = contentViewChild.getPaddingBottom();\n        final int[] contentViewInvisibleHeightPre5497 = { getContentViewInvisibleHeight(window) };\n        contentView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {\n            @Override\n            public void onGlobalLayout() {\n                int height = getContentViewInvisibleHeight(window);\n                if (contentViewInvisibleHeightPre5497[0] != height) {\n                    contentViewChild.setPadding(contentViewChild.getPaddingLeft(),\n                        contentViewChild.getPaddingTop(), contentViewChild.getPaddingRight(),\n                        paddingBottom + getDecorViewInvisibleHeight(window));\n                    contentViewInvisibleHeightPre5497[0] = height;\n                }\n            }\n        });\n    }\n\n    private static int getContentViewInvisibleHeight(final Window window) {\n        final View contentView = window.findViewById(android.R.id.content);\n        if (contentView == null) {\n            return 0;\n        }\n        final Rect outRect = new Rect();\n        contentView.getWindowVisibleDisplayFrame(outRect);\n        Log.d(\"KeyboardUtils\",\n            \"getContentViewInvisibleHeight: \" + (contentView.getBottom() - outRect.bottom));\n        int delta = Math.abs(contentView.getBottom() - outRect.bottom);\n        if (delta <= UtilsBridge.getStatusBarHeight() + UtilsBridge.getNavBarHeight()) {\n            return 0;\n        }\n        return delta;\n    }\n\n    /**\n     * Fix the leaks of soft input.\n     *\n     * @param activity The activity.\n     */\n    public static void fixSoftInputLeaks(@NonNull final Activity activity) {\n        fixSoftInputLeaks(activity.getWindow());\n    }\n\n    /**\n     * Fix the leaks of soft input.\n     *\n     * @param window The window.\n     */\n    public static void fixSoftInputLeaks(@NonNull final Window window) {\n        InputMethodManager imm =\n            (InputMethodManager) Utils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE);\n        if (imm == null) {\n            return;\n        }\n        String[] leakViews =\n            new String[] { \"mLastSrvView\", \"mCurRootView\", \"mServedView\", \"mNextServedView\" };\n        for (String leakView : leakViews) {\n            try {\n                Field leakViewField = InputMethodManager.class.getDeclaredField(leakView);\n                if (!leakViewField.isAccessible()) {\n                    leakViewField.setAccessible(true);\n                }\n                Object obj = leakViewField.get(imm);\n                if (!(obj instanceof View)) {\n                    continue;\n                }\n                View view = (View) obj;\n                if (view.getRootView() == window.getDecorView().getRootView()) {\n                    leakViewField.set(imm, null);\n                }\n            } catch (Throwable ignore) {/**/}\n        }\n    }\n\n    /**\n     * Click blank area to hide soft input.\n     * <p>Copy the following code in ur activity.</p>\n     */\n    public static void clickBlankArea2HideSoftInput() {\n        Log.i(\"KeyboardUtils\", \"Please refer to the following code.\");\n        /*\n        @Override\n        public boolean dispatchTouchEvent(MotionEvent ev) {\n            if (ev.getAction() == MotionEvent.ACTION_DOWN) {\n                View v = getCurrentFocus();\n                if (isShouldHideKeyboard(v, ev)) {\n                    KeyboardUtils.hideSoftInput(this);\n                }\n            }\n            return super.dispatchTouchEvent(ev);\n        }\n\n        // Return whether touch the view.\n        private boolean isShouldHideKeyboard(View v, MotionEvent event) {\n            if ((v instanceof EditText)) {\n                int[] l = {0, 0};\n                v.getLocationOnScreen(l);\n                int left = l[0],\n                        top = l[1],\n                        bottom = top + v.getHeight(),\n                        right = left + v.getWidth();\n                return !(event.getRawX() > left && event.getRawX() < right\n                        && event.getRawY() > top && event.getRawY() < bottom);\n            }\n            return false;\n        }\n        */\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // interface\n    ///////////////////////////////////////////////////////////////////////////\n    public interface OnSoftInputChangedListener {\n        void onSoftInputChanged(int height);\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/LanguageUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.content.res.Configuration;\nimport android.content.res.Resources;\nimport android.os.Build;\nimport android.text.TextUtils;\nimport android.util.Log;\n\nimport java.util.Locale;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/06/20\n *     desc  : utils about language\n * </pre>\n */\npublic class LanguageUtils {\n\n    private static final String KEY_LOCALE          = \"KEY_LOCALE\";\n    private static final String VALUE_FOLLOW_SYSTEM = \"VALUE_FOLLOW_SYSTEM\";\n\n    private LanguageUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Apply the system language.\n     */\n    public static void applySystemLanguage() {\n        applySystemLanguage(false);\n    }\n\n    /**\n     * Apply the system language.\n     *\n     * @param isRelaunchApp True to relaunch app, false to recreate all activities.\n     */\n    public static void applySystemLanguage(final boolean isRelaunchApp) {\n        applyLanguageReal(null, isRelaunchApp);\n    }\n\n    /**\n     * Apply the language.\n     *\n     * @param locale The language of locale.\n     */\n    public static void applyLanguage(@NonNull final Locale locale) {\n        applyLanguage(locale, false);\n    }\n\n    /**\n     * Apply the language.\n     *\n     * @param locale        The language of locale.\n     * @param isRelaunchApp True to relaunch app, false to recreate all activities.\n     */\n    public static void applyLanguage(@NonNull final Locale locale,\n                                     final boolean isRelaunchApp) {\n        applyLanguageReal(locale, isRelaunchApp);\n    }\n\n    private static void applyLanguageReal(final Locale locale,\n                                          final boolean isRelaunchApp) {\n        if (locale == null) {\n            UtilsBridge.getSpUtils4Utils().put(KEY_LOCALE, VALUE_FOLLOW_SYSTEM, true);\n        } else {\n            UtilsBridge.getSpUtils4Utils().put(KEY_LOCALE, locale2String(locale), true);\n        }\n\n        Locale destLocal = locale == null ? getLocal(Resources.getSystem().getConfiguration()) : locale;\n        updateAppContextLanguage(destLocal, new Utils.Consumer<Boolean>() {\n            @Override\n            public void accept(Boolean success) {\n                if (success) {\n                    restart(isRelaunchApp);\n                } else {\n                    // use relaunch app\n                    UtilsBridge.relaunchApp();\n                }\n            }\n        });\n    }\n\n    private static void restart(final boolean isRelaunchApp) {\n        if (isRelaunchApp) {\n            UtilsBridge.relaunchApp();\n        } else {\n            for (Activity activity : UtilsBridge.getActivityList()) {\n                activity.recreate();\n            }\n        }\n    }\n\n    /**\n     * Return whether applied the language by {@link LanguageUtils}.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isAppliedLanguage() {\n        return getAppliedLanguage() != null;\n    }\n\n    /**\n     * Return whether applied the language by {@link LanguageUtils}.\n     *\n     * @param locale The locale.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isAppliedLanguage(@NonNull Locale locale) {\n        Locale appliedLocale = getAppliedLanguage();\n        if (appliedLocale == null) {\n            return false;\n        }\n        return isSameLocale(locale, appliedLocale);\n    }\n\n    /**\n     * Return the applied locale.\n     *\n     * @return the applied locale\n     */\n    public static Locale getAppliedLanguage() {\n        final String spLocaleStr = UtilsBridge.getSpUtils4Utils().getString(KEY_LOCALE);\n        if (TextUtils.isEmpty(spLocaleStr) || VALUE_FOLLOW_SYSTEM.equals(spLocaleStr)) {\n            return null;\n        }\n        return string2Locale(spLocaleStr);\n    }\n\n    /**\n     * Return the locale of context.\n     *\n     * @return the locale of context\n     */\n    public static Locale getContextLanguage(Context context) {\n        return getLocal(context.getResources().getConfiguration());\n    }\n\n    /**\n     * Return the locale of applicationContext.\n     *\n     * @return the locale of applicationContext\n     */\n    public static Locale getAppContextLanguage() {\n        return getContextLanguage(Utils.getApp());\n    }\n\n    /**\n     * Return the locale of system\n     *\n     * @return the locale of system\n     */\n    public static Locale getSystemLanguage() {\n        return getLocal(Resources.getSystem().getConfiguration());\n    }\n\n    /**\n     * Update the locale of applicationContext.\n     *\n     * @param destLocale The dest locale.\n     * @param consumer   The consumer.\n     */\n    public static void updateAppContextLanguage(@NonNull Locale destLocale, @Nullable Utils.Consumer<Boolean> consumer) {\n        pollCheckAppContextLocal(destLocale, 0, consumer);\n    }\n\n    static void pollCheckAppContextLocal(final Locale destLocale, final int index, final Utils.Consumer<Boolean> consumer) {\n        Resources appResources = Utils.getApp().getResources();\n        Configuration appConfig = appResources.getConfiguration();\n        Locale appLocal = getLocal(appConfig);\n\n        setLocal(appConfig, destLocale);\n\n        Utils.getApp().getResources().updateConfiguration(appConfig, appResources.getDisplayMetrics());\n\n        if (consumer == null) return;\n\n        if (isSameLocale(appLocal, destLocale)) {\n            consumer.accept(true);\n        } else {\n            if (index < 20) {\n                UtilsBridge.runOnUiThreadDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        pollCheckAppContextLocal(destLocale, index + 1, consumer);\n                    }\n                }, 16);\n                return;\n            }\n            Log.e(\"LanguageUtils\", \"appLocal didn't update.\");\n            consumer.accept(false);\n        }\n    }\n\n    /**\n     * If applyLanguage not work, try to call it in {@link Activity#attachBaseContext(Context)}.\n     *\n     * @param context The baseContext.\n     * @return the context with language\n     */\n    public static Context attachBaseContext(Context context) {\n        String spLocaleStr = UtilsBridge.getSpUtils4Utils().getString(KEY_LOCALE);\n        if (TextUtils.isEmpty(spLocaleStr) || VALUE_FOLLOW_SYSTEM.equals(spLocaleStr)) {\n            return context;\n        }\n\n        Locale settingsLocale = string2Locale(spLocaleStr);\n        if (settingsLocale == null) return context;\n\n        Resources resources = context.getResources();\n        Configuration config = resources.getConfiguration();\n\n        setLocal(config, settingsLocale);\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            return context.createConfigurationContext(config);\n        } else {\n            resources.updateConfiguration(config, resources.getDisplayMetrics());\n            return context;\n        }\n    }\n\n    static void applyLanguage(final Activity activity) {\n        String spLocale = UtilsBridge.getSpUtils4Utils().getString(KEY_LOCALE);\n        if (TextUtils.isEmpty(spLocale)) {\n            return;\n        }\n\n        Locale destLocal;\n        if (VALUE_FOLLOW_SYSTEM.equals(spLocale)) {\n            destLocal = getLocal(Resources.getSystem().getConfiguration());\n        } else {\n            destLocal = string2Locale(spLocale);\n        }\n\n        if (destLocal == null) return;\n\n        updateConfiguration(activity, destLocal);\n        updateConfiguration(Utils.getApp(), destLocal);\n    }\n\n    private static void updateConfiguration(Context context, Locale destLocal) {\n        Resources resources = context.getResources();\n        Configuration config = resources.getConfiguration();\n        setLocal(config, destLocal);\n        resources.updateConfiguration(config, resources.getDisplayMetrics());\n    }\n\n    private static String locale2String(Locale locale) {\n        String localLanguage = locale.getLanguage(); // this may be empty\n        String localCountry = locale.getCountry(); // this may be empty\n        return localLanguage + \"$\" + localCountry;\n    }\n\n    private static Locale string2Locale(String str) {\n        Locale locale = string2LocaleReal(str);\n        if (locale == null) {\n            Log.e(\"LanguageUtils\", \"The string of \" + str + \" is not in the correct format.\");\n            UtilsBridge.getSpUtils4Utils().remove(KEY_LOCALE);\n        }\n        return locale;\n    }\n\n    private static Locale string2LocaleReal(String str) {\n        if (!isRightFormatLocalStr(str)) {\n            return null;\n        }\n\n        try {\n            int splitIndex = str.indexOf(\"$\");\n            return new Locale(str.substring(0, splitIndex), str.substring(splitIndex + 1));\n        } catch (Exception ignore) {\n            return null;\n        }\n    }\n\n    private static boolean isRightFormatLocalStr(String localStr) {\n        char[] chars = localStr.toCharArray();\n        int count = 0;\n        for (char c : chars) {\n            if (c == '$') {\n                if (count >= 1) {\n                    return false;\n                }\n                ++count;\n            }\n        }\n        return count == 1;\n    }\n\n    private static boolean isSameLocale(Locale l0, Locale l1) {\n        return UtilsBridge.equals(l1.getLanguage(), l0.getLanguage())\n                && UtilsBridge.equals(l1.getCountry(), l0.getCountry());\n    }\n\n    private static Locale getLocal(Configuration configuration) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n            return configuration.getLocales().get(0);\n        } else {\n            return configuration.locale;\n        }\n    }\n\n    private static void setLocal(Configuration configuration, Locale locale) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            configuration.setLocale(locale);\n        } else {\n            configuration.locale = locale;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/LogUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.content.ClipData;\nimport android.content.ComponentName;\nimport android.content.Intent;\nimport android.graphics.Rect;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.util.Log;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.io.File;\nimport java.io.FilenameFilter;\nimport java.io.IOException;\nimport java.io.StringReader;\nimport java.io.StringWriter;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Date;\nimport java.util.Formatter;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.Executors;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\nimport javax.xml.transform.OutputKeys;\nimport javax.xml.transform.Source;\nimport javax.xml.transform.Transformer;\nimport javax.xml.transform.TransformerFactory;\nimport javax.xml.transform.stream.StreamResult;\nimport javax.xml.transform.stream.StreamSource;\n\nimport androidx.annotation.IntDef;\nimport androidx.annotation.IntRange;\nimport androidx.annotation.RequiresApi;\nimport androidx.collection.SimpleArrayMap;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/09/21\n *     desc  : utils about log\n * </pre>\n */\npublic final class LogUtils {\n\n    public static final int V = Log.VERBOSE;\n    public static final int D = Log.DEBUG;\n    public static final int I = Log.INFO;\n    public static final int W = Log.WARN;\n    public static final int E = Log.ERROR;\n    public static final int A = Log.ASSERT;\n\n    @IntDef({V, D, I, W, E, A})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface TYPE {\n    }\n\n    private static final char[] T = new char[]{'V', 'D', 'I', 'W', 'E', 'A'};\n\n    private static final int FILE = 0x10;\n    private static final int JSON = 0x20;\n    private static final int XML  = 0x30;\n\n    private static final String FILE_SEP       = System.getProperty(\"file.separator\");\n    private static final String LINE_SEP       = System.getProperty(\"line.separator\");\n    private static final String TOP_CORNER     = \"┌\";\n    private static final String MIDDLE_CORNER  = \"├\";\n    private static final String LEFT_BORDER    = \"│ \";\n    private static final String BOTTOM_CORNER  = \"└\";\n    private static final String SIDE_DIVIDER   =\n            \"────────────────────────────────────────────────────────\";\n    private static final String MIDDLE_DIVIDER =\n            \"┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄\";\n    private static final String TOP_BORDER     = TOP_CORNER + SIDE_DIVIDER + SIDE_DIVIDER;\n    private static final String MIDDLE_BORDER  = MIDDLE_CORNER + MIDDLE_DIVIDER + MIDDLE_DIVIDER;\n    private static final String BOTTOM_BORDER  = BOTTOM_CORNER + SIDE_DIVIDER + SIDE_DIVIDER;\n    private static final int    MAX_LEN        = 1100;// fit for Chinese character\n    private static final String NOTHING        = \"log nothing\";\n    private static final String NULL           = \"null\";\n    private static final String ARGS           = \"args\";\n    private static final String PLACEHOLDER    = \" \";\n    private static final Config CONFIG         = new Config();\n\n    private static SimpleDateFormat simpleDateFormat;\n\n    private static final ExecutorService EXECUTOR = Executors.newSingleThreadExecutor();\n\n    private static final SimpleArrayMap<Class, IFormatter> I_FORMATTER_MAP = new SimpleArrayMap<>();\n\n    private LogUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    public static Config getConfig() {\n        return CONFIG;\n    }\n\n    public static void v(final Object... contents) {\n        log(V, CONFIG.getGlobalTag(), contents);\n    }\n\n    public static void vTag(final String tag, final Object... contents) {\n        log(V, tag, contents);\n    }\n\n    public static void d(final Object... contents) {\n        log(D, CONFIG.getGlobalTag(), contents);\n    }\n\n    public static void dTag(final String tag, final Object... contents) {\n        log(D, tag, contents);\n    }\n\n    public static void i(final Object... contents) {\n        log(I, CONFIG.getGlobalTag(), contents);\n    }\n\n    public static void iTag(final String tag, final Object... contents) {\n        log(I, tag, contents);\n    }\n\n    public static void w(final Object... contents) {\n        log(W, CONFIG.getGlobalTag(), contents);\n    }\n\n    public static void wTag(final String tag, final Object... contents) {\n        log(W, tag, contents);\n    }\n\n    public static void e(final Object... contents) {\n        log(E, CONFIG.getGlobalTag(), contents);\n    }\n\n    public static void eTag(final String tag, final Object... contents) {\n        log(E, tag, contents);\n    }\n\n    public static void a(final Object... contents) {\n        log(A, CONFIG.getGlobalTag(), contents);\n    }\n\n    public static void aTag(final String tag, final Object... contents) {\n        log(A, tag, contents);\n    }\n\n    public static void file(final Object content) {\n        log(FILE | D, CONFIG.getGlobalTag(), content);\n    }\n\n    public static void file(@TYPE final int type, final Object content) {\n        log(FILE | type, CONFIG.getGlobalTag(), content);\n    }\n\n    public static void file(final String tag, final Object content) {\n        log(FILE | D, tag, content);\n    }\n\n    public static void file(@TYPE final int type, final String tag, final Object content) {\n        log(FILE | type, tag, content);\n    }\n\n    public static void json(final Object content) {\n        log(JSON | D, CONFIG.getGlobalTag(), content);\n    }\n\n    public static void json(@TYPE final int type, final Object content) {\n        log(JSON | type, CONFIG.getGlobalTag(), content);\n    }\n\n    public static void json(final String tag, final Object content) {\n        log(JSON | D, tag, content);\n    }\n\n    public static void json(@TYPE final int type, final String tag, final Object content) {\n        log(JSON | type, tag, content);\n    }\n\n    public static void xml(final String content) {\n        log(XML | D, CONFIG.getGlobalTag(), content);\n    }\n\n    public static void xml(@TYPE final int type, final String content) {\n        log(XML | type, CONFIG.getGlobalTag(), content);\n    }\n\n    public static void xml(final String tag, final String content) {\n        log(XML | D, tag, content);\n    }\n\n    public static void xml(@TYPE final int type, final String tag, final String content) {\n        log(XML | type, tag, content);\n    }\n\n    public static void log(final int type, final String tag, final Object... contents) {\n        if (!CONFIG.isLogSwitch()) return;\n        final int type_low = type & 0x0f, type_high = type & 0xf0;\n        if (CONFIG.isLog2ConsoleSwitch() || CONFIG.isLog2FileSwitch() || type_high == FILE) {\n            if (type_low < CONFIG.mConsoleFilter && type_low < CONFIG.mFileFilter) return;\n            final TagHead tagHead = processTagAndHead(tag);\n            final String body = processBody(type_high, contents);\n            if (CONFIG.isLog2ConsoleSwitch() && type_high != FILE && type_low >= CONFIG.mConsoleFilter) {\n                print2Console(type_low, tagHead.tag, tagHead.consoleHead, body);\n            }\n            if ((CONFIG.isLog2FileSwitch() || type_high == FILE) && type_low >= CONFIG.mFileFilter) {\n                EXECUTOR.execute(new Runnable() {\n                    @Override\n                    public void run() {\n                        print2File(type_low, tagHead.tag, tagHead.fileHead + body);\n                    }\n                });\n            }\n        }\n    }\n\n    public static String getCurrentLogFilePath() {\n        return getCurrentLogFilePath(new Date());\n    }\n\n    public static List<File> getLogFiles() {\n        String dir = CONFIG.getDir();\n        File logDir = new File(dir);\n        if (!logDir.exists()) return new ArrayList<>();\n        File[] files = logDir.listFiles(new FilenameFilter() {\n            @Override\n            public boolean accept(File dir, String name) {\n                return isMatchLogFileName(name);\n            }\n        });\n        List<File> list = new ArrayList<>();\n        Collections.addAll(list, files);\n        return list;\n    }\n\n    private static TagHead processTagAndHead(String tag) {\n        if (!CONFIG.mTagIsSpace && !CONFIG.isLogHeadSwitch()) {\n            tag = CONFIG.getGlobalTag();\n        } else {\n            final StackTraceElement[] stackTrace = new Throwable().getStackTrace();\n            final int stackIndex = 3 + CONFIG.getStackOffset();\n            if (stackIndex >= stackTrace.length) {\n                StackTraceElement targetElement = stackTrace[3];\n                final String fileName = getFileName(targetElement);\n                if (CONFIG.mTagIsSpace && UtilsBridge.isSpace(tag)) {\n                    int index = fileName.indexOf('.');// Use proguard may not find '.'.\n                    tag = index == -1 ? fileName : fileName.substring(0, index);\n                }\n                return new TagHead(tag, null, \": \");\n            }\n            StackTraceElement targetElement = stackTrace[stackIndex];\n            final String fileName = getFileName(targetElement);\n            if (CONFIG.mTagIsSpace && UtilsBridge.isSpace(tag)) {\n                int index = fileName.indexOf('.');// Use proguard may not find '.'.\n                tag = index == -1 ? fileName : fileName.substring(0, index);\n            }\n            if (CONFIG.isLogHeadSwitch()) {\n                String tName = Thread.currentThread().getName();\n                final String head = new Formatter()\n                        .format(\"%s, %s.%s(%s:%d)\",\n                                tName,\n                                targetElement.getClassName(),\n                                targetElement.getMethodName(),\n                                fileName,\n                                targetElement.getLineNumber())\n                        .toString();\n                final String fileHead = \" [\" + head + \"]: \";\n                if (CONFIG.getStackDeep() <= 1) {\n                    return new TagHead(tag, new String[]{head}, fileHead);\n                } else {\n                    final String[] consoleHead =\n                            new String[Math.min(\n                                    CONFIG.getStackDeep(),\n                                    stackTrace.length - stackIndex\n                            )];\n                    consoleHead[0] = head;\n                    int spaceLen = tName.length() + 2;\n                    String space = new Formatter().format(\"%\" + spaceLen + \"s\", \"\").toString();\n                    for (int i = 1, len = consoleHead.length; i < len; ++i) {\n                        targetElement = stackTrace[i + stackIndex];\n                        consoleHead[i] = new Formatter()\n                                .format(\"%s%s.%s(%s:%d)\",\n                                        space,\n                                        targetElement.getClassName(),\n                                        targetElement.getMethodName(),\n                                        getFileName(targetElement),\n                                        targetElement.getLineNumber())\n                                .toString();\n                    }\n                    return new TagHead(tag, consoleHead, fileHead);\n                }\n            }\n        }\n        return new TagHead(tag, null, \": \");\n    }\n\n    private static String getFileName(final StackTraceElement targetElement) {\n        String fileName = targetElement.getFileName();\n        if (fileName != null) return fileName;\n        // If name of file is null, should add\n        // \"-keepattributes SourceFile,LineNumberTable\" in proguard file.\n        String className = targetElement.getClassName();\n        String[] classNameInfo = className.split(\"\\\\.\");\n        if (classNameInfo.length > 0) {\n            className = classNameInfo[classNameInfo.length - 1];\n        }\n        int index = className.indexOf('$');\n        if (index != -1) {\n            className = className.substring(0, index);\n        }\n        return className + \".java\";\n    }\n\n    private static String processBody(final int type, final Object... contents) {\n        String body = NULL;\n        if (contents != null) {\n            if (contents.length == 1) {\n                body = formatObject(type, contents[0]);\n            } else {\n                StringBuilder sb = new StringBuilder();\n                for (int i = 0, len = contents.length; i < len; ++i) {\n                    Object content = contents[i];\n                    sb.append(ARGS)\n                            .append(\"[\")\n                            .append(i)\n                            .append(\"]\")\n                            .append(\" = \")\n                            .append(formatObject(content))\n                            .append(LINE_SEP);\n                }\n                body = sb.toString();\n            }\n        }\n        return body.length() == 0 ? NOTHING : body;\n    }\n\n    private static String formatObject(int type, Object object) {\n        if (object == null) return NULL;\n        if (type == JSON) return LogFormatter.object2String(object, JSON);\n        if (type == XML) return LogFormatter.object2String(object, XML);\n        return formatObject(object);\n    }\n\n    private static String formatObject(Object object) {\n        if (object == null) return NULL;\n        if (!I_FORMATTER_MAP.isEmpty()) {\n            IFormatter iFormatter = I_FORMATTER_MAP.get(getClassFromObject(object));\n            if (iFormatter != null) {\n                //noinspection unchecked\n                return iFormatter.format(object);\n            }\n        }\n        return LogFormatter.object2String(object);\n    }\n\n    private static void print2Console(final int type,\n                                      final String tag,\n                                      final String[] head,\n                                      final String msg) {\n        if (CONFIG.isSingleTagSwitch()) {\n            printSingleTagMsg(type, tag, processSingleTagMsg(type, tag, head, msg));\n        } else {\n            printBorder(type, tag, true);\n            printHead(type, tag, head);\n            printMsg(type, tag, msg);\n            printBorder(type, tag, false);\n        }\n    }\n\n    private static void printBorder(final int type, final String tag, boolean isTop) {\n        if (CONFIG.isLogBorderSwitch()) {\n            print2Console(type, tag, isTop ? TOP_BORDER : BOTTOM_BORDER);\n        }\n    }\n\n    private static void printHead(final int type, final String tag, final String[] head) {\n        if (head != null) {\n            for (String aHead : head) {\n                print2Console(type, tag, CONFIG.isLogBorderSwitch() ? LEFT_BORDER + aHead : aHead);\n            }\n            if (CONFIG.isLogBorderSwitch()) print2Console(type, tag, MIDDLE_BORDER);\n        }\n    }\n\n    private static void printMsg(final int type, final String tag, final String msg) {\n        int len = msg.length();\n        int countOfSub = len / MAX_LEN;\n        if (countOfSub > 0) {\n            int index = 0;\n            for (int i = 0; i < countOfSub; i++) {\n                printSubMsg(type, tag, msg.substring(index, index + MAX_LEN));\n                index += MAX_LEN;\n            }\n            if (index != len) {\n                printSubMsg(type, tag, msg.substring(index, len));\n            }\n        } else {\n            printSubMsg(type, tag, msg);\n        }\n    }\n\n    private static void printSubMsg(final int type, final String tag, final String msg) {\n        if (!CONFIG.isLogBorderSwitch()) {\n            print2Console(type, tag, msg);\n            return;\n        }\n        StringBuilder sb = new StringBuilder();\n        String[] lines = msg.split(LINE_SEP);\n        for (String line : lines) {\n            print2Console(type, tag, LEFT_BORDER + line);\n        }\n    }\n\n    private static String processSingleTagMsg(final int type,\n                                              final String tag,\n                                              final String[] head,\n                                              final String msg) {\n        StringBuilder sb = new StringBuilder();\n        if (CONFIG.isLogBorderSwitch()) {\n            sb.append(PLACEHOLDER).append(LINE_SEP);\n            sb.append(TOP_BORDER).append(LINE_SEP);\n            if (head != null) {\n                for (String aHead : head) {\n                    sb.append(LEFT_BORDER).append(aHead).append(LINE_SEP);\n                }\n                sb.append(MIDDLE_BORDER).append(LINE_SEP);\n            }\n            for (String line : msg.split(LINE_SEP)) {\n                sb.append(LEFT_BORDER).append(line).append(LINE_SEP);\n            }\n            sb.append(BOTTOM_BORDER);\n        } else {\n            if (head != null) {\n                sb.append(PLACEHOLDER).append(LINE_SEP);\n                for (String aHead : head) {\n                    sb.append(aHead).append(LINE_SEP);\n                }\n            }\n            sb.append(msg);\n        }\n        return sb.toString();\n    }\n\n    private static void printSingleTagMsg(final int type, final String tag, final String msg) {\n        int len = msg.length();\n        int countOfSub = CONFIG.isLogBorderSwitch() ? (len - BOTTOM_BORDER.length()) / MAX_LEN : len / MAX_LEN;\n        if (countOfSub > 0) {\n            if (CONFIG.isLogBorderSwitch()) {\n                print2Console(type, tag, msg.substring(0, MAX_LEN) + LINE_SEP + BOTTOM_BORDER);\n                int index = MAX_LEN;\n                for (int i = 1; i < countOfSub; i++) {\n                    print2Console(type, tag, PLACEHOLDER + LINE_SEP + TOP_BORDER + LINE_SEP\n                            + LEFT_BORDER + msg.substring(index, index + MAX_LEN)\n                            + LINE_SEP + BOTTOM_BORDER);\n                    index += MAX_LEN;\n                }\n                if (index != len - BOTTOM_BORDER.length()) {\n                    print2Console(type, tag, PLACEHOLDER + LINE_SEP + TOP_BORDER + LINE_SEP\n                            + LEFT_BORDER + msg.substring(index, len));\n                }\n            } else {\n                print2Console(type, tag, msg.substring(0, MAX_LEN));\n                int index = MAX_LEN;\n                for (int i = 1; i < countOfSub; i++) {\n                    print2Console(type, tag,\n                            PLACEHOLDER + LINE_SEP + msg.substring(index, index + MAX_LEN));\n                    index += MAX_LEN;\n                }\n                if (index != len) {\n                    print2Console(type, tag, PLACEHOLDER + LINE_SEP + msg.substring(index, len));\n                }\n            }\n        } else {\n            print2Console(type, tag, msg);\n        }\n    }\n\n    private static void print2Console(int type, String tag, String msg) {\n        Log.println(type, tag, msg);\n        if (CONFIG.mOnConsoleOutputListener != null) {\n            CONFIG.mOnConsoleOutputListener.onConsoleOutput(type, tag, msg);\n        }\n    }\n\n    private static void print2File(final int type, final String tag, final String msg) {\n        Date d = new Date();\n        String format = getSdf().format(d);\n        String date = format.substring(0, 10);\n        String currentLogFilePath = getCurrentLogFilePath(d);\n        if (!createOrExistsFile(currentLogFilePath, date)) {\n            Log.e(\"LogUtils\", \"create \" + currentLogFilePath + \" failed!\");\n            return;\n        }\n        String time = format.substring(11);\n        final String content = time +\n                T[type - V] +\n                \"/\" +\n                tag +\n                msg +\n                LINE_SEP;\n        input2File(currentLogFilePath, content);\n    }\n\n    private static String getCurrentLogFilePath(Date d) {\n        String format = getSdf().format(d);\n        String date = format.substring(0, 10);\n        return CONFIG.getDir() + CONFIG.getFilePrefix() + \"_\"\n                + date + \"_\" +\n                CONFIG.getProcessName() + CONFIG.getFileExtension();\n    }\n\n\n    private static SimpleDateFormat getSdf() {\n        if (simpleDateFormat == null) {\n            simpleDateFormat = new SimpleDateFormat(\"yyyy_MM_dd HH:mm:ss.SSS \", Locale.getDefault());\n        }\n        return simpleDateFormat;\n    }\n\n    private static boolean createOrExistsFile(final String filePath, final String date) {\n        File file = new File(filePath);\n        if (file.exists()) return file.isFile();\n        if (!UtilsBridge.createOrExistsDir(file.getParentFile())) return false;\n        try {\n            deleteDueLogs(filePath, date);\n            boolean isCreate = file.createNewFile();\n            if (isCreate) {\n                printDeviceInfo(filePath, date);\n            }\n            return isCreate;\n        } catch (IOException e) {\n            e.printStackTrace();\n            return false;\n        }\n    }\n\n    private static void deleteDueLogs(final String filePath, final String date) {\n        if (CONFIG.getSaveDays() <= 0) return;\n        File file = new File(filePath);\n        File parentFile = file.getParentFile();\n        File[] files = parentFile.listFiles(new FilenameFilter() {\n            @Override\n            public boolean accept(File dir, String name) {\n                return isMatchLogFileName(name);\n            }\n        });\n        if (files == null || files.length <= 0) return;\n        final SimpleDateFormat sdf = new SimpleDateFormat(\"yyyy_MM_dd\", Locale.getDefault());\n        try {\n            long dueMillis = sdf.parse(date).getTime() - CONFIG.getSaveDays() * 86400000L;\n            for (final File aFile : files) {\n                String name = aFile.getName();\n                int l = name.length();\n                String logDay = findDate(name);\n                if (sdf.parse(logDay).getTime() <= dueMillis) {\n                    EXECUTOR.execute(new Runnable() {\n                        @Override\n                        public void run() {\n                            boolean delete = aFile.delete();\n                            if (!delete) {\n                                Log.e(\"LogUtils\", \"delete \" + aFile + \" failed!\");\n                            }\n                        }\n                    });\n                }\n            }\n        } catch (ParseException e) {\n            e.printStackTrace();\n        }\n    }\n\n    private static boolean isMatchLogFileName(String name) {\n        return name.matches(\"^\" + CONFIG.getFilePrefix() + \"_[0-9]{4}_[0-9]{2}_[0-9]{2}_.*$\");\n    }\n\n    private static String findDate(String str) {\n        Pattern pattern = Pattern.compile(\"[0-9]{4}_[0-9]{2}_[0-9]{2}\");\n        Matcher matcher = pattern.matcher(str);\n        if (matcher.find()) {\n            return matcher.group();\n        }\n        return \"\";\n    }\n\n    private static void printDeviceInfo(final String filePath, final String date) {\n        CONFIG.mFileHead.addFirst(\"Date of Log\", date);\n        input2File(filePath, CONFIG.mFileHead.toString());\n    }\n\n    private static void input2File(final String filePath, final String input) {\n        if (CONFIG.mFileWriter == null) {\n            UtilsBridge.writeFileFromString(filePath, input, true);\n        } else {\n            CONFIG.mFileWriter.write(filePath, input);\n        }\n        if (CONFIG.mOnFileOutputListener != null) {\n            CONFIG.mOnFileOutputListener.onFileOutput(filePath, input);\n        }\n    }\n\n    public static final class Config {\n        private String                  mDefaultDir;                // The default storage directory of log.\n        private String                  mDir;                       // The storage directory of log.\n        private String                  mFilePrefix        = \"util\";// The file prefix of log.\n        private String                  mFileExtension     = \".txt\";// The file extension of log.\n        private boolean                 mLogSwitch         = true;  // The switch of log.\n        private boolean                 mLog2ConsoleSwitch = true;  // The logcat's switch of log.\n        private String                  mGlobalTag         = \"\";    // The global tag of log.\n        private boolean                 mTagIsSpace        = true;  // The global tag is space.\n        private boolean                 mLogHeadSwitch     = true;  // The head's switch of log.\n        private boolean                 mLog2FileSwitch    = false; // The file's switch of log.\n        private boolean                 mLogBorderSwitch   = true;  // The border's switch of log.\n        private boolean                 mSingleTagSwitch   = true;  // The single tag of log.\n        private int                     mConsoleFilter     = V;     // The console's filter of log.\n        private int                     mFileFilter        = V;     // The file's filter of log.\n        private int                     mStackDeep         = 1;     // The stack's deep of log.\n        private int                     mStackOffset       = 0;     // The stack's offset of log.\n        private int                     mSaveDays          = -1;    // The save days of log.\n        private String                  mProcessName       = UtilsBridge.getCurrentProcessName();\n        private IFileWriter             mFileWriter;\n        private OnConsoleOutputListener mOnConsoleOutputListener;\n        private OnFileOutputListener    mOnFileOutputListener;\n        private UtilsBridge.FileHead    mFileHead          = new UtilsBridge.FileHead(\"Log\");\n\n        private Config() {\n            if (UtilsBridge.isSDCardEnableByEnvironment()\n                    && Utils.getApp().getExternalFilesDir(null) != null)\n                mDefaultDir = Utils.getApp().getExternalFilesDir(null) + FILE_SEP + \"log\" + FILE_SEP;\n            else {\n                mDefaultDir = Utils.getApp().getFilesDir() + FILE_SEP + \"log\" + FILE_SEP;\n            }\n        }\n\n        public final Config setLogSwitch(final boolean logSwitch) {\n            mLogSwitch = logSwitch;\n            return this;\n        }\n\n        public final Config setConsoleSwitch(final boolean consoleSwitch) {\n            mLog2ConsoleSwitch = consoleSwitch;\n            return this;\n        }\n\n        public final Config setGlobalTag(final String tag) {\n            if (UtilsBridge.isSpace(tag)) {\n                mGlobalTag = \"\";\n                mTagIsSpace = true;\n            } else {\n                mGlobalTag = tag;\n                mTagIsSpace = false;\n            }\n            return this;\n        }\n\n        public final Config setLogHeadSwitch(final boolean logHeadSwitch) {\n            mLogHeadSwitch = logHeadSwitch;\n            return this;\n        }\n\n        public final Config setLog2FileSwitch(final boolean log2FileSwitch) {\n            mLog2FileSwitch = log2FileSwitch;\n            return this;\n        }\n\n        public final Config setDir(final String dir) {\n            if (UtilsBridge.isSpace(dir)) {\n                mDir = null;\n            } else {\n                mDir = dir.endsWith(FILE_SEP) ? dir : dir + FILE_SEP;\n            }\n            return this;\n        }\n\n        public final Config setDir(final File dir) {\n            mDir = dir == null ? null : (dir.getAbsolutePath() + FILE_SEP);\n            return this;\n        }\n\n        public final Config setFilePrefix(final String filePrefix) {\n            if (UtilsBridge.isSpace(filePrefix)) {\n                mFilePrefix = \"util\";\n            } else {\n                mFilePrefix = filePrefix;\n            }\n            return this;\n        }\n\n        public final Config setFileExtension(final String fileExtension) {\n            if (UtilsBridge.isSpace(fileExtension)) {\n                mFileExtension = \".txt\";\n            } else {\n                if (fileExtension.startsWith(\".\")) {\n                    mFileExtension = fileExtension;\n                } else {\n                    mFileExtension = \".\" + fileExtension;\n                }\n            }\n            return this;\n        }\n\n        public final Config setBorderSwitch(final boolean borderSwitch) {\n            mLogBorderSwitch = borderSwitch;\n            return this;\n        }\n\n        public final Config setSingleTagSwitch(final boolean singleTagSwitch) {\n            mSingleTagSwitch = singleTagSwitch;\n            return this;\n        }\n\n        public final Config setConsoleFilter(@TYPE final int consoleFilter) {\n            mConsoleFilter = consoleFilter;\n            return this;\n        }\n\n        public final Config setFileFilter(@TYPE final int fileFilter) {\n            mFileFilter = fileFilter;\n            return this;\n        }\n\n        public final Config setStackDeep(@IntRange(from = 1) final int stackDeep) {\n            mStackDeep = stackDeep;\n            return this;\n        }\n\n        public final Config setStackOffset(@IntRange(from = 0) final int stackOffset) {\n            mStackOffset = stackOffset;\n            return this;\n        }\n\n        public final Config setSaveDays(@IntRange(from = 1) final int saveDays) {\n            mSaveDays = saveDays;\n            return this;\n        }\n\n        public final <T> Config addFormatter(final IFormatter<T> iFormatter) {\n            if (iFormatter != null) {\n                I_FORMATTER_MAP.put(getTypeClassFromParadigm(iFormatter), iFormatter);\n            }\n            return this;\n        }\n\n        public final Config setFileWriter(final IFileWriter fileWriter) {\n            mFileWriter = fileWriter;\n            return this;\n        }\n\n        public final Config setOnConsoleOutputListener(final OnConsoleOutputListener listener) {\n            mOnConsoleOutputListener = listener;\n            return this;\n        }\n\n        public final Config setOnFileOutputListener(final OnFileOutputListener listener) {\n            mOnFileOutputListener = listener;\n            return this;\n        }\n\n        public final Config addFileExtraHead(final Map<String, String> fileExtraHead) {\n            mFileHead.append(fileExtraHead);\n            return this;\n        }\n\n        public final Config addFileExtraHead(final String key, final String value) {\n            mFileHead.append(key, value);\n            return this;\n        }\n\n        public final String getProcessName() {\n            if (mProcessName == null) return \"\";\n            return mProcessName.replace(\":\", \"_\");\n        }\n\n        public final String getDefaultDir() {\n            return mDefaultDir;\n        }\n\n        public final String getDir() {\n            return mDir == null ? mDefaultDir : mDir;\n        }\n\n        public final String getFilePrefix() {\n            return mFilePrefix;\n        }\n\n        public final String getFileExtension() {\n            return mFileExtension;\n        }\n\n        public final boolean isLogSwitch() {\n            return mLogSwitch;\n        }\n\n        public final boolean isLog2ConsoleSwitch() {\n            return mLog2ConsoleSwitch;\n        }\n\n        public final String getGlobalTag() {\n            if (UtilsBridge.isSpace(mGlobalTag)) return \"\";\n            return mGlobalTag;\n        }\n\n        public final boolean isLogHeadSwitch() {\n            return mLogHeadSwitch;\n        }\n\n        public final boolean isLog2FileSwitch() {\n            return mLog2FileSwitch;\n        }\n\n        public final boolean isLogBorderSwitch() {\n            return mLogBorderSwitch;\n        }\n\n        public final boolean isSingleTagSwitch() {\n            return mSingleTagSwitch;\n        }\n\n        public final char getConsoleFilter() {\n            return T[mConsoleFilter - V];\n        }\n\n        public final char getFileFilter() {\n            return T[mFileFilter - V];\n        }\n\n        public final int getStackDeep() {\n            return mStackDeep;\n        }\n\n        public final int getStackOffset() {\n            return mStackOffset;\n        }\n\n        public final int getSaveDays() {\n            return mSaveDays;\n        }\n\n        public final boolean haveSetOnConsoleOutputListener() {\n            return mOnConsoleOutputListener != null;\n        }\n\n        public final boolean haveSetOnFileOutputListener() {\n            return mOnFileOutputListener != null;\n        }\n\n        @Override\n        public String toString() {\n            return \"process: \" + getProcessName()\n                    + LINE_SEP + \"logSwitch: \" + isLogSwitch()\n                    + LINE_SEP + \"consoleSwitch: \" + isLog2ConsoleSwitch()\n                    + LINE_SEP + \"tag: \" + (getGlobalTag().equals(\"\") ? \"null\" : getGlobalTag())\n                    + LINE_SEP + \"headSwitch: \" + isLogHeadSwitch()\n                    + LINE_SEP + \"fileSwitch: \" + isLog2FileSwitch()\n                    + LINE_SEP + \"dir: \" + getDir()\n                    + LINE_SEP + \"filePrefix: \" + getFilePrefix()\n                    + LINE_SEP + \"borderSwitch: \" + isLogBorderSwitch()\n                    + LINE_SEP + \"singleTagSwitch: \" + isSingleTagSwitch()\n                    + LINE_SEP + \"consoleFilter: \" + getConsoleFilter()\n                    + LINE_SEP + \"fileFilter: \" + getFileFilter()\n                    + LINE_SEP + \"stackDeep: \" + getStackDeep()\n                    + LINE_SEP + \"stackOffset: \" + getStackOffset()\n                    + LINE_SEP + \"saveDays: \" + getSaveDays()\n                    + LINE_SEP + \"formatter: \" + I_FORMATTER_MAP\n                    + LINE_SEP + \"fileWriter: \" + mFileWriter\n                    + LINE_SEP + \"onConsoleOutputListener: \" + mOnConsoleOutputListener\n                    + LINE_SEP + \"onFileOutputListener: \" + mOnFileOutputListener\n                    + LINE_SEP + \"fileExtraHeader: \" + mFileHead.getAppended();\n        }\n    }\n\n    public abstract static class IFormatter<T> {\n        public abstract String format(T t);\n    }\n\n    public interface IFileWriter {\n        void write(String file, String content);\n    }\n\n    public interface OnConsoleOutputListener {\n        void onConsoleOutput(@TYPE int type, String tag, String content);\n    }\n\n    public interface OnFileOutputListener {\n        void onFileOutput(String filePath, String content);\n    }\n\n    private final static class TagHead {\n        String   tag;\n        String[] consoleHead;\n        String   fileHead;\n\n        TagHead(String tag, String[] consoleHead, String fileHead) {\n            this.tag = tag;\n            this.consoleHead = consoleHead;\n            this.fileHead = fileHead;\n        }\n    }\n\n    private final static class LogFormatter {\n\n        static String object2String(Object object) {\n            return object2String(object, -1);\n        }\n\n        static String object2String(Object object, int type) {\n            if (object.getClass().isArray()) return array2String(object);\n            if (object instanceof Throwable)\n                return UtilsBridge.getFullStackTrace((Throwable) object);\n            if (object instanceof Bundle) return bundle2String((Bundle) object);\n            if (object instanceof Intent) return intent2String((Intent) object);\n            if (type == JSON) {\n                return object2Json(object);\n            } else if (type == XML) {\n                return formatXml(object.toString());\n            }\n            return object.toString();\n        }\n\n        private static String bundle2String(Bundle bundle) {\n            Iterator<String> iterator = bundle.keySet().iterator();\n            if (!iterator.hasNext()) {\n                return \"Bundle {}\";\n            }\n            StringBuilder sb = new StringBuilder(128);\n            sb.append(\"Bundle { \");\n            for (; ; ) {\n                String key = iterator.next();\n                Object value = bundle.get(key);\n                sb.append(key).append('=');\n                if (value instanceof Bundle) {\n                    sb.append(value == bundle ? \"(this Bundle)\" : bundle2String((Bundle) value));\n                } else {\n                    sb.append(formatObject(value));\n                }\n                if (!iterator.hasNext()) return sb.append(\" }\").toString();\n                sb.append(',').append(' ');\n            }\n        }\n\n        private static String intent2String(Intent intent) {\n            StringBuilder sb = new StringBuilder(128);\n            sb.append(\"Intent { \");\n            boolean first = true;\n            String mAction = intent.getAction();\n            if (mAction != null) {\n                sb.append(\"act=\").append(mAction);\n                first = false;\n            }\n            Set<String> mCategories = intent.getCategories();\n            if (mCategories != null) {\n                if (!first) {\n                    sb.append(' ');\n                }\n                first = false;\n                sb.append(\"cat=[\");\n                boolean firstCategory = true;\n                for (String c : mCategories) {\n                    if (!firstCategory) {\n                        sb.append(',');\n                    }\n                    sb.append(c);\n                    firstCategory = false;\n                }\n                sb.append(\"]\");\n            }\n            Uri mData = intent.getData();\n            if (mData != null) {\n                if (!first) {\n                    sb.append(' ');\n                }\n                first = false;\n                sb.append(\"dat=\").append(mData);\n            }\n            String mType = intent.getType();\n            if (mType != null) {\n                if (!first) {\n                    sb.append(' ');\n                }\n                first = false;\n                sb.append(\"typ=\").append(mType);\n            }\n            int mFlags = intent.getFlags();\n            if (mFlags != 0) {\n                if (!first) {\n                    sb.append(' ');\n                }\n                first = false;\n                sb.append(\"flg=0x\").append(Integer.toHexString(mFlags));\n            }\n            String mPackage = intent.getPackage();\n            if (mPackage != null) {\n                if (!first) {\n                    sb.append(' ');\n                }\n                first = false;\n                sb.append(\"pkg=\").append(mPackage);\n            }\n            ComponentName mComponent = intent.getComponent();\n            if (mComponent != null) {\n                if (!first) {\n                    sb.append(' ');\n                }\n                first = false;\n                sb.append(\"cmp=\").append(mComponent.flattenToShortString());\n            }\n            Rect mSourceBounds = intent.getSourceBounds();\n            if (mSourceBounds != null) {\n                if (!first) {\n                    sb.append(' ');\n                }\n                first = false;\n                sb.append(\"bnds=\").append(mSourceBounds.toShortString());\n            }\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n                ClipData mClipData = intent.getClipData();\n                if (mClipData != null) {\n                    if (!first) {\n                        sb.append(' ');\n                    }\n                    first = false;\n                    clipData2String(mClipData, sb);\n                }\n            }\n            Bundle mExtras = intent.getExtras();\n            if (mExtras != null) {\n                if (!first) {\n                    sb.append(' ');\n                }\n                first = false;\n                sb.append(\"extras={\");\n                sb.append(bundle2String(mExtras));\n                sb.append('}');\n            }\n            if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {\n                Intent mSelector = intent.getSelector();\n                if (mSelector != null) {\n                    if (!first) {\n                        sb.append(' ');\n                    }\n                    first = false;\n                    sb.append(\"sel={\");\n                    sb.append(mSelector == intent ? \"(this Intent)\" : intent2String(mSelector));\n                    sb.append(\"}\");\n                }\n            }\n            sb.append(\" }\");\n            return sb.toString();\n        }\n\n        @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)\n        private static void clipData2String(ClipData clipData, StringBuilder sb) {\n            ClipData.Item item = clipData.getItemAt(0);\n            if (item == null) {\n                sb.append(\"ClipData.Item {}\");\n                return;\n            }\n            sb.append(\"ClipData.Item { \");\n            String mHtmlText = item.getHtmlText();\n            if (mHtmlText != null) {\n                sb.append(\"H:\");\n                sb.append(mHtmlText);\n                sb.append(\"}\");\n                return;\n            }\n            CharSequence mText = item.getText();\n            if (mText != null) {\n                sb.append(\"T:\");\n                sb.append(mText);\n                sb.append(\"}\");\n                return;\n            }\n            Uri uri = item.getUri();\n            if (uri != null) {\n                sb.append(\"U:\").append(uri);\n                sb.append(\"}\");\n                return;\n            }\n            Intent intent = item.getIntent();\n            if (intent != null) {\n                sb.append(\"I:\");\n                sb.append(intent2String(intent));\n                sb.append(\"}\");\n                return;\n            }\n            sb.append(\"NULL\");\n            sb.append(\"}\");\n        }\n\n        private static String object2Json(Object object) {\n            if (object instanceof CharSequence) {\n                return UtilsBridge.formatJson(object.toString());\n            }\n            try {\n                return UtilsBridge.getGson4LogUtils().toJson(object);\n            } catch (Throwable t) {\n                return object.toString();\n            }\n        }\n\n        private static String formatJson(String json) {\n            try {\n                for (int i = 0, len = json.length(); i < len; i++) {\n                    char c = json.charAt(i);\n                    if (c == '{') {\n                        return new JSONObject(json).toString(2);\n                    } else if (c == '[') {\n                        return new JSONArray(json).toString(2);\n                    } else if (!Character.isWhitespace(c)) {\n                        return json;\n                    }\n                }\n            } catch (JSONException e) {\n                e.printStackTrace();\n            }\n            return json;\n        }\n\n        private static String formatXml(String xml) {\n            try {\n                Source xmlInput = new StreamSource(new StringReader(xml));\n                StreamResult xmlOutput = new StreamResult(new StringWriter());\n                Transformer transformer = TransformerFactory.newInstance().newTransformer();\n                transformer.setOutputProperty(OutputKeys.INDENT, \"yes\");\n                transformer.setOutputProperty(\"{http://xml.apache.org/xslt}indent-amount\", \"2\");\n                transformer.transform(xmlInput, xmlOutput);\n                xml = xmlOutput.getWriter().toString().replaceFirst(\">\", \">\" + LINE_SEP);\n            } catch (Exception e) {\n                e.printStackTrace();\n            }\n            return xml;\n        }\n\n        private static String array2String(Object object) {\n            if (object instanceof Object[]) {\n                return Arrays.deepToString((Object[]) object);\n            } else if (object instanceof boolean[]) {\n                return Arrays.toString((boolean[]) object);\n            } else if (object instanceof byte[]) {\n                return Arrays.toString((byte[]) object);\n            } else if (object instanceof char[]) {\n                return Arrays.toString((char[]) object);\n            } else if (object instanceof double[]) {\n                return Arrays.toString((double[]) object);\n            } else if (object instanceof float[]) {\n                return Arrays.toString((float[]) object);\n            } else if (object instanceof int[]) {\n                return Arrays.toString((int[]) object);\n            } else if (object instanceof long[]) {\n                return Arrays.toString((long[]) object);\n            } else if (object instanceof short[]) {\n                return Arrays.toString((short[]) object);\n            }\n            throw new IllegalArgumentException(\"Array has incompatible type: \" + object.getClass());\n        }\n    }\n\n    private static <T> Class getTypeClassFromParadigm(final IFormatter<T> formatter) {\n        Type[] genericInterfaces = formatter.getClass().getGenericInterfaces();\n        Type type;\n        if (genericInterfaces.length == 1) {\n            type = genericInterfaces[0];\n        } else {\n            type = formatter.getClass().getGenericSuperclass();\n        }\n        type = ((ParameterizedType) type).getActualTypeArguments()[0];\n        while (type instanceof ParameterizedType) {\n            type = ((ParameterizedType) type).getRawType();\n        }\n        String className = type.toString();\n        if (className.startsWith(\"class \")) {\n            className = className.substring(6);\n        } else if (className.startsWith(\"interface \")) {\n            className = className.substring(10);\n        }\n        try {\n            return Class.forName(className);\n        } catch (ClassNotFoundException e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    private static Class getClassFromObject(final Object obj) {\n        Class objClass = obj.getClass();\n        if (objClass.isAnonymousClass() || objClass.isSynthetic()) {\n            Type[] genericInterfaces = objClass.getGenericInterfaces();\n            String className;\n            if (genericInterfaces.length == 1) {// interface\n                Type type = genericInterfaces[0];\n                while (type instanceof ParameterizedType) {\n                    type = ((ParameterizedType) type).getRawType();\n                }\n                className = type.toString();\n            } else {// abstract class or lambda\n                Type type = objClass.getGenericSuperclass();\n                while (type instanceof ParameterizedType) {\n                    type = ((ParameterizedType) type).getRawType();\n                }\n                className = type.toString();\n            }\n\n            if (className.startsWith(\"class \")) {\n                className = className.substring(6);\n            } else if (className.startsWith(\"interface \")) {\n                className = className.substring(10);\n            }\n            try {\n                return Class.forName(className);\n            } catch (ClassNotFoundException e) {\n                e.printStackTrace();\n            }\n        }\n        return objClass;\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/MapUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.util.Pair;\n\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.Hashtable;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.TreeMap;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/12\n *     desc  : utils about map\n * </pre>\n */\npublic class MapUtils {\n\n    private MapUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Returns a new read-only map with the specified contents, given as a list of pairs\n     * where the first value is the key and the second is the value.\n     *\n     * @param pairs a list of pairs\n     * @return a new read-only map with the specified contents\n     */\n    @SafeVarargs\n    public static <K, V> Map<K, V> newUnmodifiableMap(final Pair<K, V>... pairs) {\n        return Collections.unmodifiableMap(newHashMap(pairs));\n    }\n\n    @SafeVarargs\n    public static <K, V> HashMap<K, V> newHashMap(final Pair<K, V>... pairs) {\n        HashMap<K, V> map = new HashMap<>();\n        if (pairs == null || pairs.length == 0) {\n            return map;\n        }\n        for (Pair<K, V> pair : pairs) {\n            if (pair == null) continue;\n            map.put(pair.first, pair.second);\n        }\n        return map;\n    }\n\n    @SafeVarargs\n    public static <K, V> LinkedHashMap<K, V> newLinkedHashMap(final Pair<K, V>... pairs) {\n        LinkedHashMap<K, V> map = new LinkedHashMap<>();\n        if (pairs == null || pairs.length == 0) {\n            return map;\n        }\n        for (Pair<K, V> pair : pairs) {\n            if (pair == null) continue;\n            map.put(pair.first, pair.second);\n        }\n        return map;\n    }\n\n    @SafeVarargs\n    public static <K, V> TreeMap<K, V> newTreeMap(final Comparator<K> comparator,\n                                                  final Pair<K, V>... pairs) {\n        if (comparator == null) {\n            throw new IllegalArgumentException(\"comparator must not be null\");\n        }\n        TreeMap<K, V> map = new TreeMap<>(comparator);\n        if (pairs == null || pairs.length == 0) {\n            return map;\n        }\n        for (Pair<K, V> pair : pairs) {\n            if (pair == null) continue;\n            map.put(pair.first, pair.second);\n        }\n        return map;\n    }\n\n    @SafeVarargs\n    public static <K, V> Hashtable<K, V> newHashTable(final Pair<K, V>... pairs) {\n        Hashtable<K, V> map = new Hashtable<>();\n        if (pairs == null || pairs.length == 0) {\n            return map;\n        }\n        for (Pair<K, V> pair : pairs) {\n            if (pair == null) continue;\n            map.put(pair.first, pair.second);\n        }\n        return map;\n    }\n\n    /**\n     * Null-safe check if the specified map is empty.\n     * <p>\n     * Null returns true.\n     *\n     * @param map the map to check, may be null\n     * @return true if empty or null\n     */\n    public static boolean isEmpty(Map map) {\n        return map == null || map.size() == 0;\n    }\n\n    /**\n     * Null-safe check if the specified map is not empty.\n     * <p>\n     * Null returns false.\n     *\n     * @param map the map to check, may be null\n     * @return true if non-null and non-empty\n     */\n    public static boolean isNotEmpty(Map map) {\n        return !isEmpty(map);\n    }\n\n    /**\n     * Gets the size of the map specified.\n     *\n     * @param map The map.\n     * @return the size of the map specified\n     */\n    public static int size(Map map) {\n        if (map == null) return 0;\n        return map.size();\n    }\n\n    /**\n     * Executes the given closure on each element in the collection.\n     * <p>\n     * If the input collection or closure is null, there is no change made.\n     *\n     * @param map     the map to get the input from, may be null\n     * @param closure the closure to perform, may be null\n     */\n    public static <K, V> void forAllDo(Map<K, V> map, Closure<K, V> closure) {\n        if (map == null || closure == null) return;\n        for (Map.Entry<K, V> kvEntry : map.entrySet()) {\n            closure.execute(kvEntry.getKey(), kvEntry.getValue());\n        }\n    }\n\n    /**\n     * Transform the map by applying a Transformer to each element.\n     * <p>\n     * If the input map or transformer is null, there is no change made.\n     *\n     * @param map         the map to get the input from, may be null\n     * @param transformer the transformer to perform, may be null\n     */\n    public static <K1, V1, K2, V2> Map<K2, V2> transform(Map<K1, V1> map, final Transformer<K1, V1, K2, V2> transformer) {\n        if (map == null || transformer == null) return null;\n        try {\n            final Map<K2, V2> transMap = map.getClass().newInstance();\n            forAllDo(map, new Closure<K1, V1>() {\n                @Override\n                public void execute(K1 key, V1 value) {\n                    Pair<K2, V2> pair = transformer.transform(key, value);\n                    transMap.put(pair.first, pair.second);\n                }\n            });\n            return transMap;\n        } catch (IllegalAccessException e) {\n            e.printStackTrace();\n        } catch (InstantiationException e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    /**\n     * Return the string of map.\n     *\n     * @param map The map.\n     * @return the string of map\n     */\n    public static String toString(Map map) {\n        if (map == null) return \"null\";\n        return map.toString();\n    }\n\n    public interface Closure<K, V> {\n        void execute(K key, V value);\n    }\n\n    public interface Transformer<K1, V1, K2, V2> {\n        Pair<K2, V2> transform(K1 k1, V1 v1);\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/MessengerUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.annotation.SuppressLint;\nimport android.app.Notification;\nimport android.app.Service;\nimport android.content.ComponentName;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.ServiceConnection;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.IBinder;\nimport android.os.Message;\nimport android.os.Messenger;\nimport android.os.RemoteException;\nimport android.text.TextUtils;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.HashMap;\nimport java.util.LinkedList;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/10\n *     desc  : utils about messenger\n * </pre>\n */\npublic class MessengerUtils {\n\n    private static ConcurrentHashMap<String, MessageCallback> subscribers = new ConcurrentHashMap<>();\n\n    private static Map<String, Client> sClientMap = new HashMap<>();\n    private static Client              sLocalClient;\n\n    private static final int    WHAT_SUBSCRIBE   = 0x00;\n    private static final int    WHAT_UNSUBSCRIBE = 0x01;\n    private static final int    WHAT_SEND        = 0x02;\n    private static final String KEY_STRING       = \"MESSENGER_UTILS\";\n\n    public static void register() {\n        if (UtilsBridge.isMainProcess()) {\n            if (UtilsBridge.isServiceRunning(ServerService.class.getName())) {\n                Log.i(\"MessengerUtils\", \"Server service is running.\");\n                return;\n            }\n            startServiceCompat(new Intent(Utils.getApp(), ServerService.class));\n            return;\n        }\n        if (sLocalClient == null) {\n            Client client = new Client(null);\n            if (client.bind()) {\n                sLocalClient = client;\n            } else {\n                Log.e(\"MessengerUtils\", \"Bind service failed.\");\n            }\n        } else {\n            Log.i(\"MessengerUtils\", \"The client have been bind.\");\n        }\n    }\n\n    public static void unregister() {\n        if (UtilsBridge.isMainProcess()) {\n            if (!UtilsBridge.isServiceRunning(ServerService.class.getName())) {\n                Log.i(\"MessengerUtils\", \"Server service isn't running.\");\n                return;\n            }\n            Intent intent = new Intent(Utils.getApp(), ServerService.class);\n            Utils.getApp().stopService(intent);\n        }\n        if (sLocalClient != null) {\n            sLocalClient.unbind();\n        }\n    }\n\n    public static void register(final String pkgName) {\n        if (sClientMap.containsKey(pkgName)) {\n            Log.i(\"MessengerUtils\", \"register: client registered: \" + pkgName);\n            return;\n        }\n        Client client = new Client(pkgName);\n        if (client.bind()) {\n            sClientMap.put(pkgName, client);\n        } else {\n            Log.e(\"MessengerUtils\", \"register: client bind failed: \" + pkgName);\n        }\n    }\n\n    public static void unregister(final String pkgName) {\n        if (!sClientMap.containsKey(pkgName)) {\n            Log.i(\"MessengerUtils\", \"unregister: client didn't register: \" + pkgName);\n            return;\n        }\n        Client client = sClientMap.get(pkgName);\n        sClientMap.remove(pkgName);\n        if (client != null) {\n            client.unbind();\n        }\n    }\n\n    public static void subscribe(@NonNull final String key, @NonNull final MessageCallback callback) {\n        subscribers.put(key, callback);\n    }\n\n    public static void unsubscribe(@NonNull final String key) {\n        subscribers.remove(key);\n    }\n\n    public static void post(@NonNull String key, @NonNull Bundle data) {\n        data.putString(KEY_STRING, key);\n        if (sLocalClient != null) {\n            sLocalClient.sendMsg2Server(data);\n        } else {\n            Intent intent = new Intent(Utils.getApp(), ServerService.class);\n            intent.putExtras(data);\n            startServiceCompat(intent);\n        }\n        for (Client client : sClientMap.values()) {\n            client.sendMsg2Server(data);\n        }\n    }\n\n    private static void startServiceCompat(Intent intent) {\n        try {\n            intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                Utils.getApp().startForegroundService(intent);\n            } else {\n                Utils.getApp().startService(intent);\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    static class Client {\n\n        String             mPkgName;\n        Messenger          mServer;\n        LinkedList<Bundle> mCached = new LinkedList<>();\n        @SuppressLint(\"HandlerLeak\")\n        Handler mReceiveServeMsgHandler = new Handler() {\n            @Override\n            public void handleMessage(Message msg) {\n                Bundle data = msg.getData();\n                data.setClassLoader(MessengerUtils.class.getClassLoader());\n                String key = data.getString(KEY_STRING);\n                if (key != null) {\n                    MessageCallback callback = subscribers.get(key);\n                    if (callback != null) {\n                        callback.messageCall(data);\n                    }\n                }\n            }\n        };\n        Messenger         mClient = new Messenger(mReceiveServeMsgHandler);\n        ServiceConnection mConn   = new ServiceConnection() {\n\n            @Override\n            public void onServiceConnected(ComponentName name, IBinder service) {\n                Log.d(\"MessengerUtils\", \"client service connected \" + name);\n                mServer = new Messenger(service);\n                int key = UtilsBridge.getCurrentProcessName().hashCode();\n                Message msg = Message.obtain(mReceiveServeMsgHandler, WHAT_SUBSCRIBE, key, 0);\n                msg.getData().setClassLoader(MessengerUtils.class.getClassLoader());\n                msg.replyTo = mClient;\n                try {\n                    mServer.send(msg);\n                } catch (RemoteException e) {\n                    e.printStackTrace();\n                }\n                sendCachedMsg2Server();\n            }\n\n            @Override\n            public void onServiceDisconnected(ComponentName name) {\n                Log.w(\"MessengerUtils\", \"client service disconnected:\" + name);\n                mServer = null;\n                if (!bind()) {\n                    Log.e(\"MessengerUtils\", \"client service rebind failed: \" + name);\n                }\n            }\n        };\n\n        Client(String pkgName) {\n            this.mPkgName = pkgName;\n        }\n\n        boolean bind() {\n            if (TextUtils.isEmpty(mPkgName)) {\n                Intent intent = new Intent(Utils.getApp(), ServerService.class);\n                return Utils.getApp().bindService(intent, mConn, Context.BIND_AUTO_CREATE);\n            }\n            if (UtilsBridge.isAppInstalled(mPkgName)) {\n                if (UtilsBridge.isAppRunning(mPkgName)) {\n                    Intent intent = new Intent(mPkgName + \".messenger\");\n                    intent.setPackage(mPkgName);\n                    return Utils.getApp().bindService(intent, mConn, Context.BIND_AUTO_CREATE);\n                } else {\n                    Log.e(\"MessengerUtils\", \"bind: the app is not running -> \" + mPkgName);\n                    return false;\n                }\n            } else {\n                Log.e(\"MessengerUtils\", \"bind: the app is not installed -> \" + mPkgName);\n                return false;\n            }\n        }\n\n        void unbind() {\n            int key = UtilsBridge.getCurrentProcessName().hashCode();\n            Message msg = Message.obtain(mReceiveServeMsgHandler, WHAT_UNSUBSCRIBE, key, 0);\n            msg.replyTo = mClient;\n            try {\n                mServer.send(msg);\n            } catch (RemoteException e) {\n                e.printStackTrace();\n            }\n            try {\n                Utils.getApp().unbindService(mConn);\n            } catch (Exception ignore) {/*ignore*/}\n        }\n\n        void sendMsg2Server(Bundle bundle) {\n            if (mServer == null) {\n                mCached.addFirst(bundle);\n                Log.i(\"MessengerUtils\", \"save the bundle \" + bundle);\n            } else {\n                sendCachedMsg2Server();\n                if (!send2Server(bundle)) {\n                    mCached.addFirst(bundle);\n                }\n            }\n        }\n\n        private void sendCachedMsg2Server() {\n            if (mCached.isEmpty()) return;\n            for (int i = mCached.size() - 1; i >= 0; --i) {\n                if (send2Server(mCached.get(i))) {\n                    mCached.remove(i);\n                }\n            }\n        }\n\n        private boolean send2Server(Bundle bundle) {\n            Message msg = Message.obtain(mReceiveServeMsgHandler, WHAT_SEND);\n            bundle.setClassLoader(MessengerUtils.class.getClassLoader());\n            msg.setData(bundle);\n            msg.replyTo = mClient;\n            try {\n                mServer.send(msg);\n                return true;\n            } catch (RemoteException e) {\n                e.printStackTrace();\n                return false;\n            }\n        }\n    }\n\n    public static class ServerService extends Service {\n\n        private final ConcurrentHashMap<Integer, Messenger> mClientMap = new ConcurrentHashMap<>();\n\n        @SuppressLint(\"HandlerLeak\")\n        private final Handler mReceiveClientMsgHandler = new Handler() {\n            @Override\n            public void handleMessage(Message msg) {\n                switch (msg.what) {\n                    case WHAT_SUBSCRIBE:\n                        mClientMap.put(msg.arg1, msg.replyTo);\n                        break;\n                    case WHAT_SEND:\n                        sendMsg2Client(msg);\n                        consumeServerProcessCallback(msg);\n                        break;\n                    case WHAT_UNSUBSCRIBE:\n                        mClientMap.remove(msg.arg1);\n                        break;\n                    default:\n                        super.handleMessage(msg);\n                }\n            }\n        };\n\n        private final Messenger messenger = new Messenger(mReceiveClientMsgHandler);\n\n        @Nullable\n        @Override\n        public IBinder onBind(Intent intent) {\n            return messenger.getBinder();\n        }\n\n        @Override\n        public int onStartCommand(Intent intent, int flags, int startId) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                Notification notification = UtilsBridge.getNotification(\n                        NotificationUtils.ChannelConfig.DEFAULT_CHANNEL_CONFIG, null\n                );\n                startForeground(1, notification);\n            }\n            if (intent != null) {\n                Bundle extras = intent.getExtras();\n                if (extras != null) {\n                    Message msg = Message.obtain(mReceiveClientMsgHandler, WHAT_SEND);\n                    msg.replyTo = messenger;\n                    msg.setData(extras);\n                    sendMsg2Client(msg);\n                    consumeServerProcessCallback(msg);\n                }\n            }\n            return START_NOT_STICKY;\n        }\n\n        private void sendMsg2Client(final Message msg) {\n           final Message obtain = Message.obtain(msg); //Copy the original\n            for (Messenger client : mClientMap.values()) {\n                try {\n                    if (client != null) {\n                        client.send(Message.obtain(obtain));\n                    }\n                } catch (RemoteException e) {\n                    e.printStackTrace();\n                }\n            }\n            obtain.recycle(); //Recycled copy\n        }\n\n        private void consumeServerProcessCallback(final Message msg) {\n            Bundle data = msg.getData();\n            if (data != null) {\n                String key = data.getString(KEY_STRING);\n                if (key != null) {\n                    MessageCallback callback = subscribers.get(key);\n                    if (callback != null) {\n                        callback.messageCall(data);\n                    }\n                }\n            }\n        }\n    }\n\n    public interface MessageCallback {\n        void messageCall(Bundle data);\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/MetaDataUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.app.Activity;\nimport android.app.Service;\nimport android.content.BroadcastReceiver;\nimport android.content.ComponentName;\nimport android.content.pm.ActivityInfo;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ServiceInfo;\nimport androidx.annotation.NonNull;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/05/15\n *     desc  : utils about meta-data\n * </pre>\n */\npublic final class MetaDataUtils {\n\n    private MetaDataUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Return the value of meta-data in application.\n     *\n     * @param key The key of meta-data.\n     * @return the value of meta-data in application\n     */\n    public static String getMetaDataInApp(@NonNull final String key) {\n        String value = \"\";\n        PackageManager pm = Utils.getApp().getPackageManager();\n        String packageName = Utils.getApp().getPackageName();\n        try {\n            ApplicationInfo ai = pm.getApplicationInfo(packageName, PackageManager.GET_META_DATA);\n            value = String.valueOf(ai.metaData.get(key));\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n        }\n        return value;\n    }\n\n    /**\n     * Return the value of meta-data in activity.\n     *\n     * @param activity The activity.\n     * @param key      The key of meta-data.\n     * @return the value of meta-data in activity\n     */\n    public static String getMetaDataInActivity(@NonNull final Activity activity,\n                                               @NonNull final String key) {\n        return getMetaDataInActivity(activity.getClass(), key);\n    }\n\n    /**\n     * Return the value of meta-data in activity.\n     *\n     * @param clz The activity class.\n     * @param key The key of meta-data.\n     * @return the value of meta-data in activity\n     */\n    public static String getMetaDataInActivity(@NonNull final Class<? extends Activity> clz,\n                                               @NonNull final String key) {\n        String value = \"\";\n        PackageManager pm = Utils.getApp().getPackageManager();\n        ComponentName componentName = new ComponentName(Utils.getApp(), clz);\n        try {\n            ActivityInfo ai = pm.getActivityInfo(componentName, PackageManager.GET_META_DATA);\n            value = String.valueOf(ai.metaData.get(key));\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n        }\n        return value;\n    }\n\n    /**\n     * Return the value of meta-data in service.\n     *\n     * @param service The service.\n     * @param key     The key of meta-data.\n     * @return the value of meta-data in service\n     */\n    public static String getMetaDataInService(@NonNull final Service service,\n                                              @NonNull final String key) {\n        return getMetaDataInService(service.getClass(), key);\n    }\n\n    /**\n     * Return the value of meta-data in service.\n     *\n     * @param clz The service class.\n     * @param key The key of meta-data.\n     * @return the value of meta-data in service\n     */\n    public static String getMetaDataInService(@NonNull final Class<? extends Service> clz,\n                                              @NonNull final String key) {\n        String value = \"\";\n        PackageManager pm = Utils.getApp().getPackageManager();\n        ComponentName componentName = new ComponentName(Utils.getApp(), clz);\n        try {\n            ServiceInfo info = pm.getServiceInfo(componentName, PackageManager.GET_META_DATA);\n            value = String.valueOf(info.metaData.get(key));\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n        }\n        return value;\n    }\n\n    /**\n     * Return the value of meta-data in receiver.\n     *\n     * @param receiver The receiver.\n     * @param key      The key of meta-data.\n     * @return the value of meta-data in receiver\n     */\n    public static String getMetaDataInReceiver(@NonNull final BroadcastReceiver receiver,\n                                               @NonNull final String key) {\n        return getMetaDataInReceiver(receiver.getClass(), key);\n    }\n\n    /**\n     * Return the value of meta-data in receiver.\n     *\n     * @param clz The receiver class.\n     * @param key The key of meta-data.\n     * @return the value of meta-data in receiver\n     */\n    public static String getMetaDataInReceiver(@NonNull final Class<? extends BroadcastReceiver> clz,\n                                               @NonNull final String key) {\n        String value = \"\";\n        PackageManager pm = Utils.getApp().getPackageManager();\n        ComponentName componentName = new ComponentName(Utils.getApp(), clz);\n        try {\n            ActivityInfo info = pm.getReceiverInfo(componentName, PackageManager.GET_META_DATA);\n            value = String.valueOf(info.metaData.get(key));\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n        }\n        return value;\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/NetworkUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.annotation.SuppressLint;\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.IntentFilter;\nimport android.net.ConnectivityManager;\nimport android.net.NetworkCapabilities;\nimport android.net.NetworkInfo;\nimport android.net.wifi.ScanResult;\nimport android.net.wifi.WifiInfo;\nimport android.net.wifi.WifiManager;\nimport android.os.Build;\nimport android.telephony.TelephonyManager;\nimport android.text.TextUtils;\nimport android.text.format.Formatter;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.RequiresPermission;\n\nimport java.lang.reflect.Method;\nimport java.net.InetAddress;\nimport java.net.InterfaceAddress;\nimport java.net.NetworkInterface;\nimport java.net.SocketException;\nimport java.net.UnknownHostException;\nimport java.util.ArrayList;\nimport java.util.Enumeration;\nimport java.util.HashSet;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Set;\nimport java.util.Timer;\nimport java.util.TimerTask;\nimport java.util.concurrent.CopyOnWriteArraySet;\n\nimport static android.Manifest.permission.ACCESS_COARSE_LOCATION;\nimport static android.Manifest.permission.ACCESS_NETWORK_STATE;\nimport static android.Manifest.permission.ACCESS_WIFI_STATE;\nimport static android.Manifest.permission.CHANGE_WIFI_STATE;\nimport static android.Manifest.permission.INTERNET;\nimport static android.content.Context.WIFI_SERVICE;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/02\n *     desc  : utils about network\n * </pre>\n */\npublic final class NetworkUtils {\n\n    private NetworkUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    public enum NetworkType {\n        NETWORK_ETHERNET,\n        NETWORK_WIFI,\n        NETWORK_5G,\n        NETWORK_4G,\n        NETWORK_3G,\n        NETWORK_2G,\n        NETWORK_UNKNOWN,\n        NETWORK_NO\n    }\n\n    /**\n     * Open the settings of wireless.\n     */\n    public static void openWirelessSettings() {\n        Utils.getApp().startActivity(\n                new Intent(android.provider.Settings.ACTION_WIRELESS_SETTINGS)\n                        .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)\n        );\n    }\n\n    /**\n     * Return whether network is connected.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />}</p>\n     *\n     * @return {@code true}: connected<br>{@code false}: disconnected\n     */\n    @RequiresPermission(ACCESS_NETWORK_STATE)\n    public static boolean isConnected() {\n        NetworkInfo info = getActiveNetworkInfo();\n        return info != null && info.isConnected();\n    }\n\n    /**\n     * Return whether network is available.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.INTERNET\" />}</p>\n     *\n     * @param consumer The consumer.\n     * @return the task\n     */\n    @RequiresPermission(INTERNET)\n    public static Utils.Task<Boolean> isAvailableAsync(@NonNull final Utils.Consumer<Boolean> consumer) {\n        return UtilsBridge.doAsync(new Utils.Task<Boolean>(consumer) {\n            @RequiresPermission(INTERNET)\n            @Override\n            public Boolean doInBackground() {\n                return isAvailable();\n            }\n        });\n    }\n\n    /**\n     * Return whether network is available.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.INTERNET\" />}</p>\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    @RequiresPermission(INTERNET)\n    public static boolean isAvailable() {\n        return isAvailableByDns() || isAvailableByPing(null);\n    }\n\n    /**\n     * Return whether network is available using ping.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.INTERNET\" />}</p>\n     * <p>The default ping ip: 223.5.5.5</p>\n     *\n     * @param consumer The consumer.\n     */\n    @RequiresPermission(INTERNET)\n    public static void isAvailableByPingAsync(final Utils.Consumer<Boolean> consumer) {\n        isAvailableByPingAsync(\"\", consumer);\n    }\n\n    /**\n     * Return whether network is available using ping.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.INTERNET\" />}</p>\n     *\n     * @param ip       The ip address.\n     * @param consumer The consumer.\n     * @return the task\n     */\n    @RequiresPermission(INTERNET)\n    public static Utils.Task<Boolean> isAvailableByPingAsync(final String ip,\n                                                             @NonNull final Utils.Consumer<Boolean> consumer) {\n        return UtilsBridge.doAsync(new Utils.Task<Boolean>(consumer) {\n            @RequiresPermission(INTERNET)\n            @Override\n            public Boolean doInBackground() {\n                return isAvailableByPing(ip);\n            }\n        });\n    }\n\n    /**\n     * Return whether network is available using ping.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.INTERNET\" />}</p>\n     * <p>The default ping ip: 223.5.5.5</p>\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    @RequiresPermission(INTERNET)\n    public static boolean isAvailableByPing() {\n        return isAvailableByPing(\"\");\n    }\n\n    /**\n     * Return whether network is available using ping.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.INTERNET\" />}</p>\n     *\n     * @param ip The ip address.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    @RequiresPermission(INTERNET)\n    public static boolean isAvailableByPing(final String ip) {\n        final String realIp = TextUtils.isEmpty(ip) ? \"223.5.5.5\" : ip;\n        ShellUtils.CommandResult result = ShellUtils.execCmd(String.format(\"ping -c 1 %s\", realIp), false);\n        return result.result == 0;\n    }\n\n    /**\n     * Return whether network is available using domain.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.INTERNET\" />}</p>\n     *\n     * @param consumer The consumer.\n     */\n    @RequiresPermission(INTERNET)\n    public static void isAvailableByDnsAsync(final Utils.Consumer<Boolean> consumer) {\n        isAvailableByDnsAsync(\"\", consumer);\n    }\n\n    /**\n     * Return whether network is available using domain.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.INTERNET\" />}</p>\n     *\n     * @param domain   The name of domain.\n     * @param consumer The consumer.\n     * @return the task\n     */\n    @RequiresPermission(INTERNET)\n    public static Utils.Task isAvailableByDnsAsync(final String domain,\n                                                   @NonNull final Utils.Consumer<Boolean> consumer) {\n        return UtilsBridge.doAsync(new Utils.Task<Boolean>(consumer) {\n            @RequiresPermission(INTERNET)\n            @Override\n            public Boolean doInBackground() {\n                return isAvailableByDns(domain);\n            }\n        });\n    }\n\n    /**\n     * Return whether network is available using domain.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.INTERNET\" />}</p>\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    @RequiresPermission(INTERNET)\n    public static boolean isAvailableByDns() {\n        return isAvailableByDns(\"\");\n    }\n\n    /**\n     * Return whether network is available using domain.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.INTERNET\" />}</p>\n     *\n     * @param domain The name of domain.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    @RequiresPermission(INTERNET)\n    public static boolean isAvailableByDns(final String domain) {\n        final String realDomain = TextUtils.isEmpty(domain) ? \"www.baidu.com\" : domain;\n        InetAddress inetAddress;\n        try {\n            inetAddress = InetAddress.getByName(realDomain);\n            return inetAddress != null;\n        } catch (UnknownHostException e) {\n            e.printStackTrace();\n            return false;\n        }\n    }\n\n    /**\n     * Return whether mobile data is enabled.\n     *\n     * @return {@code true}: enabled<br>{@code false}: disabled\n     */\n    public static boolean getMobileDataEnabled() {\n        try {\n            TelephonyManager tm =\n                    (TelephonyManager) Utils.getApp().getSystemService(Context.TELEPHONY_SERVICE);\n            if (tm == null) return false;\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                return tm.isDataEnabled();\n            }\n            @SuppressLint(\"PrivateApi\")\n            Method getMobileDataEnabledMethod =\n                    tm.getClass().getDeclaredMethod(\"getDataEnabled\");\n            if (null != getMobileDataEnabledMethod) {\n                return (boolean) getMobileDataEnabledMethod.invoke(tm);\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return false;\n    }\n    \n     /**\n     * Returns true if device is connecting to the internet via a proxy, works for both Wi-Fi and Mobile Data.\n     *\n     * @return true if using proxy to connect to the internet.\n     */\n    public static boolean isBehindProxy(){\n        return !(System.getProperty(\"http.proxyHost\") == null || System.getProperty(\"http.proxyPort\") == null);\n    }\n\n    /**\n     * Returns true if device is connecting to the internet via a VPN.\n     *\n     * @return true if using VPN to conncet to the internet.\n     */\n    public static boolean isUsingVPN(){\n        ConnectivityManager cm = (ConnectivityManager) com.blankj.utilcode.util.Utils.getApp().getSystemService(Context.CONNECTIVITY_SERVICE);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n            return cm.getNetworkInfo(ConnectivityManager.TYPE_VPN).isConnectedOrConnecting();\n        } else {\n            return cm.getNetworkInfo(NetworkCapabilities.TRANSPORT_VPN).isConnectedOrConnecting();\n        }\n    }\n\n    \n    \n    \n    \n\n    /**\n     * Return whether using mobile data.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />}</p>\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    @RequiresPermission(ACCESS_NETWORK_STATE)\n    public static boolean isMobileData() {\n        NetworkInfo info = getActiveNetworkInfo();\n        return null != info\n                && info.isAvailable()\n                && info.getType() == ConnectivityManager.TYPE_MOBILE;\n    }\n\n    /**\n     * Return whether using 4G.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />}</p>\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    @RequiresPermission(ACCESS_NETWORK_STATE)\n    public static boolean is4G() {\n        NetworkInfo info = getActiveNetworkInfo();\n        return info != null\n                && info.isAvailable()\n                && info.getSubtype() == TelephonyManager.NETWORK_TYPE_LTE;\n    }\n\n    /**\n     * Return whether using 4G.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />}</p>\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    @RequiresPermission(ACCESS_NETWORK_STATE)\n    public static boolean is5G() {\n        NetworkInfo info = getActiveNetworkInfo();\n        return info != null\n                && info.isAvailable()\n                && info.getSubtype() == TelephonyManager.NETWORK_TYPE_NR;\n    }\n\n    /**\n     * Return whether wifi is enabled.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />}</p>\n     *\n     * @return {@code true}: enabled<br>{@code false}: disabled\n     */\n    @RequiresPermission(ACCESS_WIFI_STATE)\n    public static boolean getWifiEnabled() {\n        @SuppressLint(\"WifiManagerLeak\")\n        WifiManager manager = (WifiManager) Utils.getApp().getSystemService(WIFI_SERVICE);\n        if (manager == null) return false;\n        return manager.isWifiEnabled();\n    }\n\n    /**\n     * Enable or disable wifi.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\" />}</p>\n     *\n     * @param enabled True to enabled, false otherwise.\n     */\n    @RequiresPermission(CHANGE_WIFI_STATE)\n    public static void setWifiEnabled(final boolean enabled) {\n        @SuppressLint(\"WifiManagerLeak\")\n        WifiManager manager = (WifiManager) Utils.getApp().getSystemService(WIFI_SERVICE);\n        if (manager == null) return;\n        if (enabled == manager.isWifiEnabled()) return;\n        manager.setWifiEnabled(enabled);\n    }\n\n    /**\n     * Return whether wifi is connected.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />}</p>\n     *\n     * @return {@code true}: connected<br>{@code false}: disconnected\n     */\n    @RequiresPermission(ACCESS_NETWORK_STATE)\n    public static boolean isWifiConnected() {\n        ConnectivityManager cm =\n                (ConnectivityManager) Utils.getApp().getSystemService(Context.CONNECTIVITY_SERVICE);\n        if (cm == null) return false;\n        NetworkInfo ni = cm.getActiveNetworkInfo();\n        return ni != null && ni.getType() == ConnectivityManager.TYPE_WIFI;\n    }\n\n    /**\n     * Return whether wifi is available.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />},\n     * {@code <uses-permission android:name=\"android.permission.INTERNET\" />}</p>\n     *\n     * @return {@code true}: available<br>{@code false}: unavailable\n     */\n    @RequiresPermission(allOf = {ACCESS_WIFI_STATE, INTERNET})\n    public static boolean isWifiAvailable() {\n        return getWifiEnabled() && isAvailable();\n    }\n\n    /**\n     * Return whether wifi is available.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\" />},\n     * {@code <uses-permission android:name=\"android.permission.INTERNET\" />}</p>\n     *\n     * @param consumer The consumer.\n     * @return the task\n     */\n    @RequiresPermission(allOf = {ACCESS_WIFI_STATE, INTERNET})\n    public static Utils.Task<Boolean> isWifiAvailableAsync(@NonNull final Utils.Consumer<Boolean> consumer) {\n        return UtilsBridge.doAsync(new Utils.Task<Boolean>(consumer) {\n            @RequiresPermission(allOf = {ACCESS_WIFI_STATE, INTERNET})\n            @Override\n            public Boolean doInBackground() {\n                return isWifiAvailable();\n            }\n        });\n    }\n\n    /**\n     * Return the name of network operate.\n     *\n     * @return the name of network operate\n     */\n    public static String getNetworkOperatorName() {\n        TelephonyManager tm =\n                (TelephonyManager) Utils.getApp().getSystemService(Context.TELEPHONY_SERVICE);\n        if (tm == null) return \"\";\n        return tm.getNetworkOperatorName();\n    }\n\n    /**\n     * Return type of network.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />}</p>\n     *\n     * @return type of network\n     * <ul>\n     * <li>{@link NetworkUtils.NetworkType#NETWORK_ETHERNET} </li>\n     * <li>{@link NetworkUtils.NetworkType#NETWORK_WIFI    } </li>\n     * <li>{@link NetworkUtils.NetworkType#NETWORK_4G      } </li>\n     * <li>{@link NetworkUtils.NetworkType#NETWORK_3G      } </li>\n     * <li>{@link NetworkUtils.NetworkType#NETWORK_2G      } </li>\n     * <li>{@link NetworkUtils.NetworkType#NETWORK_UNKNOWN } </li>\n     * <li>{@link NetworkUtils.NetworkType#NETWORK_NO      } </li>\n     * </ul>\n     */\n    @RequiresPermission(ACCESS_NETWORK_STATE)\n    public static NetworkType getNetworkType() {\n        if (isEthernet()) {\n            return NetworkType.NETWORK_ETHERNET;\n        }\n        NetworkInfo info = getActiveNetworkInfo();\n        if (info != null && info.isAvailable()) {\n            if (info.getType() == ConnectivityManager.TYPE_WIFI) {\n                return NetworkType.NETWORK_WIFI;\n            } else if (info.getType() == ConnectivityManager.TYPE_MOBILE) {\n                switch (info.getSubtype()) {\n                    case TelephonyManager.NETWORK_TYPE_GSM:\n                    case TelephonyManager.NETWORK_TYPE_GPRS:\n                    case TelephonyManager.NETWORK_TYPE_CDMA:\n                    case TelephonyManager.NETWORK_TYPE_EDGE:\n                    case TelephonyManager.NETWORK_TYPE_1xRTT:\n                    case TelephonyManager.NETWORK_TYPE_IDEN:\n                        return NetworkType.NETWORK_2G;\n\n                    case TelephonyManager.NETWORK_TYPE_TD_SCDMA:\n                    case TelephonyManager.NETWORK_TYPE_EVDO_A:\n                    case TelephonyManager.NETWORK_TYPE_UMTS:\n                    case TelephonyManager.NETWORK_TYPE_EVDO_0:\n                    case TelephonyManager.NETWORK_TYPE_HSDPA:\n                    case TelephonyManager.NETWORK_TYPE_HSUPA:\n                    case TelephonyManager.NETWORK_TYPE_HSPA:\n                    case TelephonyManager.NETWORK_TYPE_EVDO_B:\n                    case TelephonyManager.NETWORK_TYPE_EHRPD:\n                    case TelephonyManager.NETWORK_TYPE_HSPAP:\n                        return NetworkType.NETWORK_3G;\n\n                    case TelephonyManager.NETWORK_TYPE_IWLAN:\n                    case TelephonyManager.NETWORK_TYPE_LTE:\n                        return NetworkType.NETWORK_4G;\n\n                    case TelephonyManager.NETWORK_TYPE_NR:\n                        return NetworkType.NETWORK_5G;\n                    default:\n                        String subtypeName = info.getSubtypeName();\n                        if (subtypeName.equalsIgnoreCase(\"TD-SCDMA\")\n                                || subtypeName.equalsIgnoreCase(\"WCDMA\")\n                                || subtypeName.equalsIgnoreCase(\"CDMA2000\")) {\n                            return NetworkType.NETWORK_3G;\n                        } else {\n                            return NetworkType.NETWORK_UNKNOWN;\n                        }\n                }\n            } else {\n                return NetworkType.NETWORK_UNKNOWN;\n            }\n        }\n        return NetworkType.NETWORK_NO;\n    }\n\n    /**\n     * Return whether using ethernet.\n     * <p>Must hold\n     * {@code <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />}</p>\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    @RequiresPermission(ACCESS_NETWORK_STATE)\n    private static boolean isEthernet() {\n        final ConnectivityManager cm =\n                (ConnectivityManager) Utils.getApp().getSystemService(Context.CONNECTIVITY_SERVICE);\n        if (cm == null) return false;\n        final NetworkInfo info = cm.getNetworkInfo(ConnectivityManager.TYPE_ETHERNET);\n        if (info == null) return false;\n        NetworkInfo.State state = info.getState();\n        if (null == state) return false;\n        return state == NetworkInfo.State.CONNECTED || state == NetworkInfo.State.CONNECTING;\n    }\n\n    @RequiresPermission(ACCESS_NETWORK_STATE)\n    private static NetworkInfo getActiveNetworkInfo() {\n        ConnectivityManager cm =\n                (ConnectivityManager) Utils.getApp().getSystemService(Context.CONNECTIVITY_SERVICE);\n        if (cm == null) return null;\n        return cm.getActiveNetworkInfo();\n    }\n\n    /**\n     * Return the ip address.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.INTERNET\" />}</p>\n     *\n     * @param useIPv4  True to use ipv4, false otherwise.\n     * @param consumer The consumer.\n     * @return the task\n     */\n    public static Utils.Task<String> getIPAddressAsync(final boolean useIPv4,\n                                                       @NonNull final Utils.Consumer<String> consumer) {\n        return UtilsBridge.doAsync(new Utils.Task<String>(consumer) {\n            @RequiresPermission(INTERNET)\n            @Override\n            public String doInBackground() {\n                return getIPAddress(useIPv4);\n            }\n        });\n    }\n\n    /**\n     * Return the ip address.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.INTERNET\" />}</p>\n     *\n     * @param useIPv4 True to use ipv4, false otherwise.\n     * @return the ip address\n     */\n    @RequiresPermission(INTERNET)\n    public static String getIPAddress(final boolean useIPv4) {\n        try {\n            Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();\n            LinkedList<InetAddress> adds = new LinkedList<>();\n            while (nis.hasMoreElements()) {\n                NetworkInterface ni = nis.nextElement();\n                // To prevent phone of xiaomi return \"10.0.2.15\"\n                if (!ni.isUp() || ni.isLoopback()) continue;\n                Enumeration<InetAddress> addresses = ni.getInetAddresses();\n                while (addresses.hasMoreElements()) {\n                    adds.addFirst(addresses.nextElement());\n                }\n            }\n            for (InetAddress add : adds) {\n                if (!add.isLoopbackAddress()) {\n                    String hostAddress = add.getHostAddress();\n                    boolean isIPv4 = hostAddress.indexOf(':') < 0;\n                    if (useIPv4) {\n                        if (isIPv4) return hostAddress;\n                    } else {\n                        if (!isIPv4) {\n                            int index = hostAddress.indexOf('%');\n                            return index < 0\n                                    ? hostAddress.toUpperCase()\n                                    : hostAddress.substring(0, index).toUpperCase();\n                        }\n                    }\n                }\n            }\n        } catch (SocketException e) {\n            e.printStackTrace();\n        }\n        return \"\";\n    }\n\n    /**\n     * Return the ip address of broadcast.\n     *\n     * @return the ip address of broadcast\n     */\n    public static String getBroadcastIpAddress() {\n        try {\n            Enumeration<NetworkInterface> nis = NetworkInterface.getNetworkInterfaces();\n            LinkedList<InetAddress> adds = new LinkedList<>();\n            while (nis.hasMoreElements()) {\n                NetworkInterface ni = nis.nextElement();\n                if (!ni.isUp() || ni.isLoopback()) continue;\n                List<InterfaceAddress> ias = ni.getInterfaceAddresses();\n                for (int i = 0, size = ias.size(); i < size; i++) {\n                    InterfaceAddress ia = ias.get(i);\n                    InetAddress broadcast = ia.getBroadcast();\n                    if (broadcast != null) {\n                        return broadcast.getHostAddress();\n                    }\n                }\n            }\n        } catch (SocketException e) {\n            e.printStackTrace();\n        }\n        return \"\";\n    }\n\n    /**\n     * Return the domain address.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.INTERNET\" />}</p>\n     *\n     * @param domain   The name of domain.\n     * @param consumer The consumer.\n     * @return the task\n     */\n    @RequiresPermission(INTERNET)\n    public static Utils.Task<String> getDomainAddressAsync(final String domain,\n                                                           @NonNull final Utils.Consumer<String> consumer) {\n        return UtilsBridge.doAsync(new Utils.Task<String>(consumer) {\n            @RequiresPermission(INTERNET)\n            @Override\n            public String doInBackground() {\n                return getDomainAddress(domain);\n            }\n        });\n    }\n\n    /**\n     * Return the domain address.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.INTERNET\" />}</p>\n     *\n     * @param domain The name of domain.\n     * @return the domain address\n     */\n    @RequiresPermission(INTERNET)\n    public static String getDomainAddress(final String domain) {\n        InetAddress inetAddress;\n        try {\n            inetAddress = InetAddress.getByName(domain);\n            return inetAddress.getHostAddress();\n        } catch (UnknownHostException e) {\n            e.printStackTrace();\n            return \"\";\n        }\n    }\n\n    /**\n     * Return the ip address by wifi.\n     *\n     * @return the ip address by wifi\n     */\n    @RequiresPermission(ACCESS_WIFI_STATE)\n    public static String getIpAddressByWifi() {\n        @SuppressLint(\"WifiManagerLeak\")\n        WifiManager wm = (WifiManager) Utils.getApp().getSystemService(Context.WIFI_SERVICE);\n        if (wm == null) return \"\";\n        return Formatter.formatIpAddress(wm.getDhcpInfo().ipAddress);\n    }\n\n    /**\n     * Return the gate way by wifi.\n     *\n     * @return the gate way by wifi\n     */\n    @RequiresPermission(ACCESS_WIFI_STATE)\n    public static String getGatewayByWifi() {\n        @SuppressLint(\"WifiManagerLeak\")\n        WifiManager wm = (WifiManager) Utils.getApp().getSystemService(Context.WIFI_SERVICE);\n        if (wm == null) return \"\";\n        return Formatter.formatIpAddress(wm.getDhcpInfo().gateway);\n    }\n\n    /**\n     * Return the net mask by wifi.\n     *\n     * @return the net mask by wifi\n     */\n    @RequiresPermission(ACCESS_WIFI_STATE)\n    public static String getNetMaskByWifi() {\n        @SuppressLint(\"WifiManagerLeak\")\n        WifiManager wm = (WifiManager) Utils.getApp().getSystemService(Context.WIFI_SERVICE);\n        if (wm == null) return \"\";\n        return Formatter.formatIpAddress(wm.getDhcpInfo().netmask);\n    }\n\n    /**\n     * Return the server address by wifi.\n     *\n     * @return the server address by wifi\n     */\n    @RequiresPermission(ACCESS_WIFI_STATE)\n    public static String getServerAddressByWifi() {\n        @SuppressLint(\"WifiManagerLeak\")\n        WifiManager wm = (WifiManager) Utils.getApp().getSystemService(Context.WIFI_SERVICE);\n        if (wm == null) return \"\";\n        return Formatter.formatIpAddress(wm.getDhcpInfo().serverAddress);\n    }\n\n    /**\n     * Return the ssid.\n     *\n     * @return the ssid.\n     */\n    @RequiresPermission(ACCESS_WIFI_STATE)\n    public static String getSSID() {\n        WifiManager wm = (WifiManager) Utils.getApp().getApplicationContext().getSystemService(WIFI_SERVICE);\n        if (wm == null) return \"\";\n        WifiInfo wi = wm.getConnectionInfo();\n        if (wi == null) return \"\";\n        String ssid = wi.getSSID();\n        if (TextUtils.isEmpty(ssid)) {\n            return \"\";\n        }\n        if (ssid.length() > 2 && ssid.charAt(0) == '\"' && ssid.charAt(ssid.length() - 1) == '\"') {\n            return ssid.substring(1, ssid.length() - 1);\n        }\n        return ssid;\n    }\n\n    /**\n     * Register the status of network changed listener.\n     *\n     * @param listener The status of network changed listener\n     */\n    @RequiresPermission(ACCESS_NETWORK_STATE)\n    public static void registerNetworkStatusChangedListener(final OnNetworkStatusChangedListener listener) {\n        NetworkChangedReceiver.getInstance().registerListener(listener);\n    }\n\n    /**\n     * Return whether the status of network changed listener has been registered.\n     *\n     * @param listener The listener\n     * @return true to registered, false otherwise.\n     */\n    public static boolean isRegisteredNetworkStatusChangedListener(final OnNetworkStatusChangedListener listener) {\n        return NetworkChangedReceiver.getInstance().isRegistered(listener);\n    }\n\n    /**\n     * Unregister the status of network changed listener.\n     *\n     * @param listener The status of network changed listener.\n     */\n    public static void unregisterNetworkStatusChangedListener(final OnNetworkStatusChangedListener listener) {\n        NetworkChangedReceiver.getInstance().unregisterListener(listener);\n    }\n\n    @RequiresPermission(allOf = {ACCESS_WIFI_STATE, ACCESS_COARSE_LOCATION})\n    public static WifiScanResults getWifiScanResult() {\n        WifiScanResults result = new WifiScanResults();\n        if (!getWifiEnabled()) return result;\n        @SuppressLint(\"WifiManagerLeak\")\n        WifiManager wm = (WifiManager) Utils.getApp().getSystemService(WIFI_SERVICE);\n        //noinspection ConstantConditions\n        List<ScanResult> results = wm.getScanResults();\n        if (results != null) {\n            result.setAllResults(results);\n        }\n        return result;\n    }\n\n    private static final long                                 SCAN_PERIOD_MILLIS    = 3000;\n    private static final Set<Utils.Consumer<WifiScanResults>> SCAN_RESULT_CONSUMERS = new CopyOnWriteArraySet<>();\n    private static       Timer                                sScanWifiTimer;\n    private static       WifiScanResults                      sPreWifiScanResults;\n\n    @RequiresPermission(allOf = {ACCESS_WIFI_STATE, CHANGE_WIFI_STATE, ACCESS_COARSE_LOCATION})\n    public static void addOnWifiChangedConsumer(final Utils.Consumer<WifiScanResults> consumer) {\n        if (consumer == null) return;\n        UtilsBridge.runOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                if (SCAN_RESULT_CONSUMERS.isEmpty()) {\n                    SCAN_RESULT_CONSUMERS.add(consumer);\n                    startScanWifi();\n                    return;\n                }\n                consumer.accept(sPreWifiScanResults);\n                SCAN_RESULT_CONSUMERS.add(consumer);\n            }\n        });\n    }\n\n    private static void startScanWifi() {\n        sPreWifiScanResults = new WifiScanResults();\n        sScanWifiTimer = new Timer();\n        sScanWifiTimer.schedule(new TimerTask() {\n            @RequiresPermission(allOf = {ACCESS_WIFI_STATE, CHANGE_WIFI_STATE, ACCESS_COARSE_LOCATION})\n            @Override\n            public void run() {\n                startScanWifiIfEnabled();\n                WifiScanResults scanResults = getWifiScanResult();\n                if (isSameScanResults(sPreWifiScanResults.allResults, scanResults.allResults)) {\n                    return;\n                }\n                sPreWifiScanResults = scanResults;\n                UtilsBridge.runOnUiThread(new Runnable() {\n                    @Override\n                    public void run() {\n                        for (Utils.Consumer<WifiScanResults> consumer : SCAN_RESULT_CONSUMERS) {\n                            consumer.accept(sPreWifiScanResults);\n                        }\n                    }\n                });\n            }\n        }, 0, SCAN_PERIOD_MILLIS);\n    }\n\n    @RequiresPermission(allOf = {ACCESS_WIFI_STATE, CHANGE_WIFI_STATE})\n    private static void startScanWifiIfEnabled() {\n        if (!getWifiEnabled()) return;\n        @SuppressLint(\"WifiManagerLeak\")\n        WifiManager wm = (WifiManager) Utils.getApp().getSystemService(WIFI_SERVICE);\n        //noinspection ConstantConditions\n        wm.startScan();\n    }\n\n    public static void removeOnWifiChangedConsumer(final Utils.Consumer<WifiScanResults> consumer) {\n        if (consumer == null) return;\n        UtilsBridge.runOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                SCAN_RESULT_CONSUMERS.remove(consumer);\n                if (SCAN_RESULT_CONSUMERS.isEmpty()) {\n                    stopScanWifi();\n                }\n            }\n        });\n    }\n\n    private static void stopScanWifi() {\n        if (sScanWifiTimer != null) {\n            sScanWifiTimer.cancel();\n            sScanWifiTimer = null;\n        }\n    }\n\n    private static boolean isSameScanResults(List<ScanResult> l1, List<ScanResult> l2) {\n        if (l1 == null && l2 == null) {\n            return true;\n        }\n        if (l1 == null || l2 == null) {\n            return false;\n        }\n        if (l1.size() != l2.size()) {\n            return false;\n        }\n        for (int i = 0; i < l1.size(); i++) {\n            ScanResult r1 = l1.get(i);\n            ScanResult r2 = l2.get(i);\n            if (!isSameScanResultContent(r1, r2)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    private static boolean isSameScanResultContent(ScanResult r1, ScanResult r2) {\n        return r1 != null && r2 != null && UtilsBridge.equals(r1.BSSID, r2.BSSID)\n                && UtilsBridge.equals(r1.SSID, r2.SSID)\n                && UtilsBridge.equals(r1.capabilities, r2.capabilities)\n                && r1.level == r2.level;\n    }\n\n    public static final class NetworkChangedReceiver extends BroadcastReceiver {\n\n        private static NetworkChangedReceiver getInstance() {\n            return LazyHolder.INSTANCE;\n        }\n\n        private NetworkType                         mType;\n        private Set<OnNetworkStatusChangedListener> mListeners = new HashSet<>();\n\n        @RequiresPermission(ACCESS_NETWORK_STATE)\n        void registerListener(final OnNetworkStatusChangedListener listener) {\n            if (listener == null) return;\n            UtilsBridge.runOnUiThread(new Runnable() {\n                @Override\n                @RequiresPermission(ACCESS_NETWORK_STATE)\n                public void run() {\n                    int preSize = mListeners.size();\n                    mListeners.add(listener);\n                    if (preSize == 0 && mListeners.size() == 1) {\n                        mType = getNetworkType();\n                        IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);\n                        Utils.getApp().registerReceiver(NetworkChangedReceiver.getInstance(), intentFilter);\n                    }\n                }\n            });\n        }\n\n        boolean isRegistered(final OnNetworkStatusChangedListener listener) {\n            if (listener == null) return false;\n            return mListeners.contains(listener);\n        }\n\n        void unregisterListener(final OnNetworkStatusChangedListener listener) {\n            if (listener == null) return;\n            UtilsBridge.runOnUiThread(new Runnable() {\n                @Override\n                public void run() {\n                    int preSize = mListeners.size();\n                    mListeners.remove(listener);\n                    if (preSize == 1 && mListeners.size() == 0) {\n                        Utils.getApp().unregisterReceiver(NetworkChangedReceiver.getInstance());\n                    }\n                }\n            });\n        }\n\n        @Override\n        public void onReceive(Context context, Intent intent) {\n            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {\n                // debouncing\n                UtilsBridge.runOnUiThreadDelayed(new Runnable() {\n                    @Override\n                    @RequiresPermission(ACCESS_NETWORK_STATE)\n                    public void run() {\n                        NetworkType networkType = NetworkUtils.getNetworkType();\n                        if (mType == networkType) return;\n                        mType = networkType;\n                        if (networkType == NetworkType.NETWORK_NO) {\n                            for (OnNetworkStatusChangedListener listener : mListeners) {\n                                listener.onDisconnected();\n                            }\n                        } else {\n                            for (OnNetworkStatusChangedListener listener : mListeners) {\n                                listener.onConnected(networkType);\n                            }\n                        }\n                    }\n                }, 1000);\n            }\n        }\n\n        private static class LazyHolder {\n            private static final NetworkChangedReceiver INSTANCE = new NetworkChangedReceiver();\n        }\n    }\n\n//    /**\n//     * Register the status of network changed listener.\n//     */\n//    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)\n//    @RequiresPermission(ACCESS_NETWORK_STATE)\n//    public static void registerNetworkStatusChangedListener() {\n//        ConnectivityManager cm = (ConnectivityManager) Utils.getApp().getSystemService(Context.CONNECTIVITY_SERVICE);\n//        if (cm == null) return;\n//        NetworkCallbackImpl networkCallback = NetworkCallbackImpl.LazyHolder.INSTANCE;\n//        NetworkRequest.Builder builder = new NetworkRequest.Builder();\n//        NetworkRequest request = builder.build();\n//        cm.registerNetworkCallback(new NetworkRequest.Builder().build(), networkCallback);\n//    }\n//\n//\n//    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)\n//    public static final class NetworkCallbackImpl extends ConnectivityManager.NetworkCallback {\n//\n//        @Override\n//        public void onAvailable(@NonNull Network network) {\n//            super.onAvailable(network);\n//            LogUtils.d(TAG, \"onAvailable: \" + network);\n//        }\n//\n//        @Override\n//        public void onLosing(@NonNull Network network, int maxMsToLive) {\n//            super.onLosing(network, maxMsToLive);\n//            LogUtils.d(TAG, \"onLosing: \" + network);\n//        }\n//\n//        @Override\n//        public void onLost(@NonNull Network network) {\n//            super.onLost(network);\n//            LogUtils.e(TAG, \"onLost: \" + network);\n//        }\n//\n//        @Override\n//        public void onUnavailable() {\n//            super.onUnavailable();\n//            LogUtils.e(TAG, \"onUnavailable\");\n//        }\n//\n//        @Override\n//        public void onCapabilitiesChanged(@NonNull Network network, @NonNull NetworkCapabilities cap) {\n//            super.onCapabilitiesChanged(network, cap);\n//            if (cap.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {\n//                if (cap.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {\n//                    LogUtils.d(TAG, \"onCapabilitiesChanged: 网络类型为wifi\");\n//                } else if (cap.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {\n//                    LogUtils.d(TAG, \"onCapabilitiesChanged: 蜂窝网络\");\n//                } else {\n//                    LogUtils.d(TAG, \"onCapabilitiesChanged: 其他网络\");\n//                }\n//                LogUtils.d(TAG, \"onCapabilitiesChanged: \" + network + \", \" + cap);\n//            }\n//        }\n//\n//        @Override\n//        public void onLinkPropertiesChanged(@NonNull Network network, @NonNull LinkProperties lp) {\n//            super.onLinkPropertiesChanged(network, lp);\n//            LogUtils.d(TAG, \"onLinkPropertiesChanged: \" + network + \", \" + lp);\n//        }\n//\n//        @Override\n//        public void onBlockedStatusChanged(@NonNull Network network, boolean blocked) {\n//            super.onBlockedStatusChanged(network, blocked);\n//            LogUtils.d(TAG, \"onBlockedStatusChanged: \" + network + \", \" + blocked);\n//        }\n//\n//        private static class LazyHolder {\n//            private static final NetworkCallbackImpl INSTANCE = new NetworkCallbackImpl();\n//        }\n//    }\n\n    public interface OnNetworkStatusChangedListener {\n        void onDisconnected();\n\n        void onConnected(NetworkType networkType);\n    }\n\n    public static final class WifiScanResults {\n\n        private List<ScanResult> allResults    = new ArrayList<>();\n        private List<ScanResult> filterResults = new ArrayList<>();\n\n        public WifiScanResults() {\n        }\n\n        public List<ScanResult> getAllResults() {\n            return allResults;\n        }\n\n        public List<ScanResult> getFilterResults() {\n            return filterResults;\n        }\n\n        public void setAllResults(List<ScanResult> allResults) {\n            this.allResults = allResults;\n            filterResults = filterScanResult(allResults);\n        }\n\n        private static List<ScanResult> filterScanResult(final List<ScanResult> results) {\n            if (results == null || results.isEmpty()) {\n                return new ArrayList<>();\n            }\n            LinkedHashMap<String, ScanResult> map = new LinkedHashMap<>(results.size());\n            for (ScanResult result : results) {\n                if (TextUtils.isEmpty(result.SSID)) {\n                    continue;\n                }\n                ScanResult resultInMap = map.get(result.SSID);\n                if (resultInMap != null && resultInMap.level >= result.level) {\n                    continue;\n                }\n                map.put(result.SSID, result);\n            }\n            return new ArrayList<>(map.values());\n        }\n\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/NotificationUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.annotation.SuppressLint;\nimport android.app.Notification;\nimport android.app.NotificationChannel;\nimport android.app.NotificationManager;\nimport android.content.Context;\nimport android.media.AudioAttributes;\nimport android.net.Uri;\nimport android.os.Build;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.reflect.Method;\n\nimport androidx.annotation.IntDef;\nimport androidx.annotation.RequiresPermission;\nimport androidx.core.app.NotificationCompat;\nimport androidx.core.app.NotificationManagerCompat;\n\nimport static android.Manifest.permission.EXPAND_STATUS_BAR;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/10/20\n *     desc  : utils about notification\n * </pre>\n */\npublic class NotificationUtils {\n\n    public static final int IMPORTANCE_UNSPECIFIED = -1000;\n    public static final int IMPORTANCE_NONE        = 0;\n    public static final int IMPORTANCE_MIN         = 1;\n    public static final int IMPORTANCE_LOW         = 2;\n    public static final int IMPORTANCE_DEFAULT     = 3;\n    public static final int IMPORTANCE_HIGH        = 4;\n\n    @IntDef({IMPORTANCE_UNSPECIFIED, IMPORTANCE_NONE, IMPORTANCE_MIN, IMPORTANCE_LOW, IMPORTANCE_DEFAULT, IMPORTANCE_HIGH})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface Importance {\n    }\n\n    /**\n     * Return whether the notifications enabled.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean areNotificationsEnabled() {\n        return NotificationManagerCompat.from(Utils.getApp()).areNotificationsEnabled();\n    }\n\n    /**\n     * Post a notification to be shown in the status bar.\n     *\n     * @param id       An identifier for this notification.\n     * @param consumer The consumer of create the builder of notification.\n     */\n    public static void notify(int id, Utils.Consumer<NotificationCompat.Builder> consumer) {\n        notify(null, id, ChannelConfig.DEFAULT_CHANNEL_CONFIG, consumer);\n    }\n\n    /**\n     * Post a notification to be shown in the status bar.\n     *\n     * @param tag      A string identifier for this notification.  May be {@code null}.\n     * @param id       An identifier for this notification.\n     * @param consumer The consumer of create the builder of notification.\n     */\n    public static void notify(String tag, int id, Utils.Consumer<NotificationCompat.Builder> consumer) {\n        notify(tag, id, ChannelConfig.DEFAULT_CHANNEL_CONFIG, consumer);\n    }\n\n    /**\n     * Post a notification to be shown in the status bar.\n     *\n     * @param id            An identifier for this notification.\n     * @param channelConfig The notification channel of config.\n     * @param consumer      The consumer of create the builder of notification.\n     */\n    public static void notify(int id, ChannelConfig channelConfig, Utils.Consumer<NotificationCompat.Builder> consumer) {\n        notify(null, id, channelConfig, consumer);\n    }\n\n    /**\n     * Post a notification to be shown in the status bar.\n     *\n     * @param tag           A string identifier for this notification.  May be {@code null}.\n     * @param id            An identifier for this notification.\n     * @param channelConfig The notification channel of config.\n     * @param consumer      The consumer of create the builder of notification.\n     */\n    public static void notify(String tag, int id, ChannelConfig channelConfig, Utils.Consumer<NotificationCompat.Builder> consumer) {\n        NotificationManagerCompat.from(Utils.getApp()).notify(tag, id, getNotification(channelConfig, consumer));\n    }\n\n\n    public static Notification getNotification(ChannelConfig channelConfig, Utils.Consumer<NotificationCompat.Builder> consumer) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n            NotificationManager nm = (NotificationManager) Utils.getApp().getSystemService(Context.NOTIFICATION_SERVICE);\n            //noinspection ConstantConditions\n            nm.createNotificationChannel(channelConfig.getNotificationChannel());\n        }\n\n        NotificationCompat.Builder builder = new NotificationCompat.Builder(Utils.getApp());\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n            builder.setChannelId(channelConfig.mNotificationChannel.getId());\n        }\n        if (consumer != null) {\n            consumer.accept(builder);\n        }\n\n        return builder.build();\n    }\n\n    /**\n     * Cancel The notification.\n     *\n     * @param tag The tag for the notification will be cancelled.\n     * @param id  The identifier for the notification will be cancelled.\n     */\n    public static void cancel(String tag, final int id) {\n        NotificationManagerCompat.from(Utils.getApp()).cancel(tag, id);\n    }\n\n    /**\n     * Cancel The notification.\n     *\n     * @param id The identifier for the notification will be cancelled.\n     */\n    public static void cancel(final int id) {\n        NotificationManagerCompat.from(Utils.getApp()).cancel(id);\n    }\n\n    /**\n     * Cancel all of the notifications.\n     */\n    public static void cancelAll() {\n        NotificationManagerCompat.from(Utils.getApp()).cancelAll();\n    }\n\n    /**\n     * Set the notification bar's visibility.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.EXPAND_STATUS_BAR\" />}</p>\n     *\n     * @param isVisible True to set notification bar visible, false otherwise.\n     */\n    @RequiresPermission(EXPAND_STATUS_BAR)\n    public static void setNotificationBarVisibility(final boolean isVisible) {\n        String methodName;\n        if (isVisible) {\n            methodName = (Build.VERSION.SDK_INT <= 16) ? \"expand\" : \"expandNotificationsPanel\";\n        } else {\n            methodName = (Build.VERSION.SDK_INT <= 16) ? \"collapse\" : \"collapsePanels\";\n        }\n        invokePanels(methodName);\n    }\n\n    private static void invokePanels(final String methodName) {\n        try {\n            @SuppressLint(\"WrongConstant\")\n            Object service = Utils.getApp().getSystemService(\"statusbar\");\n            @SuppressLint(\"PrivateApi\")\n            Class<?> statusBarManager = Class.forName(\"android.app.StatusBarManager\");\n            Method expand = statusBarManager.getMethod(methodName);\n            expand.invoke(service);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    public static class ChannelConfig {\n\n        public static final ChannelConfig DEFAULT_CHANNEL_CONFIG = new ChannelConfig(\n                Utils.getApp().getPackageName(), Utils.getApp().getPackageName(), IMPORTANCE_DEFAULT\n        );\n\n        private NotificationChannel mNotificationChannel;\n\n        public ChannelConfig(String id, CharSequence name, @Importance int importance) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                mNotificationChannel = new NotificationChannel(id, name, importance);\n            }\n        }\n\n        public NotificationChannel getNotificationChannel() {\n            return mNotificationChannel;\n        }\n\n        /**\n         * Sets whether or not notifications posted to this channel can interrupt the user in\n         * {@link android.app.NotificationManager.Policy#INTERRUPTION_FILTER_PRIORITY} mode.\n         * <p>\n         * Only modifiable by the system and notification ranker.\n         */\n        public ChannelConfig setBypassDnd(boolean bypassDnd) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                mNotificationChannel.setBypassDnd(bypassDnd);\n            }\n            return this;\n        }\n\n        /**\n         * Sets the user visible description of this channel.\n         *\n         * <p>The recommended maximum length is 300 characters; the value may be truncated if it is too\n         * long.\n         */\n        public ChannelConfig setDescription(String description) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                mNotificationChannel.setDescription(description);\n            }\n            return this;\n        }\n\n        /**\n         * Sets what group this channel belongs to.\n         * <p>\n         * Group information is only used for presentation, not for behavior.\n         * <p>\n         * Only modifiable before the channel is submitted to\n         * {@link NotificationManager#createNotificationChannel(NotificationChannel)}, unless the\n         * channel is not currently part of a group.\n         *\n         * @param groupId the id of a group created by\n         *                {@link NotificationManager#createNotificationChannelGroup)}.\n         */\n        public ChannelConfig setGroup(String groupId) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                mNotificationChannel.setGroup(groupId);\n            }\n            return this;\n        }\n\n        /**\n         * Sets the level of interruption of this notification channel.\n         * <p>\n         * Only modifiable before the channel is submitted to\n         * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.\n         *\n         * @param importance the amount the user should be interrupted by\n         *                   notifications from this channel.\n         */\n        public ChannelConfig setImportance(@Importance int importance) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                mNotificationChannel.setImportance(importance);\n            }\n            return this;\n        }\n\n        /**\n         * Sets the notification light color for notifications posted to this channel, if lights are\n         * {@link NotificationChannel#enableLights(boolean) enabled} on this channel and the device supports that feature.\n         * <p>\n         * Only modifiable before the channel is submitted to\n         * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.\n         */\n        public ChannelConfig setLightColor(int argb) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                mNotificationChannel.setLightColor(argb);\n            }\n            return this;\n        }\n\n        /**\n         * Sets whether notifications posted to this channel appear on the lockscreen or not, and if so,\n         * whether they appear in a redacted form. See e.g. {@link Notification#VISIBILITY_SECRET}.\n         * <p>\n         * Only modifiable by the system and notification ranker.\n         */\n        public ChannelConfig setLockscreenVisibility(int lockscreenVisibility) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                mNotificationChannel.setLockscreenVisibility(lockscreenVisibility);\n            }\n            return this;\n        }\n\n        /**\n         * Sets the user visible name of this channel.\n         *\n         * <p>The recommended maximum length is 40 characters; the value may be truncated if it is too\n         * long.\n         */\n        public ChannelConfig setName(CharSequence name) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                mNotificationChannel.setName(name);\n            }\n            return this;\n        }\n\n        /**\n         * Sets whether notifications posted to this channel can appear as application icon badges\n         * in a Launcher.\n         * <p>\n         * Only modifiable before the channel is submitted to\n         * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.\n         *\n         * @param showBadge true if badges should be allowed to be shown.\n         */\n        public ChannelConfig setShowBadge(boolean showBadge) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                mNotificationChannel.setShowBadge(showBadge);\n            }\n            return this;\n        }\n\n        /**\n         * Sets the sound that should be played for notifications posted to this channel and its\n         * audio attributes. Notification channels with an {@link NotificationChannel#getImportance() importance} of at\n         * least {@link NotificationManager#IMPORTANCE_DEFAULT} should have a sound.\n         * <p>\n         * Only modifiable before the channel is submitted to\n         * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.\n         */\n        public ChannelConfig setSound(Uri sound, AudioAttributes audioAttributes) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                mNotificationChannel.setSound(sound, audioAttributes);\n            }\n            return this;\n        }\n\n        /**\n         * Sets the vibration pattern for notifications posted to this channel. If the provided\n         * pattern is valid (non-null, non-empty), will {@link NotificationChannel#enableVibration(boolean)} enable\n         * vibration} as well. Otherwise, vibration will be disabled.\n         * <p>\n         * Only modifiable before the channel is submitted to\n         * {@link NotificationManager#createNotificationChannel(NotificationChannel)}.\n         */\n        public ChannelConfig setVibrationPattern(long[] vibrationPattern) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                mNotificationChannel.setVibrationPattern(vibrationPattern);\n            }\n            return this;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/NumberUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport java.math.BigDecimal;\nimport java.math.RoundingMode;\nimport java.text.DecimalFormat;\nimport java.text.NumberFormat;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2020/04/12\n *     desc  : utils about number\n * </pre>\n */\npublic final class NumberUtils {\n\n    private static final ThreadLocal<DecimalFormat> DF_THREAD_LOCAL = new ThreadLocal<DecimalFormat>() {\n        @Override\n        protected DecimalFormat initialValue() {\n            return (DecimalFormat) NumberFormat.getInstance();\n        }\n    };\n\n    public static DecimalFormat getSafeDecimalFormat() {\n        return DF_THREAD_LOCAL.get();\n    }\n\n    private NumberUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Format the value.\n     *\n     * @param value          The value.\n     * @param fractionDigits The number of digits allowed in the fraction portion of value.\n     * @return the format value\n     */\n    public static String format(float value, int fractionDigits) {\n        return format(value, false, 1, fractionDigits, true);\n    }\n\n    /**\n     * Format the value.\n     *\n     * @param value          The value.\n     * @param fractionDigits The number of digits allowed in the fraction portion of value.\n     * @param isHalfUp       True to rounded towards the nearest neighbor.\n     * @return the format value\n     */\n    public static String format(float value, int fractionDigits, boolean isHalfUp) {\n        return format(value, false, 1, fractionDigits, isHalfUp);\n    }\n\n    /**\n     * Format the value.\n     *\n     * @param value            The value.\n     * @param minIntegerDigits The minimum number of digits allowed in the integer portion of value.\n     * @param fractionDigits   The number of digits allowed in the fraction portion of value.\n     * @param isHalfUp         True to rounded towards the nearest neighbor.\n     * @return the format value\n     */\n    public static String format(float value, int minIntegerDigits, int fractionDigits, boolean isHalfUp) {\n        return format(value, false, minIntegerDigits, fractionDigits, isHalfUp);\n    }\n\n    /**\n     * Format the value.\n     *\n     * @param value          The value.\n     * @param isGrouping     True to set grouping will be used in this format, false otherwise.\n     * @param fractionDigits The number of digits allowed in the fraction portion of value.\n     * @return the format value\n     */\n    public static String format(float value, boolean isGrouping, int fractionDigits) {\n        return format(value, isGrouping, 1, fractionDigits, true);\n    }\n\n    /**\n     * Format the value.\n     *\n     * @param value          The value.\n     * @param isGrouping     True to set grouping will be used in this format, false otherwise.\n     * @param fractionDigits The number of digits allowed in the fraction portion of value.\n     * @param isHalfUp       True to rounded towards the nearest neighbor.\n     * @return the format value\n     */\n    public static String format(float value, boolean isGrouping, int fractionDigits, boolean isHalfUp) {\n        return format(value, isGrouping, 1, fractionDigits, isHalfUp);\n    }\n\n    /**\n     * Format the value.\n     *\n     * @param value            The value.\n     * @param isGrouping       True to set grouping will be used in this format, false otherwise.\n     * @param minIntegerDigits The minimum number of digits allowed in the integer portion of value.\n     * @param fractionDigits   The number of digits allowed in the fraction portion of value.\n     * @param isHalfUp         True to rounded towards the nearest neighbor.\n     * @return the format value\n     */\n    public static String format(float value, boolean isGrouping, int minIntegerDigits, int fractionDigits, boolean isHalfUp) {\n        return format(float2Double(value), isGrouping, minIntegerDigits, fractionDigits, isHalfUp);\n    }\n\n    /**\n     * Format the value.\n     *\n     * @param value          The value.\n     * @param fractionDigits The number of digits allowed in the fraction portion of value.\n     * @return the format value\n     */\n    public static String format(double value, int fractionDigits) {\n        return format(value, false, 1, fractionDigits, true);\n    }\n\n    /**\n     * Format the value.\n     *\n     * @param value          The value.\n     * @param fractionDigits The number of digits allowed in the fraction portion of value.\n     * @param isHalfUp       True to rounded towards the nearest neighbor.\n     * @return the format value\n     */\n    public static String format(double value, int fractionDigits, boolean isHalfUp) {\n        return format(value, false, 1, fractionDigits, isHalfUp);\n    }\n\n    /**\n     * Format the value.\n     *\n     * @param value            The value.\n     * @param minIntegerDigits The minimum number of digits allowed in the integer portion of value.\n     * @param fractionDigits   The number of digits allowed in the fraction portion of value.\n     * @param isHalfUp         True to rounded towards the nearest neighbor.\n     * @return the format value\n     */\n    public static String format(double value, int minIntegerDigits, int fractionDigits, boolean isHalfUp) {\n        return format(value, false, minIntegerDigits, fractionDigits, isHalfUp);\n    }\n\n    /**\n     * Format the value.\n     *\n     * @param value          The value.\n     * @param isGrouping     True to set grouping will be used in this format, false otherwise.\n     * @param fractionDigits The number of digits allowed in the fraction portion of value.\n     * @return the format value\n     */\n    public static String format(double value, boolean isGrouping, int fractionDigits) {\n        return format(value, isGrouping, 1, fractionDigits, true);\n    }\n\n    /**\n     * Format the value.\n     *\n     * @param value          The value.\n     * @param isGrouping     True to set grouping will be used in this format, false otherwise.\n     * @param fractionDigits The number of digits allowed in the fraction portion of value.\n     * @param isHalfUp       True to rounded towards the nearest neighbor.\n     * @return the format value\n     */\n    public static String format(double value, boolean isGrouping, int fractionDigits, boolean isHalfUp) {\n        return format(value, isGrouping, 1, fractionDigits, isHalfUp);\n    }\n\n    /**\n     * Format the value.\n     *\n     * @param value            The value.\n     * @param isGrouping       True to set grouping will be used in this format, false otherwise.\n     * @param minIntegerDigits The minimum number of digits allowed in the integer portion of value.\n     * @param fractionDigits   The number of digits allowed in the fraction portion of value.\n     * @param isHalfUp         True to rounded towards the nearest neighbor.\n     * @return the format value\n     */\n    public static String format(double value, boolean isGrouping, int minIntegerDigits, int fractionDigits, boolean isHalfUp) {\n        DecimalFormat nf = getSafeDecimalFormat();\n        nf.setGroupingUsed(isGrouping);\n        nf.setRoundingMode(isHalfUp ? RoundingMode.HALF_UP : RoundingMode.DOWN);\n        nf.setMinimumIntegerDigits(minIntegerDigits);\n        nf.setMinimumFractionDigits(fractionDigits);\n        nf.setMaximumFractionDigits(fractionDigits);\n        return nf.format(value);\n    }\n\n    /**\n     * Float to double.\n     *\n     * @param value The value.\n     * @return the number of double\n     */\n    public static double float2Double(float value) {\n        return new BigDecimal(String.valueOf(value)).doubleValue();\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ObjectUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.os.Build;\nimport android.util.SparseArray;\nimport android.util.SparseBooleanArray;\nimport android.util.SparseIntArray;\nimport android.util.SparseLongArray;\n\nimport java.lang.reflect.Array;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Comparator;\nimport java.util.Map;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.RequiresApi;\nimport androidx.collection.LongSparseArray;\nimport androidx.collection.SimpleArrayMap;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/12/24\n *     desc  : utils about object\n * </pre>\n */\npublic final class ObjectUtils {\n\n    private ObjectUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Return whether object is empty.\n     *\n     * @param obj The object.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isEmpty(final Object obj) {\n        if (obj == null) {\n            return true;\n        }\n        if (obj.getClass().isArray() && Array.getLength(obj) == 0) {\n            return true;\n        }\n        if (obj instanceof CharSequence && obj.toString().length() == 0) {\n            return true;\n        }\n        if (obj instanceof Collection && ((Collection) obj).isEmpty()) {\n            return true;\n        }\n        if (obj instanceof Map && ((Map) obj).isEmpty()) {\n            return true;\n        }\n        if (obj instanceof SimpleArrayMap && ((SimpleArrayMap) obj).isEmpty()) {\n            return true;\n        }\n        if (obj instanceof SparseArray && ((SparseArray) obj).size() == 0) {\n            return true;\n        }\n        if (obj instanceof SparseBooleanArray && ((SparseBooleanArray) obj).size() == 0) {\n            return true;\n        }\n        if (obj instanceof SparseIntArray && ((SparseIntArray) obj).size() == 0) {\n            return true;\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {\n            if (obj instanceof SparseLongArray && ((SparseLongArray) obj).size() == 0) {\n                return true;\n            }\n        }\n        if (obj instanceof LongSparseArray && ((LongSparseArray) obj).size() == 0) {\n            return true;\n        }\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {\n            if (obj instanceof android.util.LongSparseArray\n                    && ((android.util.LongSparseArray) obj).size() == 0) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public static boolean isEmpty(final CharSequence obj) {\n        return obj == null || obj.toString().length() == 0;\n    }\n\n    public static boolean isEmpty(final Collection obj) {\n        return obj == null || obj.isEmpty();\n    }\n\n    public static boolean isEmpty(final Map obj) {\n        return obj == null || obj.isEmpty();\n    }\n\n    public static boolean isEmpty(final SimpleArrayMap obj) {\n        return obj == null || obj.isEmpty();\n    }\n\n    public static boolean isEmpty(final SparseArray obj) {\n        return obj == null || obj.size() == 0;\n    }\n\n    public static boolean isEmpty(final SparseBooleanArray obj) {\n        return obj == null || obj.size() == 0;\n    }\n\n    public static boolean isEmpty(final SparseIntArray obj) {\n        return obj == null || obj.size() == 0;\n    }\n\n    public static boolean isEmpty(final LongSparseArray obj) {\n        return obj == null || obj.size() == 0;\n    }\n\n    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)\n    public static boolean isEmpty(final SparseLongArray obj) {\n        return obj == null || obj.size() == 0;\n    }\n\n    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)\n    public static boolean isEmpty(final android.util.LongSparseArray obj) {\n        return obj == null || obj.size() == 0;\n    }\n\n    /**\n     * Return whether object is not empty.\n     *\n     * @param obj The object.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isNotEmpty(final Object obj) {\n        return !isEmpty(obj);\n    }\n\n\n    public static boolean isNotEmpty(final CharSequence obj) {\n        return !isEmpty(obj);\n    }\n\n    public static boolean isNotEmpty(final Collection obj) {\n        return !isEmpty(obj);\n    }\n\n    public static boolean isNotEmpty(final Map obj) {\n        return !isEmpty(obj);\n    }\n\n    public static boolean isNotEmpty(final SimpleArrayMap obj) {\n        return !isEmpty(obj);\n    }\n\n    public static boolean isNotEmpty(final SparseArray obj) {\n        return !isEmpty(obj);\n    }\n\n    public static boolean isNotEmpty(final SparseBooleanArray obj) {\n        return !isEmpty(obj);\n    }\n\n    public static boolean isNotEmpty(final SparseIntArray obj) {\n        return !isEmpty(obj);\n    }\n\n    public static boolean isNotEmpty(final LongSparseArray obj) {\n        return !isEmpty(obj);\n    }\n\n    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)\n    public static boolean isNotEmpty(final SparseLongArray obj) {\n        return !isEmpty(obj);\n    }\n\n    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)\n    public static boolean isNotEmpty(final android.util.LongSparseArray obj) {\n        return !isEmpty(obj);\n    }\n\n    /**\n     * Return whether object1 is equals to object2.\n     *\n     * @param o1 The first object.\n     * @param o2 The second object.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean equals(final Object o1, final Object o2) {\n        return o1 == o2 || (o1 != null && o1.equals(o2));\n    }\n\n    /**\n     * Returns 0 if the arguments are identical and {@code\n     * c.compare(a, b)} otherwise.\n     * Consequently, if both arguments are {@code null} 0\n     * is returned.\n     */\n    public static <T> int compare(T a, T b, @NonNull Comparator<? super T> c) {\n        return (a == b) ? 0 : c.compare(a, b);\n    }\n\n    /**\n     * Checks that the specified object reference is not {@code null}.\n     */\n    public static <T> T requireNonNull(T obj) {\n        if (obj == null) throw new NullPointerException();\n        return obj;\n    }\n\n    /**\n     * Checks that the specified object reference is not {@code null} and\n     * throws a customized {@link NullPointerException} if it is.\n     */\n    public static <T> T requireNonNull(T obj, String ifNullTip) {\n        if (obj == null) throw new NullPointerException(ifNullTip);\n        return obj;\n    }\n\n    /**\n     * Require the objects are not null.\n     *\n     * @param objects The object.\n     * @throws NullPointerException if any object is null in objects\n     */\n    public static void requireNonNulls(final Object... objects) {\n        if (objects == null) throw new NullPointerException();\n        for (Object object : objects) {\n            if (object == null) throw new NullPointerException();\n        }\n    }\n\n    /**\n     * Return the nonnull object or default object.\n     *\n     * @param object        The object.\n     * @param defaultObject The default object to use with the object is null.\n     * @param <T>           The value type.\n     * @return the nonnull object or default object\n     */\n    public static <T> T getOrDefault(final T object, final T defaultObject) {\n        if (object == null) {\n            return defaultObject;\n        }\n        return object;\n    }\n\n    /**\n     * Returns the result of calling {@code toString} for a non-{@code\n     * null} argument and {@code \"null\"} for a {@code null} argument.\n     */\n    public static String toString(Object obj) {\n        return String.valueOf(obj);\n    }\n\n    /**\n     * Returns the result of calling {@code toString} on the first\n     * argument if the first argument is not {@code null} and returns\n     * the second argument otherwise.\n     */\n    public static String toString(Object o, String nullDefault) {\n        return (o != null) ? o.toString() : nullDefault;\n    }\n\n    /**\n     * Return the hash code of object.\n     *\n     * @param o The object.\n     * @return the hash code of object\n     */\n    public static int hashCode(final Object o) {\n        return o != null ? o.hashCode() : 0;\n    }\n\n    /**\n     * Return the hash code of objects.\n     */\n    public static int hashCodes(Object... values) {\n        return Arrays.hashCode(values);\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/PathUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.os.Build;\nimport android.os.Environment;\nimport android.text.TextUtils;\n\nimport java.io.File;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/04/15\n *     desc  : utils about path\n * </pre>\n */\npublic final class PathUtils {\n\n    private static final char SEP = File.separatorChar;\n\n    private PathUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Join the path.\n     *\n     * @param parent The parent of path.\n     * @param child  The child path.\n     * @return the path\n     */\n    public static String join(String parent, String child) {\n        if (TextUtils.isEmpty(child)) return parent;\n        if (parent == null) {\n            parent = \"\";\n        }\n        int len = parent.length();\n        String legalSegment = getLegalSegment(child);\n        String newPath;\n        if (len == 0) {\n            newPath = SEP + legalSegment;\n        } else if (parent.charAt(len - 1) == SEP) {\n            newPath = parent + legalSegment;\n        } else {\n            newPath = parent + SEP + legalSegment;\n        }\n        return newPath;\n    }\n\n    private static String getLegalSegment(String segment) {\n        int st = -1, end = -1;\n        char[] charArray = segment.toCharArray();\n        for (int i = 0; i < charArray.length; i++) {\n            char c = charArray[i];\n            if (c != SEP) {\n                if (st == -1) {\n                    st = i;\n                }\n                end = i;\n            }\n        }\n        if (st >= 0 && end >= st) {\n            return segment.substring(st, end + 1);\n        }\n        throw new IllegalArgumentException(\"segment of <\" + segment + \"> is illegal\");\n    }\n\n    /**\n     * Return the path of /system.\n     *\n     * @return the path of /system\n     */\n    public static String getRootPath() {\n        return getAbsolutePath(Environment.getRootDirectory());\n    }\n\n    /**\n     * Return the path of /data.\n     *\n     * @return the path of /data\n     */\n    public static String getDataPath() {\n        return getAbsolutePath(Environment.getDataDirectory());\n    }\n\n    /**\n     * Return the path of /cache.\n     *\n     * @return the path of /cache\n     */\n    public static String getDownloadCachePath() {\n        return getAbsolutePath(Environment.getDownloadCacheDirectory());\n    }\n\n    /**\n     * Return the path of /data/data/package.\n     *\n     * @return the path of /data/data/package\n     */\n    public static String getInternalAppDataPath() {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {\n            return Utils.getApp().getApplicationInfo().dataDir;\n        }\n        return getAbsolutePath(Utils.getApp().getDataDir());\n    }\n\n    /**\n     * Return the path of /data/data/package/code_cache.\n     *\n     * @return the path of /data/data/package/code_cache\n     */\n    public static String getInternalAppCodeCacheDir() {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {\n            return Utils.getApp().getApplicationInfo().dataDir + \"/code_cache\";\n        }\n        return getAbsolutePath(Utils.getApp().getCodeCacheDir());\n    }\n\n    /**\n     * Return the path of /data/data/package/cache.\n     *\n     * @return the path of /data/data/package/cache\n     */\n    public static String getInternalAppCachePath() {\n        return getAbsolutePath(Utils.getApp().getCacheDir());\n    }\n\n    /**\n     * Return the path of /data/data/package/databases.\n     *\n     * @return the path of /data/data/package/databases\n     */\n    public static String getInternalAppDbsPath() {\n        return Utils.getApp().getApplicationInfo().dataDir + \"/databases\";\n    }\n\n    /**\n     * Return the path of /data/data/package/databases/name.\n     *\n     * @param name The name of database.\n     * @return the path of /data/data/package/databases/name\n     */\n    public static String getInternalAppDbPath(String name) {\n        return getAbsolutePath(Utils.getApp().getDatabasePath(name));\n    }\n\n    /**\n     * Return the path of /data/data/package/files.\n     *\n     * @return the path of /data/data/package/files\n     */\n    public static String getInternalAppFilesPath() {\n        return getAbsolutePath(Utils.getApp().getFilesDir());\n    }\n\n    /**\n     * Return the path of /data/data/package/shared_prefs.\n     *\n     * @return the path of /data/data/package/shared_prefs\n     */\n    public static String getInternalAppSpPath() {\n        return Utils.getApp().getApplicationInfo().dataDir + \"/shared_prefs\";\n    }\n\n    /**\n     * Return the path of /data/data/package/no_backup.\n     *\n     * @return the path of /data/data/package/no_backup\n     */\n    public static String getInternalAppNoBackupFilesPath() {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {\n            return Utils.getApp().getApplicationInfo().dataDir + \"/no_backup\";\n        }\n        return getAbsolutePath(Utils.getApp().getNoBackupFilesDir());\n    }\n\n    /**\n     * Return the path of /storage/emulated/0.\n     *\n     * @return the path of /storage/emulated/0\n     */\n    public static String getExternalStoragePath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Environment.getExternalStorageDirectory());\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Music.\n     *\n     * @return the path of /storage/emulated/0/Music\n     */\n    public static String getExternalMusicPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Podcasts.\n     *\n     * @return the path of /storage/emulated/0/Podcasts\n     */\n    public static String getExternalPodcastsPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PODCASTS));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Ringtones.\n     *\n     * @return the path of /storage/emulated/0/Ringtones\n     */\n    public static String getExternalRingtonesPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_RINGTONES));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Alarms.\n     *\n     * @return the path of /storage/emulated/0/Alarms\n     */\n    public static String getExternalAlarmsPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_ALARMS));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Notifications.\n     *\n     * @return the path of /storage/emulated/0/Notifications\n     */\n    public static String getExternalNotificationsPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_NOTIFICATIONS));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Pictures.\n     *\n     * @return the path of /storage/emulated/0/Pictures\n     */\n    public static String getExternalPicturesPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Movies.\n     *\n     * @return the path of /storage/emulated/0/Movies\n     */\n    public static String getExternalMoviesPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Download.\n     *\n     * @return the path of /storage/emulated/0/Download\n     */\n    public static String getExternalDownloadsPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/DCIM.\n     *\n     * @return the path of /storage/emulated/0/DCIM\n     */\n    public static String getExternalDcimPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Documents.\n     *\n     * @return the path of /storage/emulated/0/Documents\n     */\n    public static String getExternalDocumentsPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {\n            return getAbsolutePath(Environment.getExternalStorageDirectory()) + \"/Documents\";\n        }\n        return getAbsolutePath(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Android/data/package.\n     *\n     * @return the path of /storage/emulated/0/Android/data/package\n     */\n    public static String getExternalAppDataPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        File externalCacheDir = Utils.getApp().getExternalCacheDir();\n        if (externalCacheDir == null) return \"\";\n        return getAbsolutePath(externalCacheDir.getParentFile());\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Android/data/package/cache.\n     *\n     * @return the path of /storage/emulated/0/Android/data/package/cache\n     */\n    public static String getExternalAppCachePath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Utils.getApp().getExternalCacheDir());\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Android/data/package/files.\n     *\n     * @return the path of /storage/emulated/0/Android/data/package/files\n     */\n    public static String getExternalAppFilesPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Utils.getApp().getExternalFilesDir(null));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Android/data/package/files/Music.\n     *\n     * @return the path of /storage/emulated/0/Android/data/package/files/Music\n     */\n    public static String getExternalAppMusicPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Utils.getApp().getExternalFilesDir(Environment.DIRECTORY_MUSIC));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Android/data/package/files/Podcasts.\n     *\n     * @return the path of /storage/emulated/0/Android/data/package/files/Podcasts\n     */\n    public static String getExternalAppPodcastsPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Utils.getApp().getExternalFilesDir(Environment.DIRECTORY_PODCASTS));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Android/data/package/files/Ringtones.\n     *\n     * @return the path of /storage/emulated/0/Android/data/package/files/Ringtones\n     */\n    public static String getExternalAppRingtonesPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Utils.getApp().getExternalFilesDir(Environment.DIRECTORY_RINGTONES));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Android/data/package/files/Alarms.\n     *\n     * @return the path of /storage/emulated/0/Android/data/package/files/Alarms\n     */\n    public static String getExternalAppAlarmsPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Utils.getApp().getExternalFilesDir(Environment.DIRECTORY_ALARMS));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Android/data/package/files/Notifications.\n     *\n     * @return the path of /storage/emulated/0/Android/data/package/files/Notifications\n     */\n    public static String getExternalAppNotificationsPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Utils.getApp().getExternalFilesDir(Environment.DIRECTORY_NOTIFICATIONS));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Android/data/package/files/Pictures.\n     *\n     * @return path of /storage/emulated/0/Android/data/package/files/Pictures\n     */\n    public static String getExternalAppPicturesPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Utils.getApp().getExternalFilesDir(Environment.DIRECTORY_PICTURES));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Android/data/package/files/Movies.\n     *\n     * @return the path of /storage/emulated/0/Android/data/package/files/Movies\n     */\n    public static String getExternalAppMoviesPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Utils.getApp().getExternalFilesDir(Environment.DIRECTORY_MOVIES));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Android/data/package/files/Download.\n     *\n     * @return the path of /storage/emulated/0/Android/data/package/files/Download\n     */\n    public static String getExternalAppDownloadPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Utils.getApp().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Android/data/package/files/DCIM.\n     *\n     * @return the path of /storage/emulated/0/Android/data/package/files/DCIM\n     */\n    public static String getExternalAppDcimPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Utils.getApp().getExternalFilesDir(Environment.DIRECTORY_DCIM));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Android/data/package/files/Documents.\n     *\n     * @return the path of /storage/emulated/0/Android/data/package/files/Documents\n     */\n    public static String getExternalAppDocumentsPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {\n            return getAbsolutePath(Utils.getApp().getExternalFilesDir(null)) + \"/Documents\";\n        }\n        return getAbsolutePath(Utils.getApp().getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS));\n    }\n\n    /**\n     * Return the path of /storage/emulated/0/Android/obb/package.\n     *\n     * @return the path of /storage/emulated/0/Android/obb/package\n     */\n    public static String getExternalAppObbPath() {\n        if (!UtilsBridge.isSDCardEnableByEnvironment()) return \"\";\n        return getAbsolutePath(Utils.getApp().getObbDir());\n    }\n\n    public static String getRootPathExternalFirst() {\n        String rootPath = getExternalStoragePath();\n        if (TextUtils.isEmpty(rootPath)) {\n            rootPath = getRootPath();\n        }\n        return rootPath;\n    }\n\n    public static String getAppDataPathExternalFirst() {\n        String appDataPath = getExternalAppDataPath();\n        if (TextUtils.isEmpty(appDataPath)) {\n            appDataPath = getInternalAppDataPath();\n        }\n        return appDataPath;\n    }\n\n    public static String getFilesPathExternalFirst() {\n        String filePath = getExternalAppFilesPath();\n        if (TextUtils.isEmpty(filePath)) {\n            filePath = getInternalAppFilesPath();\n        }\n        return filePath;\n    }\n\n    public static String getCachePathExternalFirst() {\n        String appPath = getExternalAppCachePath();\n        if (TextUtils.isEmpty(appPath)) {\n            appPath = getInternalAppCachePath();\n        }\n        return appPath;\n    }\n\n    private static String getAbsolutePath(final File file) {\n        if (file == null) return \"\";\n        return file.getAbsolutePath();\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/PermissionUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.annotation.TargetApi;\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.content.pm.PackageManager;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.provider.Settings;\nimport android.util.Log;\nimport android.util.Pair;\nimport android.view.MotionEvent;\nimport android.view.WindowManager;\n\nimport com.blankj.utilcode.constant.PermissionConstants;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.RequiresApi;\nimport androidx.core.content.ContextCompat;\n\nimport static com.blankj.utilcode.constant.PermissionConstants.PermissionGroup;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/12/29\n *     desc  : utils about permission\n * </pre>\n */\npublic final class PermissionUtils {\n\n    private static PermissionUtils sInstance;\n\n    private String[]            mPermissionsParam;\n    private OnExplainListener   mOnExplainListener;\n    private OnRationaleListener mOnRationaleListener;\n    private SingleCallback      mSingleCallback;\n    private SimpleCallback      mSimpleCallback;\n    private FullCallback        mFullCallback;\n    private ThemeCallback       mThemeCallback;\n    private Set<String>         mPermissions;\n    private List<String>        mPermissionsRequest;\n    private List<String>        mPermissionsGranted;\n    private List<String>        mPermissionsDenied;\n    private List<String>        mPermissionsDeniedForever;\n\n    private static SimpleCallback sSimpleCallback4WriteSettings;\n    private static SimpleCallback sSimpleCallback4DrawOverlays;\n\n    /**\n     * Return the permissions used in application.\n     *\n     * @return the permissions used in application\n     */\n    public static List<String> getPermissions() {\n        return getPermissions(Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the permissions used in application.\n     *\n     * @param packageName The name of the package.\n     * @return the permissions used in application\n     */\n    public static List<String> getPermissions(final String packageName) {\n        PackageManager pm = Utils.getApp().getPackageManager();\n        try {\n            String[] permissions = pm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS).requestedPermissions;\n            if (permissions == null) return Collections.emptyList();\n            return Arrays.asList(permissions);\n        } catch (PackageManager.NameNotFoundException e) {\n            e.printStackTrace();\n            return Collections.emptyList();\n        }\n    }\n\n    /**\n     * Return whether <em>you</em> have been granted the permissions.\n     *\n     * @param permissions The permissions.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isGranted(final String... permissions) {\n        Pair<List<String>, List<String>> requestAndDeniedPermissions = getRequestAndDeniedPermissions(permissions);\n        List<String> deniedPermissions = requestAndDeniedPermissions.second;\n        if (!deniedPermissions.isEmpty()) {\n            return false;\n        }\n        List<String> requestPermissions = requestAndDeniedPermissions.first;\n        for (String permission : requestPermissions) {\n            if (!isGranted(permission)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    private static Pair<List<String>, List<String>> getRequestAndDeniedPermissions(final String... permissionsParam) {\n        List<String> requestPermissions = new ArrayList<>();\n        List<String> deniedPermissions = new ArrayList<>();\n        List<String> appPermissions = getPermissions();\n        for (String param : permissionsParam) {\n            boolean isIncludeInManifest = false;\n            String[] permissions = PermissionConstants.getPermissions(param);\n            for (String permission : permissions) {\n                if (appPermissions.contains(permission)) {\n                    requestPermissions.add(permission);\n                    isIncludeInManifest = true;\n                }\n            }\n            if (!isIncludeInManifest) {\n                deniedPermissions.add(param);\n                Log.e(\"PermissionUtils\", \"U should add the permission of \" + param + \" in manifest.\");\n            }\n        }\n        return Pair.create(requestPermissions, deniedPermissions);\n    }\n\n    private static boolean isGranted(final String permission) {\n        return Build.VERSION.SDK_INT < Build.VERSION_CODES.M\n                || PackageManager.PERMISSION_GRANTED\n                == ContextCompat.checkSelfPermission(Utils.getApp(), permission);\n    }\n\n    /**\n     * Return whether the app can modify system settings.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    @RequiresApi(api = Build.VERSION_CODES.M)\n    public static boolean isGrantedWriteSettings() {\n        return Settings.System.canWrite(Utils.getApp());\n    }\n\n    @RequiresApi(api = Build.VERSION_CODES.M)\n    public static void requestWriteSettings(final SimpleCallback callback) {\n        if (isGrantedWriteSettings()) {\n            if (callback != null) callback.onGranted();\n            return;\n        }\n        sSimpleCallback4WriteSettings = callback;\n        PermissionActivityImpl.start(PermissionActivityImpl.TYPE_WRITE_SETTINGS);\n    }\n\n    @TargetApi(Build.VERSION_CODES.M)\n    private static void startWriteSettingsActivity(final Activity activity, final int requestCode) {\n        Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS);\n        intent.setData(Uri.parse(\"package:\" + Utils.getApp().getPackageName()));\n        if (!UtilsBridge.isIntentAvailable(intent)) {\n            launchAppDetailsSettings();\n            return;\n        }\n        activity.startActivityForResult(intent, requestCode);\n    }\n\n    /**\n     * Return whether the app can draw on top of other apps.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    @RequiresApi(api = Build.VERSION_CODES.M)\n    public static boolean isGrantedDrawOverlays() {\n        return Settings.canDrawOverlays(Utils.getApp());\n    }\n\n    @RequiresApi(api = Build.VERSION_CODES.M)\n    public static void requestDrawOverlays(final SimpleCallback callback) {\n        if (isGrantedDrawOverlays()) {\n            if (callback != null) callback.onGranted();\n            return;\n        }\n        sSimpleCallback4DrawOverlays = callback;\n        PermissionActivityImpl.start(PermissionActivityImpl.TYPE_DRAW_OVERLAYS);\n    }\n\n    @TargetApi(Build.VERSION_CODES.M)\n    private static void startOverlayPermissionActivity(final Activity activity, final int requestCode) {\n        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);\n        intent.setData(Uri.parse(\"package:\" + Utils.getApp().getPackageName()));\n        if (!UtilsBridge.isIntentAvailable(intent)) {\n            launchAppDetailsSettings();\n            return;\n        }\n        activity.startActivityForResult(intent, requestCode);\n    }\n\n    /**\n     * Launch the application's details settings.\n     */\n    public static void launchAppDetailsSettings() {\n        Intent intent = UtilsBridge.getLaunchAppDetailsSettingsIntent(Utils.getApp().getPackageName(), true);\n        if (!UtilsBridge.isIntentAvailable(intent)) return;\n        Utils.getApp().startActivity(intent);\n    }\n\n    /**\n     * Set the permissions.\n     *\n     * @param permissions The permissions.\n     * @return the single {@link PermissionUtils} instance\n     */\n    public static PermissionUtils permissionGroup(@PermissionGroup final String... permissions) {\n        return permission(permissions);\n    }\n\n    /**\n     * Set the permissions.\n     *\n     * @param permissions The permissions.\n     * @return the single {@link PermissionUtils} instance\n     */\n    public static PermissionUtils permission(final String... permissions) {\n        return new PermissionUtils(permissions);\n    }\n\n    private PermissionUtils(final String... permissions) {\n        mPermissionsParam = permissions;\n        sInstance = this;\n    }\n\n    /**\n     * Set explain listener.\n     *\n     * @param listener The explain listener.\n     * @return the single {@link PermissionUtils} instance\n     */\n    public PermissionUtils explain(final OnExplainListener listener) {\n        mOnExplainListener = listener;\n        return this;\n    }\n\n    /**\n     * Set rationale listener.\n     *\n     * @param listener The rationale listener.\n     * @return the single {@link PermissionUtils} instance\n     */\n    public PermissionUtils rationale(final OnRationaleListener listener) {\n        mOnRationaleListener = listener;\n        return this;\n    }\n\n    /**\n     * Set the simple call back.\n     *\n     * @param callback the single call back\n     * @return the single {@link PermissionUtils} instance\n     */\n    public PermissionUtils callback(final SingleCallback callback) {\n        mSingleCallback = callback;\n        return this;\n    }\n\n    /**\n     * Set the simple call back.\n     *\n     * @param callback the simple call back\n     * @return the single {@link PermissionUtils} instance\n     */\n    public PermissionUtils callback(final SimpleCallback callback) {\n        mSimpleCallback = callback;\n        return this;\n    }\n\n    /**\n     * Set the full call back.\n     *\n     * @param callback the full call back\n     * @return the single {@link PermissionUtils} instance\n     */\n    public PermissionUtils callback(final FullCallback callback) {\n        mFullCallback = callback;\n        return this;\n    }\n\n    /**\n     * Set the theme callback.\n     *\n     * @param callback The theme callback.\n     * @return the single {@link PermissionUtils} instance\n     */\n    public PermissionUtils theme(final ThemeCallback callback) {\n        mThemeCallback = callback;\n        return this;\n    }\n\n    /**\n     * Start request.\n     */\n    public void request() {\n        if (mPermissionsParam == null || mPermissionsParam.length <= 0) {\n            Log.w(\"PermissionUtils\", \"No permissions to request.\");\n            return;\n        }\n\n        mPermissions = new LinkedHashSet<>();\n        mPermissionsRequest = new ArrayList<>();\n        mPermissionsGranted = new ArrayList<>();\n        mPermissionsDenied = new ArrayList<>();\n        mPermissionsDeniedForever = new ArrayList<>();\n\n        Pair<List<String>, List<String>> requestAndDeniedPermissions = getRequestAndDeniedPermissions(mPermissionsParam);\n        mPermissions.addAll(requestAndDeniedPermissions.first);\n        mPermissionsDenied.addAll(requestAndDeniedPermissions.second);\n\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {\n            mPermissionsGranted.addAll(mPermissions);\n            requestCallback();\n        } else {\n            for (String permission : mPermissions) {\n                if (isGranted(permission)) {\n                    mPermissionsGranted.add(permission);\n                } else {\n                    mPermissionsRequest.add(permission);\n                }\n            }\n            if (mPermissionsRequest.isEmpty()) {\n                requestCallback();\n            } else {\n                startPermissionActivity();\n            }\n        }\n    }\n\n    @RequiresApi(api = Build.VERSION_CODES.M)\n    private void startPermissionActivity() {\n        PermissionActivityImpl.start(PermissionActivityImpl.TYPE_RUNTIME);\n    }\n\n    @RequiresApi(api = Build.VERSION_CODES.M)\n    private boolean shouldRationale(final UtilsTransActivity activity,\n                                    final Runnable againRunnable) {\n        boolean isRationale = false;\n        if (mOnRationaleListener != null) {\n            for (String permission : mPermissionsRequest) {\n                if (activity.shouldShowRequestPermissionRationale(permission)) {\n                    rationalInner(activity, againRunnable);\n                    isRationale = true;\n                    break;\n                }\n            }\n            mOnRationaleListener = null;\n        }\n        return isRationale;\n    }\n\n    private void rationalInner(final UtilsTransActivity activity, final Runnable againRunnable) {\n        getPermissionsStatus(activity);\n        mOnRationaleListener.rationale(activity, new OnRationaleListener.ShouldRequest() {\n            @Override\n            public void again(boolean again) {\n                if (again) {\n                    mPermissionsDenied = new ArrayList<>();\n                    mPermissionsDeniedForever = new ArrayList<>();\n                    againRunnable.run();\n                } else {\n                    activity.finish();\n                    requestCallback();\n                }\n            }\n        });\n    }\n\n    private void getPermissionsStatus(final Activity activity) {\n        for (String permission : mPermissionsRequest) {\n            if (isGranted(permission)) {\n                mPermissionsGranted.add(permission);\n            } else {\n                mPermissionsDenied.add(permission);\n                if (!activity.shouldShowRequestPermissionRationale(permission)) {\n                    mPermissionsDeniedForever.add(permission);\n                }\n            }\n        }\n    }\n\n    private void requestCallback() {\n        if (mSingleCallback != null) {\n            mSingleCallback.callback(mPermissionsDenied.isEmpty(),\n                    mPermissionsGranted, mPermissionsDeniedForever, mPermissionsDenied);\n            mSingleCallback = null;\n        }\n        if (mSimpleCallback != null) {\n            if (mPermissionsDenied.isEmpty()) {\n                mSimpleCallback.onGranted();\n            } else {\n                mSimpleCallback.onDenied();\n            }\n            mSimpleCallback = null;\n        }\n        if (mFullCallback != null) {\n            if (mPermissionsRequest.size() == 0\n                    || mPermissionsGranted.size() > 0) {\n                mFullCallback.onGranted(mPermissionsGranted);\n            }\n            if (!mPermissionsDenied.isEmpty()) {\n                mFullCallback.onDenied(mPermissionsDeniedForever, mPermissionsDenied);\n            }\n            mFullCallback = null;\n        }\n        mOnRationaleListener = null;\n        mThemeCallback = null;\n    }\n\n    private void onRequestPermissionsResult(final Activity activity) {\n        getPermissionsStatus(activity);\n        requestCallback();\n    }\n\n    @RequiresApi(api = Build.VERSION_CODES.M)\n    static final class PermissionActivityImpl extends UtilsTransActivity.TransActivityDelegate {\n\n        private static final String TYPE                = \"TYPE\";\n        private static final int    TYPE_RUNTIME        = 0x01;\n        private static final int    TYPE_WRITE_SETTINGS = 0x02;\n        private static final int    TYPE_DRAW_OVERLAYS  = 0x03;\n\n        private static int currentRequestCode = -1;\n\n        private static PermissionActivityImpl INSTANCE = new PermissionActivityImpl();\n\n        public static void start(final int type) {\n            UtilsTransActivity.start(new Utils.Consumer<Intent>() {\n                @Override\n                public void accept(Intent data) {\n                    data.putExtra(TYPE, type);\n                }\n            }, INSTANCE);\n        }\n\n        @Override\n        public void onCreated(@NonNull final UtilsTransActivity activity, @Nullable Bundle savedInstanceState) {\n            activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE\n                    | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);\n            int type = activity.getIntent().getIntExtra(TYPE, -1);\n            if (type == TYPE_RUNTIME) {\n                if (sInstance == null) {\n                    Log.e(\"PermissionUtils\", \"sInstance is null.\");\n                    activity.finish();\n                    return;\n                }\n                if (sInstance.mPermissionsRequest == null) {\n                    Log.e(\"PermissionUtils\", \"mPermissionsRequest is null.\");\n                    activity.finish();\n                    return;\n                }\n                if (sInstance.mPermissionsRequest.size() <= 0) {\n                    Log.e(\"PermissionUtils\", \"mPermissionsRequest's size is no more than 0.\");\n                    activity.finish();\n                    return;\n                }\n                if (sInstance.mThemeCallback != null) {\n                    sInstance.mThemeCallback.onActivityCreate(activity);\n                }\n                if (sInstance.mOnExplainListener != null) {\n                    sInstance.mOnExplainListener.explain(activity, sInstance.mPermissionsRequest, new OnExplainListener.ShouldRequest() {\n                        @Override\n                        public void start(boolean start) {\n                            if (!start) {\n                                activity.finish();\n                            } else {\n                                requestPermissions(activity);\n                            }\n                        }\n                    });\n                    sInstance.mOnExplainListener = null;\n                    return;\n                }\n                requestPermissions(activity);\n            } else if (type == TYPE_WRITE_SETTINGS) {\n                currentRequestCode = TYPE_WRITE_SETTINGS;\n                startWriteSettingsActivity(activity, TYPE_WRITE_SETTINGS);\n            } else if (type == TYPE_DRAW_OVERLAYS) {\n                currentRequestCode = TYPE_DRAW_OVERLAYS;\n                startOverlayPermissionActivity(activity, TYPE_DRAW_OVERLAYS);\n            } else {\n                activity.finish();\n                Log.e(\"PermissionUtils\", \"type is wrong.\");\n            }\n        }\n\n        private void requestPermissions(final UtilsTransActivity activity) {\n            if (sInstance.shouldRationale(activity, new Runnable() {\n                @Override\n                public void run() {\n                    activity.requestPermissions(sInstance.mPermissionsRequest.toArray(new String[0]), 1);\n                }\n            })) {\n                return;\n            }\n            activity.requestPermissions(sInstance.mPermissionsRequest.toArray(new String[0]), 1);\n        }\n\n        @Override\n        public void onRequestPermissionsResult(@NonNull UtilsTransActivity activity,\n                                               int requestCode,\n                                               @NonNull String[] permissions,\n                                               @NonNull int[] grantResults) {\n            activity.finish();\n            if (sInstance != null && sInstance.mPermissionsRequest != null) {\n                sInstance.onRequestPermissionsResult(activity);\n            }\n        }\n\n\n        @Override\n        public boolean dispatchTouchEvent(@NonNull UtilsTransActivity activity, MotionEvent ev) {\n            activity.finish();\n            return true;\n        }\n\n        @Override\n        public void onDestroy(@NonNull final UtilsTransActivity activity) {\n            if (currentRequestCode != -1) {\n                checkRequestCallback(currentRequestCode);\n                currentRequestCode = -1;\n            }\n            super.onDestroy(activity);\n        }\n\n        @Override\n        public void onActivityResult(@NonNull UtilsTransActivity activity, int requestCode, int resultCode, Intent data) {\n            activity.finish();\n        }\n\n        private void checkRequestCallback(int requestCode) {\n            if (requestCode == TYPE_WRITE_SETTINGS) {\n                if (sSimpleCallback4WriteSettings == null) return;\n                if (isGrantedWriteSettings()) {\n                    sSimpleCallback4WriteSettings.onGranted();\n                } else {\n                    sSimpleCallback4WriteSettings.onDenied();\n                }\n                sSimpleCallback4WriteSettings = null;\n            } else if (requestCode == TYPE_DRAW_OVERLAYS) {\n                if (sSimpleCallback4DrawOverlays == null) return;\n                if (isGrantedDrawOverlays()) {\n                    sSimpleCallback4DrawOverlays.onGranted();\n                } else {\n                    sSimpleCallback4DrawOverlays.onDenied();\n                }\n                sSimpleCallback4DrawOverlays = null;\n            }\n        }\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // interface\n    ///////////////////////////////////////////////////////////////////////////\n\n    public interface OnExplainListener {\n\n        void explain(@NonNull UtilsTransActivity activity, @NonNull List<String> denied, @NonNull ShouldRequest shouldRequest);\n\n        interface ShouldRequest {\n            void start(boolean start);\n        }\n    }\n\n    public interface OnRationaleListener {\n\n        void rationale(@NonNull UtilsTransActivity activity, @NonNull ShouldRequest shouldRequest);\n\n        interface ShouldRequest {\n            void again(boolean again);\n        }\n    }\n\n    public interface SingleCallback {\n        void callback(boolean isAllGranted, @NonNull List<String> granted,\n                      @NonNull List<String> deniedForever, @NonNull List<String> denied);\n    }\n\n\n    public interface SimpleCallback {\n        void onGranted();\n\n        void onDenied();\n    }\n\n    public interface FullCallback {\n        void onGranted(@NonNull List<String> granted);\n\n        void onDenied(@NonNull List<String> deniedForever, @NonNull List<String> denied);\n    }\n\n    public interface ThemeCallback {\n        void onActivityCreate(@NonNull Activity activity);\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/PhoneUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.os.Build;\nimport android.telephony.TelephonyManager;\nimport android.text.TextUtils;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.RequiresPermission;\n\nimport static android.Manifest.permission.CALL_PHONE;\nimport static android.Manifest.permission.READ_PHONE_STATE;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/02\n *     desc  : utils about phone\n * </pre>\n */\npublic final class PhoneUtils {\n\n    private PhoneUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Return whether the device is phone.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isPhone() {\n        TelephonyManager tm = getTelephonyManager();\n        return tm.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;\n    }\n\n    /**\n     * Return the unique device id.\n     * <p>If the version of SDK is greater than 28, it will return an empty string.</p>\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />}</p>\n     *\n     * @return the unique device id\n     */\n    @SuppressLint(\"HardwareIds\")\n    @RequiresPermission(READ_PHONE_STATE)\n    public static String getDeviceId() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n            return \"\";\n        }\n        TelephonyManager tm = getTelephonyManager();\n        String deviceId = tm.getDeviceId();\n        if (!TextUtils.isEmpty(deviceId)) return deviceId;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n            String imei = tm.getImei();\n            if (!TextUtils.isEmpty(imei)) return imei;\n            String meid = tm.getMeid();\n            return TextUtils.isEmpty(meid) ? \"\" : meid;\n        }\n        return \"\";\n    }\n\n    /**\n     * Return the serial of device.\n     *\n     * @return the serial of device\n     */\n    @SuppressLint(\"HardwareIds\")\n    @RequiresPermission(READ_PHONE_STATE)\n    public static String getSerial() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n            try {\n                return Build.getSerial();\n            } catch (SecurityException e) {\n                e.printStackTrace();\n                return \"\";\n            }\n        }\n        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? Build.getSerial() : Build.SERIAL;\n    }\n\n    /**\n     * Return the IMEI.\n     * <p>If the version of SDK is greater than 28, it will return an empty string.</p>\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />}</p>\n     *\n     * @return the IMEI\n     */\n    @RequiresPermission(READ_PHONE_STATE)\n    public static String getIMEI() {\n        return getImeiOrMeid(true);\n    }\n\n    /**\n     * Return the MEID.\n     * <p>If the version of SDK is greater than 28, it will return an empty string.</p>\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />}</p>\n     *\n     * @return the MEID\n     */\n    @RequiresPermission(READ_PHONE_STATE)\n    public static String getMEID() {\n        return getImeiOrMeid(false);\n    }\n\n    @SuppressLint(\"HardwareIds\")\n    @RequiresPermission(READ_PHONE_STATE)\n    public static String getImeiOrMeid(boolean isImei) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n            return \"\";\n        }\n        TelephonyManager tm = getTelephonyManager();\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n            if (isImei) {\n                return getMinOne(tm.getImei(0), tm.getImei(1));\n            } else {\n                return getMinOne(tm.getMeid(0), tm.getMeid(1));\n            }\n        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            String ids = getSystemPropertyByReflect(isImei ? \"ril.gsm.imei\" : \"ril.cdma.meid\");\n            if (!TextUtils.isEmpty(ids)) {\n                String[] idArr = ids.split(\",\");\n                if (idArr.length == 2) {\n                    return getMinOne(idArr[0], idArr[1]);\n                } else {\n                    return idArr[0];\n                }\n            }\n\n            String id0 = tm.getDeviceId();\n            String id1 = \"\";\n            try {\n                Method method = tm.getClass().getMethod(\"getDeviceId\", int.class);\n                id1 = (String) method.invoke(tm,\n                        isImei ? TelephonyManager.PHONE_TYPE_GSM\n                                : TelephonyManager.PHONE_TYPE_CDMA);\n            } catch (NoSuchMethodException e) {\n                e.printStackTrace();\n            } catch (IllegalAccessException e) {\n                e.printStackTrace();\n            } catch (InvocationTargetException e) {\n                e.printStackTrace();\n            }\n            if (isImei) {\n                if (id0 != null && id0.length() < 15) {\n                    id0 = \"\";\n                }\n                if (id1 != null && id1.length() < 15) {\n                    id1 = \"\";\n                }\n            } else {\n                if (id0 != null && id0.length() == 14) {\n                    id0 = \"\";\n                }\n                if (id1 != null && id1.length() == 14) {\n                    id1 = \"\";\n                }\n            }\n            return getMinOne(id0, id1);\n        } else {\n            String deviceId = tm.getDeviceId();\n            if (isImei) {\n                if (deviceId != null && deviceId.length() >= 15) {\n                    return deviceId;\n                }\n            } else {\n                if (deviceId != null && deviceId.length() == 14) {\n                    return deviceId;\n                }\n            }\n        }\n        return \"\";\n    }\n\n    private static String getMinOne(String s0, String s1) {\n        boolean empty0 = TextUtils.isEmpty(s0);\n        boolean empty1 = TextUtils.isEmpty(s1);\n        if (empty0 && empty1) return \"\";\n        if (!empty0 && !empty1) {\n            if (s0.compareTo(s1) <= 0) {\n                return s0;\n            } else {\n                return s1;\n            }\n        }\n        if (!empty0) return s0;\n        return s1;\n    }\n\n    private static String getSystemPropertyByReflect(String key) {\n        try {\n            @SuppressLint(\"PrivateApi\")\n            Class<?> clz = Class.forName(\"android.os.SystemProperties\");\n            Method getMethod = clz.getMethod(\"get\", String.class, String.class);\n            return (String) getMethod.invoke(clz, key, \"\");\n        } catch (Exception e) {/**/}\n        return \"\";\n    }\n\n    /**\n     * Return the IMSI.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />}</p>\n     *\n     * @return the IMSI\n     */\n    @SuppressLint(\"HardwareIds\")\n    @RequiresPermission(READ_PHONE_STATE)\n    public static String getIMSI() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n            try {\n                getTelephonyManager().getSubscriberId();\n            } catch (SecurityException e) {\n                e.printStackTrace();\n                return \"\";\n            }\n        }\n        return getTelephonyManager().getSubscriberId();\n    }\n\n    /**\n     * Returns the current phone type.\n     *\n     * @return the current phone type\n     * <ul>\n     * <li>{@link TelephonyManager#PHONE_TYPE_NONE}</li>\n     * <li>{@link TelephonyManager#PHONE_TYPE_GSM }</li>\n     * <li>{@link TelephonyManager#PHONE_TYPE_CDMA}</li>\n     * <li>{@link TelephonyManager#PHONE_TYPE_SIP }</li>\n     * </ul>\n     */\n    public static int getPhoneType() {\n        TelephonyManager tm = getTelephonyManager();\n        return tm.getPhoneType();\n    }\n\n    /**\n     * Return whether sim card state is ready.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isSimCardReady() {\n        TelephonyManager tm = getTelephonyManager();\n        return tm.getSimState() == TelephonyManager.SIM_STATE_READY;\n    }\n\n    /**\n     * Return the sim operator name.\n     *\n     * @return the sim operator name\n     */\n    public static String getSimOperatorName() {\n        TelephonyManager tm = getTelephonyManager();\n        return tm.getSimOperatorName();\n    }\n\n    /**\n     * Return the sim operator using mnc.\n     *\n     * @return the sim operator\n     */\n    public static String getSimOperatorByMnc() {\n        TelephonyManager tm = getTelephonyManager();\n        String operator = tm.getSimOperator();\n        if (operator == null) return \"\";\n        switch (operator) {\n            case \"46000\":\n            case \"46002\":\n            case \"46007\":\n            case \"46020\":\n                return \"中国移动\";\n            case \"46001\":\n            case \"46006\":\n            case \"46009\":\n                return \"中国联通\";\n            case \"46003\":\n            case \"46005\":\n            case \"46011\":\n                return \"中国电信\";\n            default:\n                return operator;\n        }\n    }\n\n    /**\n     * Skip to dial.\n     *\n     * @param phoneNumber The phone number.\n     */\n    public static void dial(@NonNull final String phoneNumber) {\n        Utils.getApp().startActivity(UtilsBridge.getDialIntent(phoneNumber));\n    }\n\n    /**\n     * Make a phone call.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.CALL_PHONE\" />}</p>\n     *\n     * @param phoneNumber The phone number.\n     */\n    @RequiresPermission(CALL_PHONE)\n    public static void call(@NonNull final String phoneNumber) {\n        Utils.getApp().startActivity(UtilsBridge.getCallIntent(phoneNumber));\n    }\n\n    /**\n     * Send sms.\n     *\n     * @param phoneNumber The phone number.\n     * @param content     The content.\n     */\n    public static void sendSms(@NonNull final String phoneNumber, final String content) {\n        Utils.getApp().startActivity(UtilsBridge.getSendSmsIntent(phoneNumber, content));\n    }\n\n    private static TelephonyManager getTelephonyManager() {\n        return (TelephonyManager) Utils.getApp().getSystemService(Context.TELEPHONY_SERVICE);\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ProcessUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.app.ActivityManager;\nimport android.app.AppOpsManager;\nimport android.app.Application;\nimport android.app.usage.UsageStats;\nimport android.app.usage.UsageStatsManager;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.pm.ApplicationInfo;\nimport android.content.pm.PackageManager;\nimport android.content.pm.ResolveInfo;\nimport android.provider.Settings;\nimport android.text.TextUtils;\nimport android.util.Log;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileReader;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.RequiresPermission;\n\nimport static android.Manifest.permission.KILL_BACKGROUND_PROCESSES;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/10/18\n *     desc  : utils about process\n * </pre>\n */\npublic final class ProcessUtils {\n\n    private ProcessUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Return the foreground process name.\n     * <p>Target APIs greater than 21 must hold\n     * {@code <uses-permission android:name=\"android.permission.PACKAGE_USAGE_STATS\" />}</p>\n     *\n     * @return the foreground process name\n     */\n    public static String getForegroundProcessName() {\n        ActivityManager am =\n                (ActivityManager) Utils.getApp().getSystemService(Context.ACTIVITY_SERVICE);\n        //noinspection ConstantConditions\n        List<ActivityManager.RunningAppProcessInfo> pInfo = am.getRunningAppProcesses();\n        if (pInfo != null && pInfo.size() > 0) {\n            for (ActivityManager.RunningAppProcessInfo aInfo : pInfo) {\n                if (aInfo.importance\n                        == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {\n                    return aInfo.processName;\n                }\n            }\n        }\n        if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.LOLLIPOP) {\n            PackageManager pm = Utils.getApp().getPackageManager();\n            Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);\n            List<ResolveInfo> list =\n                    pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);\n            Log.i(\"ProcessUtils\", list.toString());\n            if (list.size() <= 0) {\n                Log.i(\"ProcessUtils\",\n                        \"getForegroundProcessName: noun of access to usage information.\");\n                return \"\";\n            }\n            try {// Access to usage information.\n                ApplicationInfo info =\n                        pm.getApplicationInfo(Utils.getApp().getPackageName(), 0);\n                AppOpsManager aom =\n                        (AppOpsManager) Utils.getApp().getSystemService(Context.APP_OPS_SERVICE);\n                if (aom.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,\n                        info.uid,\n                        info.packageName) != AppOpsManager.MODE_ALLOWED) {\n                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n                    Utils.getApp().startActivity(intent);\n                }\n                if (aom.checkOpNoThrow(AppOpsManager.OPSTR_GET_USAGE_STATS,\n                        info.uid,\n                        info.packageName) != AppOpsManager.MODE_ALLOWED) {\n                    Log.i(\"ProcessUtils\",\n                            \"getForegroundProcessName: refuse to device usage stats.\");\n                    return \"\";\n                }\n                UsageStatsManager usageStatsManager = (UsageStatsManager) Utils.getApp()\n                        .getSystemService(Context.USAGE_STATS_SERVICE);\n                List<UsageStats> usageStatsList = null;\n                if (usageStatsManager != null) {\n                    long endTime = System.currentTimeMillis();\n                    long beginTime = endTime - 86400000 * 7;\n                    usageStatsList = usageStatsManager\n                            .queryUsageStats(UsageStatsManager.INTERVAL_BEST,\n                                    beginTime, endTime);\n                }\n                if (usageStatsList == null || usageStatsList.isEmpty()) return \"\";\n                UsageStats recentStats = null;\n                for (UsageStats usageStats : usageStatsList) {\n                    if (recentStats == null\n                            || usageStats.getLastTimeUsed() > recentStats.getLastTimeUsed()) {\n                        recentStats = usageStats;\n                    }\n                }\n                return recentStats == null ? null : recentStats.getPackageName();\n            } catch (PackageManager.NameNotFoundException e) {\n                e.printStackTrace();\n            }\n        }\n        return \"\";\n    }\n\n    /**\n     * Return all background processes.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.KILL_BACKGROUND_PROCESSES\" />}</p>\n     *\n     * @return all background processes\n     */\n    @RequiresPermission(KILL_BACKGROUND_PROCESSES)\n    public static Set<String> getAllBackgroundProcesses() {\n        ActivityManager am =\n                (ActivityManager) Utils.getApp().getSystemService(Context.ACTIVITY_SERVICE);\n        List<ActivityManager.RunningAppProcessInfo> info = am.getRunningAppProcesses();\n        Set<String> set = new HashSet<>();\n        if (info != null) {\n            for (ActivityManager.RunningAppProcessInfo aInfo : info) {\n                Collections.addAll(set, aInfo.pkgList);\n            }\n        }\n        return set;\n    }\n\n    /**\n     * Kill all background processes.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.KILL_BACKGROUND_PROCESSES\" />}</p>\n     *\n     * @return background processes were killed\n     */\n    @RequiresPermission(KILL_BACKGROUND_PROCESSES)\n    public static Set<String> killAllBackgroundProcesses() {\n        ActivityManager am =\n                (ActivityManager) Utils.getApp().getSystemService(Context.ACTIVITY_SERVICE);\n        List<ActivityManager.RunningAppProcessInfo> info = am.getRunningAppProcesses();\n        Set<String> set = new HashSet<>();\n        if (info == null) return set;\n        for (ActivityManager.RunningAppProcessInfo aInfo : info) {\n            for (String pkg : aInfo.pkgList) {\n                am.killBackgroundProcesses(pkg);\n                set.add(pkg);\n            }\n        }\n        info = am.getRunningAppProcesses();\n        for (ActivityManager.RunningAppProcessInfo aInfo : info) {\n            for (String pkg : aInfo.pkgList) {\n                set.remove(pkg);\n            }\n        }\n        return set;\n    }\n\n    /**\n     * Kill background processes.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.KILL_BACKGROUND_PROCESSES\" />}</p>\n     *\n     * @param packageName The name of the package.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    @RequiresPermission(KILL_BACKGROUND_PROCESSES)\n    public static boolean killBackgroundProcesses(@NonNull final String packageName) {\n        ActivityManager am =\n                (ActivityManager) Utils.getApp().getSystemService(Context.ACTIVITY_SERVICE);\n        List<ActivityManager.RunningAppProcessInfo> info = am.getRunningAppProcesses();\n        if (info == null || info.size() == 0) return true;\n        for (ActivityManager.RunningAppProcessInfo aInfo : info) {\n            if (Arrays.asList(aInfo.pkgList).contains(packageName)) {\n                am.killBackgroundProcesses(packageName);\n            }\n        }\n        info = am.getRunningAppProcesses();\n        if (info == null || info.size() == 0) return true;\n        for (ActivityManager.RunningAppProcessInfo aInfo : info) {\n            if (Arrays.asList(aInfo.pkgList).contains(packageName)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Return whether app running in the main process.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isMainProcess() {\n        return Utils.getApp().getPackageName().equals(getCurrentProcessName());\n    }\n\n    /**\n     * Return the name of current process.\n     *\n     * @return the name of current process\n     */\n    public static String getCurrentProcessName() {\n        String name = getCurrentProcessNameByFile();\n        if (!TextUtils.isEmpty(name)) return name;\n        name = getCurrentProcessNameByAms();\n        if (!TextUtils.isEmpty(name)) return name;\n        name = getCurrentProcessNameByReflect();\n        return name;\n    }\n\n    private static String getCurrentProcessNameByFile() {\n        try {\n            File file = new File(\"/proc/\" + android.os.Process.myPid() + \"/\" + \"cmdline\");\n            BufferedReader mBufferedReader = new BufferedReader(new FileReader(file));\n            String processName = mBufferedReader.readLine().trim();\n            mBufferedReader.close();\n            return processName;\n        } catch (Exception e) {\n            e.printStackTrace();\n            return \"\";\n        }\n    }\n\n    private static String getCurrentProcessNameByAms() {\n        try {\n            ActivityManager am = (ActivityManager) Utils.getApp().getSystemService(Context.ACTIVITY_SERVICE);\n            if (am == null) return \"\";\n            List<ActivityManager.RunningAppProcessInfo> info = am.getRunningAppProcesses();\n            if (info == null || info.size() == 0) return \"\";\n            int pid = android.os.Process.myPid();\n            for (ActivityManager.RunningAppProcessInfo aInfo : info) {\n                if (aInfo.pid == pid) {\n                    if (aInfo.processName != null) {\n                        return aInfo.processName;\n                    }\n                }\n            }\n        } catch (Exception e) {\n            return \"\";\n        }\n        return \"\";\n    }\n\n    private static String getCurrentProcessNameByReflect() {\n        String processName = \"\";\n        try {\n            Application app = Utils.getApp();\n            Field loadedApkField = app.getClass().getField(\"mLoadedApk\");\n            loadedApkField.setAccessible(true);\n            Object loadedApk = loadedApkField.get(app);\n\n            Field activityThreadField = loadedApk.getClass().getDeclaredField(\"mActivityThread\");\n            activityThreadField.setAccessible(true);\n            Object activityThread = activityThreadField.get(loadedApk);\n\n            Method getProcessName = activityThread.getClass().getDeclaredMethod(\"getProcessName\");\n            processName = (String) getProcessName.invoke(activityThread);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return processName;\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ReflectUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport java.lang.reflect.AccessibleObject;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Member;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Modifier;\nimport java.lang.reflect.Proxy;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/12/15\n *     desc  : utils about reflect\n * </pre>\n */\npublic final class ReflectUtils {\n\n    private final Class<?> type;\n\n    private final Object object;\n\n    private ReflectUtils(final Class<?> type) {\n        this(type, type);\n    }\n\n    private ReflectUtils(final Class<?> type, Object object) {\n        this.type = type;\n        this.object = object;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // reflect\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Reflect the class.\n     *\n     * @param className The name of class.\n     * @return the single {@link ReflectUtils} instance\n     * @throws ReflectException if reflect unsuccessfully\n     */\n    public static ReflectUtils reflect(final String className)\n            throws ReflectException {\n        return reflect(forName(className));\n    }\n\n    /**\n     * Reflect the class.\n     *\n     * @param className   The name of class.\n     * @param classLoader The loader of class.\n     * @return the single {@link ReflectUtils} instance\n     * @throws ReflectException if reflect unsuccessfully\n     */\n    public static ReflectUtils reflect(final String className, final ClassLoader classLoader)\n            throws ReflectException {\n        return reflect(forName(className, classLoader));\n    }\n\n    /**\n     * Reflect the class.\n     *\n     * @param clazz The class.\n     * @return the single {@link ReflectUtils} instance\n     * @throws ReflectException if reflect unsuccessfully\n     */\n    public static ReflectUtils reflect(final Class<?> clazz)\n            throws ReflectException {\n        return new ReflectUtils(clazz);\n    }\n\n    /**\n     * Reflect the class.\n     *\n     * @param object The object.\n     * @return the single {@link ReflectUtils} instance\n     * @throws ReflectException if reflect unsuccessfully\n     */\n    public static ReflectUtils reflect(final Object object)\n            throws ReflectException {\n        return new ReflectUtils(object == null ? Object.class : object.getClass(), object);\n    }\n\n    private static Class<?> forName(String className) {\n        try {\n            return Class.forName(className);\n        } catch (ClassNotFoundException e) {\n            throw new ReflectException(e);\n        }\n    }\n\n    private static Class<?> forName(String name, ClassLoader classLoader) {\n        try {\n            return Class.forName(name, true, classLoader);\n        } catch (ClassNotFoundException e) {\n            throw new ReflectException(e);\n        }\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // newInstance\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Create and initialize a new instance.\n     *\n     * @return the single {@link ReflectUtils} instance\n     */\n    public ReflectUtils newInstance() {\n        return newInstance(new Object[0]);\n    }\n\n    /**\n     * Create and initialize a new instance.\n     *\n     * @param args The args.\n     * @return the single {@link ReflectUtils} instance\n     */\n    public ReflectUtils newInstance(Object... args) {\n        Class<?>[] types = getArgsType(args);\n        try {\n            Constructor<?> constructor = type().getDeclaredConstructor(types);\n            return newInstance(constructor, args);\n        } catch (NoSuchMethodException e) {\n            List<Constructor<?>> list = new ArrayList<>();\n            for (Constructor<?> constructor : type().getDeclaredConstructors()) {\n                if (match(constructor.getParameterTypes(), types)) {\n                    list.add(constructor);\n                }\n            }\n            if (list.isEmpty()) {\n                throw new ReflectException(e);\n            } else {\n                sortConstructors(list);\n                return newInstance(list.get(0), args);\n            }\n        }\n    }\n\n    private Class<?>[] getArgsType(final Object... args) {\n        if (args == null) return new Class[0];\n        Class<?>[] result = new Class[args.length];\n        for (int i = 0; i < args.length; i++) {\n            Object value = args[i];\n            result[i] = value == null ? NULL.class : value.getClass();\n        }\n        return result;\n    }\n\n    private void sortConstructors(List<Constructor<?>> list) {\n        Collections.sort(list, new Comparator<Constructor<?>>() {\n            @Override\n            public int compare(Constructor<?> o1, Constructor<?> o2) {\n                Class<?>[] types1 = o1.getParameterTypes();\n                Class<?>[] types2 = o2.getParameterTypes();\n                int len = types1.length;\n                for (int i = 0; i < len; i++) {\n                    if (!types1[i].equals(types2[i])) {\n                        if (wrapper(types1[i]).isAssignableFrom(wrapper(types2[i]))) {\n                            return 1;\n                        } else {\n                            return -1;\n                        }\n                    }\n                }\n                return 0;\n            }\n        });\n    }\n\n    private ReflectUtils newInstance(final Constructor<?> constructor, final Object... args) {\n        try {\n            return new ReflectUtils(\n                    constructor.getDeclaringClass(),\n                    accessible(constructor).newInstance(args)\n            );\n        } catch (Exception e) {\n            throw new ReflectException(e);\n        }\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // field\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Get the field.\n     *\n     * @param name The name of field.\n     * @return the single {@link ReflectUtils} instance\n     */\n    public ReflectUtils field(final String name) {\n        try {\n            Field field = getField(name);\n            return new ReflectUtils(field.getType(), field.get(object));\n        } catch (IllegalAccessException e) {\n            throw new ReflectException(e);\n        }\n    }\n\n    /**\n     * Set the field.\n     *\n     * @param name  The name of field.\n     * @param value The value.\n     * @return the single {@link ReflectUtils} instance\n     */\n    public ReflectUtils field(String name, Object value) {\n        try {\n            Field field = getField(name);\n            field.set(object, unwrap(value));\n            return this;\n        } catch (Exception e) {\n            throw new ReflectException(e);\n        }\n    }\n\n    private Field getField(String name) throws IllegalAccessException {\n        Field field = getAccessibleField(name);\n        if ((field.getModifiers() & Modifier.FINAL) == Modifier.FINAL) {\n            try {\n                Field modifiersField = Field.class.getDeclaredField(\"modifiers\");\n                modifiersField.setAccessible(true);\n                modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);\n            } catch (NoSuchFieldException ignore) {\n                // runs in android will happen\n                field.setAccessible(true);\n            }\n        }\n        return field;\n    }\n\n    private Field getAccessibleField(String name) {\n        Class<?> type = type();\n        try {\n            return accessible(type.getField(name));\n        } catch (NoSuchFieldException e) {\n            do {\n                try {\n                    return accessible(type.getDeclaredField(name));\n                } catch (NoSuchFieldException ignore) {\n                }\n                type = type.getSuperclass();\n            } while (type != null);\n            throw new ReflectException(e);\n        }\n    }\n\n    private Object unwrap(Object object) {\n        if (object instanceof ReflectUtils) {\n            return ((ReflectUtils) object).get();\n        }\n        return object;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // method\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Invoke the method.\n     *\n     * @param name The name of method.\n     * @return the single {@link ReflectUtils} instance\n     * @throws ReflectException if reflect unsuccessfully\n     */\n    public ReflectUtils method(final String name) throws ReflectException {\n        return method(name, new Object[0]);\n    }\n\n    /**\n     * Invoke the method.\n     *\n     * @param name The name of method.\n     * @param args The args.\n     * @return the single {@link ReflectUtils} instance\n     * @throws ReflectException if reflect unsuccessfully\n     */\n    public ReflectUtils method(final String name, final Object... args) throws ReflectException {\n        Class<?>[] types = getArgsType(args);\n        try {\n            Method method = exactMethod(name, types);\n            return method(method, object, args);\n        } catch (NoSuchMethodException e) {\n            try {\n                Method method = similarMethod(name, types);\n                return method(method, object, args);\n            } catch (NoSuchMethodException e1) {\n                throw new ReflectException(e1);\n            }\n        }\n    }\n\n    private ReflectUtils method(final Method method, final Object obj, final Object... args) {\n        try {\n            accessible(method);\n            if (method.getReturnType() == void.class) {\n                method.invoke(obj, args);\n                return reflect(obj);\n            } else {\n                return reflect(method.invoke(obj, args));\n            }\n        } catch (Exception e) {\n            throw new ReflectException(e);\n        }\n    }\n\n    private Method exactMethod(final String name, final Class<?>[] types)\n            throws NoSuchMethodException {\n        Class<?> type = type();\n        try {\n            return type.getMethod(name, types);\n        } catch (NoSuchMethodException e) {\n            do {\n                try {\n                    return type.getDeclaredMethod(name, types);\n                } catch (NoSuchMethodException ignore) {\n                }\n                type = type.getSuperclass();\n            } while (type != null);\n            throw new NoSuchMethodException();\n        }\n    }\n\n    private Method similarMethod(final String name, final Class<?>[] types)\n            throws NoSuchMethodException {\n        Class<?> type = type();\n        List<Method> methods = new ArrayList<>();\n        for (Method method : type.getMethods()) {\n            if (isSimilarSignature(method, name, types)) {\n                methods.add(method);\n            }\n        }\n        if (!methods.isEmpty()) {\n            sortMethods(methods);\n            return methods.get(0);\n        }\n        do {\n            for (Method method : type.getDeclaredMethods()) {\n                if (isSimilarSignature(method, name, types)) {\n                    methods.add(method);\n                }\n            }\n            if (!methods.isEmpty()) {\n                sortMethods(methods);\n                return methods.get(0);\n            }\n            type = type.getSuperclass();\n        } while (type != null);\n\n        throw new NoSuchMethodException(\"No similar method \" + name + \" with params \"\n                + Arrays.toString(types) + \" could be found on type \" + type() + \".\");\n    }\n\n    private void sortMethods(final List<Method> methods) {\n        Collections.sort(methods, new Comparator<Method>() {\n            @Override\n            public int compare(Method o1, Method o2) {\n                Class<?>[] types1 = o1.getParameterTypes();\n                Class<?>[] types2 = o2.getParameterTypes();\n                int len = types1.length;\n                for (int i = 0; i < len; i++) {\n                    if (!types1[i].equals(types2[i])) {\n                        if (wrapper(types1[i]).isAssignableFrom(wrapper(types2[i]))) {\n                            return 1;\n                        } else {\n                            return -1;\n                        }\n                    }\n                }\n                return 0;\n            }\n        });\n    }\n\n    private boolean isSimilarSignature(final Method possiblyMatchingMethod,\n                                       final String desiredMethodName,\n                                       final Class<?>[] desiredParamTypes) {\n        return possiblyMatchingMethod.getName().equals(desiredMethodName)\n                && match(possiblyMatchingMethod.getParameterTypes(), desiredParamTypes);\n    }\n\n    private boolean match(final Class<?>[] declaredTypes, final Class<?>[] actualTypes) {\n        if (declaredTypes.length == actualTypes.length) {\n            for (int i = 0; i < actualTypes.length; i++) {\n                if (actualTypes[i] == NULL.class\n                        || wrapper(declaredTypes[i]).isAssignableFrom(wrapper(actualTypes[i]))) {\n                    continue;\n                }\n                return false;\n            }\n            return true;\n        } else {\n            return false;\n        }\n    }\n\n    private <T extends AccessibleObject> T accessible(T accessible) {\n        if (accessible == null) return null;\n        if (accessible instanceof Member) {\n            Member member = (Member) accessible;\n            if (Modifier.isPublic(member.getModifiers())\n                    && Modifier.isPublic(member.getDeclaringClass().getModifiers())) {\n                return accessible;\n            }\n        }\n        if (!accessible.isAccessible()) accessible.setAccessible(true);\n        return accessible;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // proxy\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Create a proxy for the wrapped object allowing to typesafely invoke\n     * methods on it using a custom interface.\n     *\n     * @param proxyType The interface type that is implemented by the proxy.\n     * @return a proxy for the wrapped object\n     */\n    @SuppressWarnings(\"unchecked\")\n    public <P> P proxy(final Class<P> proxyType) {\n        final boolean isMap = (object instanceof Map);\n        final InvocationHandler handler = new InvocationHandler() {\n            @Override\n            @SuppressWarnings(\"null\")\n            public Object invoke(Object proxy, Method method, Object[] args) {\n                String name = method.getName();\n                try {\n                    return reflect(object).method(name, args).get();\n                } catch (ReflectException e) {\n                    if (isMap) {\n                        Map<String, Object> map = (Map<String, Object>) object;\n                        int length = (args == null ? 0 : args.length);\n\n                        if (length == 0 && name.startsWith(\"get\")) {\n                            return map.get(property(name.substring(3)));\n                        } else if (length == 0 && name.startsWith(\"is\")) {\n                            return map.get(property(name.substring(2)));\n                        } else if (length == 1 && name.startsWith(\"set\")) {\n                            map.put(property(name.substring(3)), args[0]);\n                            return null;\n                        }\n                    }\n                    throw e;\n                }\n            }\n        };\n        return (P) Proxy.newProxyInstance(proxyType.getClassLoader(),\n                new Class[]{proxyType},\n                handler);\n    }\n\n    /**\n     * Get the POJO property name of an getter/setter\n     */\n    private static String property(String string) {\n        int length = string.length();\n\n        if (length == 0) {\n            return \"\";\n        } else if (length == 1) {\n            return string.toLowerCase();\n        } else {\n            return string.substring(0, 1).toLowerCase() + string.substring(1);\n        }\n    }\n\n    private Class<?> type() {\n        return type;\n    }\n\n    private Class<?> wrapper(final Class<?> type) {\n        if (type == null) {\n            return null;\n        } else if (type.isPrimitive()) {\n            if (boolean.class == type) {\n                return Boolean.class;\n            } else if (int.class == type) {\n                return Integer.class;\n            } else if (long.class == type) {\n                return Long.class;\n            } else if (short.class == type) {\n                return Short.class;\n            } else if (byte.class == type) {\n                return Byte.class;\n            } else if (double.class == type) {\n                return Double.class;\n            } else if (float.class == type) {\n                return Float.class;\n            } else if (char.class == type) {\n                return Character.class;\n            } else if (void.class == type) {\n                return Void.class;\n            }\n        }\n        return type;\n    }\n\n    /**\n     * Get the result.\n     *\n     * @param <T> The value type.\n     * @return the result\n     */\n    @SuppressWarnings(\"unchecked\")\n    public <T> T get() {\n        return (T) object;\n    }\n\n    @Override\n    public int hashCode() {\n        return object.hashCode();\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        return obj instanceof ReflectUtils && object.equals(((ReflectUtils) obj).get());\n    }\n\n    @Override\n    public String toString() {\n        return object.toString();\n    }\n\n    private static class NULL {\n    }\n\n    public static class ReflectException extends RuntimeException {\n\n        private static final long serialVersionUID = 858774075258496016L;\n\n        public ReflectException(String message) {\n            super(message);\n        }\n\n        public ReflectException(String message, Throwable cause) {\n            super(message, cause);\n        }\n\n        public ReflectException(Throwable cause) {\n            super(cause);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/RegexUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport androidx.collection.SimpleArrayMap;\n\nimport com.blankj.utilcode.constant.RegexConstants;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/02\n *     desc  : utils about regex\n * </pre>\n */\npublic final class RegexUtils {\n\n    private final static SimpleArrayMap<String, String> CITY_MAP = new SimpleArrayMap<>();\n\n    private RegexUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // If u want more please visit http://toutiao.com/i6231678548520731137\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Return whether input matches regex of simple mobile.\n     *\n     * @param input The input.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isMobileSimple(final CharSequence input) {\n        return isMatch(RegexConstants.REGEX_MOBILE_SIMPLE, input);\n    }\n\n    \n    /**\n     * Returns the domain part of a given Email address\n     *\n     * @param email The Email address. E.g Returns \"protonmail.com\" from the given Email \"johnsmith@protonmail.com\".\n     * @return the domain part of a given Email address.\n     */\n    public static String extractEmailProvider(String email) {\n        return email.substring(email.lastIndexOf(\"@\") + 1);\n    }\n\n    /**\n     * Returns the username part of a given Email address. E.g. Returns \"johnsmith\" from the given Email \"johnsmith@protonmail.com\".\n     *\n     * @param email The Email address.\n     * @return the username part of a given Email address.\n     */\n    public static String extractEmailUsername(String email) {\n        return email.substring(0, email.lastIndexOf(\"@\"));\n    }\n\n\n    /**\n     * Return whether a given Email address is on a specified Email provider. E.g. \"johnsmith@protonmail.com\" and \"gmail.com\" will return false.\n     *\n     * @param email The Email address.\n     * @param emailProvider The Email provider to testify against.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isFromEmailProvider(String email, String emailProvider) {\n        return extractEmailProvider(email).equalsIgnoreCase(emailProvider);\n    }\n\n    /**\n     * Return whether a given Email address is on any of the specified Email providers list (array). E.g. Useful if you pass it a list of real Email provider services and check if the Email is a disposable Email or a real one.\n     *\n     * @param email The Email address.\n     * @param emailProviders The list of Email providers to testify against.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isFromAnyOfEmailProviders(String email, String[] emailProviders) {\n        return com.blankj.utilcode.util.ArrayUtils.contains(emailProviders, extractEmailProvider(email));\n    }\n    \n    \n    \n    \n    \n    /**\n     * Return whether input matches regex of exact mobile.\n     *\n     * @param input The input.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isMobileExact(final CharSequence input) {\n        return isMobileExact(input, null);\n    }\n\n    /**\n     * Return whether input matches regex of exact mobile.\n     *\n     * @param input       The input.\n     * @param newSegments The new segments of mobile number.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isMobileExact(final CharSequence input, List<String> newSegments) {\n        boolean match = isMatch(RegexConstants.REGEX_MOBILE_EXACT, input);\n        if (match) return true;\n        if (newSegments == null) return false;\n        if (input == null || input.length() != 11) return false;\n        String content = input.toString();\n        for (char c : content.toCharArray()) {\n            if (!Character.isDigit(c)) {\n                return false;\n            }\n        }\n        for (String newSegment : newSegments) {\n            if (content.startsWith(newSegment)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Return whether input matches regex of telephone number.\n     *\n     * @param input The input.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isTel(final CharSequence input) {\n        return isMatch(RegexConstants.REGEX_TEL, input);\n    }\n\n    /**\n     * Return whether input matches regex of id card number which length is 15.\n     *\n     * @param input The input.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isIDCard15(final CharSequence input) {\n        return isMatch(RegexConstants.REGEX_ID_CARD15, input);\n    }\n\n    /**\n     * Return whether input matches regex of id card number which length is 18.\n     *\n     * @param input The input.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isIDCard18(final CharSequence input) {\n        return isMatch(RegexConstants.REGEX_ID_CARD18, input);\n    }\n\n    /**\n     * Return whether input matches regex of exact id card number which length is 18.\n     *\n     * @param input The input.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isIDCard18Exact(final CharSequence input) {\n        if (isIDCard18(input)) {\n            int[] factor = new int[]{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};\n            char[] suffix = new char[]{'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};\n            if (CITY_MAP.isEmpty()) {\n                CITY_MAP.put(\"11\", \"北京\");\n                CITY_MAP.put(\"12\", \"天津\");\n                CITY_MAP.put(\"13\", \"河北\");\n                CITY_MAP.put(\"14\", \"山西\");\n                CITY_MAP.put(\"15\", \"内蒙古\");\n\n                CITY_MAP.put(\"21\", \"辽宁\");\n                CITY_MAP.put(\"22\", \"吉林\");\n                CITY_MAP.put(\"23\", \"黑龙江\");\n\n                CITY_MAP.put(\"31\", \"上海\");\n                CITY_MAP.put(\"32\", \"江苏\");\n                CITY_MAP.put(\"33\", \"浙江\");\n                CITY_MAP.put(\"34\", \"安徽\");\n                CITY_MAP.put(\"35\", \"福建\");\n                CITY_MAP.put(\"36\", \"江西\");\n                CITY_MAP.put(\"37\", \"山东\");\n\n                CITY_MAP.put(\"41\", \"河南\");\n                CITY_MAP.put(\"42\", \"湖北\");\n                CITY_MAP.put(\"43\", \"湖南\");\n                CITY_MAP.put(\"44\", \"广东\");\n                CITY_MAP.put(\"45\", \"广西\");\n                CITY_MAP.put(\"46\", \"海南\");\n\n                CITY_MAP.put(\"50\", \"重庆\");\n                CITY_MAP.put(\"51\", \"四川\");\n                CITY_MAP.put(\"52\", \"贵州\");\n                CITY_MAP.put(\"53\", \"云南\");\n                CITY_MAP.put(\"54\", \"西藏\");\n\n                CITY_MAP.put(\"61\", \"陕西\");\n                CITY_MAP.put(\"62\", \"甘肃\");\n                CITY_MAP.put(\"63\", \"青海\");\n                CITY_MAP.put(\"64\", \"宁夏\");\n                CITY_MAP.put(\"65\", \"新疆\");\n\n                CITY_MAP.put(\"71\", \"台湾老\");\n                CITY_MAP.put(\"81\", \"香港\");\n                CITY_MAP.put(\"82\", \"澳门\");\n                CITY_MAP.put(\"83\", \"台湾新\");\n                CITY_MAP.put(\"91\", \"国外\");\n            }\n            if (CITY_MAP.get(input.subSequence(0, 2).toString()) != null) {\n                int weightSum = 0;\n                for (int i = 0; i < 17; ++i) {\n                    weightSum += (input.charAt(i) - '0') * factor[i];\n                }\n                int idCardMod = weightSum % 11;\n                char idCardLast = input.charAt(17);\n                return idCardLast == suffix[idCardMod];\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Return whether input matches regex of email.\n     *\n     * @param input The input.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isEmail(final CharSequence input) {\n        return isMatch(RegexConstants.REGEX_EMAIL, input);\n    }\n\n    /**\n     * Return whether input matches regex of url.\n     *\n     * @param input The input.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isURL(final CharSequence input) {\n        return isMatch(RegexConstants.REGEX_URL, input);\n    }\n\n    /**\n     * Return whether input matches regex of Chinese character.\n     *\n     * @param input The input.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isZh(final CharSequence input) {\n        return isMatch(RegexConstants.REGEX_ZH, input);\n    }\n\n    /**\n     * Return whether input matches regex of username.\n     * <p>scope for \"a-z\", \"A-Z\", \"0-9\", \"_\", \"Chinese character\"</p>\n     * <p>can't end with \"_\"</p>\n     * <p>length is between 6 to 20</p>.\n     *\n     * @param input The input.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isUsername(final CharSequence input) {\n        return isMatch(RegexConstants.REGEX_USERNAME, input);\n    }\n\n    /**\n     * Return whether input matches regex of date which pattern is \"yyyy-MM-dd\".\n     *\n     * @param input The input.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isDate(final CharSequence input) {\n        return isMatch(RegexConstants.REGEX_DATE, input);\n    }\n\n    /**\n     * Return whether input matches regex of ip address.\n     *\n     * @param input The input.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isIP(final CharSequence input) {\n        return isMatch(RegexConstants.REGEX_IP, input);\n    }\n\n    /**\n     * Return whether input matches the regex.\n     *\n     * @param regex The regex.\n     * @param input The input.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isMatch(final String regex, final CharSequence input) {\n        return input != null && input.length() > 0 && Pattern.matches(regex, input);\n    }\n\n    /**\n     * Return the list of input matches the regex.\n     *\n     * @param regex The regex.\n     * @param input The input.\n     * @return the list of input matches the regex\n     */\n    public static List<String> getMatches(final String regex, final CharSequence input) {\n        if (input == null) return Collections.emptyList();\n        List<String> matches = new ArrayList<>();\n        Pattern pattern = Pattern.compile(regex);\n        Matcher matcher = pattern.matcher(input);\n        while (matcher.find()) {\n            matches.add(matcher.group());\n        }\n        return matches;\n    }\n\n    /**\n     * Splits input around matches of the regex.\n     *\n     * @param input The input.\n     * @param regex The regex.\n     * @return the array of strings computed by splitting input around matches of regex\n     */\n    public static String[] getSplits(final String input, final String regex) {\n        if (input == null) return new String[0];\n        return input.split(regex);\n    }\n\n    /**\n     * Replace the first subsequence of the input sequence that matches the\n     * regex with the given replacement string.\n     *\n     * @param input       The input.\n     * @param regex       The regex.\n     * @param replacement The replacement string.\n     * @return the string constructed by replacing the first matching\n     * subsequence by the replacement string, substituting captured\n     * subsequences as needed\n     */\n    public static String getReplaceFirst(final String input,\n                                         final String regex,\n                                         final String replacement) {\n        if (input == null) return \"\";\n        return Pattern.compile(regex).matcher(input).replaceFirst(replacement);\n    }\n\n    /**\n     * Replace every subsequence of the input sequence that matches the\n     * pattern with the given replacement string.\n     *\n     * @param input       The input.\n     * @param regex       The regex.\n     * @param replacement The replacement string.\n     * @return the string constructed by replacing each matching subsequence\n     * by the replacement string, substituting captured subsequences\n     * as needed\n     */\n    public static String getReplaceAll(final String input,\n                                       final String regex,\n                                       final String replacement) {\n        if (input == null) return \"\";\n        return Pattern.compile(regex).matcher(input).replaceAll(replacement);\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ResourceUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.graphics.drawable.Drawable;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.UnsupportedEncodingException;\nimport java.util.Collections;\nimport java.util.List;\n\nimport androidx.annotation.DrawableRes;\nimport androidx.annotation.RawRes;\nimport androidx.core.content.ContextCompat;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/05/07\n *     desc  : utils about resource\n * </pre>\n */\npublic final class ResourceUtils {\n\n    private static final int BUFFER_SIZE = 8192;\n\n    private ResourceUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Return the drawable by identifier.\n     *\n     * @param id The identifier.\n     * @return the drawable by identifier\n     */\n    public static Drawable getDrawable(@DrawableRes int id) {\n        return ContextCompat.getDrawable(Utils.getApp(), id);\n    }\n\n    /**\n     * Return the id identifier by name.\n     *\n     * @param name The name of id.\n     * @return the id identifier by name\n     */\n    public static int getIdByName(String name) {\n        return Utils.getApp().getResources().getIdentifier(name, \"id\", Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the string identifier by name.\n     *\n     * @param name The name of string.\n     * @return the string identifier by name\n     */\n    public static int getStringIdByName(String name) {\n        return Utils.getApp().getResources().getIdentifier(name, \"string\", Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the color identifier by name.\n     *\n     * @param name The name of color.\n     * @return the color identifier by name\n     */\n    public static int getColorIdByName(String name) {\n        return Utils.getApp().getResources().getIdentifier(name, \"color\", Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the dimen identifier by name.\n     *\n     * @param name The name of dimen.\n     * @return the dimen identifier by name\n     */\n    public static int getDimenIdByName(String name) {\n        return Utils.getApp().getResources().getIdentifier(name, \"dimen\", Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the drawable identifier by name.\n     *\n     * @param name The name of drawable.\n     * @return the drawable identifier by name\n     */\n    public static int getDrawableIdByName(String name) {\n        return Utils.getApp().getResources().getIdentifier(name, \"drawable\", Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the mipmap identifier by name.\n     *\n     * @param name The name of mipmap.\n     * @return the mipmap identifier by name\n     */\n    public static int getMipmapIdByName(String name) {\n        return Utils.getApp().getResources().getIdentifier(name, \"mipmap\", Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the layout identifier by name.\n     *\n     * @param name The name of layout.\n     * @return the layout identifier by name\n     */\n    public static int getLayoutIdByName(String name) {\n        return Utils.getApp().getResources().getIdentifier(name, \"layout\", Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the style identifier by name.\n     *\n     * @param name The name of style.\n     * @return the style identifier by name\n     */\n    public static int getStyleIdByName(String name) {\n        return Utils.getApp().getResources().getIdentifier(name, \"style\", Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the anim identifier by name.\n     *\n     * @param name The name of anim.\n     * @return the anim identifier by name\n     */\n    public static int getAnimIdByName(String name) {\n        return Utils.getApp().getResources().getIdentifier(name, \"anim\", Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Return the menu identifier by name.\n     *\n     * @param name The name of menu.\n     * @return the menu identifier by name\n     */\n    public static int getMenuIdByName(String name) {\n        return Utils.getApp().getResources().getIdentifier(name, \"menu\", Utils.getApp().getPackageName());\n    }\n\n    /**\n     * Copy the file from assets.\n     *\n     * @param assetsFilePath The path of file in assets.\n     * @param destFilePath   The path of destination file.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean copyFileFromAssets(final String assetsFilePath, final String destFilePath) {\n        boolean res = true;\n        try {\n            String[] assets = Utils.getApp().getAssets().list(assetsFilePath);\n            if (assets != null && assets.length > 0) {\n                for (String asset : assets) {\n                    res &= copyFileFromAssets(assetsFilePath + \"/\" + asset, destFilePath + \"/\" + asset);\n                }\n            } else {\n                res = UtilsBridge.writeFileFromIS(\n                        destFilePath,\n                        Utils.getApp().getAssets().open(assetsFilePath)\n                );\n            }\n        } catch (IOException e) {\n            e.printStackTrace();\n            res = false;\n        }\n        return res;\n    }\n\n    /**\n     * Return the content of assets.\n     *\n     * @param assetsFilePath The path of file in assets.\n     * @return the content of assets\n     */\n    public static String readAssets2String(final String assetsFilePath) {\n        return readAssets2String(assetsFilePath, null);\n    }\n\n    /**\n     * Return the content of assets.\n     *\n     * @param assetsFilePath The path of file in assets.\n     * @param charsetName    The name of charset.\n     * @return the content of assets\n     */\n    public static String readAssets2String(final String assetsFilePath, final String charsetName) {\n        try {\n            InputStream is = Utils.getApp().getAssets().open(assetsFilePath);\n            byte[] bytes = UtilsBridge.inputStream2Bytes(is);\n            if (bytes == null) return \"\";\n            if (UtilsBridge.isSpace(charsetName)) {\n                return new String(bytes);\n            } else {\n                try {\n                    return new String(bytes, charsetName);\n                } catch (UnsupportedEncodingException e) {\n                    e.printStackTrace();\n                    return \"\";\n                }\n            }\n        } catch (IOException e) {\n            e.printStackTrace();\n            return \"\";\n        }\n    }\n\n    /**\n     * Return the content of file in assets.\n     *\n     * @param assetsPath The path of file in assets.\n     * @return the content of file in assets\n     */\n    public static List<String> readAssets2List(final String assetsPath) {\n        return readAssets2List(assetsPath, \"\");\n    }\n\n    /**\n     * Return the content of file in assets.\n     *\n     * @param assetsPath  The path of file in assets.\n     * @param charsetName The name of charset.\n     * @return the content of file in assets\n     */\n    public static List<String> readAssets2List(final String assetsPath,\n                                               final String charsetName) {\n        try {\n            return UtilsBridge.inputStream2Lines(Utils.getApp().getResources().getAssets().open(assetsPath), charsetName);\n        } catch (IOException e) {\n            e.printStackTrace();\n            return Collections.emptyList();\n        }\n    }\n\n\n    /**\n     * Copy the file from raw.\n     *\n     * @param resId        The resource id.\n     * @param destFilePath The path of destination file.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean copyFileFromRaw(@RawRes final int resId, final String destFilePath) {\n        return UtilsBridge.writeFileFromIS(\n                destFilePath,\n                Utils.getApp().getResources().openRawResource(resId)\n        );\n    }\n\n    /**\n     * Return the content of resource in raw.\n     *\n     * @param resId The resource id.\n     * @return the content of resource in raw\n     */\n    public static String readRaw2String(@RawRes final int resId) {\n        return readRaw2String(resId, null);\n    }\n\n    /**\n     * Return the content of resource in raw.\n     *\n     * @param resId       The resource id.\n     * @param charsetName The name of charset.\n     * @return the content of resource in raw\n     */\n    public static String readRaw2String(@RawRes final int resId, final String charsetName) {\n        InputStream is = Utils.getApp().getResources().openRawResource(resId);\n        byte[] bytes = UtilsBridge.inputStream2Bytes(is);\n        if (bytes == null) return null;\n        if (UtilsBridge.isSpace(charsetName)) {\n            return new String(bytes);\n        } else {\n            try {\n                return new String(bytes, charsetName);\n            } catch (UnsupportedEncodingException e) {\n                e.printStackTrace();\n                return \"\";\n            }\n        }\n    }\n\n    /**\n     * Return the content of resource in raw.\n     *\n     * @param resId The resource id.\n     * @return the content of file in assets\n     */\n    public static List<String> readRaw2List(@RawRes final int resId) {\n        return readRaw2List(resId, \"\");\n    }\n\n    /**\n     * Return the content of resource in raw.\n     *\n     * @param resId       The resource id.\n     * @param charsetName The name of charset.\n     * @return the content of file in assets\n     */\n    public static List<String> readRaw2List(@RawRes final int resId,\n                                            final String charsetName) {\n        return UtilsBridge.inputStream2Lines(Utils.getApp().getResources().openRawResource(resId), charsetName);\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/RomUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.annotation.SuppressLint;\nimport android.os.Build;\nimport android.os.Environment;\nimport android.text.TextUtils;\n\nimport java.io.BufferedReader;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.lang.reflect.Method;\nimport java.util.Properties;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/07/04\n *     desc  : utils about rom\n * </pre>\n */\npublic final class RomUtils {\n\n    private static final String[] ROM_HUAWEI    = {\"huawei\"};\n    private static final String[] ROM_VIVO      = {\"vivo\"};\n    private static final String[] ROM_XIAOMI    = {\"xiaomi\"};\n    private static final String[] ROM_OPPO      = {\"oppo\"};\n    private static final String[] ROM_LEECO     = {\"leeco\", \"letv\"};\n    private static final String[] ROM_360       = {\"360\", \"qiku\"};\n    private static final String[] ROM_ZTE       = {\"zte\"};\n    private static final String[] ROM_ONEPLUS   = {\"oneplus\"};\n    private static final String[] ROM_NUBIA     = {\"nubia\"};\n    private static final String[] ROM_COOLPAD   = {\"coolpad\", \"yulong\"};\n    private static final String[] ROM_LG        = {\"lg\", \"lge\"};\n    private static final String[] ROM_GOOGLE    = {\"google\"};\n    private static final String[] ROM_SAMSUNG   = {\"samsung\"};\n    private static final String[] ROM_MEIZU     = {\"meizu\"};\n    private static final String[] ROM_LENOVO    = {\"lenovo\"};\n    private static final String[] ROM_SMARTISAN = {\"smartisan\", \"deltainno\"};\n    private static final String[] ROM_HTC       = {\"htc\"};\n    private static final String[] ROM_SONY      = {\"sony\"};\n    private static final String[] ROM_GIONEE    = {\"gionee\", \"amigo\"};\n    private static final String[] ROM_MOTOROLA  = {\"motorola\"};\n\n    private static final String VERSION_PROPERTY_HUAWEI  = \"ro.build.version.emui\";\n    private static final String VERSION_PROPERTY_VIVO    = \"ro.vivo.os.build.display.id\";\n    private static final String VERSION_PROPERTY_XIAOMI  = \"ro.build.version.incremental\";\n    private static final String VERSION_PROPERTY_OPPO    = \"ro.build.version.opporom\";\n    private static final String VERSION_PROPERTY_LEECO   = \"ro.letv.release.version\";\n    private static final String VERSION_PROPERTY_360     = \"ro.build.uiversion\";\n    private static final String VERSION_PROPERTY_ZTE     = \"ro.build.MiFavor_version\";\n    private static final String VERSION_PROPERTY_ONEPLUS = \"ro.rom.version\";\n    private static final String VERSION_PROPERTY_NUBIA   = \"ro.build.rom.id\";\n    private final static String UNKNOWN                  = \"unknown\";\n\n    private static RomInfo bean = null;\n\n    private RomUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Return whether the rom is made by huawei.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isHuawei() {\n        return ROM_HUAWEI[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return whether the rom is made by vivo.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isVivo() {\n        return ROM_VIVO[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return whether the rom is made by xiaomi.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isXiaomi() {\n        return ROM_XIAOMI[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return whether the rom is made by oppo.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isOppo() {\n        return ROM_OPPO[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return whether the rom is made by leeco.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isLeeco() {\n        return ROM_LEECO[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return whether the rom is made by 360.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean is360() {\n        return ROM_360[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return whether the rom is made by zte.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isZte() {\n        return ROM_ZTE[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return whether the rom is made by oneplus.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isOneplus() {\n        return ROM_ONEPLUS[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return whether the rom is made by nubia.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isNubia() {\n        return ROM_NUBIA[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return whether the rom is made by coolpad.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isCoolpad() {\n        return ROM_COOLPAD[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return whether the rom is made by lg.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isLg() {\n        return ROM_LG[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return whether the rom is made by google.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isGoogle() {\n        return ROM_GOOGLE[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return whether the rom is made by samsung.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isSamsung() {\n        return ROM_SAMSUNG[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return whether the rom is made by meizu.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isMeizu() {\n        return ROM_MEIZU[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return whether the rom is made by lenovo.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isLenovo() {\n        return ROM_LENOVO[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return whether the rom is made by smartisan.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isSmartisan() {\n        return ROM_SMARTISAN[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return whether the rom is made by htc.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isHtc() {\n        return ROM_HTC[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return whether the rom is made by sony.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isSony() {\n        return ROM_SONY[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return whether the rom is made by gionee.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isGionee() {\n        return ROM_GIONEE[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return whether the rom is made by motorola.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isMotorola() {\n        return ROM_MOTOROLA[0].equals(getRomInfo().name);\n    }\n\n    /**\n     * Return the rom's information.\n     *\n     * @return the rom's information\n     */\n    public static RomInfo getRomInfo() {\n        if (bean != null) return bean;\n        bean = new RomInfo();\n        final String brand = getBrand();\n        final String manufacturer = getManufacturer();\n        if (isRightRom(brand, manufacturer, ROM_HUAWEI)) {\n            bean.name = ROM_HUAWEI[0];\n            String version = getRomVersion(VERSION_PROPERTY_HUAWEI);\n            String[] temp = version.split(\"_\");\n            if (temp.length > 1) {\n                bean.version = temp[1];\n            } else {\n                bean.version = version;\n            }\n            return bean;\n        }\n        if (isRightRom(brand, manufacturer, ROM_VIVO)) {\n            bean.name = ROM_VIVO[0];\n            bean.version = getRomVersion(VERSION_PROPERTY_VIVO);\n            return bean;\n        }\n        if (isRightRom(brand, manufacturer, ROM_XIAOMI)) {\n            bean.name = ROM_XIAOMI[0];\n            bean.version = getRomVersion(VERSION_PROPERTY_XIAOMI);\n            return bean;\n        }\n        if (isRightRom(brand, manufacturer, ROM_OPPO)) {\n            bean.name = ROM_OPPO[0];\n            bean.version = getRomVersion(VERSION_PROPERTY_OPPO);\n            return bean;\n        }\n        if (isRightRom(brand, manufacturer, ROM_LEECO)) {\n            bean.name = ROM_LEECO[0];\n            bean.version = getRomVersion(VERSION_PROPERTY_LEECO);\n            return bean;\n        }\n\n        if (isRightRom(brand, manufacturer, ROM_360)) {\n            bean.name = ROM_360[0];\n            bean.version = getRomVersion(VERSION_PROPERTY_360);\n            return bean;\n        }\n        if (isRightRom(brand, manufacturer, ROM_ZTE)) {\n            bean.name = ROM_ZTE[0];\n            bean.version = getRomVersion(VERSION_PROPERTY_ZTE);\n            return bean;\n        }\n        if (isRightRom(brand, manufacturer, ROM_ONEPLUS)) {\n            bean.name = ROM_ONEPLUS[0];\n            bean.version = getRomVersion(VERSION_PROPERTY_ONEPLUS);\n            return bean;\n        }\n        if (isRightRom(brand, manufacturer, ROM_NUBIA)) {\n            bean.name = ROM_NUBIA[0];\n            bean.version = getRomVersion(VERSION_PROPERTY_NUBIA);\n            return bean;\n        }\n\n        if (isRightRom(brand, manufacturer, ROM_COOLPAD)) {\n            bean.name = ROM_COOLPAD[0];\n        } else if (isRightRom(brand, manufacturer, ROM_LG)) {\n            bean.name = ROM_LG[0];\n        } else if (isRightRom(brand, manufacturer, ROM_GOOGLE)) {\n            bean.name = ROM_GOOGLE[0];\n        } else if (isRightRom(brand, manufacturer, ROM_SAMSUNG)) {\n            bean.name = ROM_SAMSUNG[0];\n        } else if (isRightRom(brand, manufacturer, ROM_MEIZU)) {\n            bean.name = ROM_MEIZU[0];\n        } else if (isRightRom(brand, manufacturer, ROM_LENOVO)) {\n            bean.name = ROM_LENOVO[0];\n        } else if (isRightRom(brand, manufacturer, ROM_SMARTISAN)) {\n            bean.name = ROM_SMARTISAN[0];\n        } else if (isRightRom(brand, manufacturer, ROM_HTC)) {\n            bean.name = ROM_HTC[0];\n        } else if (isRightRom(brand, manufacturer, ROM_SONY)) {\n            bean.name = ROM_SONY[0];\n        } else if (isRightRom(brand, manufacturer, ROM_GIONEE)) {\n            bean.name = ROM_GIONEE[0];\n        } else if (isRightRom(brand, manufacturer, ROM_MOTOROLA)) {\n            bean.name = ROM_MOTOROLA[0];\n        } else {\n            bean.name = manufacturer;\n        }\n        bean.version = getRomVersion(\"\");\n        return bean;\n    }\n\n    private static boolean isRightRom(final String brand, final String manufacturer, final String... names) {\n        for (String name : names) {\n            if (brand.contains(name) || manufacturer.contains(name)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    private static String getManufacturer() {\n        try {\n            String manufacturer = Build.MANUFACTURER;\n            if (!TextUtils.isEmpty(manufacturer)) {\n                return manufacturer.toLowerCase();\n            }\n        } catch (Throwable ignore) {/**/}\n        return UNKNOWN;\n    }\n\n    private static String getBrand() {\n        try {\n            String brand = Build.BRAND;\n            if (!TextUtils.isEmpty(brand)) {\n                return brand.toLowerCase();\n            }\n        } catch (Throwable ignore) {/**/}\n        return UNKNOWN;\n    }\n\n    private static String getRomVersion(final String propertyName) {\n        String ret = \"\";\n        if (!TextUtils.isEmpty(propertyName)) {\n            ret = getSystemProperty(propertyName);\n        }\n        if (TextUtils.isEmpty(ret) || ret.equals(UNKNOWN)) {\n            try {\n                String display = Build.DISPLAY;\n                if (!TextUtils.isEmpty(display)) {\n                    ret = display.toLowerCase();\n                }\n            } catch (Throwable ignore) {/**/}\n        }\n        if (TextUtils.isEmpty(ret)) {\n            return UNKNOWN;\n        }\n        return ret;\n    }\n\n    private static String getSystemProperty(final String name) {\n        String prop = getSystemPropertyByShell(name);\n        if (!TextUtils.isEmpty(prop)) return prop;\n        prop = getSystemPropertyByStream(name);\n        if (!TextUtils.isEmpty(prop)) return prop;\n        if (Build.VERSION.SDK_INT < 28) {\n            return getSystemPropertyByReflect(name);\n        }\n        return prop;\n    }\n\n    private static String getSystemPropertyByShell(final String propName) {\n        String line;\n        BufferedReader input = null;\n        try {\n            Process p = Runtime.getRuntime().exec(\"getprop \" + propName);\n            input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024);\n            String ret = input.readLine();\n            if (ret != null) {\n                return ret;\n            }\n        } catch (IOException ignore) {\n        } finally {\n            if (input != null) {\n                try {\n                    input.close();\n                } catch (IOException ignore) {/**/}\n            }\n        }\n        return \"\";\n    }\n\n    private static String getSystemPropertyByStream(final String key) {\n        try {\n            Properties prop = new Properties();\n            FileInputStream is = new FileInputStream(\n                    new File(Environment.getRootDirectory(), \"build.prop\")\n            );\n            prop.load(is);\n            return prop.getProperty(key, \"\");\n        } catch (Exception ignore) {/**/}\n        return \"\";\n    }\n\n    private static String getSystemPropertyByReflect(String key) {\n        try {\n            @SuppressLint(\"PrivateApi\")\n            Class<?> clz = Class.forName(\"android.os.SystemProperties\");\n            Method getMethod = clz.getMethod(\"get\", String.class, String.class);\n            return (String) getMethod.invoke(clz, key, \"\");\n        } catch (Exception e) {/**/}\n        return \"\";\n    }\n\n    public static class RomInfo {\n        private String name;\n        private String version;\n\n        public String getName() {\n            return name;\n        }\n\n        public String getVersion() {\n            return version;\n        }\n\n        @Override\n        public String toString() {\n            return \"RomInfo{name=\" + name +\n                    \", version=\" + version + \"}\";\n        }\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/SDCardUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.content.Context;\nimport android.os.Environment;\nimport android.os.storage.StorageManager;\nimport android.os.storage.StorageVolume;\nimport android.text.format.Formatter;\n\nimport java.lang.reflect.Array;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/11\n *     desc  : utils about sdcard\n * </pre>\n */\npublic final class SDCardUtils {\n\n    private SDCardUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Return whether sdcard is enabled by environment.\n     *\n     * @return {@code true}: enabled<br>{@code false}: disabled\n     */\n    public static boolean isSDCardEnableByEnvironment() {\n        return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());\n    }\n\n    /**\n     * Return the path of sdcard by environment.\n     *\n     * @return the path of sdcard by environment\n     */\n    public static String getSDCardPathByEnvironment() {\n        if (isSDCardEnableByEnvironment()) {\n            return Environment.getExternalStorageDirectory().getAbsolutePath();\n        }\n        return \"\";\n    }\n\n    /**\n     * Return the information of sdcard.\n     *\n     * @return the information of sdcard\n     */\n    public static List<SDCardInfo> getSDCardInfo() {\n        List<SDCardInfo> paths = new ArrayList<>();\n        StorageManager sm = (StorageManager) Utils.getApp().getSystemService(Context.STORAGE_SERVICE);\n        if (sm == null) return paths;\n        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {\n            List<StorageVolume> storageVolumes = sm.getStorageVolumes();\n            try {\n                //noinspection JavaReflectionMemberAccess\n                Method getPathMethod = StorageVolume.class.getMethod(\"getPath\");\n                for (StorageVolume storageVolume : storageVolumes) {\n                    boolean isRemovable = storageVolume.isRemovable();\n                    String state = storageVolume.getState();\n                    String path = (String) getPathMethod.invoke(storageVolume);\n                    paths.add(new SDCardInfo(path, state, isRemovable));\n                }\n            } catch (NoSuchMethodException e) {\n                e.printStackTrace();\n            } catch (IllegalAccessException e) {\n                e.printStackTrace();\n            } catch (InvocationTargetException e) {\n                e.printStackTrace();\n            }\n        } else {\n            try {\n                Class<?> storageVolumeClazz = Class.forName(\"android.os.storage.StorageVolume\");\n                //noinspection JavaReflectionMemberAccess\n                Method getPathMethod = storageVolumeClazz.getMethod(\"getPath\");\n                Method isRemovableMethod = storageVolumeClazz.getMethod(\"isRemovable\");\n                //noinspection JavaReflectionMemberAccess\n                Method getVolumeStateMethod = StorageManager.class.getMethod(\"getVolumeState\", String.class);\n                //noinspection JavaReflectionMemberAccess\n                Method getVolumeListMethod = StorageManager.class.getMethod(\"getVolumeList\");\n                Object result = getVolumeListMethod.invoke(sm);\n                final int length = Array.getLength(result);\n                for (int i = 0; i < length; i++) {\n                    Object storageVolumeElement = Array.get(result, i);\n                    String path = (String) getPathMethod.invoke(storageVolumeElement);\n                    boolean isRemovable = (Boolean) isRemovableMethod.invoke(storageVolumeElement);\n                    String state = (String) getVolumeStateMethod.invoke(sm, path);\n                    paths.add(new SDCardInfo(path, state, isRemovable));\n                }\n            } catch (ClassNotFoundException e) {\n                e.printStackTrace();\n            } catch (InvocationTargetException e) {\n                e.printStackTrace();\n            } catch (NoSuchMethodException e) {\n                e.printStackTrace();\n            } catch (IllegalAccessException e) {\n                e.printStackTrace();\n            }\n        }\n        return paths;\n    }\n\n    /**\n     * Return the ptah of mounted sdcard.\n     *\n     * @return the ptah of mounted sdcard.\n     */\n    public static List<String> getMountedSDCardPath() {\n        List<String> path = new ArrayList<>();\n        List<SDCardInfo> sdCardInfo = getSDCardInfo();\n        if (sdCardInfo == null || sdCardInfo.isEmpty()) return path;\n        for (SDCardInfo cardInfo : sdCardInfo) {\n            String state = cardInfo.state;\n            if (state == null) continue;\n            if (\"mounted\".equals(state.toLowerCase())) {\n                path.add(cardInfo.path);\n            }\n        }\n        return path;\n    }\n\n\n    /**\n     * Return the total size of external storage\n     *\n     * @return the total size of external storage\n     */\n    public static long getExternalTotalSize() {\n        return UtilsBridge.getFsTotalSize(getSDCardPathByEnvironment());\n    }\n\n    /**\n     * Return the available size of external storage.\n     *\n     * @return the available size of external storage\n     */\n    public static long getExternalAvailableSize() {\n        return UtilsBridge.getFsAvailableSize(getSDCardPathByEnvironment());\n    }\n\n    /**\n     * Return the total size of internal storage\n     *\n     * @return the total size of internal storage\n     */\n    public static long getInternalTotalSize() {\n        return UtilsBridge.getFsTotalSize(Environment.getDataDirectory().getAbsolutePath());\n    }\n\n    /**\n     * Return the available size of internal storage.\n     *\n     * @return the available size of internal storage\n     */\n    public static long getInternalAvailableSize() {\n        return UtilsBridge.getFsAvailableSize(Environment.getDataDirectory().getAbsolutePath());\n    }\n\n    public static class SDCardInfo {\n\n        private String  path;\n        private String  state;\n        private boolean isRemovable;\n        private long    totalSize;\n        private long    availableSize;\n\n        SDCardInfo(String path, String state, boolean isRemovable) {\n            this.path = path;\n            this.state = state;\n            this.isRemovable = isRemovable;\n            this.totalSize = UtilsBridge.getFsTotalSize(path);\n            this.availableSize = UtilsBridge.getFsAvailableSize(path);\n        }\n\n        public String getPath() {\n            return path;\n        }\n\n        public String getState() {\n            return state;\n        }\n\n        public boolean isRemovable() {\n            return isRemovable;\n        }\n\n        public long getTotalSize() {\n            return totalSize;\n        }\n\n        public long getAvailableSize() {\n            return availableSize;\n        }\n\n        @Override\n        public String toString() {\n            return \"SDCardInfo {\" +\n                    \"path = \" + path +\n                    \", state = \" + state +\n                    \", isRemovable = \" + isRemovable +\n                    \", totalSize = \" + Formatter.formatFileSize(Utils.getApp(), totalSize) +\n                    \", availableSize = \" + Formatter.formatFileSize(Utils.getApp(), availableSize) +\n                    '}';\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/SPStaticUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.content.SharedPreferences;\nimport androidx.annotation.NonNull;\n\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/01/04\n *     desc  : utils about shared preference\n * </pre>\n */\npublic final class SPStaticUtils {\n\n    private static SPUtils sDefaultSPUtils;\n\n    /**\n     * Set the default instance of {@link SPUtils}.\n     *\n     * @param spUtils The default instance of {@link SPUtils}.\n     */\n    public static void setDefaultSPUtils(final SPUtils spUtils) {\n        sDefaultSPUtils = spUtils;\n    }\n\n    /**\n     * Put the string value in sp.\n     *\n     * @param key   The key of sp.\n     * @param value The value of sp.\n     */\n    public static void put(@NonNull final String key, final String value) {\n        put(key, value, getDefaultSPUtils());\n    }\n\n    /**\n     * Put the string value in sp.\n     *\n     * @param key      The key of sp.\n     * @param value    The value of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     */\n    public static void put(@NonNull final String key, final String value, final boolean isCommit) {\n        put(key, value, isCommit, getDefaultSPUtils());\n    }\n\n\n    /**\n     * Return the string value in sp.\n     *\n     * @param key The key of sp.\n     * @return the string value if sp exists or {@code \"\"} otherwise\n     */\n    public static String getString(@NonNull final String key) {\n        return getString(key, getDefaultSPUtils());\n    }\n\n    /**\n     * Return the string value in sp.\n     *\n     * @param key          The key of sp.\n     * @param defaultValue The default value if the sp doesn't exist.\n     * @return the string value if sp exists or {@code defaultValue} otherwise\n     */\n    public static String getString(@NonNull final String key, final String defaultValue) {\n        return getString(key, defaultValue, getDefaultSPUtils());\n    }\n\n\n    /**\n     * Put the int value in sp.\n     *\n     * @param key   The key of sp.\n     * @param value The value of sp.\n     */\n    public static void put(@NonNull final String key, final int value) {\n        put(key, value, getDefaultSPUtils());\n    }\n\n    /**\n     * Put the int value in sp.\n     *\n     * @param key      The key of sp.\n     * @param value    The value of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     */\n    public static void put(@NonNull final String key, final int value, final boolean isCommit) {\n        put(key, value, isCommit, getDefaultSPUtils());\n    }\n\n    /**\n     * Return the int value in sp.\n     *\n     * @param key The key of sp.\n     * @return the int value if sp exists or {@code -1} otherwise\n     */\n    public static int getInt(@NonNull final String key) {\n        return getInt(key, getDefaultSPUtils());\n    }\n\n    /**\n     * Return the int value in sp.\n     *\n     * @param key          The key of sp.\n     * @param defaultValue The default value if the sp doesn't exist.\n     * @return the int value if sp exists or {@code defaultValue} otherwise\n     */\n    public static int getInt(@NonNull final String key, final int defaultValue) {\n        return getInt(key, defaultValue, getDefaultSPUtils());\n    }\n\n    /**\n     * Put the long value in sp.\n     *\n     * @param key   The key of sp.\n     * @param value The value of sp.\n     */\n    public static void put(@NonNull final String key, final long value) {\n        put(key, value, getDefaultSPUtils());\n    }\n\n    /**\n     * Put the long value in sp.\n     *\n     * @param key      The key of sp.\n     * @param value    The value of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     */\n    public static void put(@NonNull final String key, final long value, final boolean isCommit) {\n        put(key, value, isCommit, getDefaultSPUtils());\n    }\n\n    /**\n     * Return the long value in sp.\n     *\n     * @param key The key of sp.\n     * @return the long value if sp exists or {@code -1} otherwise\n     */\n    public static long getLong(@NonNull final String key) {\n        return getLong(key, getDefaultSPUtils());\n    }\n\n    /**\n     * Return the long value in sp.\n     *\n     * @param key          The key of sp.\n     * @param defaultValue The default value if the sp doesn't exist.\n     * @return the long value if sp exists or {@code defaultValue} otherwise\n     */\n    public static long getLong(@NonNull final String key, final long defaultValue) {\n        return getLong(key, defaultValue, getDefaultSPUtils());\n    }\n\n    /**\n     * Put the float value in sp.\n     *\n     * @param key   The key of sp.\n     * @param value The value of sp.\n     */\n    public static void put(@NonNull final String key, final float value) {\n        put(key, value, getDefaultSPUtils());\n    }\n\n    /**\n     * Put the float value in sp.\n     *\n     * @param key      The key of sp.\n     * @param value    The value of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     */\n    public static void put(@NonNull final String key, final float value, final boolean isCommit) {\n        put(key, value, isCommit, getDefaultSPUtils());\n    }\n\n    /**\n     * Return the float value in sp.\n     *\n     * @param key The key of sp.\n     * @return the float value if sp exists or {@code -1f} otherwise\n     */\n    public static float getFloat(@NonNull final String key) {\n        return getFloat(key, getDefaultSPUtils());\n    }\n\n    /**\n     * Return the float value in sp.\n     *\n     * @param key          The key of sp.\n     * @param defaultValue The default value if the sp doesn't exist.\n     * @return the float value if sp exists or {@code defaultValue} otherwise\n     */\n    public static float getFloat(@NonNull final String key, final float defaultValue) {\n        return getFloat(key, defaultValue, getDefaultSPUtils());\n    }\n\n    /**\n     * Put the boolean value in sp.\n     *\n     * @param key   The key of sp.\n     * @param value The value of sp.\n     */\n    public static void put(@NonNull final String key, final boolean value) {\n        put(key, value, getDefaultSPUtils());\n    }\n\n    /**\n     * Put the boolean value in sp.\n     *\n     * @param key      The key of sp.\n     * @param value    The value of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     */\n    public static void put(@NonNull final String key, final boolean value, final boolean isCommit) {\n        put(key, value, isCommit, getDefaultSPUtils());\n    }\n\n    /**\n     * Return the boolean value in sp.\n     *\n     * @param key The key of sp.\n     * @return the boolean value if sp exists or {@code false} otherwise\n     */\n    public static boolean getBoolean(@NonNull final String key) {\n        return getBoolean(key, getDefaultSPUtils());\n    }\n\n    /**\n     * Return the boolean value in sp.\n     *\n     * @param key          The key of sp.\n     * @param defaultValue The default value if the sp doesn't exist.\n     * @return the boolean value if sp exists or {@code defaultValue} otherwise\n     */\n    public static boolean getBoolean(@NonNull final String key, final boolean defaultValue) {\n        return getBoolean(key, defaultValue, getDefaultSPUtils());\n    }\n\n    /**\n     * Put the set of string value in sp.\n     *\n     * @param key   The key of sp.\n     * @param value The value of sp.\n     */\n    public static void put(@NonNull final String key, final Set<String> value) {\n        put(key, value, getDefaultSPUtils());\n    }\n\n    /**\n     * Put the set of string value in sp.\n     *\n     * @param key      The key of sp.\n     * @param value    The value of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     */\n    public static void put(@NonNull final String key,\n                           final Set<String> value,\n                           final boolean isCommit) {\n        put(key, value, isCommit, getDefaultSPUtils());\n    }\n\n    /**\n     * Return the set of string value in sp.\n     *\n     * @param key The key of sp.\n     * @return the set of string value if sp exists\n     * or {@code Collections.<String>emptySet()} otherwise\n     */\n    public static Set<String> getStringSet(@NonNull final String key) {\n        return getStringSet(key, getDefaultSPUtils());\n    }\n\n    /**\n     * Return the set of string value in sp.\n     *\n     * @param key          The key of sp.\n     * @param defaultValue The default value if the sp doesn't exist.\n     * @return the set of string value if sp exists or {@code defaultValue} otherwise\n     */\n    public static Set<String> getStringSet(@NonNull final String key,\n                                           final Set<String> defaultValue) {\n        return getStringSet(key, defaultValue, getDefaultSPUtils());\n    }\n\n    /**\n     * Return all values in sp.\n     *\n     * @return all values in sp\n     */\n    public static Map<String, ?> getAll() {\n        return getAll(getDefaultSPUtils());\n    }\n\n    /**\n     * Return whether the sp contains the preference.\n     *\n     * @param key The key of sp.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean contains(@NonNull final String key) {\n        return contains(key, getDefaultSPUtils());\n    }\n\n    /**\n     * Remove the preference in sp.\n     *\n     * @param key The key of sp.\n     */\n    public static void remove(@NonNull final String key) {\n        remove(key, getDefaultSPUtils());\n    }\n\n    /**\n     * Remove the preference in sp.\n     *\n     * @param key      The key of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     */\n    public static void remove(@NonNull final String key, final boolean isCommit) {\n        remove(key, isCommit, getDefaultSPUtils());\n    }\n\n    /**\n     * Remove all preferences in sp.\n     */\n    public static void clear() {\n        clear(getDefaultSPUtils());\n    }\n\n    /**\n     * Remove all preferences in sp.\n     *\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     */\n    public static void clear(final boolean isCommit) {\n        clear(isCommit, getDefaultSPUtils());\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // dividing line\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * Put the string value in sp.\n     *\n     * @param key     The key of sp.\n     * @param value   The value of sp.\n     * @param spUtils The instance of {@link SPUtils}.\n     */\n    public static void put(@NonNull final String key, final String value, @NonNull final SPUtils spUtils) {\n        spUtils.put(key, value);\n    }\n\n    /**\n     * Put the string value in sp.\n     *\n     * @param key      The key of sp.\n     * @param value    The value of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     * @param spUtils  The instance of {@link SPUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final String value,\n                           final boolean isCommit,\n                           @NonNull final SPUtils spUtils) {\n        spUtils.put(key, value, isCommit);\n    }\n\n\n    /**\n     * Return the string value in sp.\n     *\n     * @param key     The key of sp.\n     * @param spUtils The instance of {@link SPUtils}.\n     * @return the string value if sp exists or {@code \"\"} otherwise\n     */\n    public static String getString(@NonNull final String key, @NonNull final SPUtils spUtils) {\n        return spUtils.getString(key);\n    }\n\n    /**\n     * Return the string value in sp.\n     *\n     * @param key          The key of sp.\n     * @param defaultValue The default value if the sp doesn't exist.\n     * @param spUtils      The instance of {@link SPUtils}.\n     * @return the string value if sp exists or {@code defaultValue} otherwise\n     */\n    public static String getString(@NonNull final String key,\n                                   final String defaultValue,\n                                   @NonNull final SPUtils spUtils) {\n        return spUtils.getString(key, defaultValue);\n    }\n\n\n    /**\n     * Put the int value in sp.\n     *\n     * @param key     The key of sp.\n     * @param value   The value of sp.\n     * @param spUtils The instance of {@link SPUtils}.\n     */\n    public static void put(@NonNull final String key, final int value, @NonNull final SPUtils spUtils) {\n        spUtils.put(key, value);\n    }\n\n    /**\n     * Put the int value in sp.\n     *\n     * @param key      The key of sp.\n     * @param value    The value of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     * @param spUtils  The instance of {@link SPUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final int value,\n                           final boolean isCommit,\n                           @NonNull final SPUtils spUtils) {\n        spUtils.put(key, value, isCommit);\n    }\n\n    /**\n     * Return the int value in sp.\n     *\n     * @param key     The key of sp.\n     * @param spUtils The instance of {@link SPUtils}.\n     * @return the int value if sp exists or {@code -1} otherwise\n     */\n    public static int getInt(@NonNull final String key, @NonNull final SPUtils spUtils) {\n        return spUtils.getInt(key);\n    }\n\n    /**\n     * Return the int value in sp.\n     *\n     * @param key          The key of sp.\n     * @param defaultValue The default value if the sp doesn't exist.\n     * @param spUtils      The instance of {@link SPUtils}.\n     * @return the int value if sp exists or {@code defaultValue} otherwise\n     */\n    public static int getInt(@NonNull final String key, final int defaultValue, @NonNull final SPUtils spUtils) {\n        return spUtils.getInt(key, defaultValue);\n    }\n\n    /**\n     * Put the long value in sp.\n     *\n     * @param key     The key of sp.\n     * @param value   The value of sp.\n     * @param spUtils The instance of {@link SPUtils}.\n     */\n    public static void put(@NonNull final String key, final long value, @NonNull final SPUtils spUtils) {\n        spUtils.put(key, value);\n    }\n\n    /**\n     * Put the long value in sp.\n     *\n     * @param key      The key of sp.\n     * @param value    The value of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     * @param spUtils  The instance of {@link SPUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final long value,\n                           final boolean isCommit,\n                           @NonNull final SPUtils spUtils) {\n        spUtils.put(key, value, isCommit);\n    }\n\n    /**\n     * Return the long value in sp.\n     *\n     * @param key     The key of sp.\n     * @param spUtils The instance of {@link SPUtils}.\n     * @return the long value if sp exists or {@code -1} otherwise\n     */\n    public static long getLong(@NonNull final String key, @NonNull final SPUtils spUtils) {\n        return spUtils.getLong(key);\n    }\n\n    /**\n     * Return the long value in sp.\n     *\n     * @param key          The key of sp.\n     * @param defaultValue The default value if the sp doesn't exist.\n     * @param spUtils      The instance of {@link SPUtils}.\n     * @return the long value if sp exists or {@code defaultValue} otherwise\n     */\n    public static long getLong(@NonNull final String key, final long defaultValue, @NonNull final SPUtils spUtils) {\n        return spUtils.getLong(key, defaultValue);\n    }\n\n    /**\n     * Put the float value in sp.\n     *\n     * @param key     The key of sp.\n     * @param value   The value of sp.\n     * @param spUtils The instance of {@link SPUtils}.\n     */\n    public static void put(@NonNull final String key, final float value, @NonNull final SPUtils spUtils) {\n        spUtils.put(key, value);\n    }\n\n    /**\n     * Put the float value in sp.\n     *\n     * @param key      The key of sp.\n     * @param value    The value of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     * @param spUtils  The instance of {@link SPUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final float value,\n                           final boolean isCommit,\n                           @NonNull final SPUtils spUtils) {\n        spUtils.put(key, value, isCommit);\n    }\n\n    /**\n     * Return the float value in sp.\n     *\n     * @param key     The key of sp.\n     * @param spUtils The instance of {@link SPUtils}.\n     * @return the float value if sp exists or {@code -1f} otherwise\n     */\n    public static float getFloat(@NonNull final String key, @NonNull final SPUtils spUtils) {\n        return spUtils.getFloat(key);\n    }\n\n    /**\n     * Return the float value in sp.\n     *\n     * @param key          The key of sp.\n     * @param defaultValue The default value if the sp doesn't exist.\n     * @param spUtils      The instance of {@link SPUtils}.\n     * @return the float value if sp exists or {@code defaultValue} otherwise\n     */\n    public static float getFloat(@NonNull final String key, final float defaultValue, @NonNull final SPUtils spUtils) {\n        return spUtils.getFloat(key, defaultValue);\n    }\n\n    /**\n     * Put the boolean value in sp.\n     *\n     * @param key     The key of sp.\n     * @param value   The value of sp.\n     * @param spUtils The instance of {@link SPUtils}.\n     */\n    public static void put(@NonNull final String key, final boolean value, @NonNull final SPUtils spUtils) {\n        spUtils.put(key, value);\n    }\n\n    /**\n     * Put the boolean value in sp.\n     *\n     * @param key      The key of sp.\n     * @param value    The value of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     * @param spUtils  The instance of {@link SPUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final boolean value,\n                           final boolean isCommit,\n                           @NonNull final SPUtils spUtils) {\n        spUtils.put(key, value, isCommit);\n    }\n\n    /**\n     * Return the boolean value in sp.\n     *\n     * @param key     The key of sp.\n     * @param spUtils The instance of {@link SPUtils}.\n     * @return the boolean value if sp exists or {@code false} otherwise\n     */\n    public static boolean getBoolean(@NonNull final String key, @NonNull final SPUtils spUtils) {\n        return spUtils.getBoolean(key);\n    }\n\n    /**\n     * Return the boolean value in sp.\n     *\n     * @param key          The key of sp.\n     * @param defaultValue The default value if the sp doesn't exist.\n     * @param spUtils      The instance of {@link SPUtils}.\n     * @return the boolean value if sp exists or {@code defaultValue} otherwise\n     */\n    public static boolean getBoolean(@NonNull final String key,\n                                     final boolean defaultValue,\n                                     @NonNull final SPUtils spUtils) {\n        return spUtils.getBoolean(key, defaultValue);\n    }\n\n    /**\n     * Put the set of string value in sp.\n     *\n     * @param key     The key of sp.\n     * @param value   The value of sp.\n     * @param spUtils The instance of {@link SPUtils}.\n     */\n    public static void put(@NonNull final String key, final Set<String> value, @NonNull final SPUtils spUtils) {\n        spUtils.put(key, value);\n    }\n\n    /**\n     * Put the set of string value in sp.\n     *\n     * @param key      The key of sp.\n     * @param value    The value of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     * @param spUtils  The instance of {@link SPUtils}.\n     */\n    public static void put(@NonNull final String key,\n                           final Set<String> value,\n                           final boolean isCommit,\n                           @NonNull final SPUtils spUtils) {\n        spUtils.put(key, value, isCommit);\n    }\n\n    /**\n     * Return the set of string value in sp.\n     *\n     * @param key     The key of sp.\n     * @param spUtils The instance of {@link SPUtils}.\n     * @return the set of string value if sp exists\n     * or {@code Collections.<String>emptySet()} otherwise\n     */\n    public static Set<String> getStringSet(@NonNull final String key, @NonNull final SPUtils spUtils) {\n        return spUtils.getStringSet(key);\n    }\n\n    /**\n     * Return the set of string value in sp.\n     *\n     * @param key          The key of sp.\n     * @param defaultValue The default value if the sp doesn't exist.\n     * @param spUtils      The instance of {@link SPUtils}.\n     * @return the set of string value if sp exists or {@code defaultValue} otherwise\n     */\n    public static Set<String> getStringSet(@NonNull final String key,\n                                           final Set<String> defaultValue,\n                                           @NonNull final SPUtils spUtils) {\n        return spUtils.getStringSet(key, defaultValue);\n    }\n\n    /**\n     * Return all values in sp.\n     *\n     * @param spUtils The instance of {@link SPUtils}.\n     * @return all values in sp\n     */\n    public static Map<String, ?> getAll(@NonNull final SPUtils spUtils) {\n        return spUtils.getAll();\n    }\n\n    /**\n     * Return whether the sp contains the preference.\n     *\n     * @param key     The key of sp.\n     * @param spUtils The instance of {@link SPUtils}.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean contains(@NonNull final String key, @NonNull final SPUtils spUtils) {\n        return spUtils.contains(key);\n    }\n\n    /**\n     * Remove the preference in sp.\n     *\n     * @param key     The key of sp.\n     * @param spUtils The instance of {@link SPUtils}.\n     */\n    public static void remove(@NonNull final String key, @NonNull final SPUtils spUtils) {\n        spUtils.remove(key);\n    }\n\n    /**\n     * Remove the preference in sp.\n     *\n     * @param key      The key of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     * @param spUtils  The instance of {@link SPUtils}.\n     */\n    public static void remove(@NonNull final String key, final boolean isCommit, @NonNull final SPUtils spUtils) {\n        spUtils.remove(key, isCommit);\n    }\n\n    /**\n     * Remove all preferences in sp.\n     *\n     * @param spUtils The instance of {@link SPUtils}.\n     */\n    public static void clear(@NonNull final SPUtils spUtils) {\n        spUtils.clear();\n    }\n\n    /**\n     * Remove all preferences in sp.\n     *\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     * @param spUtils  The instance of {@link SPUtils}.\n     */\n    public static void clear(final boolean isCommit, @NonNull final SPUtils spUtils) {\n        spUtils.clear(isCommit);\n    }\n\n    private static SPUtils getDefaultSPUtils() {\n        return sDefaultSPUtils != null ? sDefaultSPUtils : SPUtils.getInstance();\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/SPUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.content.SharedPreferences;\nimport androidx.annotation.NonNull;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/02\n *     desc  : utils about shared preference\n * </pre>\n */\n@SuppressLint(\"ApplySharedPref\")\npublic final class SPUtils {\n\n    private static final Map<String, SPUtils> SP_UTILS_MAP = new HashMap<>();\n\n    private SharedPreferences sp;\n\n    /**\n     * Return the single {@link SPUtils} instance\n     *\n     * @return the single {@link SPUtils} instance\n     */\n    public static SPUtils getInstance() {\n        return getInstance(\"\", Context.MODE_PRIVATE);\n    }\n\n    /**\n     * Return the single {@link SPUtils} instance\n     *\n     * @param mode Operating mode.\n     * @return the single {@link SPUtils} instance\n     */\n    public static SPUtils getInstance(final int mode) {\n        return getInstance(\"\", mode);\n    }\n\n    /**\n     * Return the single {@link SPUtils} instance\n     *\n     * @param spName The name of sp.\n     * @return the single {@link SPUtils} instance\n     */\n    public static SPUtils getInstance(String spName) {\n        return getInstance(spName, Context.MODE_PRIVATE);\n    }\n\n    /**\n     * Return the single {@link SPUtils} instance\n     *\n     * @param spName The name of sp.\n     * @param mode   Operating mode.\n     * @return the single {@link SPUtils} instance\n     */\n    public static SPUtils getInstance(String spName, final int mode) {\n        if (isSpace(spName)) spName = \"spUtils\";\n        SPUtils spUtils = SP_UTILS_MAP.get(spName);\n        if (spUtils == null) {\n            synchronized (SPUtils.class) {\n                spUtils = SP_UTILS_MAP.get(spName);\n                if (spUtils == null) {\n                    spUtils = new SPUtils(spName, mode);\n                    SP_UTILS_MAP.put(spName, spUtils);\n                }\n            }\n        }\n        return spUtils;\n    }\n\n    private SPUtils(final String spName) {\n        sp = Utils.getApp().getSharedPreferences(spName, Context.MODE_PRIVATE);\n    }\n\n    private SPUtils(final String spName, final int mode) {\n        sp = Utils.getApp().getSharedPreferences(spName, mode);\n    }\n\n    /**\n     * Put the string value in sp.\n     *\n     * @param key   The key of sp.\n     * @param value The value of sp.\n     */\n    public void put(@NonNull final String key, final String value) {\n        put(key, value, false);\n    }\n\n    /**\n     * Put the string value in sp.\n     *\n     * @param key      The key of sp.\n     * @param value    The value of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     */\n    public void put(@NonNull final String key, final String value, final boolean isCommit) {\n        if (isCommit) {\n            sp.edit().putString(key, value).commit();\n        } else {\n            sp.edit().putString(key, value).apply();\n        }\n    }\n\n    /**\n     * Return the string value in sp.\n     *\n     * @param key The key of sp.\n     * @return the string value if sp exists or {@code \"\"} otherwise\n     */\n    public String getString(@NonNull final String key) {\n        return getString(key, \"\");\n    }\n\n    /**\n     * Return the string value in sp.\n     *\n     * @param key          The key of sp.\n     * @param defaultValue The default value if the sp doesn't exist.\n     * @return the string value if sp exists or {@code defaultValue} otherwise\n     */\n    public String getString(@NonNull final String key, final String defaultValue) {\n        return sp.getString(key, defaultValue);\n    }\n\n    /**\n     * Put the int value in sp.\n     *\n     * @param key   The key of sp.\n     * @param value The value of sp.\n     */\n    public void put(@NonNull final String key, final int value) {\n        put(key, value, false);\n    }\n\n    /**\n     * Put the int value in sp.\n     *\n     * @param key      The key of sp.\n     * @param value    The value of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     */\n    public void put(@NonNull final String key, final int value, final boolean isCommit) {\n        if (isCommit) {\n            sp.edit().putInt(key, value).commit();\n        } else {\n            sp.edit().putInt(key, value).apply();\n        }\n    }\n\n    /**\n     * Return the int value in sp.\n     *\n     * @param key The key of sp.\n     * @return the int value if sp exists or {@code -1} otherwise\n     */\n    public int getInt(@NonNull final String key) {\n        return getInt(key, -1);\n    }\n\n    /**\n     * Return the int value in sp.\n     *\n     * @param key          The key of sp.\n     * @param defaultValue The default value if the sp doesn't exist.\n     * @return the int value if sp exists or {@code defaultValue} otherwise\n     */\n    public int getInt(@NonNull final String key, final int defaultValue) {\n        return sp.getInt(key, defaultValue);\n    }\n\n    /**\n     * Put the long value in sp.\n     *\n     * @param key   The key of sp.\n     * @param value The value of sp.\n     */\n    public void put(@NonNull final String key, final long value) {\n        put(key, value, false);\n    }\n\n    /**\n     * Put the long value in sp.\n     *\n     * @param key      The key of sp.\n     * @param value    The value of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     */\n    public void put(@NonNull final String key, final long value, final boolean isCommit) {\n        if (isCommit) {\n            sp.edit().putLong(key, value).commit();\n        } else {\n            sp.edit().putLong(key, value).apply();\n        }\n    }\n\n    /**\n     * Return the long value in sp.\n     *\n     * @param key The key of sp.\n     * @return the long value if sp exists or {@code -1} otherwise\n     */\n    public long getLong(@NonNull final String key) {\n        return getLong(key, -1L);\n    }\n\n    /**\n     * Return the long value in sp.\n     *\n     * @param key          The key of sp.\n     * @param defaultValue The default value if the sp doesn't exist.\n     * @return the long value if sp exists or {@code defaultValue} otherwise\n     */\n    public long getLong(@NonNull final String key, final long defaultValue) {\n        return sp.getLong(key, defaultValue);\n    }\n\n    /**\n     * Put the float value in sp.\n     *\n     * @param key   The key of sp.\n     * @param value The value of sp.\n     */\n    public void put(@NonNull final String key, final float value) {\n        put(key, value, false);\n    }\n\n    /**\n     * Put the float value in sp.\n     *\n     * @param key      The key of sp.\n     * @param value    The value of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     */\n    public void put(@NonNull final String key, final float value, final boolean isCommit) {\n        if (isCommit) {\n            sp.edit().putFloat(key, value).commit();\n        } else {\n            sp.edit().putFloat(key, value).apply();\n        }\n    }\n\n    /**\n     * Return the float value in sp.\n     *\n     * @param key The key of sp.\n     * @return the float value if sp exists or {@code -1f} otherwise\n     */\n    public float getFloat(@NonNull final String key) {\n        return getFloat(key, -1f);\n    }\n\n    /**\n     * Return the float value in sp.\n     *\n     * @param key          The key of sp.\n     * @param defaultValue The default value if the sp doesn't exist.\n     * @return the float value if sp exists or {@code defaultValue} otherwise\n     */\n    public float getFloat(@NonNull final String key, final float defaultValue) {\n        return sp.getFloat(key, defaultValue);\n    }\n\n    /**\n     * Put the boolean value in sp.\n     *\n     * @param key   The key of sp.\n     * @param value The value of sp.\n     */\n    public void put(@NonNull final String key, final boolean value) {\n        put(key, value, false);\n    }\n\n    /**\n     * Put the boolean value in sp.\n     *\n     * @param key      The key of sp.\n     * @param value    The value of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     */\n    public void put(@NonNull final String key, final boolean value, final boolean isCommit) {\n        if (isCommit) {\n            sp.edit().putBoolean(key, value).commit();\n        } else {\n            sp.edit().putBoolean(key, value).apply();\n        }\n    }\n\n    /**\n     * Return the boolean value in sp.\n     *\n     * @param key The key of sp.\n     * @return the boolean value if sp exists or {@code false} otherwise\n     */\n    public boolean getBoolean(@NonNull final String key) {\n        return getBoolean(key, false);\n    }\n\n    /**\n     * Return the boolean value in sp.\n     *\n     * @param key          The key of sp.\n     * @param defaultValue The default value if the sp doesn't exist.\n     * @return the boolean value if sp exists or {@code defaultValue} otherwise\n     */\n    public boolean getBoolean(@NonNull final String key, final boolean defaultValue) {\n        return sp.getBoolean(key, defaultValue);\n    }\n\n    /**\n     * Put the set of string value in sp.\n     *\n     * @param key   The key of sp.\n     * @param value The value of sp.\n     */\n    public void put(@NonNull final String key, final Set<String> value) {\n        put(key, value, false);\n    }\n\n    /**\n     * Put the set of string value in sp.\n     *\n     * @param key      The key of sp.\n     * @param value    The value of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     */\n    public void put(@NonNull final String key,\n                    final Set<String> value,\n                    final boolean isCommit) {\n        if (isCommit) {\n            sp.edit().putStringSet(key, value).commit();\n        } else {\n            sp.edit().putStringSet(key, value).apply();\n        }\n    }\n\n    /**\n     * Return the set of string value in sp.\n     *\n     * @param key The key of sp.\n     * @return the set of string value if sp exists\n     * or {@code Collections.<String>emptySet()} otherwise\n     */\n    public Set<String> getStringSet(@NonNull final String key) {\n        return getStringSet(key, Collections.<String>emptySet());\n    }\n\n    /**\n     * Return the set of string value in sp.\n     *\n     * @param key          The key of sp.\n     * @param defaultValue The default value if the sp doesn't exist.\n     * @return the set of string value if sp exists or {@code defaultValue} otherwise\n     */\n    public Set<String> getStringSet(@NonNull final String key,\n                                    final Set<String> defaultValue) {\n        return sp.getStringSet(key, defaultValue);\n    }\n\n    /**\n     * Return all values in sp.\n     *\n     * @return all values in sp\n     */\n    public Map<String, ?> getAll() {\n        return sp.getAll();\n    }\n\n    /**\n     * Return whether the sp contains the preference.\n     *\n     * @param key The key of sp.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public boolean contains(@NonNull final String key) {\n        return sp.contains(key);\n    }\n\n    /**\n     * Remove the preference in sp.\n     *\n     * @param key The key of sp.\n     */\n    public void remove(@NonNull final String key) {\n        remove(key, false);\n    }\n\n    /**\n     * Remove the preference in sp.\n     *\n     * @param key      The key of sp.\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     */\n    public void remove(@NonNull final String key, final boolean isCommit) {\n        if (isCommit) {\n            sp.edit().remove(key).commit();\n        } else {\n            sp.edit().remove(key).apply();\n        }\n    }\n\n    /**\n     * Remove all preferences in sp.\n     */\n    public void clear() {\n        clear(false);\n    }\n\n    /**\n     * Remove all preferences in sp.\n     *\n     * @param isCommit True to use {@link SharedPreferences.Editor#commit()},\n     *                 false to use {@link SharedPreferences.Editor#apply()}\n     */\n    public void clear(final boolean isCommit) {\n        if (isCommit) {\n            sp.edit().clear().commit();\n        } else {\n            sp.edit().clear().apply();\n        }\n    }\n\n    private static boolean isSpace(final String s) {\n        if (s == null) return true;\n        for (int i = 0, len = s.length(); i < len; ++i) {\n            if (!Character.isWhitespace(s.charAt(i))) {\n                return false;\n            }\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ScreenUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.app.KeyguardManager;\nimport android.content.Context;\nimport android.content.pm.ActivityInfo;\nimport android.content.res.Configuration;\nimport android.content.res.Resources;\nimport android.graphics.Bitmap;\nimport android.graphics.Point;\nimport android.os.Build;\nimport android.provider.Settings;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.RequiresPermission;\nimport android.util.DisplayMetrics;\nimport android.view.Surface;\nimport android.view.View;\nimport android.view.Window;\nimport android.view.WindowManager;\n\nimport static android.Manifest.permission.WRITE_SETTINGS;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/02\n *     desc  : utils about screen\n * </pre>\n */\npublic final class ScreenUtils {\n\n    private ScreenUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Return the width of screen, in pixel.\n     *\n     * @return the width of screen, in pixel\n     */\n    public static int getScreenWidth() {\n        WindowManager wm = (WindowManager) Utils.getApp().getSystemService(Context.WINDOW_SERVICE);\n        if (wm == null) return -1;\n        Point point = new Point();\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            wm.getDefaultDisplay().getRealSize(point);\n        } else {\n            wm.getDefaultDisplay().getSize(point);\n        }\n        return point.x;\n    }\n\n    /**\n     * Return the height of screen, in pixel.\n     *\n     * @return the height of screen, in pixel\n     */\n    public static int getScreenHeight() {\n        WindowManager wm = (WindowManager) Utils.getApp().getSystemService(Context.WINDOW_SERVICE);\n        if (wm == null) return -1;\n        Point point = new Point();\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            wm.getDefaultDisplay().getRealSize(point);\n        } else {\n            wm.getDefaultDisplay().getSize(point);\n        }\n        return point.y;\n    }\n\n    /**\n     * Return the application's width of screen, in pixel.\n     *\n     * @return the application's width of screen, in pixel\n     */\n    public static int getAppScreenWidth() {\n        WindowManager wm = (WindowManager) Utils.getApp().getSystemService(Context.WINDOW_SERVICE);\n        if (wm == null) return -1;\n        Point point = new Point();\n        wm.getDefaultDisplay().getSize(point);\n        return point.x;\n    }\n\n    /**\n     * Return the application's height of screen, in pixel.\n     *\n     * @return the application's height of screen, in pixel\n     */\n    public static int getAppScreenHeight() {\n        WindowManager wm = (WindowManager) Utils.getApp().getSystemService(Context.WINDOW_SERVICE);\n        if (wm == null) return -1;\n        Point point = new Point();\n        wm.getDefaultDisplay().getSize(point);\n        return point.y;\n    }\n\n    /**\n     * Return the density of screen.\n     *\n     * @return the density of screen\n     */\n    public static float getScreenDensity() {\n        return Resources.getSystem().getDisplayMetrics().density;\n    }\n\n    /**\n     * Return the screen density expressed as dots-per-inch.\n     *\n     * @return the screen density expressed as dots-per-inch\n     */\n    public static int getScreenDensityDpi() {\n        return Resources.getSystem().getDisplayMetrics().densityDpi;\n    }\n\n    /**\n     * Return the exact physical pixels per inch of the screen in the Y dimension.\n     *\n     * @return the exact physical pixels per inch of the screen in the Y dimension\n     */\n    public static float getScreenXDpi() {\n        return Resources.getSystem().getDisplayMetrics().xdpi;\n    }\n\n    /**\n     * Return the exact physical pixels per inch of the screen in the Y dimension.\n     *\n     * @return the exact physical pixels per inch of the screen in the Y dimension\n     */\n    public static float getScreenYDpi() {\n        return Resources.getSystem().getDisplayMetrics().ydpi;\n    }\n\n    /**\n     * Return the distance between the given View's X (start point of View's width) and the screen width.\n     *\n     * @return the distance between the given View's X (start point of View's width) and the screen width.\n     */\n    public int calculateDistanceByX(View view) {\n        int[] point = new int[2];\n        view.getLocationOnScreen(point);\n        return getScreenWidth() - point[0];\n    }\n\n    /**\n     * Return the distance between the given View's Y (start point of View's height) and the screen height.\n     *\n     * @return the distance between the given View's Y (start point of View's height) and the screen height.\n     */\n    public int calculateDistanceByY(View view) {\n        int[] point = new int[2];\n        view.getLocationOnScreen(point);\n        return getScreenHeight() - point[1];\n    }\n\n    /**\n     * Return the X coordinate of the given View on the screen.\n     *\n     * @return X coordinate of the given View on the screen.\n     */\n    public int getViewX(View view) {\n        int[] point = new int[2];\n        view.getLocationOnScreen(point);\n        return point[0];\n    }\n\n    /**\n     * Return the Y coordinate of the given View on the screen.\n     *\n     * @return Y coordinate of the given View on the screen.\n     */\n    public int getViewY(View view) {\n        int[] point = new int[2];\n        view.getLocationOnScreen(point);\n        return point[1];\n    }\n\n    /**\n     * Set full screen.\n     *\n     * @param activity The activity.\n     */\n    public static void setFullScreen(@NonNull final Activity activity) {\n        activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);\n    }\n\n    /**\n     * Set non full screen.\n     *\n     * @param activity The activity.\n     */\n    public static void setNonFullScreen(@NonNull final Activity activity) {\n        activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);\n    }\n\n    /**\n     * Toggle full screen.\n     *\n     * @param activity The activity.\n     */\n    public static void toggleFullScreen(@NonNull final Activity activity) {\n        boolean isFullScreen = isFullScreen(activity);\n        Window window = activity.getWindow();\n        if (isFullScreen) {\n            window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);\n        } else {\n            window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);\n        }\n    }\n\n    /**\n     * Return whether screen is full.\n     *\n     * @param activity The activity.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isFullScreen(@NonNull final Activity activity) {\n        int fullScreenFlag = WindowManager.LayoutParams.FLAG_FULLSCREEN;\n        return (activity.getWindow().getAttributes().flags & fullScreenFlag) == fullScreenFlag;\n    }\n\n    /**\n     * Set the screen to landscape.\n     *\n     * @param activity The activity.\n     */\n    @SuppressLint(\"SourceLockedOrientationActivity\")\n    public static void setLandscape(@NonNull final Activity activity) {\n        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);\n    }\n\n    /**\n     * Set the screen to portrait.\n     *\n     * @param activity The activity.\n     */\n    @SuppressLint(\"SourceLockedOrientationActivity\")\n    public static void setPortrait(@NonNull final Activity activity) {\n        activity.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);\n    }\n\n    /**\n     * Return whether screen is landscape.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isLandscape() {\n        return Utils.getApp().getResources().getConfiguration().orientation\n                == Configuration.ORIENTATION_LANDSCAPE;\n    }\n\n    /**\n     * Return whether screen is portrait.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isPortrait() {\n        return Utils.getApp().getResources().getConfiguration().orientation\n                == Configuration.ORIENTATION_PORTRAIT;\n    }\n\n    /**\n     * Return the rotation of screen.\n     *\n     * @param activity The activity.\n     * @return the rotation of screen\n     */\n    public static int getScreenRotation(@NonNull final Activity activity) {\n        switch (activity.getWindowManager().getDefaultDisplay().getRotation()) {\n            case Surface.ROTATION_0:\n                return 0;\n            case Surface.ROTATION_90:\n                return 90;\n            case Surface.ROTATION_180:\n                return 180;\n            case Surface.ROTATION_270:\n                return 270;\n            default:\n                return 0;\n        }\n    }\n\n    /**\n     * Return the bitmap of screen.\n     *\n     * @param activity The activity.\n     * @return the bitmap of screen\n     */\n    public static Bitmap screenShot(@NonNull final Activity activity) {\n        return screenShot(activity, false);\n    }\n\n    /**\n     * Return the bitmap of screen.\n     *\n     * @param activity          The activity.\n     * @param isDeleteStatusBar True to delete status bar, false otherwise.\n     * @return the bitmap of screen\n     */\n    public static Bitmap screenShot(@NonNull final Activity activity, boolean isDeleteStatusBar) {\n        View decorView = activity.getWindow().getDecorView();\n        Bitmap bmp = UtilsBridge.view2Bitmap(decorView);\n        DisplayMetrics dm = new DisplayMetrics();\n        activity.getWindowManager().getDefaultDisplay().getMetrics(dm);\n        if (isDeleteStatusBar) {\n            int statusBarHeight = UtilsBridge.getStatusBarHeight();\n            return Bitmap.createBitmap(\n                    bmp,\n                    0,\n                    statusBarHeight,\n                    dm.widthPixels,\n                    dm.heightPixels - statusBarHeight\n            );\n        } else {\n            return Bitmap.createBitmap(bmp, 0, 0, dm.widthPixels, dm.heightPixels);\n        }\n    }\n\n    /**\n     * Return whether screen is locked.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isScreenLock() {\n        KeyguardManager km =\n                (KeyguardManager) Utils.getApp().getSystemService(Context.KEYGUARD_SERVICE);\n        if (km == null) return false;\n        return km.inKeyguardRestrictedInputMode();\n    }\n\n    /**\n     * Set the duration of sleep.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.WRITE_SETTINGS\" />}</p>\n     *\n     * @param duration The duration.\n     */\n    @RequiresPermission(WRITE_SETTINGS)\n    public static void setSleepDuration(final int duration) {\n        Settings.System.putInt(\n                Utils.getApp().getContentResolver(),\n                Settings.System.SCREEN_OFF_TIMEOUT,\n                duration\n        );\n    }\n\n    /**\n     * Return the duration of sleep.\n     *\n     * @return the duration of sleep.\n     */\n    public static int getSleepDuration() {\n        try {\n            return Settings.System.getInt(\n                    Utils.getApp().getContentResolver(),\n                    Settings.System.SCREEN_OFF_TIMEOUT\n            );\n        } catch (Settings.SettingNotFoundException e) {\n            e.printStackTrace();\n            return -123;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ServiceUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.app.ActivityManager;\nimport android.app.ActivityManager.RunningServiceInfo;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.content.ServiceConnection;\nimport android.os.Build;\n\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport androidx.annotation.NonNull;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/02\n *     desc  : utils about service\n * </pre>\n */\npublic final class ServiceUtils {\n\n    private ServiceUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Return all of the services are running.\n     *\n     * @return all of the services are running\n     */\n    public static Set<String> getAllRunningServices() {\n        ActivityManager am = (ActivityManager) Utils.getApp().getSystemService(Context.ACTIVITY_SERVICE);\n        List<RunningServiceInfo> info = am.getRunningServices(0x7FFFFFFF);\n        Set<String> names = new HashSet<>();\n        if (info == null || info.size() == 0) return null;\n        for (RunningServiceInfo aInfo : info) {\n            names.add(aInfo.service.getClassName());\n        }\n        return names;\n    }\n\n    /**\n     * Start the service.\n     *\n     * @param className The name of class.\n     */\n    public static void startService(@NonNull final String className) {\n        try {\n            startService(Class.forName(className));\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    /**\n     * Start the service.\n     *\n     * @param cls The service class.\n     */\n    public static void startService(@NonNull final Class<?> cls) {\n        startService(new Intent(Utils.getApp(), cls));\n    }\n\n    /**\n     * Start the service.\n     *\n     * @param intent The intent.\n     */\n    public static void startService(Intent intent) {\n        try {\n            intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                Utils.getApp().startForegroundService(intent);\n            } else {\n                Utils.getApp().startService(intent);\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    /**\n     * Stop the service.\n     *\n     * @param className The name of class.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean stopService(@NonNull final String className) {\n        try {\n            return stopService(Class.forName(className));\n        } catch (Exception e) {\n            e.printStackTrace();\n            return false;\n        }\n    }\n\n    /**\n     * Stop the service.\n     *\n     * @param cls The name of class.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean stopService(@NonNull final Class<?> cls) {\n        return stopService(new Intent(Utils.getApp(), cls));\n    }\n\n    /**\n     * Stop the service.\n     *\n     * @param intent The intent.\n     * @return {@code true}: success<br>{@code false}: fail\n     */\n    public static boolean stopService(@NonNull Intent intent) {\n        try {\n            return Utils.getApp().stopService(intent);\n        } catch (Exception e) {\n            e.printStackTrace();\n            return false;\n        }\n    }\n\n    /**\n     * Bind the service.\n     *\n     * @param className The name of class.\n     * @param conn      The ServiceConnection object.\n     * @param flags     Operation options for the binding.\n     *                  <ul>\n     *                  <li>0</li>\n     *                  <li>{@link Context#BIND_AUTO_CREATE}</li>\n     *                  <li>{@link Context#BIND_DEBUG_UNBIND}</li>\n     *                  <li>{@link Context#BIND_NOT_FOREGROUND}</li>\n     *                  <li>{@link Context#BIND_ABOVE_CLIENT}</li>\n     *                  <li>{@link Context#BIND_ALLOW_OOM_MANAGEMENT}</li>\n     *                  <li>{@link Context#BIND_WAIVE_PRIORITY}</li>\n     *                  </ul>\n     */\n    public static void bindService(@NonNull final String className,\n                                   @NonNull final ServiceConnection conn,\n                                   final int flags) {\n        try {\n            bindService(Class.forName(className), conn, flags);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    /**\n     * Bind the service.\n     *\n     * @param cls   The service class.\n     * @param conn  The ServiceConnection object.\n     * @param flags Operation options for the binding.\n     *              <ul>\n     *              <li>0</li>\n     *              <li>{@link Context#BIND_AUTO_CREATE}</li>\n     *              <li>{@link Context#BIND_DEBUG_UNBIND}</li>\n     *              <li>{@link Context#BIND_NOT_FOREGROUND}</li>\n     *              <li>{@link Context#BIND_ABOVE_CLIENT}</li>\n     *              <li>{@link Context#BIND_ALLOW_OOM_MANAGEMENT}</li>\n     *              <li>{@link Context#BIND_WAIVE_PRIORITY}</li>\n     *              </ul>\n     */\n    public static void bindService(@NonNull final Class<?> cls,\n                                   @NonNull final ServiceConnection conn,\n                                   final int flags) {\n        bindService(new Intent(Utils.getApp(), cls), conn, flags);\n    }\n\n    /**\n     * Bind the service.\n     *\n     * @param intent The intent.\n     * @param conn   The ServiceConnection object.\n     * @param flags  Operation options for the binding.\n     *               <ul>\n     *               <li>0</li>\n     *               <li>{@link Context#BIND_AUTO_CREATE}</li>\n     *               <li>{@link Context#BIND_DEBUG_UNBIND}</li>\n     *               <li>{@link Context#BIND_NOT_FOREGROUND}</li>\n     *               <li>{@link Context#BIND_ABOVE_CLIENT}</li>\n     *               <li>{@link Context#BIND_ALLOW_OOM_MANAGEMENT}</li>\n     *               <li>{@link Context#BIND_WAIVE_PRIORITY}</li>\n     *               </ul>\n     */\n    public static void bindService(@NonNull final Intent intent,\n                                   @NonNull final ServiceConnection conn,\n                                   final int flags) {\n        try {\n            Utils.getApp().bindService(intent, conn, flags);\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n    }\n\n    /**\n     * Unbind the service.\n     *\n     * @param conn The ServiceConnection object.\n     */\n    public static void unbindService(@NonNull final ServiceConnection conn) {\n        Utils.getApp().unbindService(conn);\n    }\n\n    /**\n     * Return whether service is running.\n     *\n     * @param cls The service class.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isServiceRunning(@NonNull final Class<?> cls) {\n        return isServiceRunning(cls.getName());\n    }\n\n    /**\n     * Return whether service is running.\n     *\n     * @param className The name of class.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isServiceRunning(@NonNull final String className) {\n        try {\n            ActivityManager am = (ActivityManager) Utils.getApp().getSystemService(Context.ACTIVITY_SERVICE);\n            List<RunningServiceInfo> info = am.getRunningServices(0x7FFFFFFF);\n            if (info == null || info.size() == 0) return false;\n            for (RunningServiceInfo aInfo : info) {\n                if (className.equals(aInfo.service.getClassName())) return true;\n            }\n            return false;\n        } catch (Exception ignore) {\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ShadowUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.content.res.ColorStateList;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.ColorFilter;\nimport android.graphics.LinearGradient;\nimport android.graphics.Paint;\nimport android.graphics.Path;\nimport android.graphics.PixelFormat;\nimport android.graphics.PorterDuff.Mode;\nimport android.graphics.RadialGradient;\nimport android.graphics.Rect;\nimport android.graphics.RectF;\nimport android.graphics.Region;\nimport android.graphics.Shader;\nimport android.graphics.drawable.ColorDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.graphics.drawable.StateListDrawable;\nimport android.util.StateSet;\nimport android.view.View;\n\nimport androidx.core.graphics.drawable.DrawableCompat;\nimport androidx.core.view.ViewCompat;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/13\n *     desc  : utils about shadow\n * </pre>\n */\npublic class ShadowUtils {\n\n    private static final int SHADOW_TAG = -16;\n\n    public static void apply(View... views) {\n        if (views == null) return;\n        for (View view : views) {\n            apply(view, new Config());\n        }\n    }\n\n    public static void apply(View view, Config builder) {\n        if (view == null || builder == null) return;\n        Drawable background = view.getBackground();\n        Object tag = view.getTag(SHADOW_TAG);\n        if (tag instanceof Drawable) {\n            ViewCompat.setBackground(view, (Drawable) tag);\n        } else {\n            background = builder.apply(background);\n            ViewCompat.setBackground(view, background);\n            view.setTag(SHADOW_TAG, background);\n        }\n    }\n\n    public static class Config {\n\n        private static final int SHADOW_COLOR_DEFAULT = 0x44000000;\n        private static final int SHADOW_SIZE          = UtilsBridge.dp2px(8);\n\n        private float   mShadowRadius         = -1;\n        private float   mShadowSizeNormal     = -1;\n        private float   mShadowSizePressed    = -1;\n        private float   mShadowMaxSizeNormal  = -1;\n        private float   mShadowMaxSizePressed = -1;\n        private int     mShadowColorNormal    = SHADOW_COLOR_DEFAULT;\n        private int     mShadowColorPressed   = SHADOW_COLOR_DEFAULT;\n        private boolean isCircle              = false;\n\n        public Config() {\n        }\n\n        public Config setShadowRadius(float radius) {\n            this.mShadowRadius = radius;\n            if (isCircle) {\n                throw new IllegalArgumentException(\"Set circle needn't set radius.\");\n            }\n            return this;\n        }\n\n        public Config setCircle() {\n            isCircle = true;\n            if (mShadowRadius != -1) {\n                throw new IllegalArgumentException(\"Set circle needn't set radius.\");\n            }\n            return this;\n        }\n\n        public Config setShadowSize(int size) {\n            return setShadowSize(size, size);\n        }\n\n        public Config setShadowSize(int sizeNormal, int sizePressed) {\n            this.mShadowSizeNormal = sizeNormal;\n            this.mShadowSizePressed = sizePressed;\n            return this;\n        }\n\n        public Config setShadowMaxSize(int maxSize) {\n            return setShadowMaxSize(maxSize, maxSize);\n        }\n\n        public Config setShadowMaxSize(int maxSizeNormal, int maxSizePressed) {\n            this.mShadowMaxSizeNormal = maxSizeNormal;\n            this.mShadowMaxSizePressed = maxSizePressed;\n            return this;\n        }\n\n        public Config setShadowColor(int color) {\n            return setShadowColor(color, color);\n        }\n\n        public Config setShadowColor(int colorNormal, int colorPressed) {\n            this.mShadowColorNormal = colorNormal;\n            this.mShadowColorPressed = colorPressed;\n            return this;\n        }\n\n        Drawable apply(Drawable src) {\n            if (src == null) {\n                src = new ColorDrawable(Color.TRANSPARENT);\n            }\n            StateListDrawable drawable = new StateListDrawable();\n            drawable.addState(\n                    new int[]{android.R.attr.state_pressed},\n                    new ShadowDrawable(src, getShadowRadius(), getShadowSizeNormal(),\n                            getShadowMaxSizeNormal(), mShadowColorPressed, isCircle)\n            );\n            drawable.addState(\n                    StateSet.WILD_CARD,\n                    new ShadowDrawable(src, getShadowRadius(), getShadowSizePressed(),\n                            getShadowMaxSizePressed(), mShadowColorNormal, isCircle)\n            );\n            return drawable;\n        }\n\n        private float getShadowRadius() {\n            if (mShadowRadius < 0) {\n                mShadowRadius = 0;\n            }\n            return mShadowRadius;\n        }\n\n        private float getShadowSizeNormal() {\n            if (mShadowSizeNormal == -1) {\n                mShadowSizeNormal = SHADOW_SIZE;\n            }\n            return mShadowSizeNormal;\n        }\n\n        private float getShadowSizePressed() {\n            if (mShadowSizePressed == -1) {\n                mShadowSizePressed = getShadowSizeNormal();\n            }\n            return mShadowSizePressed;\n        }\n\n        private float getShadowMaxSizeNormal() {\n            if (mShadowMaxSizeNormal == -1) {\n                mShadowMaxSizeNormal = getShadowSizeNormal();\n            }\n            return mShadowMaxSizeNormal;\n        }\n\n        private float getShadowMaxSizePressed() {\n            if (mShadowMaxSizePressed == -1) {\n                mShadowMaxSizePressed = getShadowSizePressed();\n            }\n            return mShadowMaxSizePressed;\n        }\n    }\n\n    public static class ShadowDrawable extends DrawableWrapper {\n        // used to calculate content padding\n        private static final double COS_45 = Math.cos(Math.toRadians(45));\n\n        private float mShadowMultiplier = 1f;\n\n        private float mShadowTopScale    = 1f;\n        private float mShadowHorizScale  = 1f;\n        private float mShadowBottomScale = 1f;\n\n        private Paint mCornerShadowPaint;\n        private Paint mEdgeShadowPaint;\n\n        private RectF mContentBounds;\n\n        private float mCornerRadius;\n\n        private Path mCornerShadowPath;\n\n        // updated value with inset\n        private float mMaxShadowSize;\n        // actual value set by developer\n        private float mRawMaxShadowSize;\n\n        // multiplied value to account for shadow offset\n        private float mShadowSize;\n        // actual value set by developer\n        private float mRawShadowSize;\n\n        private boolean mDirty = true;\n\n        private final int mShadowStartColor;\n        private final int mShadowEndColor;\n\n        private boolean mAddPaddingForCorners = false;\n\n        private float mRotation;\n\n        private boolean isCircle;\n\n        public ShadowDrawable(Drawable content, float radius,\n                              float shadowSize, float maxShadowSize, int shadowColor, boolean isCircle) {\n            super(content);\n            mShadowStartColor = shadowColor;\n            mShadowEndColor = mShadowStartColor & 0x00ffffff;\n            this.isCircle = isCircle;\n            if (isCircle) {\n                mShadowMultiplier = 1;\n                mShadowTopScale = 1;\n                mShadowHorizScale = 1;\n                mShadowBottomScale = 1;\n            }\n\n            mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);\n            mCornerShadowPaint.setStyle(Paint.Style.FILL);\n            mCornerRadius = Math.round(radius);\n            mContentBounds = new RectF();\n            mEdgeShadowPaint = new Paint(mCornerShadowPaint);\n            mEdgeShadowPaint.setAntiAlias(false);\n            setShadowSize(shadowSize, maxShadowSize);\n        }\n\n        /**\n         * Casts the value to an even integer.\n         */\n        private static int toEven(float value) {\n            int i = Math.round(value);\n            return (i % 2 == 1) ? i - 1 : i;\n        }\n\n        public void setAddPaddingForCorners(boolean addPaddingForCorners) {\n            mAddPaddingForCorners = addPaddingForCorners;\n            invalidateSelf();\n        }\n\n        @Override\n        public void setAlpha(int alpha) {\n            super.setAlpha(alpha);\n            mCornerShadowPaint.setAlpha(alpha);\n            mEdgeShadowPaint.setAlpha(alpha);\n        }\n\n        @Override\n        protected void onBoundsChange(Rect bounds) {\n            mDirty = true;\n        }\n\n        void setShadowSize(float shadowSize, float maxShadowSize) {\n            if (shadowSize < 0 || maxShadowSize < 0) {\n                throw new IllegalArgumentException(\"invalid shadow size\");\n            }\n            shadowSize = toEven(shadowSize);\n            maxShadowSize = toEven(maxShadowSize);\n            if (shadowSize > maxShadowSize) {\n                shadowSize = maxShadowSize;\n            }\n            if (mRawShadowSize == shadowSize && mRawMaxShadowSize == maxShadowSize) {\n                return;\n            }\n            mRawShadowSize = shadowSize;\n            mRawMaxShadowSize = maxShadowSize;\n            mShadowSize = Math.round(shadowSize * mShadowMultiplier);\n            mMaxShadowSize = maxShadowSize;\n            mDirty = true;\n            invalidateSelf();\n        }\n\n        @Override\n        public boolean getPadding(Rect padding) {\n            int vOffset = (int) Math.ceil(calculateVerticalPadding(mRawMaxShadowSize, mCornerRadius,\n                    mAddPaddingForCorners));\n            int hOffset = (int) Math.ceil(calculateHorizontalPadding(mRawMaxShadowSize, mCornerRadius,\n                    mAddPaddingForCorners));\n            padding.set(hOffset, vOffset, hOffset, vOffset);\n            return true;\n        }\n\n        private float calculateVerticalPadding(float maxShadowSize, float cornerRadius,\n                                               boolean addPaddingForCorners) {\n            if (addPaddingForCorners) {\n                return (float) (maxShadowSize * mShadowMultiplier + (1 - COS_45) * cornerRadius);\n            } else {\n                return maxShadowSize * mShadowMultiplier;\n            }\n        }\n\n        private static float calculateHorizontalPadding(float maxShadowSize, float cornerRadius,\n                                                        boolean addPaddingForCorners) {\n            if (addPaddingForCorners) {\n                return (float) (maxShadowSize + (1 - COS_45) * cornerRadius);\n            } else {\n                return maxShadowSize;\n            }\n        }\n\n        @Override\n        public int getOpacity() {\n            return PixelFormat.TRANSLUCENT;\n        }\n\n        public void setCornerRadius(float radius) {\n            radius = Math.round(radius);\n            if (mCornerRadius == radius) {\n                return;\n            }\n            mCornerRadius = radius;\n            mDirty = true;\n            invalidateSelf();\n        }\n\n        @Override\n        public void draw(Canvas canvas) {\n            if (mDirty) {\n                buildComponents(getBounds());\n                mDirty = false;\n            }\n            drawShadow(canvas);\n\n            super.draw(canvas);\n        }\n\n        final void setRotation(float rotation) {\n            if (mRotation != rotation) {\n                mRotation = rotation;\n                invalidateSelf();\n            }\n        }\n\n        private void drawShadow(Canvas canvas) {\n            if (isCircle) {\n                int saved = canvas.save();\n                canvas.translate(mContentBounds.centerX(), mContentBounds.centerY());\n                canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);\n                canvas.restoreToCount(saved);\n                return;\n            }\n\n            final int rotateSaved = canvas.save();\n            canvas.rotate(mRotation, mContentBounds.centerX(), mContentBounds.centerY());\n\n            final float edgeShadowTop = -mCornerRadius - mShadowSize;\n            final float shadowOffset = mCornerRadius;\n            final boolean drawHorizontalEdges = mContentBounds.width() - 2 * shadowOffset > 0;\n            final boolean drawVerticalEdges = mContentBounds.height() - 2 * shadowOffset > 0;\n\n            final float shadowOffsetTop = mRawShadowSize - (mRawShadowSize * mShadowTopScale);\n            final float shadowOffsetHorizontal = mRawShadowSize - (mRawShadowSize * mShadowHorizScale);\n            final float shadowOffsetBottom = mRawShadowSize - (mRawShadowSize * mShadowBottomScale);\n\n            final float shadowScaleHorizontal = shadowOffset == 0 ? 1 : shadowOffset / (shadowOffset + shadowOffsetHorizontal);\n            final float shadowScaleTop = shadowOffset == 0 ? 1 : shadowOffset / (shadowOffset + shadowOffsetTop);\n            final float shadowScaleBottom = shadowOffset == 0 ? 1 : shadowOffset / (shadowOffset + shadowOffsetBottom);\n\n            // LT\n            int saved = canvas.save();\n            canvas.translate(mContentBounds.left + shadowOffset, mContentBounds.top + shadowOffset);\n            canvas.scale(shadowScaleHorizontal, shadowScaleTop);\n            canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);\n            if (drawHorizontalEdges) {\n                // TE\n                canvas.scale(1f / shadowScaleHorizontal, 1f);\n                canvas.drawRect(0, edgeShadowTop,\n                        mContentBounds.width() - 2 * shadowOffset, -mCornerRadius,\n                        mEdgeShadowPaint);\n            }\n            canvas.restoreToCount(saved);\n            // RB\n            saved = canvas.save();\n            canvas.translate(mContentBounds.right - shadowOffset, mContentBounds.bottom - shadowOffset);\n            canvas.scale(shadowScaleHorizontal, shadowScaleBottom);\n            canvas.rotate(180f);\n            canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);\n            if (drawHorizontalEdges) {\n                // BE\n                canvas.scale(1f / shadowScaleHorizontal, 1f);\n                canvas.drawRect(0, edgeShadowTop,\n                        mContentBounds.width() - 2 * shadowOffset, -mCornerRadius,\n                        mEdgeShadowPaint);\n            }\n            canvas.restoreToCount(saved);\n            // LB\n            saved = canvas.save();\n            canvas.translate(mContentBounds.left + shadowOffset, mContentBounds.bottom - shadowOffset);\n            canvas.scale(shadowScaleHorizontal, shadowScaleBottom);\n            canvas.rotate(270f);\n            canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);\n            if (drawVerticalEdges) {\n                // LE\n                canvas.scale(1f / shadowScaleBottom, 1f);\n                canvas.drawRect(0, edgeShadowTop,\n                        mContentBounds.height() - 2 * shadowOffset, -mCornerRadius, mEdgeShadowPaint);\n            }\n            canvas.restoreToCount(saved);\n            // RT\n            saved = canvas.save();\n            canvas.translate(mContentBounds.right - shadowOffset, mContentBounds.top + shadowOffset);\n            canvas.scale(shadowScaleHorizontal, shadowScaleTop);\n            canvas.rotate(90f);\n            canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);\n            if (drawVerticalEdges) {\n                // RE\n                canvas.scale(1f / shadowScaleTop, 1f);\n                canvas.drawRect(0, edgeShadowTop,\n                        mContentBounds.height() - 2 * shadowOffset, -mCornerRadius, mEdgeShadowPaint);\n            }\n            canvas.restoreToCount(saved);\n\n            canvas.restoreToCount(rotateSaved);\n        }\n\n        private void buildShadowCorners() {\n            if (isCircle) {\n                float size = mContentBounds.width() / 2 - 1f;\n                RectF innerBounds = new RectF(-size, -size, size, size);\n                RectF outerBounds = new RectF(innerBounds);\n                outerBounds.inset(-mShadowSize, -mShadowSize);\n\n                if (mCornerShadowPath == null) {\n                    mCornerShadowPath = new Path();\n                } else {\n                    mCornerShadowPath.reset();\n                }\n                mCornerShadowPath.setFillType(Path.FillType.EVEN_ODD);\n                mCornerShadowPath.moveTo(-size, 0);\n                mCornerShadowPath.rLineTo(-mShadowSize, 0);\n                // outer arc\n                mCornerShadowPath.arcTo(outerBounds, 180f, 180f, false);\n                mCornerShadowPath.arcTo(outerBounds, 0f, 180f, false);\n                // inner arc\n                mCornerShadowPath.arcTo(innerBounds, 180f, 180f, false);\n                mCornerShadowPath.arcTo(innerBounds, 0f, 180f, false);\n                mCornerShadowPath.close();\n\n                float shadowRadius = -outerBounds.top;\n                if (shadowRadius > 0f) {\n                    float startRatio = size / shadowRadius;\n                    mCornerShadowPaint.setShader(new RadialGradient(0, 0, shadowRadius,\n                            new int[]{0, mShadowStartColor, mShadowEndColor}, new float[]{0.0F, startRatio, 1.0F},\n                            Shader.TileMode.CLAMP));\n                }\n                return;\n            }\n\n            RectF innerBounds = new RectF(-mCornerRadius, -mCornerRadius, mCornerRadius, mCornerRadius);\n            RectF outerBounds = new RectF(innerBounds);\n            outerBounds.inset(-mShadowSize, -mShadowSize);\n\n            if (mCornerShadowPath == null) {\n                mCornerShadowPath = new Path();\n            } else {\n                mCornerShadowPath.reset();\n            }\n            mCornerShadowPath.setFillType(Path.FillType.EVEN_ODD);\n            mCornerShadowPath.moveTo(-mCornerRadius, 0);\n            mCornerShadowPath.rLineTo(-mShadowSize, 0);\n            // outer arc\n            mCornerShadowPath.arcTo(outerBounds, 180f, 90f, false);\n            // inner arc\n            mCornerShadowPath.arcTo(innerBounds, 270f, -90f, false);\n            mCornerShadowPath.close();\n\n            float shadowRadius = -outerBounds.top;\n            if (shadowRadius > 0f) {\n                float startRatio = mCornerRadius / shadowRadius;\n                mCornerShadowPaint.setShader(new RadialGradient(0, 0, shadowRadius,\n                        new int[]{0, mShadowStartColor, mShadowEndColor}, new float[]{0F, startRatio, 1F},\n                        Shader.TileMode.CLAMP));\n            }\n\n            // we offset the content shadowSize/2 pixels up to make it more realistic.\n            // this is why edge shadow shader has some extra space\n            // When drawing bottom edge shadow, we use that extra space.\n            mEdgeShadowPaint.setShader(new LinearGradient(0, innerBounds.top, 0, outerBounds.top,\n                    mShadowStartColor, mShadowEndColor, Shader.TileMode.CLAMP));\n            mEdgeShadowPaint.setAntiAlias(false);\n        }\n\n        private void buildComponents(Rect bounds) {\n            // Card is offset mShadowMultiplier * maxShadowSize to account for the shadow shift.\n            // We could have different top-bottom offsets to avoid extra gap above but in that case\n            // center aligning Views inside the CardView would be problematic.\n            if (isCircle) {\n                mCornerRadius = bounds.width() / 2;\n            }\n            final float verticalOffset = mRawMaxShadowSize * mShadowMultiplier;\n            mContentBounds.set(bounds.left + mRawMaxShadowSize, bounds.top + verticalOffset,\n                    bounds.right - mRawMaxShadowSize, bounds.bottom - verticalOffset);\n\n            getWrappedDrawable().setBounds((int) mContentBounds.left, (int) mContentBounds.top,\n                    (int) mContentBounds.right, (int) mContentBounds.bottom);\n            buildShadowCorners();\n        }\n\n        public float getCornerRadius() {\n            return mCornerRadius;\n        }\n\n        public void setShadowSize(float size) {\n            setShadowSize(size, mRawMaxShadowSize);\n        }\n\n        public void setMaxShadowSize(float size) {\n            setShadowSize(mRawShadowSize, size);\n        }\n\n        public float getShadowSize() {\n            return mRawShadowSize;\n        }\n\n        public float getMaxShadowSize() {\n            return mRawMaxShadowSize;\n        }\n\n        public float getMinWidth() {\n            final float content = 2 *\n                    Math.max(mRawMaxShadowSize, mCornerRadius + mRawMaxShadowSize / 2);\n            return content + mRawMaxShadowSize * 2;\n        }\n\n        public float getMinHeight() {\n            final float content = 2 * Math.max(mRawMaxShadowSize, mCornerRadius\n                    + mRawMaxShadowSize * mShadowMultiplier / 2);\n            return content + (mRawMaxShadowSize * mShadowMultiplier) * 2;\n        }\n    }\n\n    static class DrawableWrapper extends Drawable implements Drawable.Callback {\n        private Drawable mDrawable;\n\n        public DrawableWrapper(Drawable drawable) {\n            this.setWrappedDrawable(drawable);\n        }\n\n        public void draw(Canvas canvas) {\n            this.mDrawable.draw(canvas);\n        }\n\n        protected void onBoundsChange(Rect bounds) {\n            this.mDrawable.setBounds(bounds);\n        }\n\n        public void setChangingConfigurations(int configs) {\n            this.mDrawable.setChangingConfigurations(configs);\n        }\n\n        public int getChangingConfigurations() {\n            return this.mDrawable.getChangingConfigurations();\n        }\n\n        public void setDither(boolean dither) {\n            this.mDrawable.setDither(dither);\n        }\n\n        public void setFilterBitmap(boolean filter) {\n            this.mDrawable.setFilterBitmap(filter);\n        }\n\n        public void setAlpha(int alpha) {\n            this.mDrawable.setAlpha(alpha);\n        }\n\n        public void setColorFilter(ColorFilter cf) {\n            this.mDrawable.setColorFilter(cf);\n        }\n\n        public boolean isStateful() {\n            return this.mDrawable.isStateful();\n        }\n\n        public boolean setState(int[] stateSet) {\n            return this.mDrawable.setState(stateSet);\n        }\n\n        public int[] getState() {\n            return this.mDrawable.getState();\n        }\n\n        public void jumpToCurrentState() {\n            DrawableCompat.jumpToCurrentState(this.mDrawable);\n        }\n\n        public Drawable getCurrent() {\n            return this.mDrawable.getCurrent();\n        }\n\n        public boolean setVisible(boolean visible, boolean restart) {\n            return super.setVisible(visible, restart) || this.mDrawable.setVisible(visible, restart);\n        }\n\n        public int getOpacity() {\n            return this.mDrawable.getOpacity();\n        }\n\n        public Region getTransparentRegion() {\n            return this.mDrawable.getTransparentRegion();\n        }\n\n        public int getIntrinsicWidth() {\n            return this.mDrawable.getIntrinsicWidth();\n        }\n\n        public int getIntrinsicHeight() {\n            return this.mDrawable.getIntrinsicHeight();\n        }\n\n        public int getMinimumWidth() {\n            return this.mDrawable.getMinimumWidth();\n        }\n\n        public int getMinimumHeight() {\n            return this.mDrawable.getMinimumHeight();\n        }\n\n        public boolean getPadding(Rect padding) {\n            return this.mDrawable.getPadding(padding);\n        }\n\n        public void invalidateDrawable(Drawable who) {\n            this.invalidateSelf();\n        }\n\n        public void scheduleDrawable(Drawable who, Runnable what, long when) {\n            this.scheduleSelf(what, when);\n        }\n\n        public void unscheduleDrawable(Drawable who, Runnable what) {\n            this.unscheduleSelf(what);\n        }\n\n        protected boolean onLevelChange(int level) {\n            return this.mDrawable.setLevel(level);\n        }\n\n        public void setAutoMirrored(boolean mirrored) {\n            DrawableCompat.setAutoMirrored(this.mDrawable, mirrored);\n        }\n\n        public boolean isAutoMirrored() {\n            return DrawableCompat.isAutoMirrored(this.mDrawable);\n        }\n\n        public void setTint(int tint) {\n            DrawableCompat.setTint(this.mDrawable, tint);\n        }\n\n        public void setTintList(ColorStateList tint) {\n            DrawableCompat.setTintList(this.mDrawable, tint);\n        }\n\n        public void setTintMode(Mode tintMode) {\n            DrawableCompat.setTintMode(this.mDrawable, tintMode);\n        }\n\n        public void setHotspot(float x, float y) {\n            DrawableCompat.setHotspot(this.mDrawable, x, y);\n        }\n\n        public void setHotspotBounds(int left, int top, int right, int bottom) {\n            DrawableCompat.setHotspotBounds(this.mDrawable, left, top, right, bottom);\n        }\n\n        public Drawable getWrappedDrawable() {\n            return this.mDrawable;\n        }\n\n        public void setWrappedDrawable(Drawable drawable) {\n            if (this.mDrawable != null) {\n                this.mDrawable.setCallback((Callback) null);\n            }\n\n            this.mDrawable = drawable;\n            if (drawable != null) {\n                drawable.setCallback(this);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ShellUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport androidx.annotation.NonNull;\n\nimport java.io.BufferedReader;\nimport java.io.DataOutputStream;\nimport java.io.IOException;\nimport java.io.InputStreamReader;\nimport java.util.List;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/07\n *     desc  : utils about shell\n * </pre>\n */\npublic final class ShellUtils {\n\n    private static final String LINE_SEP = System.getProperty(\"line.separator\");\n\n    private ShellUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Execute the command asynchronously.\n     *\n     * @param command  The command.\n     * @param isRooted True to use root, false otherwise.\n     * @param consumer The consumer.\n     * @return the task\n     */\n    public static Utils.Task<CommandResult> execCmdAsync(final String command,\n                                                         final boolean isRooted,\n                                                         final Utils.Consumer<CommandResult> consumer) {\n        return execCmdAsync(new String[]{command}, isRooted, true, consumer);\n    }\n\n    /**\n     * Execute the command asynchronously.\n     *\n     * @param commands The commands.\n     * @param isRooted True to use root, false otherwise.\n     * @param consumer The consumer.\n     * @return the task\n     */\n    public static Utils.Task<CommandResult> execCmdAsync(final List<String> commands,\n                                                         final boolean isRooted,\n                                                         final Utils.Consumer<CommandResult> consumer) {\n        return execCmdAsync(commands == null ? null : commands.toArray(new String[]{}), isRooted, true, consumer);\n    }\n\n    /**\n     * Execute the command asynchronously.\n     *\n     * @param commands The commands.\n     * @param isRooted True to use root, false otherwise.\n     * @param consumer The consumer.\n     * @return the task\n     */\n    public static Utils.Task<CommandResult> execCmdAsync(final String[] commands,\n                                                         final boolean isRooted,\n                                                         final Utils.Consumer<CommandResult> consumer) {\n        return execCmdAsync(commands, isRooted, true, consumer);\n    }\n\n    /**\n     * Execute the command asynchronously.\n     *\n     * @param command         The command.\n     * @param isRooted        True to use root, false otherwise.\n     * @param isNeedResultMsg True to return the message of result, false otherwise.\n     * @param consumer        The consumer.\n     * @return the task\n     */\n    public static Utils.Task<CommandResult> execCmdAsync(final String command,\n                                                         final boolean isRooted,\n                                                         final boolean isNeedResultMsg,\n                                                         final Utils.Consumer<CommandResult> consumer) {\n        return execCmdAsync(new String[]{command}, isRooted, isNeedResultMsg, consumer);\n    }\n\n    /**\n     * Execute the command asynchronously.\n     *\n     * @param commands        The commands.\n     * @param isRooted        True to use root, false otherwise.\n     * @param isNeedResultMsg True to return the message of result, false otherwise.\n     * @param consumer        The consumer.\n     * @return the task\n     */\n    public static Utils.Task<CommandResult> execCmdAsync(final List<String> commands,\n                                                         final boolean isRooted,\n                                                         final boolean isNeedResultMsg,\n                                                         final Utils.Consumer<CommandResult> consumer) {\n        return execCmdAsync(commands == null ? null : commands.toArray(new String[]{}),\n                isRooted,\n                isNeedResultMsg,\n                consumer);\n    }\n\n    /**\n     * Execute the command asynchronously.\n     *\n     * @param commands        The commands.\n     * @param isRooted        True to use root, false otherwise.\n     * @param isNeedResultMsg True to return the message of result, false otherwise.\n     * @param consumer        The consumer.\n     * @return the task\n     */\n    public static Utils.Task<CommandResult> execCmdAsync(final String[] commands,\n                                                         final boolean isRooted,\n                                                         final boolean isNeedResultMsg,\n                                                         @NonNull final Utils.Consumer<CommandResult> consumer) {\n        return UtilsBridge.doAsync(new Utils.Task<CommandResult>(consumer) {\n            @Override\n            public CommandResult doInBackground() {\n                return execCmd(commands, isRooted, isNeedResultMsg);\n            }\n        });\n    }\n\n    /**\n     * Execute the command.\n     *\n     * @param command  The command.\n     * @param isRooted True to use root, false otherwise.\n     * @return the single {@link CommandResult} instance\n     */\n    public static CommandResult execCmd(final String command, final boolean isRooted) {\n        return execCmd(new String[]{command}, isRooted, true);\n    }\n\n    /**\n     * Execute the command.\n     *\n     * @param command  The command.\n     * @param envp     The environment variable settings.\n     * @param isRooted True to use root, false otherwise.\n     * @return the single {@link CommandResult} instance\n     */\n    public static CommandResult execCmd(final String command, final List<String> envp, final boolean isRooted) {\n        return execCmd(new String[]{command},\n                envp == null ? null : envp.toArray(new String[]{}),\n                isRooted,\n                true);\n    }\n\n    /**\n     * Execute the command.\n     *\n     * @param commands The commands.\n     * @param isRooted True to use root, false otherwise.\n     * @return the single {@link CommandResult} instance\n     */\n    public static CommandResult execCmd(final List<String> commands, final boolean isRooted) {\n        return execCmd(commands == null ? null : commands.toArray(new String[]{}), isRooted, true);\n    }\n\n    /**\n     * Execute the command.\n     *\n     * @param commands The commands.\n     * @param envp     The environment variable settings.\n     * @param isRooted True to use root, false otherwise.\n     * @return the single {@link CommandResult} instance\n     */\n    public static CommandResult execCmd(final List<String> commands,\n                                        final List<String> envp,\n                                        final boolean isRooted) {\n        return execCmd(commands == null ? null : commands.toArray(new String[]{}),\n                envp == null ? null : envp.toArray(new String[]{}),\n                isRooted,\n                true);\n    }\n\n    /**\n     * Execute the command.\n     *\n     * @param commands The commands.\n     * @param isRooted True to use root, false otherwise.\n     * @return the single {@link CommandResult} instance\n     */\n    public static CommandResult execCmd(final String[] commands, final boolean isRooted) {\n        return execCmd(commands, isRooted, true);\n    }\n\n    /**\n     * Execute the command.\n     *\n     * @param command         The command.\n     * @param isRooted        True to use root, false otherwise.\n     * @param isNeedResultMsg True to return the message of result, false otherwise.\n     * @return the single {@link CommandResult} instance\n     */\n    public static CommandResult execCmd(final String command,\n                                        final boolean isRooted,\n                                        final boolean isNeedResultMsg) {\n        return execCmd(new String[]{command}, isRooted, isNeedResultMsg);\n    }\n\n    /**\n     * Execute the command.\n     *\n     * @param command         The command.\n     * @param envp            The environment variable settings.\n     * @param isRooted        True to use root, false otherwise.\n     * @param isNeedResultMsg True to return the message of result, false otherwise.\n     * @return the single {@link CommandResult} instance\n     */\n    public static CommandResult execCmd(final String command,\n                                        final List<String> envp,\n                                        final boolean isRooted,\n                                        final boolean isNeedResultMsg) {\n        return execCmd(new String[]{command}, envp == null ? null : envp.toArray(new String[]{}),\n                isRooted,\n                isNeedResultMsg);\n    }\n\n    /**\n     * Execute the command.\n     *\n     * @param command         The command.\n     * @param envp            The environment variable settings array.\n     * @param isRooted        True to use root, false otherwise.\n     * @param isNeedResultMsg True to return the message of result, false otherwise.\n     * @return the single {@link CommandResult} instance\n     */\n    public static CommandResult execCmd(final String command,\n                                        final String[] envp,\n                                        final boolean isRooted,\n                                        final boolean isNeedResultMsg) {\n        return execCmd(new String[]{command}, envp, isRooted, isNeedResultMsg);\n    }\n\n    /**\n     * Execute the command.\n     *\n     * @param commands        The commands.\n     * @param isRooted        True to use root, false otherwise.\n     * @param isNeedResultMsg True to return the message of result, false otherwise.\n     * @return the single {@link CommandResult} instance\n     */\n    public static CommandResult execCmd(final List<String> commands,\n                                        final boolean isRooted,\n                                        final boolean isNeedResultMsg) {\n        return execCmd(commands == null ? null : commands.toArray(new String[]{}),\n                isRooted,\n                isNeedResultMsg);\n    }\n\n    /**\n     * Execute the command.\n     *\n     * @param commands        The commands.\n     * @param isRooted        True to use root, false otherwise.\n     * @param isNeedResultMsg True to return the message of result, false otherwise.\n     * @return the single {@link CommandResult} instance\n     */\n    public static CommandResult execCmd(final String[] commands,\n                                        final boolean isRooted,\n                                        final boolean isNeedResultMsg) {\n        return execCmd(commands, null, isRooted, isNeedResultMsg);\n    }\n\n    /**\n     * Execute the command.\n     *\n     * @param commands        The commands.\n     * @param envp            Array of strings, each element of which\n     *                        has environment variable settings in the format\n     *                        <i>name</i>=<i>value</i>, or\n     *                        <tt>null</tt> if the subprocess should inherit\n     *                        the environment of the current process.\n     * @param isRooted        True to use root, false otherwise.\n     * @param isNeedResultMsg True to return the message of result, false otherwise.\n     * @return the single {@link CommandResult} instance\n     */\n    public static CommandResult execCmd(final String[] commands,\n                                        final String[] envp,\n                                        final boolean isRooted,\n                                        final boolean isNeedResultMsg) {\n        int result = -1;\n        if (commands == null || commands.length == 0) {\n            return new CommandResult(result, \"\", \"\");\n        }\n        Process process = null;\n        BufferedReader successResult = null;\n        BufferedReader errorResult = null;\n        StringBuilder successMsg = null;\n        StringBuilder errorMsg = null;\n        DataOutputStream os = null;\n        try {\n            process = Runtime.getRuntime().exec(isRooted ? \"su\" : \"sh\", envp, null);\n            os = new DataOutputStream(process.getOutputStream());\n            for (String command : commands) {\n                if (command == null) continue;\n                os.write(command.getBytes());\n                os.writeBytes(LINE_SEP);\n                os.flush();\n            }\n            os.writeBytes(\"exit\" + LINE_SEP);\n            os.flush();\n            result = process.waitFor();\n            if (isNeedResultMsg) {\n                successMsg = new StringBuilder();\n                errorMsg = new StringBuilder();\n                successResult = new BufferedReader(\n                        new InputStreamReader(process.getInputStream(), \"UTF-8\")\n                );\n                errorResult = new BufferedReader(\n                        new InputStreamReader(process.getErrorStream(), \"UTF-8\")\n                );\n                String line;\n                if ((line = successResult.readLine()) != null) {\n                    successMsg.append(line);\n                    while ((line = successResult.readLine()) != null) {\n                        successMsg.append(LINE_SEP).append(line);\n                    }\n                }\n                if ((line = errorResult.readLine()) != null) {\n                    errorMsg.append(line);\n                    while ((line = errorResult.readLine()) != null) {\n                        errorMsg.append(LINE_SEP).append(line);\n                    }\n                }\n            }\n        } catch (Exception e) {\n            e.printStackTrace();\n        } finally {\n            try {\n                if (os != null) {\n                    os.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n            try {\n                if (successResult != null) {\n                    successResult.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n            try {\n                if (errorResult != null) {\n                    errorResult.close();\n                }\n            } catch (IOException e) {\n                e.printStackTrace();\n            }\n            if (process != null) {\n                process.destroy();\n            }\n        }\n        return new CommandResult(\n                result,\n                successMsg == null ? \"\" : successMsg.toString(),\n                errorMsg == null ? \"\" : errorMsg.toString()\n        );\n    }\n\n    /**\n     * The result of command.\n     */\n    public static class CommandResult {\n        public int    result;\n        public String successMsg;\n        public String errorMsg;\n\n        public CommandResult(final int result, final String successMsg, final String errorMsg) {\n            this.result = result;\n            this.successMsg = successMsg;\n            this.errorMsg = errorMsg;\n        }\n\n        @Override\n        public String toString() {\n            return \"result: \" + result + \"\\n\" +\n                    \"successMsg: \" + successMsg + \"\\n\" +\n                    \"errorMsg: \" + errorMsg;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/SizeUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.content.res.Resources;\nimport android.util.DisplayMetrics;\nimport android.util.TypedValue;\nimport android.view.View;\nimport android.view.ViewGroup;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/02\n *     desc  : utils about size\n * </pre>\n */\npublic final class SizeUtils {\n\n    private SizeUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Value of dp to value of px.\n     *\n     * @param dpValue The value of dp.\n     * @return value of px\n     */\n    public static int dp2px(final float dpValue) {\n        final float scale = Resources.getSystem().getDisplayMetrics().density;\n        return (int) (dpValue * scale + 0.5f);\n    }\n\n    /**\n     * Value of px to value of dp.\n     *\n     * @param pxValue The value of px.\n     * @return value of dp\n     */\n    public static int px2dp(final float pxValue) {\n        final float scale = Resources.getSystem().getDisplayMetrics().density;\n        return (int) (pxValue / scale + 0.5f);\n    }\n\n    /**\n     * Value of sp to value of px.\n     *\n     * @param spValue The value of sp.\n     * @return value of px\n     */\n    public static int sp2px(final float spValue) {\n        final float fontScale = Resources.getSystem().getDisplayMetrics().scaledDensity;\n        return (int) (spValue * fontScale + 0.5f);\n    }\n\n    /**\n     * Value of px to value of sp.\n     *\n     * @param pxValue The value of px.\n     * @return value of sp\n     */\n    public static int px2sp(final float pxValue) {\n        final float fontScale = Resources.getSystem().getDisplayMetrics().scaledDensity;\n        return (int) (pxValue / fontScale + 0.5f);\n    }\n\n    /**\n     * Converts an unpacked complex data value holding a dimension to its final floating\n     * point value. The two parameters <var>unit</var> and <var>value</var>\n     * are as in {@link TypedValue#TYPE_DIMENSION}.\n     *\n     * @param value The value to apply the unit to.\n     * @param unit  The unit to convert from.\n     * @return The complex floating point value multiplied by the appropriate\n     * metrics depending on its unit.\n     */\n    public static float applyDimension(final float value, final int unit) {\n        DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();\n        switch (unit) {\n            case TypedValue.COMPLEX_UNIT_PX:\n                return value;\n            case TypedValue.COMPLEX_UNIT_DIP:\n                return value * metrics.density;\n            case TypedValue.COMPLEX_UNIT_SP:\n                return value * metrics.scaledDensity;\n            case TypedValue.COMPLEX_UNIT_PT:\n                return value * metrics.xdpi * (1.0f / 72);\n            case TypedValue.COMPLEX_UNIT_IN:\n                return value * metrics.xdpi;\n            case TypedValue.COMPLEX_UNIT_MM:\n                return value * metrics.xdpi * (1.0f / 25.4f);\n        }\n        return 0;\n    }\n\n    /**\n     * Force get the size of view.\n     * <p>e.g.</p>\n     * <pre>\n     * SizeUtils.forceGetViewSize(view, new SizeUtils.OnGetSizeListener() {\n     *     Override\n     *     public void onGetSize(final View view) {\n     *         view.getWidth();\n     *     }\n     * });\n     * </pre>\n     *\n     * @param view     The view.\n     * @param listener The get size listener.\n     */\n    public static void forceGetViewSize(final View view, final OnGetSizeListener listener) {\n        view.post(new Runnable() {\n            @Override\n            public void run() {\n                if (listener != null) {\n                    listener.onGetSize(view);\n                }\n            }\n        });\n    }\n\n    /**\n     * Return the width of view.\n     *\n     * @param view The view.\n     * @return the width of view\n     */\n    public static int getMeasuredWidth(final View view) {\n        return measureView(view)[0];\n    }\n\n    /**\n     * Return the height of view.\n     *\n     * @param view The view.\n     * @return the height of view\n     */\n    public static int getMeasuredHeight(final View view) {\n        return measureView(view)[1];\n    }\n\n    /**\n     * Measure the view.\n     *\n     * @param view The view.\n     * @return arr[0]: view's width, arr[1]: view's height\n     */\n    public static int[] measureView(final View view) {\n        ViewGroup.LayoutParams lp = view.getLayoutParams();\n        if (lp == null) {\n            lp = new ViewGroup.LayoutParams(\n                    ViewGroup.LayoutParams.MATCH_PARENT,\n                    ViewGroup.LayoutParams.WRAP_CONTENT\n            );\n        }\n        int widthSpec = ViewGroup.getChildMeasureSpec(0, 0, lp.width);\n        int lpHeight = lp.height;\n        int heightSpec;\n        if (lpHeight > 0) {\n            heightSpec = View.MeasureSpec.makeMeasureSpec(lpHeight, View.MeasureSpec.EXACTLY);\n        } else {\n            heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);\n        }\n        view.measure(widthSpec, heightSpec);\n        return new int[]{view.getMeasuredWidth(), view.getMeasuredHeight()};\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // interface\n    ///////////////////////////////////////////////////////////////////////////\n\n    public interface OnGetSizeListener {\n        void onGetSize(View view);\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/SnackbarUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.os.Build;\nimport android.text.SpannableString;\nimport android.text.Spanned;\nimport android.text.style.ForegroundColorSpan;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.ViewParent;\nimport android.widget.FrameLayout;\n\nimport com.google.android.material.snackbar.Snackbar;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.ref.WeakReference;\n\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.DrawableRes;\nimport androidx.annotation.IntDef;\nimport androidx.annotation.IntRange;\nimport androidx.annotation.LayoutRes;\nimport androidx.annotation.NonNull;\nimport androidx.coordinatorlayout.widget.CoordinatorLayout;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/10/16\n *     desc  : utils about snackbar\n * </pre>\n */\npublic final class SnackbarUtils {\n\n    public static final int LENGTH_INDEFINITE = -2;\n    public static final int LENGTH_SHORT      = -1;\n    public static final int LENGTH_LONG       = 0;\n\n    @IntDef({LENGTH_INDEFINITE, LENGTH_SHORT, LENGTH_LONG})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface Duration {\n    }\n\n    private static final int COLOR_DEFAULT = 0xFEFFFFFF;\n    private static final int COLOR_SUCCESS = 0xFF2BB600;\n    private static final int COLOR_WARNING = 0xFFFFC100;\n    private static final int COLOR_ERROR   = 0xFFFF0000;\n    private static final int COLOR_MESSAGE = 0xFFFFFFFF;\n\n    private static WeakReference<Snackbar> sWeakSnackbar;\n\n    private View                 view;\n    private CharSequence         message;\n    private int                  messageColor;\n    private int                  bgColor;\n    private int                  bgResource;\n    private int                  duration;\n    private CharSequence         actionText;\n    private int                  actionTextColor;\n    private View.OnClickListener actionListener;\n    private int                  bottomMargin;\n\n    private SnackbarUtils(final View parent) {\n        setDefault();\n        this.view = parent;\n    }\n\n    private void setDefault() {\n        message = \"\";\n        messageColor = COLOR_DEFAULT;\n        bgColor = COLOR_DEFAULT;\n        bgResource = -1;\n        duration = LENGTH_SHORT;\n        actionText = \"\";\n        actionTextColor = COLOR_DEFAULT;\n        bottomMargin = 0;\n    }\n\n    /**\n     * Set the view to find a parent from.\n     *\n     * @param view The view to find a parent from.\n     * @return the single {@link SnackbarUtils} instance\n     */\n    public static SnackbarUtils with(@NonNull final View view) {\n        return new SnackbarUtils(view);\n    }\n\n    /**\n     * Set the message.\n     *\n     * @param msg The message.\n     * @return the single {@link SnackbarUtils} instance\n     */\n    public SnackbarUtils setMessage(@NonNull final CharSequence msg) {\n        this.message = msg;\n        return this;\n    }\n\n    /**\n     * Set the color of message.\n     *\n     * @param color The color of message.\n     * @return the single {@link SnackbarUtils} instance\n     */\n    public SnackbarUtils setMessageColor(@ColorInt final int color) {\n        this.messageColor = color;\n        return this;\n    }\n\n    /**\n     * Set the color of background.\n     *\n     * @param color The color of background.\n     * @return the single {@link SnackbarUtils} instance\n     */\n    public SnackbarUtils setBgColor(@ColorInt final int color) {\n        this.bgColor = color;\n        return this;\n    }\n\n    /**\n     * Set the resource of background.\n     *\n     * @param bgResource The resource of background.\n     * @return the single {@link SnackbarUtils} instance\n     */\n    public SnackbarUtils setBgResource(@DrawableRes final int bgResource) {\n        this.bgResource = bgResource;\n        return this;\n    }\n\n    /**\n     * Set the duration.\n     *\n     * @param duration The duration.\n     *                 <ul>\n     *                 <li>{@link Duration#LENGTH_INDEFINITE}</li>\n     *                 <li>{@link Duration#LENGTH_SHORT     }</li>\n     *                 <li>{@link Duration#LENGTH_LONG      }</li>\n     *                 </ul>\n     * @return the single {@link SnackbarUtils} instance\n     */\n    public SnackbarUtils setDuration(@Duration final int duration) {\n        this.duration = duration;\n        return this;\n    }\n\n    /**\n     * Set the action.\n     *\n     * @param text     The text.\n     * @param listener The click listener.\n     * @return the single {@link SnackbarUtils} instance\n     */\n    public SnackbarUtils setAction(@NonNull final CharSequence text,\n                                   @NonNull final View.OnClickListener listener) {\n        return setAction(text, COLOR_DEFAULT, listener);\n    }\n\n    /**\n     * Set the action.\n     *\n     * @param text     The text.\n     * @param color    The color of text.\n     * @param listener The click listener.\n     * @return the single {@link SnackbarUtils} instance\n     */\n\n    public SnackbarUtils setAction(@NonNull final CharSequence text,\n                                   @ColorInt final int color,\n                                   @NonNull final View.OnClickListener listener) {\n        this.actionText = text;\n        this.actionTextColor = color;\n        this.actionListener = listener;\n        return this;\n    }\n\n    /**\n     * Set the bottom margin.\n     *\n     * @param bottomMargin The size of bottom margin, in pixel.\n     */\n    public SnackbarUtils setBottomMargin(@IntRange(from = 1) final int bottomMargin) {\n        this.bottomMargin = bottomMargin;\n        return this;\n    }\n\n    /**\n     * Show the snackbar.\n     */\n    public Snackbar show() {\n        return show(false);\n    }\n\n    /**\n     * Show the snackbar.\n     *\n     * @param isShowTop True to show the snack bar on the top, false otherwise.\n     */\n    public Snackbar show(boolean isShowTop) {\n        View view = this.view;\n        if (view == null) return null;\n        if (isShowTop) {\n            ViewGroup suitableParent = findSuitableParentCopyFromSnackbar(view);\n            View topSnackBarContainer = suitableParent.findViewWithTag(\"topSnackBarCoordinatorLayout\");\n            if (topSnackBarContainer == null) {\n                CoordinatorLayout topSnackBarCoordinatorLayout = new CoordinatorLayout(view.getContext());\n                topSnackBarCoordinatorLayout.setTag(\"topSnackBarCoordinatorLayout\");\n                topSnackBarCoordinatorLayout.setRotation(180);\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n                    // bring to front\n                    topSnackBarCoordinatorLayout.setElevation(100);\n                }\n                suitableParent.addView(topSnackBarCoordinatorLayout, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);\n                topSnackBarContainer = topSnackBarCoordinatorLayout;\n            }\n            view = topSnackBarContainer;\n        }\n        if (messageColor != COLOR_DEFAULT) {\n            SpannableString spannableString = new SpannableString(message);\n            ForegroundColorSpan colorSpan = new ForegroundColorSpan(messageColor);\n            spannableString.setSpan(\n                    colorSpan, 0, spannableString.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE\n            );\n            sWeakSnackbar = new WeakReference<>(Snackbar.make(view, spannableString, duration));\n        } else {\n            sWeakSnackbar = new WeakReference<>(Snackbar.make(view, message, duration));\n        }\n        final Snackbar snackbar = sWeakSnackbar.get();\n        final Snackbar.SnackbarLayout snackbarView = (Snackbar.SnackbarLayout) snackbar.getView();\n        if (isShowTop) {\n            for (int i = 0; i < snackbarView.getChildCount(); i++) {\n                View child = snackbarView.getChildAt(i);\n                child.setRotation(180);\n            }\n        }\n        if (bgResource != -1) {\n            snackbarView.setBackgroundResource(bgResource);\n        } else if (bgColor != COLOR_DEFAULT) {\n            snackbarView.setBackgroundColor(bgColor);\n        }\n        if (bottomMargin != 0) {\n            ViewGroup.MarginLayoutParams params =\n                    (ViewGroup.MarginLayoutParams) snackbarView.getLayoutParams();\n            params.bottomMargin = bottomMargin;\n        }\n        if (actionText.length() > 0 && actionListener != null) {\n            if (actionTextColor != COLOR_DEFAULT) {\n                snackbar.setActionTextColor(actionTextColor);\n            }\n            snackbar.setAction(actionText, actionListener);\n        }\n        snackbar.show();\n        return snackbar;\n    }\n\n    /**\n     * Show the snackbar with success style.\n     */\n    public void showSuccess() {\n        showSuccess(false);\n    }\n\n    /**\n     * Show the snackbar with success style.\n     *\n     * @param isShowTop True to show the snack bar on the top, false otherwise.\n     */\n    public void showSuccess(boolean isShowTop) {\n        bgColor = COLOR_SUCCESS;\n        messageColor = COLOR_MESSAGE;\n        actionTextColor = COLOR_MESSAGE;\n        show(isShowTop);\n    }\n\n    /**\n     * Show the snackbar with warning style.\n     */\n    public void showWarning() {\n        showWarning(false);\n    }\n\n    /**\n     * Show the snackbar with warning style.\n     *\n     * @param isShowTop True to show the snackbar on the top, false otherwise.\n     */\n    public void showWarning(boolean isShowTop) {\n        bgColor = COLOR_WARNING;\n        messageColor = COLOR_MESSAGE;\n        actionTextColor = COLOR_MESSAGE;\n        show(isShowTop);\n    }\n\n    /**\n     * Show the snackbar with error style.\n     */\n    public void showError() {\n        showError(false);\n    }\n\n    /**\n     * Show the snackbar with error style.\n     *\n     * @param isShowTop True to show the snackbar on the top, false otherwise.\n     */\n    public void showError(boolean isShowTop) {\n        bgColor = COLOR_ERROR;\n        messageColor = COLOR_MESSAGE;\n        actionTextColor = COLOR_MESSAGE;\n        show(isShowTop);\n    }\n\n    /**\n     * Dismiss the snackbar.\n     */\n    public static void dismiss() {\n        if (sWeakSnackbar != null && sWeakSnackbar.get() != null) {\n            sWeakSnackbar.get().dismiss();\n            sWeakSnackbar = null;\n        }\n    }\n\n    /**\n     * Return the view of snackbar.\n     *\n     * @return the view of snackbar\n     */\n    public static View getView() {\n        Snackbar snackbar = sWeakSnackbar.get();\n        if (snackbar == null) return null;\n        return snackbar.getView();\n    }\n\n    /**\n     * Add view to the snackbar.\n     * <p>Call it after {@link #show()}</p>\n     *\n     * @param layoutId The id of layout.\n     * @param params   The params.\n     */\n    public static void addView(@LayoutRes final int layoutId,\n                               @NonNull final ViewGroup.LayoutParams params) {\n        final View view = getView();\n        if (view != null) {\n            view.setPadding(0, 0, 0, 0);\n            Snackbar.SnackbarLayout layout = (Snackbar.SnackbarLayout) view;\n            View child = LayoutInflater.from(view.getContext()).inflate(layoutId, null);\n            layout.addView(child, -1, params);\n        }\n    }\n\n    /**\n     * Add view to the snackbar.\n     * <p>Call it after {@link #show()}</p>\n     *\n     * @param child  The child view.\n     * @param params The params.\n     */\n    public static void addView(@NonNull final View child,\n                               @NonNull final ViewGroup.LayoutParams params) {\n        final View view = getView();\n        if (view != null) {\n            view.setPadding(0, 0, 0, 0);\n            Snackbar.SnackbarLayout layout = (Snackbar.SnackbarLayout) view;\n            layout.addView(child, params);\n        }\n    }\n\n    private static ViewGroup findSuitableParentCopyFromSnackbar(View view) {\n        ViewGroup fallback = null;\n\n        do {\n            if (view instanceof CoordinatorLayout) {\n                return (ViewGroup) view;\n            }\n\n            if (view instanceof FrameLayout) {\n                if (view.getId() == android.R.id.content) {\n                    return (ViewGroup) view;\n                }\n\n                fallback = (ViewGroup) view;\n            }\n\n            if (view != null) {\n                ViewParent parent = view.getParent();\n                view = parent instanceof View ? (View) parent : null;\n            }\n        } while (view != null);\n\n        return fallback;\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/SpanUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.annotation.SuppressLint;\nimport android.content.res.Resources;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.graphics.BlurMaskFilter;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Paint;\nimport android.graphics.Path;\nimport android.graphics.Rect;\nimport android.graphics.Shader;\nimport android.graphics.Typeface;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.net.Uri;\nimport android.text.Layout;\nimport android.text.Layout.Alignment;\nimport android.text.SpannableStringBuilder;\nimport android.text.Spanned;\nimport android.text.TextPaint;\nimport android.text.method.LinkMovementMethod;\nimport android.text.style.AbsoluteSizeSpan;\nimport android.text.style.AlignmentSpan;\nimport android.text.style.BackgroundColorSpan;\nimport android.text.style.CharacterStyle;\nimport android.text.style.ClickableSpan;\nimport android.text.style.ForegroundColorSpan;\nimport android.text.style.LeadingMarginSpan;\nimport android.text.style.LineHeightSpan;\nimport android.text.style.MaskFilterSpan;\nimport android.text.style.RelativeSizeSpan;\nimport android.text.style.ReplacementSpan;\nimport android.text.style.ScaleXSpan;\nimport android.text.style.StrikethroughSpan;\nimport android.text.style.StyleSpan;\nimport android.text.style.SubscriptSpan;\nimport android.text.style.SuperscriptSpan;\nimport android.text.style.TypefaceSpan;\nimport android.text.style.URLSpan;\nimport android.text.style.UnderlineSpan;\nimport android.text.style.UpdateAppearance;\nimport android.util.Log;\nimport android.view.View;\nimport android.widget.TextView;\n\nimport java.io.InputStream;\nimport java.io.Serializable;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.ref.WeakReference;\n\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.DrawableRes;\nimport androidx.annotation.FloatRange;\nimport androidx.annotation.IntDef;\nimport androidx.annotation.IntRange;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.core.content.ContextCompat;\n\nimport static android.graphics.BlurMaskFilter.Blur;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 16/12/13\n *     desc  : utils about span\n * </pre>\n */\npublic final class SpanUtils {\n\n    private static final int COLOR_DEFAULT = 0xFEFFFFFF;\n\n    public static final int ALIGN_BOTTOM   = 0;\n    public static final int ALIGN_BASELINE = 1;\n    public static final int ALIGN_CENTER   = 2;\n    public static final int ALIGN_TOP      = 3;\n\n    @IntDef({ALIGN_BOTTOM, ALIGN_BASELINE, ALIGN_CENTER, ALIGN_TOP})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface Align {\n    }\n\n    private static final String LINE_SEPARATOR = System.getProperty(\"line.separator\");\n\n    public static SpanUtils with(final TextView textView) {\n        return new SpanUtils(textView);\n    }\n\n    private TextView      mTextView;\n    private CharSequence  mText;\n    private int           flag;\n    private int           foregroundColor;\n    private int           backgroundColor;\n    private int           lineHeight;\n    private int           alignLine;\n    private int           quoteColor;\n    private int           stripeWidth;\n    private int           quoteGapWidth;\n    private int           first;\n    private int           rest;\n    private int           bulletColor;\n    private int           bulletRadius;\n    private int           bulletGapWidth;\n    private int           fontSize;\n    private float         proportion;\n    private float         xProportion;\n    private boolean       isStrikethrough;\n    private boolean       isUnderline;\n    private boolean       isSuperscript;\n    private boolean       isSubscript;\n    private boolean       isBold;\n    private boolean       isItalic;\n    private boolean       isBoldItalic;\n    private String        fontFamily;\n    private Typeface      typeface;\n    private Alignment     alignment;\n    private int           verticalAlign;\n    private ClickableSpan clickSpan;\n    private String        url;\n    private float         blurRadius;\n    private Blur          style;\n    private Shader        shader;\n    private float         shadowRadius;\n    private float         shadowDx;\n    private float         shadowDy;\n    private int           shadowColor;\n    private Object[]      spans;\n\n    private Bitmap   imageBitmap;\n    private Drawable imageDrawable;\n    private Uri      imageUri;\n    private int      imageResourceId;\n    private int      alignImage;\n\n    private int spaceSize;\n    private int spaceColor;\n\n    private SerializableSpannableStringBuilder mBuilder;\n    private boolean                            isCreated;\n\n    private       int mType;\n    private final int mTypeCharSequence = 0;\n    private final int mTypeImage        = 1;\n    private final int mTypeSpace        = 2;\n\n    private SpanUtils(TextView textView) {\n        this();\n        mTextView = textView;\n    }\n\n    public SpanUtils() {\n        mBuilder = new SerializableSpannableStringBuilder();\n        mText = \"\";\n        mType = -1;\n        setDefault();\n    }\n\n    private void setDefault() {\n        flag = Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;\n        foregroundColor = COLOR_DEFAULT;\n        backgroundColor = COLOR_DEFAULT;\n        lineHeight = -1;\n        quoteColor = COLOR_DEFAULT;\n        first = -1;\n        bulletColor = COLOR_DEFAULT;\n        fontSize = -1;\n        proportion = -1;\n        xProportion = -1;\n        isStrikethrough = false;\n        isUnderline = false;\n        isSuperscript = false;\n        isSubscript = false;\n        isBold = false;\n        isItalic = false;\n        isBoldItalic = false;\n        fontFamily = null;\n        typeface = null;\n        alignment = null;\n        verticalAlign = -1;\n        clickSpan = null;\n        url = null;\n        blurRadius = -1;\n        shader = null;\n        shadowRadius = -1;\n        spans = null;\n\n        imageBitmap = null;\n        imageDrawable = null;\n        imageUri = null;\n        imageResourceId = -1;\n\n        spaceSize = -1;\n    }\n\n    /**\n     * Set the span of flag.\n     *\n     * @param flag The flag.\n     *             <ul>\n     *             <li>{@link Spanned#SPAN_INCLUSIVE_EXCLUSIVE}</li>\n     *             <li>{@link Spanned#SPAN_INCLUSIVE_INCLUSIVE}</li>\n     *             <li>{@link Spanned#SPAN_EXCLUSIVE_EXCLUSIVE}</li>\n     *             <li>{@link Spanned#SPAN_EXCLUSIVE_INCLUSIVE}</li>\n     *             </ul>\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setFlag(final int flag) {\n        this.flag = flag;\n        return this;\n    }\n\n    /**\n     * Set the span of foreground's color.\n     *\n     * @param color The color of foreground\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setForegroundColor(@ColorInt final int color) {\n        this.foregroundColor = color;\n        return this;\n    }\n\n    /**\n     * Set the span of background's color.\n     *\n     * @param color The color of background\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setBackgroundColor(@ColorInt final int color) {\n        this.backgroundColor = color;\n        return this;\n    }\n\n    /**\n     * Set the span of line height.\n     *\n     * @param lineHeight The line height, in pixel.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setLineHeight(@IntRange(from = 0) final int lineHeight) {\n        return setLineHeight(lineHeight, ALIGN_CENTER);\n    }\n\n    /**\n     * Set the span of line height.\n     *\n     * @param lineHeight The line height, in pixel.\n     * @param align      The alignment.\n     *                   <ul>\n     *                   <li>{@link Align#ALIGN_TOP   }</li>\n     *                   <li>{@link Align#ALIGN_CENTER}</li>\n     *                   <li>{@link Align#ALIGN_BOTTOM}</li>\n     *                   </ul>\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setLineHeight(@IntRange(from = 0) final int lineHeight,\n                                   @Align final int align) {\n        this.lineHeight = lineHeight;\n        this.alignLine = align;\n        return this;\n    }\n\n    /**\n     * Set the span of quote's color.\n     *\n     * @param color The color of quote\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setQuoteColor(@ColorInt final int color) {\n        return setQuoteColor(color, 2, 2);\n    }\n\n    /**\n     * Set the span of quote's color.\n     *\n     * @param color       The color of quote.\n     * @param stripeWidth The width of stripe, in pixel.\n     * @param gapWidth    The width of gap, in pixel.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setQuoteColor(@ColorInt final int color,\n                                   @IntRange(from = 1) final int stripeWidth,\n                                   @IntRange(from = 0) final int gapWidth) {\n        this.quoteColor = color;\n        this.stripeWidth = stripeWidth;\n        this.quoteGapWidth = gapWidth;\n        return this;\n    }\n\n    /**\n     * Set the span of leading margin.\n     *\n     * @param first The indent for the first line of the paragraph.\n     * @param rest  The indent for the remaining lines of the paragraph.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setLeadingMargin(@IntRange(from = 0) final int first,\n                                      @IntRange(from = 0) final int rest) {\n        this.first = first;\n        this.rest = rest;\n        return this;\n    }\n\n    /**\n     * Set the span of bullet.\n     *\n     * @param gapWidth The width of gap, in pixel.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setBullet(@IntRange(from = 0) final int gapWidth) {\n        return setBullet(0, 3, gapWidth);\n    }\n\n    /**\n     * Set the span of bullet.\n     *\n     * @param color    The color of bullet.\n     * @param radius   The radius of bullet, in pixel.\n     * @param gapWidth The width of gap, in pixel.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setBullet(@ColorInt final int color,\n                               @IntRange(from = 0) final int radius,\n                               @IntRange(from = 0) final int gapWidth) {\n        this.bulletColor = color;\n        this.bulletRadius = radius;\n        this.bulletGapWidth = gapWidth;\n        return this;\n    }\n\n    /**\n     * Set the span of font's size.\n     *\n     * @param size The size of font.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setFontSize(@IntRange(from = 0) final int size) {\n        return setFontSize(size, false);\n    }\n\n    /**\n     * Set the span of size of font.\n     *\n     * @param size The size of font.\n     * @param isSp True to use sp, false to use pixel.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setFontSize(@IntRange(from = 0) final int size, final boolean isSp) {\n        if (isSp) {\n            final float fontScale = Resources.getSystem().getDisplayMetrics().scaledDensity;\n            this.fontSize = (int) (size * fontScale + 0.5f);\n        } else {\n            this.fontSize = size;\n        }\n        return this;\n    }\n\n    /**\n     * Set the span of proportion of font.\n     *\n     * @param proportion The proportion of font.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setFontProportion(final float proportion) {\n        this.proportion = proportion;\n        return this;\n    }\n\n    /**\n     * Set the span of transverse proportion of font.\n     *\n     * @param proportion The transverse proportion of font.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setFontXProportion(final float proportion) {\n        this.xProportion = proportion;\n        return this;\n    }\n\n    /**\n     * Set the span of strikethrough.\n     *\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setStrikethrough() {\n        this.isStrikethrough = true;\n        return this;\n    }\n\n    /**\n     * Set the span of underline.\n     *\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setUnderline() {\n        this.isUnderline = true;\n        return this;\n    }\n\n    /**\n     * Set the span of superscript.\n     *\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setSuperscript() {\n        this.isSuperscript = true;\n        return this;\n    }\n\n    /**\n     * Set the span of subscript.\n     *\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setSubscript() {\n        this.isSubscript = true;\n        return this;\n    }\n\n    /**\n     * Set the span of bold.\n     *\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setBold() {\n        isBold = true;\n        return this;\n    }\n\n    /**\n     * Set the span of italic.\n     *\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setItalic() {\n        isItalic = true;\n        return this;\n    }\n\n    /**\n     * Set the span of bold italic.\n     *\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setBoldItalic() {\n        isBoldItalic = true;\n        return this;\n    }\n\n    /**\n     * Set the span of font family.\n     *\n     * @param fontFamily The font family.\n     *                   <ul>\n     *                   <li>monospace</li>\n     *                   <li>serif</li>\n     *                   <li>sans-serif</li>\n     *                   </ul>\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setFontFamily(@NonNull final String fontFamily) {\n        this.fontFamily = fontFamily;\n        return this;\n    }\n\n    /**\n     * Set the span of typeface.\n     *\n     * @param typeface The typeface.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setTypeface(@NonNull final Typeface typeface) {\n        this.typeface = typeface;\n        return this;\n    }\n\n    /**\n     * Set the span of horizontal alignment.\n     *\n     * @param alignment The alignment.\n     *                  <ul>\n     *                  <li>{@link Alignment#ALIGN_NORMAL  }</li>\n     *                  <li>{@link Alignment#ALIGN_OPPOSITE}</li>\n     *                  <li>{@link Alignment#ALIGN_CENTER  }</li>\n     *                  </ul>\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setHorizontalAlign(@NonNull final Alignment alignment) {\n        this.alignment = alignment;\n        return this;\n    }\n\n    /**\n     * Set the span of vertical alignment.\n     *\n     * @param align The alignment.\n     *              <ul>\n     *              <li>{@link Align#ALIGN_TOP     }</li>\n     *              <li>{@link Align#ALIGN_CENTER  }</li>\n     *              <li>{@link Align#ALIGN_BASELINE}</li>\n     *              <li>{@link Align#ALIGN_BOTTOM  }</li>\n     *              </ul>\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setVerticalAlign(@Align final int align) {\n        this.verticalAlign = align;\n        return this;\n    }\n\n    /**\n     * Set the span of click.\n     * <p>Must set {@code view.setMovementMethod(LinkMovementMethod.getInstance())}</p>\n     *\n     * @param clickSpan The span of click.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setClickSpan(@NonNull final ClickableSpan clickSpan) {\n        setMovementMethodIfNeed();\n        this.clickSpan = clickSpan;\n        return this;\n    }\n\n    /**\n     * Set the span of click.\n     * <p>Must set {@code view.setMovementMethod(LinkMovementMethod.getInstance())}</p>\n     *\n     * @param color         The color of click span.\n     * @param underlineText True to support underline, false otherwise.\n     * @param listener      The listener of click span.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setClickSpan(@ColorInt final int color,\n                                  final boolean underlineText,\n                                  final View.OnClickListener listener) {\n        setMovementMethodIfNeed();\n        this.clickSpan = new ClickableSpan() {\n\n            @Override\n            public void updateDrawState(@NonNull TextPaint paint) {\n                paint.setColor(color);\n                paint.setUnderlineText(underlineText);\n            }\n\n            @Override\n            public void onClick(@NonNull View widget) {\n                if (listener != null) {\n                    listener.onClick(widget);\n                }\n            }\n        };\n        return this;\n    }\n\n    /**\n     * Set the span of url.\n     * <p>Must set {@code view.setMovementMethod(LinkMovementMethod.getInstance())}</p>\n     *\n     * @param url The url.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setUrl(@NonNull final String url) {\n        setMovementMethodIfNeed();\n        this.url = url;\n        return this;\n    }\n\n    private void setMovementMethodIfNeed() {\n        if (mTextView != null && mTextView.getMovementMethod() == null) {\n            mTextView.setMovementMethod(LinkMovementMethod.getInstance());\n        }\n    }\n\n    /**\n     * Set the span of blur.\n     *\n     * @param radius The radius of blur.\n     * @param style  The style.\n     *               <ul>\n     *               <li>{@link Blur#NORMAL}</li>\n     *               <li>{@link Blur#SOLID}</li>\n     *               <li>{@link Blur#OUTER}</li>\n     *               <li>{@link Blur#INNER}</li>\n     *               </ul>\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setBlur(@FloatRange(from = 0, fromInclusive = false) final float radius,\n                             final Blur style) {\n        this.blurRadius = radius;\n        this.style = style;\n        return this;\n    }\n\n    /**\n     * Set the span of shader.\n     *\n     * @param shader The shader.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setShader(@NonNull final Shader shader) {\n        this.shader = shader;\n        return this;\n    }\n\n    /**\n     * Set the span of shadow.\n     *\n     * @param radius      The radius of shadow.\n     * @param dx          X-axis offset, in pixel.\n     * @param dy          Y-axis offset, in pixel.\n     * @param shadowColor The color of shadow.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setShadow(@FloatRange(from = 0, fromInclusive = false) final float radius,\n                               final float dx,\n                               final float dy,\n                               final int shadowColor) {\n        this.shadowRadius = radius;\n        this.shadowDx = dx;\n        this.shadowDy = dy;\n        this.shadowColor = shadowColor;\n        return this;\n    }\n\n\n    /**\n     * Set the spans.\n     *\n     * @param spans The spans.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils setSpans(@NonNull final Object... spans) {\n        if (spans.length > 0) {\n            this.spans = spans;\n        }\n        return this;\n    }\n\n    /**\n     * Append the text text.\n     *\n     * @param text The text.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils append(@NonNull final CharSequence text) {\n        apply(mTypeCharSequence);\n        mText = text;\n        return this;\n    }\n\n    /**\n     * Append one line.\n     *\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils appendLine() {\n        apply(mTypeCharSequence);\n        mText = LINE_SEPARATOR;\n        return this;\n    }\n\n    /**\n     * Append text and one line.\n     *\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils appendLine(@NonNull final CharSequence text) {\n        apply(mTypeCharSequence);\n        mText = text + LINE_SEPARATOR;\n        return this;\n    }\n\n    /**\n     * Append one image.\n     *\n     * @param bitmap The bitmap of image.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils appendImage(@NonNull final Bitmap bitmap) {\n        return appendImage(bitmap, ALIGN_BOTTOM);\n    }\n\n    /**\n     * Append one image.\n     *\n     * @param bitmap The bitmap.\n     * @param align  The alignment.\n     *               <ul>\n     *               <li>{@link Align#ALIGN_TOP     }</li>\n     *               <li>{@link Align#ALIGN_CENTER  }</li>\n     *               <li>{@link Align#ALIGN_BASELINE}</li>\n     *               <li>{@link Align#ALIGN_BOTTOM  }</li>\n     *               </ul>\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils appendImage(@NonNull final Bitmap bitmap, @Align final int align) {\n        apply(mTypeImage);\n        this.imageBitmap = bitmap;\n        this.alignImage = align;\n        return this;\n    }\n\n    /**\n     * Append one image.\n     *\n     * @param drawable The drawable of image.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils appendImage(@NonNull final Drawable drawable) {\n        return appendImage(drawable, ALIGN_BOTTOM);\n    }\n\n    /**\n     * Append one image.\n     *\n     * @param drawable The drawable of image.\n     * @param align    The alignment.\n     *                 <ul>\n     *                 <li>{@link Align#ALIGN_TOP     }</li>\n     *                 <li>{@link Align#ALIGN_CENTER  }</li>\n     *                 <li>{@link Align#ALIGN_BASELINE}</li>\n     *                 <li>{@link Align#ALIGN_BOTTOM  }</li>\n     *                 </ul>\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils appendImage(@NonNull final Drawable drawable, @Align final int align) {\n        apply(mTypeImage);\n        this.imageDrawable = drawable;\n        this.alignImage = align;\n        return this;\n    }\n\n    /**\n     * Append one image.\n     *\n     * @param uri The uri of image.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils appendImage(@NonNull final Uri uri) {\n        return appendImage(uri, ALIGN_BOTTOM);\n    }\n\n    /**\n     * Append one image.\n     *\n     * @param uri   The uri of image.\n     * @param align The alignment.\n     *              <ul>\n     *              <li>{@link Align#ALIGN_TOP     }</li>\n     *              <li>{@link Align#ALIGN_CENTER  }</li>\n     *              <li>{@link Align#ALIGN_BASELINE}</li>\n     *              <li>{@link Align#ALIGN_BOTTOM  }</li>\n     *              </ul>\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils appendImage(@NonNull final Uri uri, @Align final int align) {\n        apply(mTypeImage);\n        this.imageUri = uri;\n        this.alignImage = align;\n        return this;\n    }\n\n    /**\n     * Append one image.\n     *\n     * @param resourceId The resource id of image.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils appendImage(@DrawableRes final int resourceId) {\n        return appendImage(resourceId, ALIGN_BOTTOM);\n    }\n\n    /**\n     * Append one image.\n     *\n     * @param resourceId The resource id of image.\n     * @param align      The alignment.\n     *                   <ul>\n     *                   <li>{@link Align#ALIGN_TOP     }</li>\n     *                   <li>{@link Align#ALIGN_CENTER  }</li>\n     *                   <li>{@link Align#ALIGN_BASELINE}</li>\n     *                   <li>{@link Align#ALIGN_BOTTOM  }</li>\n     *                   </ul>\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils appendImage(@DrawableRes final int resourceId, @Align final int align) {\n        apply(mTypeImage);\n        this.imageResourceId = resourceId;\n        this.alignImage = align;\n        return this;\n    }\n\n    /**\n     * Append space.\n     *\n     * @param size The size of space.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils appendSpace(@IntRange(from = 0) final int size) {\n        return appendSpace(size, Color.TRANSPARENT);\n    }\n\n    /**\n     * Append space.\n     *\n     * @param size  The size of space.\n     * @param color The color of space.\n     * @return the single {@link SpanUtils} instance\n     */\n    public SpanUtils appendSpace(@IntRange(from = 0) final int size, @ColorInt final int color) {\n        apply(mTypeSpace);\n        spaceSize = size;\n        spaceColor = color;\n        return this;\n    }\n\n    private void apply(final int type) {\n        applyLast();\n        mType = type;\n    }\n\n    public SpannableStringBuilder get() {\n        return mBuilder;\n    }\n\n    /**\n     * Create the span string.\n     *\n     * @return the span string\n     */\n    public SpannableStringBuilder create() {\n        applyLast();\n        if (mTextView != null) {\n            mTextView.setText(mBuilder);\n        }\n        isCreated = true;\n        return mBuilder;\n    }\n\n    private void applyLast() {\n        if (isCreated) {\n            return;\n        }\n        if (mType == mTypeCharSequence) {\n            updateCharCharSequence();\n        } else if (mType == mTypeImage) {\n            updateImage();\n        } else if (mType == mTypeSpace) {\n            updateSpace();\n        }\n        setDefault();\n    }\n\n    private void updateCharCharSequence() {\n        if (mText.length() == 0) return;\n        int start = mBuilder.length();\n        if (start == 0 && lineHeight != -1) {// bug of LineHeightSpan when first line\n            mBuilder.append(Character.toString((char) 2))\n                    .append(\"\\n\")\n                    .setSpan(new AbsoluteSizeSpan(0), 0, 2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);\n            start = 2;\n        }\n        mBuilder.append(mText);\n        int end = mBuilder.length();\n        if (verticalAlign != -1) {\n            mBuilder.setSpan(new VerticalAlignSpan(verticalAlign), start, end, flag);\n        }\n        if (foregroundColor != COLOR_DEFAULT) {\n            mBuilder.setSpan(new ForegroundColorSpan(foregroundColor), start, end, flag);\n        }\n        if (backgroundColor != COLOR_DEFAULT) {\n            mBuilder.setSpan(new BackgroundColorSpan(backgroundColor), start, end, flag);\n        }\n        if (first != -1) {\n            mBuilder.setSpan(new LeadingMarginSpan.Standard(first, rest), start, end, flag);\n        }\n        if (quoteColor != COLOR_DEFAULT) {\n            mBuilder.setSpan(\n                    new CustomQuoteSpan(quoteColor, stripeWidth, quoteGapWidth),\n                    start,\n                    end,\n                    flag\n            );\n        }\n        if (bulletColor != COLOR_DEFAULT) {\n            mBuilder.setSpan(\n                    new CustomBulletSpan(bulletColor, bulletRadius, bulletGapWidth),\n                    start,\n                    end,\n                    flag\n            );\n        }\n        if (fontSize != -1) {\n            mBuilder.setSpan(new AbsoluteSizeSpan(fontSize, false), start, end, flag);\n        }\n        if (proportion != -1) {\n            mBuilder.setSpan(new RelativeSizeSpan(proportion), start, end, flag);\n        }\n        if (xProportion != -1) {\n            mBuilder.setSpan(new ScaleXSpan(xProportion), start, end, flag);\n        }\n        if (lineHeight != -1) {\n            mBuilder.setSpan(new CustomLineHeightSpan(lineHeight, alignLine), start, end, flag);\n        }\n        if (isStrikethrough) {\n            mBuilder.setSpan(new StrikethroughSpan(), start, end, flag);\n        }\n        if (isUnderline) {\n            mBuilder.setSpan(new UnderlineSpan(), start, end, flag);\n        }\n        if (isSuperscript) {\n            mBuilder.setSpan(new SuperscriptSpan(), start, end, flag);\n        }\n        if (isSubscript) {\n            mBuilder.setSpan(new SubscriptSpan(), start, end, flag);\n        }\n        if (isBold) {\n            mBuilder.setSpan(new StyleSpan(Typeface.BOLD), start, end, flag);\n        }\n        if (isItalic) {\n            mBuilder.setSpan(new StyleSpan(Typeface.ITALIC), start, end, flag);\n        }\n        if (isBoldItalic) {\n            mBuilder.setSpan(new StyleSpan(Typeface.BOLD_ITALIC), start, end, flag);\n        }\n        if (fontFamily != null) {\n            mBuilder.setSpan(new TypefaceSpan(fontFamily), start, end, flag);\n        }\n        if (typeface != null) {\n            mBuilder.setSpan(new CustomTypefaceSpan(typeface), start, end, flag);\n        }\n        if (alignment != null) {\n            mBuilder.setSpan(new AlignmentSpan.Standard(alignment), start, end, flag);\n        }\n        if (clickSpan != null) {\n            mBuilder.setSpan(clickSpan, start, end, flag);\n        }\n        if (url != null) {\n            mBuilder.setSpan(new URLSpan(url), start, end, flag);\n        }\n        if (blurRadius != -1) {\n            mBuilder.setSpan(\n                    new MaskFilterSpan(new BlurMaskFilter(blurRadius, style)),\n                    start,\n                    end,\n                    flag\n            );\n        }\n        if (shader != null) {\n            mBuilder.setSpan(new ShaderSpan(shader), start, end, flag);\n        }\n        if (shadowRadius != -1) {\n            mBuilder.setSpan(\n                    new ShadowSpan(shadowRadius, shadowDx, shadowDy, shadowColor),\n                    start,\n                    end,\n                    flag\n            );\n        }\n        if (spans != null) {\n            for (Object span : spans) {\n                mBuilder.setSpan(span, start, end, flag);\n            }\n        }\n    }\n\n    private void updateImage() {\n        int start = mBuilder.length();\n        mText = \"<img>\";\n        updateCharCharSequence();\n        int end = mBuilder.length();\n        if (imageBitmap != null) {\n            mBuilder.setSpan(new CustomImageSpan(imageBitmap, alignImage), start, end, flag);\n        } else if (imageDrawable != null) {\n            mBuilder.setSpan(new CustomImageSpan(imageDrawable, alignImage), start, end, flag);\n        } else if (imageUri != null) {\n            mBuilder.setSpan(new CustomImageSpan(imageUri, alignImage), start, end, flag);\n        } else if (imageResourceId != -1) {\n            mBuilder.setSpan(new CustomImageSpan(imageResourceId, alignImage), start, end, flag);\n        }\n    }\n\n    private void updateSpace() {\n        int start = mBuilder.length();\n        mText = \"< >\";\n        updateCharCharSequence();\n        int end = mBuilder.length();\n        mBuilder.setSpan(new SpaceSpan(spaceSize, spaceColor), start, end, flag);\n    }\n\n    static class VerticalAlignSpan extends ReplacementSpan {\n\n        static final int ALIGN_CENTER = 2;\n        static final int ALIGN_TOP    = 3;\n\n        final int mVerticalAlignment;\n\n        VerticalAlignSpan(int verticalAlignment) {\n            mVerticalAlignment = verticalAlignment;\n        }\n\n        @Override\n        public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, @Nullable Paint.FontMetricsInt fm) {\n            text = text.subSequence(start, end);\n            return (int) paint.measureText(text.toString());\n        }\n\n        @Override\n        public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) {\n            text = text.subSequence(start, end);\n            Paint.FontMetricsInt fm = paint.getFontMetricsInt();\n//            int need = height - (v + fm.descent - fm.ascent - spanstartv);\n//            if (need > 0) {\n//                if (mVerticalAlignment == ALIGN_TOP) {\n//                    fm.descent += need;\n//                } else if (mVerticalAlignment == ALIGN_CENTER) {\n//                    fm.descent += need / 2;\n//                    fm.ascent -= need / 2;\n//                } else {\n//                    fm.ascent -= need;\n//                }\n//            }\n//            need = height - (v + fm.bottom - fm.top - spanstartv);\n//            if (need > 0) {\n//                if (mVerticalAlignment == ALIGN_TOP) {\n//                    fm.bottom += need;\n//                } else if (mVerticalAlignment == ALIGN_CENTER) {\n//                    fm.bottom += need / 2;\n//                    fm.top -= need / 2;\n//                } else {\n//                    fm.top -= need;\n//                }\n//            }\n\n            canvas.drawText(text.toString(), x, y - ((y + fm.descent + y + fm.ascent) / 2 - (bottom + top) / 2), paint);\n        }\n    }\n\n    static class CustomLineHeightSpan implements LineHeightSpan {\n\n        private final int height;\n\n        static final int ALIGN_CENTER = 2;\n        static final int ALIGN_TOP    = 3;\n\n        final  int                  mVerticalAlignment;\n        static Paint.FontMetricsInt sfm;\n\n        CustomLineHeightSpan(int height, int verticalAlignment) {\n            this.height = height;\n            mVerticalAlignment = verticalAlignment;\n        }\n\n        @Override\n        public void chooseHeight(final CharSequence text, final int start, final int end,\n                                 final int spanstartv, final int v, final Paint.FontMetricsInt fm) {\n//            LogUtils.e(fm, sfm);\n            if (sfm == null) {\n                sfm = new Paint.FontMetricsInt();\n                sfm.top = fm.top;\n                sfm.ascent = fm.ascent;\n                sfm.descent = fm.descent;\n                sfm.bottom = fm.bottom;\n                sfm.leading = fm.leading;\n            } else {\n                fm.top = sfm.top;\n                fm.ascent = sfm.ascent;\n                fm.descent = sfm.descent;\n                fm.bottom = sfm.bottom;\n                fm.leading = sfm.leading;\n            }\n            int need = height - (v + fm.descent - fm.ascent - spanstartv);\n            if (need > 0) {\n                if (mVerticalAlignment == ALIGN_TOP) {\n                    fm.descent += need;\n                } else if (mVerticalAlignment == ALIGN_CENTER) {\n                    fm.descent += need / 2;\n                    fm.ascent -= need / 2;\n                } else {\n                    fm.ascent -= need;\n                }\n            }\n            need = height - (v + fm.bottom - fm.top - spanstartv);\n            if (need > 0) {\n                if (mVerticalAlignment == ALIGN_TOP) {\n                    fm.bottom += need;\n                } else if (mVerticalAlignment == ALIGN_CENTER) {\n                    fm.bottom += need / 2;\n                    fm.top -= need / 2;\n                } else {\n                    fm.top -= need;\n                }\n            }\n            if (end == ((Spanned) text).getSpanEnd(this)) {\n                sfm = null;\n            }\n//            LogUtils.e(fm, sfm);\n        }\n    }\n\n    static class SpaceSpan extends ReplacementSpan {\n\n        private final int   width;\n        private final Paint paint = new Paint();\n\n        private SpaceSpan(final int width) {\n            this(width, Color.TRANSPARENT);\n        }\n\n        private SpaceSpan(final int width, final int color) {\n            super();\n            this.width = width;\n            paint.setColor(color);\n            paint.setStyle(Paint.Style.FILL);\n        }\n\n        @Override\n        public int getSize(@NonNull final Paint paint, final CharSequence text,\n                           @IntRange(from = 0) final int start,\n                           @IntRange(from = 0) final int end,\n                           @Nullable final Paint.FontMetricsInt fm) {\n            return width;\n        }\n\n        @Override\n        public void draw(@NonNull final Canvas canvas, final CharSequence text,\n                         @IntRange(from = 0) final int start,\n                         @IntRange(from = 0) final int end,\n                         final float x, final int top, final int y, final int bottom,\n                         @NonNull final Paint paint) {\n            canvas.drawRect(x, top, x + width, bottom, this.paint);\n        }\n    }\n\n    static class CustomQuoteSpan implements LeadingMarginSpan {\n\n        private final int color;\n        private final int stripeWidth;\n        private final int gapWidth;\n\n        private CustomQuoteSpan(final int color, final int stripeWidth, final int gapWidth) {\n            super();\n            this.color = color;\n            this.stripeWidth = stripeWidth;\n            this.gapWidth = gapWidth;\n        }\n\n        public int getLeadingMargin(final boolean first) {\n            return stripeWidth + gapWidth;\n        }\n\n        public void drawLeadingMargin(final Canvas c, final Paint p, final int x, final int dir,\n                                      final int top, final int baseline, final int bottom,\n                                      final CharSequence text, final int start, final int end,\n                                      final boolean first, final Layout layout) {\n            Paint.Style style = p.getStyle();\n            int color = p.getColor();\n\n            p.setStyle(Paint.Style.FILL);\n            p.setColor(this.color);\n\n            c.drawRect(x, top, x + dir * stripeWidth, bottom, p);\n\n            p.setStyle(style);\n            p.setColor(color);\n        }\n    }\n\n    static class CustomBulletSpan implements LeadingMarginSpan {\n\n        private final int color;\n        private final int radius;\n        private final int gapWidth;\n\n        private Path sBulletPath = null;\n\n        private CustomBulletSpan(final int color, final int radius, final int gapWidth) {\n            this.color = color;\n            this.radius = radius;\n            this.gapWidth = gapWidth;\n        }\n\n        public int getLeadingMargin(final boolean first) {\n            return 2 * radius + gapWidth;\n        }\n\n        public void drawLeadingMargin(final Canvas c, final Paint p, final int x, final int dir,\n                                      final int top, final int baseline, final int bottom,\n                                      final CharSequence text, final int start, final int end,\n                                      final boolean first, final Layout l) {\n            if (((Spanned) text).getSpanStart(this) == start) {\n                Paint.Style style = p.getStyle();\n                int oldColor = 0;\n                oldColor = p.getColor();\n                p.setColor(color);\n                p.setStyle(Paint.Style.FILL);\n                if (c.isHardwareAccelerated()) {\n                    if (sBulletPath == null) {\n                        sBulletPath = new Path();\n                        // Bullet is slightly better to avoid aliasing artifacts on mdpi devices.\n                        sBulletPath.addCircle(0.0f, 0.0f, radius, Path.Direction.CW);\n                    }\n                    c.save();\n                    c.translate(x + dir * radius, (top + bottom) / 2.0f);\n                    c.drawPath(sBulletPath, p);\n                    c.restore();\n                } else {\n                    c.drawCircle(x + dir * radius, (top + bottom) / 2.0f, radius, p);\n                }\n                p.setColor(oldColor);\n                p.setStyle(style);\n            }\n        }\n    }\n\n    @SuppressLint(\"ParcelCreator\")\n    static class CustomTypefaceSpan extends TypefaceSpan {\n\n        private final Typeface newType;\n\n        private CustomTypefaceSpan(final Typeface type) {\n            super(\"\");\n            newType = type;\n        }\n\n        @Override\n        public void updateDrawState(final TextPaint textPaint) {\n            apply(textPaint, newType);\n        }\n\n        @Override\n        public void updateMeasureState(final TextPaint paint) {\n            apply(paint, newType);\n        }\n\n        private void apply(final Paint paint, final Typeface tf) {\n            int oldStyle;\n            Typeface old = paint.getTypeface();\n            if (old == null) {\n                oldStyle = 0;\n            } else {\n                oldStyle = old.getStyle();\n            }\n\n            int fake = oldStyle & ~tf.getStyle();\n            if ((fake & Typeface.BOLD) != 0) {\n                paint.setFakeBoldText(true);\n            }\n\n            if ((fake & Typeface.ITALIC) != 0) {\n                paint.setTextSkewX(-0.25f);\n            }\n\n            paint.getShader();\n\n            paint.setTypeface(tf);\n        }\n    }\n\n    static class CustomImageSpan extends CustomDynamicDrawableSpan {\n        private Drawable mDrawable;\n        private Uri      mContentUri;\n        private int      mResourceId;\n\n        private CustomImageSpan(final Bitmap b, final int verticalAlignment) {\n            super(verticalAlignment);\n            mDrawable = new BitmapDrawable(Utils.getApp().getResources(), b);\n            mDrawable.setBounds(\n                    0, 0, mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight()\n            );\n        }\n\n        private CustomImageSpan(final Drawable d, final int verticalAlignment) {\n            super(verticalAlignment);\n            mDrawable = d;\n            mDrawable.setBounds(\n                    0, 0, mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight()\n            );\n        }\n\n        private CustomImageSpan(final Uri uri, final int verticalAlignment) {\n            super(verticalAlignment);\n            mContentUri = uri;\n        }\n\n        private CustomImageSpan(@DrawableRes final int resourceId, final int verticalAlignment) {\n            super(verticalAlignment);\n            mResourceId = resourceId;\n        }\n\n        @Override\n        public Drawable getDrawable() {\n            Drawable drawable = null;\n            if (mDrawable != null) {\n                drawable = mDrawable;\n            } else if (mContentUri != null) {\n                Bitmap bitmap;\n                try {\n                    InputStream is =\n                            Utils.getApp().getContentResolver().openInputStream(mContentUri);\n                    bitmap = BitmapFactory.decodeStream(is);\n                    drawable = new BitmapDrawable(Utils.getApp().getResources(), bitmap);\n                    drawable.setBounds(\n                            0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()\n                    );\n                    if (is != null) {\n                        is.close();\n                    }\n                } catch (Exception e) {\n                    Log.e(\"sms\", \"Failed to loaded content \" + mContentUri, e);\n                }\n            } else {\n                try {\n                    drawable = ContextCompat.getDrawable(Utils.getApp(), mResourceId);\n                    drawable.setBounds(\n                            0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()\n                    );\n                } catch (Exception e) {\n                    Log.e(\"sms\", \"Unable to find resource: \" + mResourceId);\n                }\n            }\n            return drawable;\n        }\n    }\n\n    static abstract class CustomDynamicDrawableSpan extends ReplacementSpan {\n\n        static final int ALIGN_BOTTOM = 0;\n\n        static final int ALIGN_BASELINE = 1;\n\n        static final int ALIGN_CENTER = 2;\n\n        static final int ALIGN_TOP = 3;\n\n        final int mVerticalAlignment;\n\n        private CustomDynamicDrawableSpan() {\n            mVerticalAlignment = ALIGN_BOTTOM;\n        }\n\n        private CustomDynamicDrawableSpan(final int verticalAlignment) {\n            mVerticalAlignment = verticalAlignment;\n        }\n\n        public abstract Drawable getDrawable();\n\n        @Override\n        public int getSize(@NonNull final Paint paint, final CharSequence text,\n                           final int start, final int end, final Paint.FontMetricsInt fm) {\n            Drawable d = getCachedDrawable();\n            Rect rect = d.getBounds();\n            if (fm != null) {\n//                LogUtils.d(\"fm.top: \" + fm.top,\n//                        \"fm.ascent: \" + fm.ascent,\n//                        \"fm.descent: \" + fm.descent,\n//                        \"fm.bottom: \" + fm.bottom,\n//                        \"lineHeight: \" + (fm.bottom - fm.top));\n                int lineHeight = fm.bottom - fm.top;\n                if (lineHeight < rect.height()) {\n                    if (mVerticalAlignment == ALIGN_TOP) {\n                        fm.top = fm.top;\n                        fm.bottom = rect.height() + fm.top;\n                    } else if (mVerticalAlignment == ALIGN_CENTER) {\n                        fm.top = -rect.height() / 2 - lineHeight / 4;\n                        fm.bottom = rect.height() / 2 - lineHeight / 4;\n                    } else {\n                        fm.top = -rect.height() + fm.bottom;\n                        fm.bottom = fm.bottom;\n                    }\n                    fm.ascent = fm.top;\n                    fm.descent = fm.bottom;\n                }\n            }\n            return rect.right;\n        }\n\n        @Override\n        public void draw(@NonNull final Canvas canvas, final CharSequence text,\n                         final int start, final int end, final float x,\n                         final int top, final int y, final int bottom, @NonNull final Paint paint) {\n            Drawable d = getCachedDrawable();\n            Rect rect = d.getBounds();\n            canvas.save();\n            float transY;\n            int lineHeight = bottom - top;\n//            LogUtils.d(\"rectHeight: \" + rect.height(),\n//                    \"lineHeight: \" + (bottom - top));\n            if (rect.height() < lineHeight) {\n                if (mVerticalAlignment == ALIGN_TOP) {\n                    transY = top;\n                } else if (mVerticalAlignment == ALIGN_CENTER) {\n                    transY = (bottom + top - rect.height()) / 2;\n                } else if (mVerticalAlignment == ALIGN_BASELINE) {\n                    transY = y - rect.height();\n                } else {\n                    transY = bottom - rect.height();\n                }\n                canvas.translate(x, transY);\n            } else {\n                canvas.translate(x, top);\n            }\n            d.draw(canvas);\n            canvas.restore();\n        }\n\n        private Drawable getCachedDrawable() {\n            WeakReference<Drawable> wr = mDrawableRef;\n            Drawable d = null;\n            if (wr != null) {\n                d = wr.get();\n            }\n            if (d == null) {\n                d = getDrawable();\n                mDrawableRef = new WeakReference<>(d);\n            }\n            return d;\n        }\n\n        private WeakReference<Drawable> mDrawableRef;\n    }\n\n    static class ShaderSpan extends CharacterStyle implements UpdateAppearance {\n        private Shader mShader;\n\n        private ShaderSpan(final Shader shader) {\n            this.mShader = shader;\n        }\n\n        @Override\n        public void updateDrawState(final TextPaint tp) {\n            tp.setShader(mShader);\n        }\n    }\n\n    static class ShadowSpan extends CharacterStyle implements UpdateAppearance {\n        private float radius;\n        private float dx, dy;\n        private int shadowColor;\n\n        private ShadowSpan(final float radius,\n                           final float dx,\n                           final float dy,\n                           final int shadowColor) {\n            this.radius = radius;\n            this.dx = dx;\n            this.dy = dy;\n            this.shadowColor = shadowColor;\n        }\n\n        @Override\n        public void updateDrawState(final TextPaint tp) {\n            tp.setShadowLayer(radius, dx, dy, shadowColor);\n        }\n    }\n\n    private static class SerializableSpannableStringBuilder extends SpannableStringBuilder\n            implements Serializable {\n\n        private static final long serialVersionUID = 4909567650765875771L;\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/StringUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.content.res.Resources;\nimport androidx.annotation.ArrayRes;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.StringRes;\n\nimport java.util.IllegalFormatException;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/16\n *     desc  : utils about string\n * </pre>\n */\npublic final class StringUtils {\n\n    private StringUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Return whether the string is null or 0-length.\n     *\n     * @param s The string.\n     * @return {@code true}: yes<br> {@code false}: no\n     */\n    public static boolean isEmpty(final CharSequence s) {\n        return s == null || s.length() == 0;\n    }\n\n    /**\n     * Return whether the string is null or whitespace.\n     *\n     * @param s The string.\n     * @return {@code true}: yes<br> {@code false}: no\n     */\n    public static boolean isTrimEmpty(final String s) {\n        return (s == null || s.trim().length() == 0);\n    }\n\n    /**\n     * Return whether the string is null or white space.\n     *\n     * @param s The string.\n     * @return {@code true}: yes<br> {@code false}: no\n     */\n    public static boolean isSpace(final String s) {\n        if (s == null) return true;\n        for (int i = 0, len = s.length(); i < len; ++i) {\n            if (!Character.isWhitespace(s.charAt(i))) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Return whether string1 is equals to string2.\n     *\n     * @param s1 The first string.\n     * @param s2 The second string.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean equals(final CharSequence s1, final CharSequence s2) {\n        if (s1 == s2) return true;\n        int length;\n        if (s1 != null && s2 != null && (length = s1.length()) == s2.length()) {\n            if (s1 instanceof String && s2 instanceof String) {\n                return s1.equals(s2);\n            } else {\n                for (int i = 0; i < length; i++) {\n                    if (s1.charAt(i) != s2.charAt(i)) return false;\n                }\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Return whether string1 is equals to string2, ignoring case considerations..\n     *\n     * @param s1 The first string.\n     * @param s2 The second string.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean equalsIgnoreCase(final String s1, final String s2) {\n        return s1 == null ? s2 == null : s1.equalsIgnoreCase(s2);\n    }\n\n    /**\n     * Return {@code \"\"} if string equals null.\n     *\n     * @param s The string.\n     * @return {@code \"\"} if string equals null\n     */\n    public static String null2Length0(final String s) {\n        return s == null ? \"\" : s;\n    }\n\n    /**\n     * Return the length of string.\n     *\n     * @param s The string.\n     * @return the length of string\n     */\n    public static int length(final CharSequence s) {\n        return s == null ? 0 : s.length();\n    }\n\n    /**\n     * Set the first letter of string upper.\n     *\n     * @param s The string.\n     * @return the string with first letter upper.\n     */\n    public static String upperFirstLetter(final String s) {\n        if (s == null || s.length() == 0) return \"\";\n        if (!Character.isLowerCase(s.charAt(0))) return s;\n        return (char) (s.charAt(0) - 32) + s.substring(1);\n    }\n\n    /**\n     * Set the first letter of string lower.\n     *\n     * @param s The string.\n     * @return the string with first letter lower.\n     */\n    public static String lowerFirstLetter(final String s) {\n        if (s == null || s.length() == 0) return \"\";\n        if (!Character.isUpperCase(s.charAt(0))) return s;\n        return String.valueOf((char) (s.charAt(0) + 32)) + s.substring(1);\n    }\n\n    /**\n     * Reverse the string.\n     *\n     * @param s The string.\n     * @return the reverse string.\n     */\n    public static String reverse(final String s) {\n        if (s == null) return \"\";\n        int len = s.length();\n        if (len <= 1) return s;\n        int mid = len >> 1;\n        char[] chars = s.toCharArray();\n        char c;\n        for (int i = 0; i < mid; ++i) {\n            c = chars[i];\n            chars[i] = chars[len - i - 1];\n            chars[len - i - 1] = c;\n        }\n        return new String(chars);\n    }\n\n    /**\n     * Convert string to DBC.\n     *\n     * @param s The string.\n     * @return the DBC string\n     */\n    public static String toDBC(final String s) {\n        if (s == null || s.length() == 0) return \"\";\n        char[] chars = s.toCharArray();\n        for (int i = 0, len = chars.length; i < len; i++) {\n            if (chars[i] == 12288) {\n                chars[i] = ' ';\n            } else if (65281 <= chars[i] && chars[i] <= 65374) {\n                chars[i] = (char) (chars[i] - 65248);\n            } else {\n                chars[i] = chars[i];\n            }\n        }\n        return new String(chars);\n    }\n\n    /**\n     * Convert string to SBC.\n     *\n     * @param s The string.\n     * @return the SBC string\n     */\n    public static String toSBC(final String s) {\n        if (s == null || s.length() == 0) return \"\";\n        char[] chars = s.toCharArray();\n        for (int i = 0, len = chars.length; i < len; i++) {\n            if (chars[i] == ' ') {\n                chars[i] = (char) 12288;\n            } else if (33 <= chars[i] && chars[i] <= 126) {\n                chars[i] = (char) (chars[i] + 65248);\n            } else {\n                chars[i] = chars[i];\n            }\n        }\n        return new String(chars);\n    }\n\n    /**\n     * Return the string value associated with a particular resource ID.\n     *\n     * @param id The desired resource identifier.\n     * @return the string value associated with a particular resource ID.\n     */\n    public static String getString(@StringRes int id) {\n        return getString(id, (Object[]) null);\n    }\n\n    /**\n     * Return the string value associated with a particular resource ID.\n     *\n     * @param id         The desired resource identifier.\n     * @param formatArgs The format arguments that will be used for substitution.\n     * @return the string value associated with a particular resource ID.\n     */\n    public static String getString(@StringRes int id, Object... formatArgs) {\n        try {\n            return format(Utils.getApp().getString(id), formatArgs);\n        } catch (Resources.NotFoundException e) {\n            e.printStackTrace();\n            return String.valueOf(id);\n        }\n    }\n\n    /**\n     * Return the string array associated with a particular resource ID.\n     *\n     * @param id The desired resource identifier.\n     * @return The string array associated with the resource.\n     */\n    public static String[] getStringArray(@ArrayRes int id) {\n        try {\n            return Utils.getApp().getResources().getStringArray(id);\n        } catch (Resources.NotFoundException e) {\n            e.printStackTrace();\n            return new String[]{String.valueOf(id)};\n        }\n    }\n\n    /**\n     * Format the string.\n     *\n     * @param str  The string.\n     * @param args The args.\n     * @return a formatted string.\n     */\n    public static String format(@Nullable String str, Object... args) {\n        String text = str;\n        if (text != null) {\n            if (args != null && args.length > 0) {\n                try {\n                    text = String.format(str, args);\n                } catch (IllegalFormatException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n        return text;\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ThreadUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.util.Log;\n\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Timer;\nimport java.util.TimerTask;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.RejectedExecutionException;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicBoolean;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.concurrent.atomic.AtomicLong;\n\nimport androidx.annotation.CallSuper;\nimport androidx.annotation.IntRange;\nimport androidx.annotation.NonNull;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/05/08\n *     desc  : utils about thread\n * </pre>\n */\npublic final class ThreadUtils {\n\n    private static final Handler HANDLER = new Handler(Looper.getMainLooper());\n\n    private static final Map<Integer, Map<Integer, ExecutorService>> TYPE_PRIORITY_POOLS = new HashMap<>();\n\n    private static final Map<Task, ExecutorService> TASK_POOL_MAP = new ConcurrentHashMap<>();\n\n    private static final int   CPU_COUNT = Runtime.getRuntime().availableProcessors();\n    private static final Timer TIMER     = new Timer();\n\n    private static final byte TYPE_SINGLE = -1;\n    private static final byte TYPE_CACHED = -2;\n    private static final byte TYPE_IO     = -4;\n    private static final byte TYPE_CPU    = -8;\n\n    private static Executor sDeliver;\n\n    /**\n     * Return whether the thread is the main thread.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isMainThread() {\n        return Looper.myLooper() == Looper.getMainLooper();\n    }\n\n    public static Handler getMainHandler() {\n        return HANDLER;\n    }\n\n    public static void runOnUiThread(final Runnable runnable) {\n        if (Looper.myLooper() == Looper.getMainLooper()) {\n            runnable.run();\n        } else {\n            HANDLER.post(runnable);\n        }\n    }\n\n    public static void runOnUiThreadDelayed(final Runnable runnable, long delayMillis) {\n        HANDLER.postDelayed(runnable, delayMillis);\n    }\n\n    /**\n     * Return a thread pool that reuses a fixed number of threads\n     * operating off a shared unbounded queue, using the provided\n     * ThreadFactory to create new threads when needed.\n     *\n     * @param size The size of thread in the pool.\n     * @return a fixed thread pool\n     */\n    public static ExecutorService getFixedPool(@IntRange(from = 1) final int size) {\n        return getPoolByTypeAndPriority(size);\n    }\n\n    /**\n     * Return a thread pool that reuses a fixed number of threads\n     * operating off a shared unbounded queue, using the provided\n     * ThreadFactory to create new threads when needed.\n     *\n     * @param size     The size of thread in the pool.\n     * @param priority The priority of thread in the poll.\n     * @return a fixed thread pool\n     */\n    public static ExecutorService getFixedPool(@IntRange(from = 1) final int size,\n                                               @IntRange(from = 1, to = 10) final int priority) {\n        return getPoolByTypeAndPriority(size, priority);\n    }\n\n    /**\n     * Return a thread pool that uses a single worker thread operating\n     * off an unbounded queue, and uses the provided ThreadFactory to\n     * create a new thread when needed.\n     *\n     * @return a single thread pool\n     */\n    public static ExecutorService getSinglePool() {\n        return getPoolByTypeAndPriority(TYPE_SINGLE);\n    }\n\n    /**\n     * Return a thread pool that uses a single worker thread operating\n     * off an unbounded queue, and uses the provided ThreadFactory to\n     * create a new thread when needed.\n     *\n     * @param priority The priority of thread in the poll.\n     * @return a single thread pool\n     */\n    public static ExecutorService getSinglePool(@IntRange(from = 1, to = 10) final int priority) {\n        return getPoolByTypeAndPriority(TYPE_SINGLE, priority);\n    }\n\n    /**\n     * Return a thread pool that creates new threads as needed, but\n     * will reuse previously constructed threads when they are\n     * available.\n     *\n     * @return a cached thread pool\n     */\n    public static ExecutorService getCachedPool() {\n        return getPoolByTypeAndPriority(TYPE_CACHED);\n    }\n\n    /**\n     * Return a thread pool that creates new threads as needed, but\n     * will reuse previously constructed threads when they are\n     * available.\n     *\n     * @param priority The priority of thread in the poll.\n     * @return a cached thread pool\n     */\n    public static ExecutorService getCachedPool(@IntRange(from = 1, to = 10) final int priority) {\n        return getPoolByTypeAndPriority(TYPE_CACHED, priority);\n    }\n\n    /**\n     * Return a thread pool that creates (2 * CPU_COUNT + 1) threads\n     * operating off a queue which size is 128.\n     *\n     * @return a IO thread pool\n     */\n    public static ExecutorService getIoPool() {\n        return getPoolByTypeAndPriority(TYPE_IO);\n    }\n\n    /**\n     * Return a thread pool that creates (2 * CPU_COUNT + 1) threads\n     * operating off a queue which size is 128.\n     *\n     * @param priority The priority of thread in the poll.\n     * @return a IO thread pool\n     */\n    public static ExecutorService getIoPool(@IntRange(from = 1, to = 10) final int priority) {\n        return getPoolByTypeAndPriority(TYPE_IO, priority);\n    }\n\n    /**\n     * Return a thread pool that creates (CPU_COUNT + 1) threads\n     * operating off a queue which size is 128 and the maximum\n     * number of threads equals (2 * CPU_COUNT + 1).\n     *\n     * @return a cpu thread pool for\n     */\n    public static ExecutorService getCpuPool() {\n        return getPoolByTypeAndPriority(TYPE_CPU);\n    }\n\n    /**\n     * Return a thread pool that creates (CPU_COUNT + 1) threads\n     * operating off a queue which size is 128 and the maximum\n     * number of threads equals (2 * CPU_COUNT + 1).\n     *\n     * @param priority The priority of thread in the poll.\n     * @return a cpu thread pool for\n     */\n    public static ExecutorService getCpuPool(@IntRange(from = 1, to = 10) final int priority) {\n        return getPoolByTypeAndPriority(TYPE_CPU, priority);\n    }\n\n    /**\n     * Executes the given task in a fixed thread pool.\n     *\n     * @param size The size of thread in the fixed thread pool.\n     * @param task The task to execute.\n     * @param <T>  The type of the task's result.\n     */\n    public static <T> void executeByFixed(@IntRange(from = 1) final int size, final Task<T> task) {\n        execute(getPoolByTypeAndPriority(size), task);\n    }\n\n    /**\n     * Executes the given task in a fixed thread pool.\n     *\n     * @param size     The size of thread in the fixed thread pool.\n     * @param task     The task to execute.\n     * @param priority The priority of thread in the poll.\n     * @param <T>      The type of the task's result.\n     */\n    public static <T> void executeByFixed(@IntRange(from = 1) final int size,\n                                          final Task<T> task,\n                                          @IntRange(from = 1, to = 10) final int priority) {\n        execute(getPoolByTypeAndPriority(size, priority), task);\n    }\n\n    /**\n     * Executes the given task in a fixed thread pool after the given delay.\n     *\n     * @param size  The size of thread in the fixed thread pool.\n     * @param task  The task to execute.\n     * @param delay The time from now to delay execution.\n     * @param unit  The time unit of the delay parameter.\n     * @param <T>   The type of the task's result.\n     */\n    public static <T> void executeByFixedWithDelay(@IntRange(from = 1) final int size,\n                                                   final Task<T> task,\n                                                   final long delay,\n                                                   final TimeUnit unit) {\n        executeWithDelay(getPoolByTypeAndPriority(size), task, delay, unit);\n    }\n\n    /**\n     * Executes the given task in a fixed thread pool after the given delay.\n     *\n     * @param size     The size of thread in the fixed thread pool.\n     * @param task     The task to execute.\n     * @param delay    The time from now to delay execution.\n     * @param unit     The time unit of the delay parameter.\n     * @param priority The priority of thread in the poll.\n     * @param <T>      The type of the task's result.\n     */\n    public static <T> void executeByFixedWithDelay(@IntRange(from = 1) final int size,\n                                                   final Task<T> task,\n                                                   final long delay,\n                                                   final TimeUnit unit,\n                                                   @IntRange(from = 1, to = 10) final int priority) {\n        executeWithDelay(getPoolByTypeAndPriority(size, priority), task, delay, unit);\n    }\n\n    /**\n     * Executes the given task in a fixed thread pool at fix rate.\n     *\n     * @param size   The size of thread in the fixed thread pool.\n     * @param task   The task to execute.\n     * @param period The period between successive executions.\n     * @param unit   The time unit of the period parameter.\n     * @param <T>    The type of the task's result.\n     */\n    public static <T> void executeByFixedAtFixRate(@IntRange(from = 1) final int size,\n                                                   final Task<T> task,\n                                                   final long period,\n                                                   final TimeUnit unit) {\n        executeAtFixedRate(getPoolByTypeAndPriority(size), task, 0, period, unit);\n    }\n\n    /**\n     * Executes the given task in a fixed thread pool at fix rate.\n     *\n     * @param size     The size of thread in the fixed thread pool.\n     * @param task     The task to execute.\n     * @param period   The period between successive executions.\n     * @param unit     The time unit of the period parameter.\n     * @param priority The priority of thread in the poll.\n     * @param <T>      The type of the task's result.\n     */\n    public static <T> void executeByFixedAtFixRate(@IntRange(from = 1) final int size,\n                                                   final Task<T> task,\n                                                   final long period,\n                                                   final TimeUnit unit,\n                                                   @IntRange(from = 1, to = 10) final int priority) {\n        executeAtFixedRate(getPoolByTypeAndPriority(size, priority), task, 0, period, unit);\n    }\n\n    /**\n     * Executes the given task in a fixed thread pool at fix rate.\n     *\n     * @param size         The size of thread in the fixed thread pool.\n     * @param task         The task to execute.\n     * @param initialDelay The time to delay first execution.\n     * @param period       The period between successive executions.\n     * @param unit         The time unit of the initialDelay and period parameters.\n     * @param <T>          The type of the task's result.\n     */\n    public static <T> void executeByFixedAtFixRate(@IntRange(from = 1) final int size,\n                                                   final Task<T> task,\n                                                   long initialDelay,\n                                                   final long period,\n                                                   final TimeUnit unit) {\n        executeAtFixedRate(getPoolByTypeAndPriority(size), task, initialDelay, period, unit);\n    }\n\n    /**\n     * Executes the given task in a fixed thread pool at fix rate.\n     *\n     * @param size         The size of thread in the fixed thread pool.\n     * @param task         The task to execute.\n     * @param initialDelay The time to delay first execution.\n     * @param period       The period between successive executions.\n     * @param unit         The time unit of the initialDelay and period parameters.\n     * @param priority     The priority of thread in the poll.\n     * @param <T>          The type of the task's result.\n     */\n    public static <T> void executeByFixedAtFixRate(@IntRange(from = 1) final int size,\n                                                   final Task<T> task,\n                                                   long initialDelay,\n                                                   final long period,\n                                                   final TimeUnit unit,\n                                                   @IntRange(from = 1, to = 10) final int priority) {\n        executeAtFixedRate(getPoolByTypeAndPriority(size, priority), task, initialDelay, period, unit);\n    }\n\n    /**\n     * Executes the given task in a single thread pool.\n     *\n     * @param task The task to execute.\n     * @param <T>  The type of the task's result.\n     */\n    public static <T> void executeBySingle(final Task<T> task) {\n        execute(getPoolByTypeAndPriority(TYPE_SINGLE), task);\n    }\n\n    /**\n     * Executes the given task in a single thread pool.\n     *\n     * @param task     The task to execute.\n     * @param priority The priority of thread in the poll.\n     * @param <T>      The type of the task's result.\n     */\n    public static <T> void executeBySingle(final Task<T> task,\n                                           @IntRange(from = 1, to = 10) final int priority) {\n        execute(getPoolByTypeAndPriority(TYPE_SINGLE, priority), task);\n    }\n\n    /**\n     * Executes the given task in a single thread pool after the given delay.\n     *\n     * @param task  The task to execute.\n     * @param delay The time from now to delay execution.\n     * @param unit  The time unit of the delay parameter.\n     * @param <T>   The type of the task's result.\n     */\n    public static <T> void executeBySingleWithDelay(final Task<T> task,\n                                                    final long delay,\n                                                    final TimeUnit unit) {\n        executeWithDelay(getPoolByTypeAndPriority(TYPE_SINGLE), task, delay, unit);\n    }\n\n    /**\n     * Executes the given task in a single thread pool after the given delay.\n     *\n     * @param task     The task to execute.\n     * @param delay    The time from now to delay execution.\n     * @param unit     The time unit of the delay parameter.\n     * @param priority The priority of thread in the poll.\n     * @param <T>      The type of the task's result.\n     */\n    public static <T> void executeBySingleWithDelay(final Task<T> task,\n                                                    final long delay,\n                                                    final TimeUnit unit,\n                                                    @IntRange(from = 1, to = 10) final int priority) {\n        executeWithDelay(getPoolByTypeAndPriority(TYPE_SINGLE, priority), task, delay, unit);\n    }\n\n    /**\n     * Executes the given task in a single thread pool at fix rate.\n     *\n     * @param task   The task to execute.\n     * @param period The period between successive executions.\n     * @param unit   The time unit of the period parameter.\n     * @param <T>    The type of the task's result.\n     */\n    public static <T> void executeBySingleAtFixRate(final Task<T> task,\n                                                    final long period,\n                                                    final TimeUnit unit) {\n        executeAtFixedRate(getPoolByTypeAndPriority(TYPE_SINGLE), task, 0, period, unit);\n    }\n\n    /**\n     * Executes the given task in a single thread pool at fix rate.\n     *\n     * @param task     The task to execute.\n     * @param period   The period between successive executions.\n     * @param unit     The time unit of the period parameter.\n     * @param priority The priority of thread in the poll.\n     * @param <T>      The type of the task's result.\n     */\n    public static <T> void executeBySingleAtFixRate(final Task<T> task,\n                                                    final long period,\n                                                    final TimeUnit unit,\n                                                    @IntRange(from = 1, to = 10) final int priority) {\n        executeAtFixedRate(getPoolByTypeAndPriority(TYPE_SINGLE, priority), task, 0, period, unit);\n    }\n\n    /**\n     * Executes the given task in a single thread pool at fix rate.\n     *\n     * @param task         The task to execute.\n     * @param initialDelay The time to delay first execution.\n     * @param period       The period between successive executions.\n     * @param unit         The time unit of the initialDelay and period parameters.\n     * @param <T>          The type of the task's result.\n     */\n    public static <T> void executeBySingleAtFixRate(final Task<T> task,\n                                                    long initialDelay,\n                                                    final long period,\n                                                    final TimeUnit unit) {\n        executeAtFixedRate(getPoolByTypeAndPriority(TYPE_SINGLE), task, initialDelay, period, unit);\n    }\n\n    /**\n     * Executes the given task in a single thread pool at fix rate.\n     *\n     * @param task         The task to execute.\n     * @param initialDelay The time to delay first execution.\n     * @param period       The period between successive executions.\n     * @param unit         The time unit of the initialDelay and period parameters.\n     * @param priority     The priority of thread in the poll.\n     * @param <T>          The type of the task's result.\n     */\n    public static <T> void executeBySingleAtFixRate(final Task<T> task,\n                                                    long initialDelay,\n                                                    final long period,\n                                                    final TimeUnit unit,\n                                                    @IntRange(from = 1, to = 10) final int priority) {\n        executeAtFixedRate(\n                getPoolByTypeAndPriority(TYPE_SINGLE, priority), task, initialDelay, period, unit\n        );\n    }\n\n    /**\n     * Executes the given task in a cached thread pool.\n     *\n     * @param task The task to execute.\n     * @param <T>  The type of the task's result.\n     */\n    public static <T> void executeByCached(final Task<T> task) {\n        execute(getPoolByTypeAndPriority(TYPE_CACHED), task);\n    }\n\n    /**\n     * Executes the given task in a cached thread pool.\n     *\n     * @param task     The task to execute.\n     * @param priority The priority of thread in the poll.\n     * @param <T>      The type of the task's result.\n     */\n    public static <T> void executeByCached(final Task<T> task,\n                                           @IntRange(from = 1, to = 10) final int priority) {\n        execute(getPoolByTypeAndPriority(TYPE_CACHED, priority), task);\n    }\n\n    /**\n     * Executes the given task in a cached thread pool after the given delay.\n     *\n     * @param task  The task to execute.\n     * @param delay The time from now to delay execution.\n     * @param unit  The time unit of the delay parameter.\n     * @param <T>   The type of the task's result.\n     */\n    public static <T> void executeByCachedWithDelay(final Task<T> task,\n                                                    final long delay,\n                                                    final TimeUnit unit) {\n        executeWithDelay(getPoolByTypeAndPriority(TYPE_CACHED), task, delay, unit);\n    }\n\n    /**\n     * Executes the given task in a cached thread pool after the given delay.\n     *\n     * @param task     The task to execute.\n     * @param delay    The time from now to delay execution.\n     * @param unit     The time unit of the delay parameter.\n     * @param priority The priority of thread in the poll.\n     * @param <T>      The type of the task's result.\n     */\n    public static <T> void executeByCachedWithDelay(final Task<T> task,\n                                                    final long delay,\n                                                    final TimeUnit unit,\n                                                    @IntRange(from = 1, to = 10) final int priority) {\n        executeWithDelay(getPoolByTypeAndPriority(TYPE_CACHED, priority), task, delay, unit);\n    }\n\n    /**\n     * Executes the given task in a cached thread pool at fix rate.\n     *\n     * @param task   The task to execute.\n     * @param period The period between successive executions.\n     * @param unit   The time unit of the period parameter.\n     * @param <T>    The type of the task's result.\n     */\n    public static <T> void executeByCachedAtFixRate(final Task<T> task,\n                                                    final long period,\n                                                    final TimeUnit unit) {\n        executeAtFixedRate(getPoolByTypeAndPriority(TYPE_CACHED), task, 0, period, unit);\n    }\n\n    /**\n     * Executes the given task in a cached thread pool at fix rate.\n     *\n     * @param task     The task to execute.\n     * @param period   The period between successive executions.\n     * @param unit     The time unit of the period parameter.\n     * @param priority The priority of thread in the poll.\n     * @param <T>      The type of the task's result.\n     */\n    public static <T> void executeByCachedAtFixRate(final Task<T> task,\n                                                    final long period,\n                                                    final TimeUnit unit,\n                                                    @IntRange(from = 1, to = 10) final int priority) {\n        executeAtFixedRate(getPoolByTypeAndPriority(TYPE_CACHED, priority), task, 0, period, unit);\n    }\n\n    /**\n     * Executes the given task in a cached thread pool at fix rate.\n     *\n     * @param task         The task to execute.\n     * @param initialDelay The time to delay first execution.\n     * @param period       The period between successive executions.\n     * @param unit         The time unit of the initialDelay and period parameters.\n     * @param <T>          The type of the task's result.\n     */\n    public static <T> void executeByCachedAtFixRate(final Task<T> task,\n                                                    long initialDelay,\n                                                    final long period,\n                                                    final TimeUnit unit) {\n        executeAtFixedRate(getPoolByTypeAndPriority(TYPE_CACHED), task, initialDelay, period, unit);\n    }\n\n    /**\n     * Executes the given task in a cached thread pool at fix rate.\n     *\n     * @param task         The task to execute.\n     * @param initialDelay The time to delay first execution.\n     * @param period       The period between successive executions.\n     * @param unit         The time unit of the initialDelay and period parameters.\n     * @param priority     The priority of thread in the poll.\n     * @param <T>          The type of the task's result.\n     */\n    public static <T> void executeByCachedAtFixRate(final Task<T> task,\n                                                    long initialDelay,\n                                                    final long period,\n                                                    final TimeUnit unit,\n                                                    @IntRange(from = 1, to = 10) final int priority) {\n        executeAtFixedRate(\n                getPoolByTypeAndPriority(TYPE_CACHED, priority), task, initialDelay, period, unit\n        );\n    }\n\n    /**\n     * Executes the given task in an IO thread pool.\n     *\n     * @param task The task to execute.\n     * @param <T>  The type of the task's result.\n     */\n    public static <T> void executeByIo(final Task<T> task) {\n        execute(getPoolByTypeAndPriority(TYPE_IO), task);\n    }\n\n    /**\n     * Executes the given task in an IO thread pool.\n     *\n     * @param task     The task to execute.\n     * @param priority The priority of thread in the poll.\n     * @param <T>      The type of the task's result.\n     */\n    public static <T> void executeByIo(final Task<T> task,\n                                       @IntRange(from = 1, to = 10) final int priority) {\n        execute(getPoolByTypeAndPriority(TYPE_IO, priority), task);\n    }\n\n    /**\n     * Executes the given task in an IO thread pool after the given delay.\n     *\n     * @param task  The task to execute.\n     * @param delay The time from now to delay execution.\n     * @param unit  The time unit of the delay parameter.\n     * @param <T>   The type of the task's result.\n     */\n    public static <T> void executeByIoWithDelay(final Task<T> task,\n                                                final long delay,\n                                                final TimeUnit unit) {\n        executeWithDelay(getPoolByTypeAndPriority(TYPE_IO), task, delay, unit);\n    }\n\n    /**\n     * Executes the given task in an IO thread pool after the given delay.\n     *\n     * @param task     The task to execute.\n     * @param delay    The time from now to delay execution.\n     * @param unit     The time unit of the delay parameter.\n     * @param priority The priority of thread in the poll.\n     * @param <T>      The type of the task's result.\n     */\n    public static <T> void executeByIoWithDelay(final Task<T> task,\n                                                final long delay,\n                                                final TimeUnit unit,\n                                                @IntRange(from = 1, to = 10) final int priority) {\n        executeWithDelay(getPoolByTypeAndPriority(TYPE_IO, priority), task, delay, unit);\n    }\n\n    /**\n     * Executes the given task in an IO thread pool at fix rate.\n     *\n     * @param task   The task to execute.\n     * @param period The period between successive executions.\n     * @param unit   The time unit of the period parameter.\n     * @param <T>    The type of the task's result.\n     */\n    public static <T> void executeByIoAtFixRate(final Task<T> task,\n                                                final long period,\n                                                final TimeUnit unit) {\n        executeAtFixedRate(getPoolByTypeAndPriority(TYPE_IO), task, 0, period, unit);\n    }\n\n    /**\n     * Executes the given task in an IO thread pool at fix rate.\n     *\n     * @param task     The task to execute.\n     * @param period   The period between successive executions.\n     * @param unit     The time unit of the period parameter.\n     * @param priority The priority of thread in the poll.\n     * @param <T>      The type of the task's result.\n     */\n    public static <T> void executeByIoAtFixRate(final Task<T> task,\n                                                final long period,\n                                                final TimeUnit unit,\n                                                @IntRange(from = 1, to = 10) final int priority) {\n        executeAtFixedRate(getPoolByTypeAndPriority(TYPE_IO, priority), task, 0, period, unit);\n    }\n\n    /**\n     * Executes the given task in an IO thread pool at fix rate.\n     *\n     * @param task         The task to execute.\n     * @param initialDelay The time to delay first execution.\n     * @param period       The period between successive executions.\n     * @param unit         The time unit of the initialDelay and period parameters.\n     * @param <T>          The type of the task's result.\n     */\n    public static <T> void executeByIoAtFixRate(final Task<T> task,\n                                                long initialDelay,\n                                                final long period,\n                                                final TimeUnit unit) {\n        executeAtFixedRate(getPoolByTypeAndPriority(TYPE_IO), task, initialDelay, period, unit);\n    }\n\n    /**\n     * Executes the given task in an IO thread pool at fix rate.\n     *\n     * @param task         The task to execute.\n     * @param initialDelay The time to delay first execution.\n     * @param period       The period between successive executions.\n     * @param unit         The time unit of the initialDelay and period parameters.\n     * @param priority     The priority of thread in the poll.\n     * @param <T>          The type of the task's result.\n     */\n    public static <T> void executeByIoAtFixRate(final Task<T> task,\n                                                long initialDelay,\n                                                final long period,\n                                                final TimeUnit unit,\n                                                @IntRange(from = 1, to = 10) final int priority) {\n        executeAtFixedRate(\n                getPoolByTypeAndPriority(TYPE_IO, priority), task, initialDelay, period, unit\n        );\n    }\n\n    /**\n     * Executes the given task in a cpu thread pool.\n     *\n     * @param task The task to execute.\n     * @param <T>  The type of the task's result.\n     */\n    public static <T> void executeByCpu(final Task<T> task) {\n        execute(getPoolByTypeAndPriority(TYPE_CPU), task);\n    }\n\n    /**\n     * Executes the given task in a cpu thread pool.\n     *\n     * @param task     The task to execute.\n     * @param priority The priority of thread in the poll.\n     * @param <T>      The type of the task's result.\n     */\n    public static <T> void executeByCpu(final Task<T> task,\n                                        @IntRange(from = 1, to = 10) final int priority) {\n        execute(getPoolByTypeAndPriority(TYPE_CPU, priority), task);\n    }\n\n    /**\n     * Executes the given task in a cpu thread pool after the given delay.\n     *\n     * @param task  The task to execute.\n     * @param delay The time from now to delay execution.\n     * @param unit  The time unit of the delay parameter.\n     * @param <T>   The type of the task's result.\n     */\n    public static <T> void executeByCpuWithDelay(final Task<T> task,\n                                                 final long delay,\n                                                 final TimeUnit unit) {\n        executeWithDelay(getPoolByTypeAndPriority(TYPE_CPU), task, delay, unit);\n    }\n\n    /**\n     * Executes the given task in a cpu thread pool after the given delay.\n     *\n     * @param task     The task to execute.\n     * @param delay    The time from now to delay execution.\n     * @param unit     The time unit of the delay parameter.\n     * @param priority The priority of thread in the poll.\n     * @param <T>      The type of the task's result.\n     */\n    public static <T> void executeByCpuWithDelay(final Task<T> task,\n                                                 final long delay,\n                                                 final TimeUnit unit,\n                                                 @IntRange(from = 1, to = 10) final int priority) {\n        executeWithDelay(getPoolByTypeAndPriority(TYPE_CPU, priority), task, delay, unit);\n    }\n\n    /**\n     * Executes the given task in a cpu thread pool at fix rate.\n     *\n     * @param task   The task to execute.\n     * @param period The period between successive executions.\n     * @param unit   The time unit of the period parameter.\n     * @param <T>    The type of the task's result.\n     */\n    public static <T> void executeByCpuAtFixRate(final Task<T> task,\n                                                 final long period,\n                                                 final TimeUnit unit) {\n        executeAtFixedRate(getPoolByTypeAndPriority(TYPE_CPU), task, 0, period, unit);\n    }\n\n    /**\n     * Executes the given task in a cpu thread pool at fix rate.\n     *\n     * @param task     The task to execute.\n     * @param period   The period between successive executions.\n     * @param unit     The time unit of the period parameter.\n     * @param priority The priority of thread in the poll.\n     * @param <T>      The type of the task's result.\n     */\n    public static <T> void executeByCpuAtFixRate(final Task<T> task,\n                                                 final long period,\n                                                 final TimeUnit unit,\n                                                 @IntRange(from = 1, to = 10) final int priority) {\n        executeAtFixedRate(getPoolByTypeAndPriority(TYPE_CPU, priority), task, 0, period, unit);\n    }\n\n    /**\n     * Executes the given task in a cpu thread pool at fix rate.\n     *\n     * @param task         The task to execute.\n     * @param initialDelay The time to delay first execution.\n     * @param period       The period between successive executions.\n     * @param unit         The time unit of the initialDelay and period parameters.\n     * @param <T>          The type of the task's result.\n     */\n    public static <T> void executeByCpuAtFixRate(final Task<T> task,\n                                                 long initialDelay,\n                                                 final long period,\n                                                 final TimeUnit unit) {\n        executeAtFixedRate(getPoolByTypeAndPriority(TYPE_CPU), task, initialDelay, period, unit);\n    }\n\n    /**\n     * Executes the given task in a cpu thread pool at fix rate.\n     *\n     * @param task         The task to execute.\n     * @param initialDelay The time to delay first execution.\n     * @param period       The period between successive executions.\n     * @param unit         The time unit of the initialDelay and period parameters.\n     * @param priority     The priority of thread in the poll.\n     * @param <T>          The type of the task's result.\n     */\n    public static <T> void executeByCpuAtFixRate(final Task<T> task,\n                                                 long initialDelay,\n                                                 final long period,\n                                                 final TimeUnit unit,\n                                                 @IntRange(from = 1, to = 10) final int priority) {\n        executeAtFixedRate(\n                getPoolByTypeAndPriority(TYPE_CPU, priority), task, initialDelay, period, unit\n        );\n    }\n\n    /**\n     * Executes the given task in a custom thread pool.\n     *\n     * @param pool The custom thread pool.\n     * @param task The task to execute.\n     * @param <T>  The type of the task's result.\n     */\n    public static <T> void executeByCustom(final ExecutorService pool, final Task<T> task) {\n        execute(pool, task);\n    }\n\n    /**\n     * Executes the given task in a custom thread pool after the given delay.\n     *\n     * @param pool  The custom thread pool.\n     * @param task  The task to execute.\n     * @param delay The time from now to delay execution.\n     * @param unit  The time unit of the delay parameter.\n     * @param <T>   The type of the task's result.\n     */\n    public static <T> void executeByCustomWithDelay(final ExecutorService pool,\n                                                    final Task<T> task,\n                                                    final long delay,\n                                                    final TimeUnit unit) {\n        executeWithDelay(pool, task, delay, unit);\n    }\n\n    /**\n     * Executes the given task in a custom thread pool at fix rate.\n     *\n     * @param pool   The custom thread pool.\n     * @param task   The task to execute.\n     * @param period The period between successive executions.\n     * @param unit   The time unit of the period parameter.\n     * @param <T>    The type of the task's result.\n     */\n    public static <T> void executeByCustomAtFixRate(final ExecutorService pool,\n                                                    final Task<T> task,\n                                                    final long period,\n                                                    final TimeUnit unit) {\n        executeAtFixedRate(pool, task, 0, period, unit);\n    }\n\n    /**\n     * Executes the given task in a custom thread pool at fix rate.\n     *\n     * @param pool         The custom thread pool.\n     * @param task         The task to execute.\n     * @param initialDelay The time to delay first execution.\n     * @param period       The period between successive executions.\n     * @param unit         The time unit of the initialDelay and period parameters.\n     * @param <T>          The type of the task's result.\n     */\n    public static <T> void executeByCustomAtFixRate(final ExecutorService pool,\n                                                    final Task<T> task,\n                                                    long initialDelay,\n                                                    final long period,\n                                                    final TimeUnit unit) {\n        executeAtFixedRate(pool, task, initialDelay, period, unit);\n    }\n\n    /**\n     * Cancel the given task.\n     *\n     * @param task The task to cancel.\n     */\n    public static void cancel(final Task task) {\n        if (task == null) return;\n        task.cancel();\n    }\n\n    /**\n     * Cancel the given tasks.\n     *\n     * @param tasks The tasks to cancel.\n     */\n    public static void cancel(final Task... tasks) {\n        if (tasks == null || tasks.length == 0) return;\n        for (Task task : tasks) {\n            if (task == null) continue;\n            task.cancel();\n        }\n    }\n\n    /**\n     * Cancel the given tasks.\n     *\n     * @param tasks The tasks to cancel.\n     */\n    public static void cancel(final List<Task> tasks) {\n        if (tasks == null || tasks.size() == 0) return;\n        for (Task task : tasks) {\n            if (task == null) continue;\n            task.cancel();\n        }\n    }\n\n    /**\n     * Cancel the tasks in pool.\n     *\n     * @param executorService The pool.\n     */\n    public static void cancel(ExecutorService executorService) {\n        if (executorService instanceof ThreadPoolExecutor4Util) {\n            for (Map.Entry<Task, ExecutorService> taskTaskInfoEntry : TASK_POOL_MAP.entrySet()) {\n                if (taskTaskInfoEntry.getValue() == executorService) {\n                    cancel(taskTaskInfoEntry.getKey());\n                }\n            }\n        } else {\n            Log.e(\"ThreadUtils\", \"The executorService is not ThreadUtils's pool.\");\n        }\n    }\n\n    /**\n     * Set the deliver.\n     *\n     * @param deliver The deliver.\n     */\n    public static void setDeliver(final Executor deliver) {\n        sDeliver = deliver;\n    }\n\n    private static <T> void execute(final ExecutorService pool, final Task<T> task) {\n        execute(pool, task, 0, 0, null);\n    }\n\n    private static <T> void executeWithDelay(final ExecutorService pool,\n                                             final Task<T> task,\n                                             final long delay,\n                                             final TimeUnit unit) {\n        execute(pool, task, delay, 0, unit);\n    }\n\n    private static <T> void executeAtFixedRate(final ExecutorService pool,\n                                               final Task<T> task,\n                                               long delay,\n                                               final long period,\n                                               final TimeUnit unit) {\n        execute(pool, task, delay, period, unit);\n    }\n\n    private static <T> void execute(final ExecutorService pool, final Task<T> task,\n                                    long delay, final long period, final TimeUnit unit) {\n        synchronized (TASK_POOL_MAP) {\n            if (TASK_POOL_MAP.get(task) != null) {\n                Log.e(\"ThreadUtils\", \"Task can only be executed once.\");\n                return;\n            }\n            TASK_POOL_MAP.put(task, pool);\n        }\n        if (period == 0) {\n            if (delay == 0) {\n                pool.execute(task);\n            } else {\n                TimerTask timerTask = new TimerTask() {\n                    @Override\n                    public void run() {\n                        pool.execute(task);\n                    }\n                };\n                TIMER.schedule(timerTask, unit.toMillis(delay));\n            }\n        } else {\n            task.setSchedule(true);\n            TimerTask timerTask = new TimerTask() {\n                @Override\n                public void run() {\n                    pool.execute(task);\n                }\n            };\n            TIMER.scheduleAtFixedRate(timerTask, unit.toMillis(delay), unit.toMillis(period));\n        }\n    }\n\n    private static ExecutorService getPoolByTypeAndPriority(final int type) {\n        return getPoolByTypeAndPriority(type, Thread.NORM_PRIORITY);\n    }\n\n    private static ExecutorService getPoolByTypeAndPriority(final int type, final int priority) {\n        synchronized (TYPE_PRIORITY_POOLS) {\n            ExecutorService pool;\n            Map<Integer, ExecutorService> priorityPools = TYPE_PRIORITY_POOLS.get(type);\n            if (priorityPools == null) {\n                priorityPools = new ConcurrentHashMap<>();\n                pool = ThreadPoolExecutor4Util.createPool(type, priority);\n                priorityPools.put(priority, pool);\n                TYPE_PRIORITY_POOLS.put(type, priorityPools);\n            } else {\n                pool = priorityPools.get(priority);\n                if (pool == null) {\n                    pool = ThreadPoolExecutor4Util.createPool(type, priority);\n                    priorityPools.put(priority, pool);\n                }\n            }\n            return pool;\n        }\n    }\n\n    static final class ThreadPoolExecutor4Util extends ThreadPoolExecutor {\n\n        private static ExecutorService createPool(final int type, final int priority) {\n            switch (type) {\n                case TYPE_SINGLE:\n                    return new ThreadPoolExecutor4Util(1, 1,\n                            0L, TimeUnit.MILLISECONDS,\n                            new LinkedBlockingQueue4Util(),\n                            new UtilsThreadFactory(\"single\", priority)\n                    );\n                case TYPE_CACHED:\n                    return new ThreadPoolExecutor4Util(0, 128,\n                            60L, TimeUnit.SECONDS,\n                            new LinkedBlockingQueue4Util(true),\n                            new UtilsThreadFactory(\"cached\", priority)\n                    );\n                case TYPE_IO:\n                    return new ThreadPoolExecutor4Util(2 * CPU_COUNT + 1, 2 * CPU_COUNT + 1,\n                            30, TimeUnit.SECONDS,\n                            new LinkedBlockingQueue4Util(),\n                            new UtilsThreadFactory(\"io\", priority)\n                    );\n                case TYPE_CPU:\n                    return new ThreadPoolExecutor4Util(CPU_COUNT + 1, 2 * CPU_COUNT + 1,\n                            30, TimeUnit.SECONDS,\n                            new LinkedBlockingQueue4Util(true),\n                            new UtilsThreadFactory(\"cpu\", priority)\n                    );\n                default:\n                    return new ThreadPoolExecutor4Util(type, type,\n                            0L, TimeUnit.MILLISECONDS,\n                            new LinkedBlockingQueue4Util(),\n                            new UtilsThreadFactory(\"fixed(\" + type + \")\", priority)\n                    );\n            }\n        }\n\n        private final AtomicInteger mSubmittedCount = new AtomicInteger();\n\n        private LinkedBlockingQueue4Util mWorkQueue;\n\n        ThreadPoolExecutor4Util(int corePoolSize, int maximumPoolSize,\n                                long keepAliveTime, TimeUnit unit,\n                                LinkedBlockingQueue4Util workQueue,\n                                ThreadFactory threadFactory) {\n            super(corePoolSize, maximumPoolSize,\n                    keepAliveTime, unit,\n                    workQueue,\n                    threadFactory\n            );\n            workQueue.mPool = this;\n            mWorkQueue = workQueue;\n        }\n\n        private int getSubmittedCount() {\n            return mSubmittedCount.get();\n        }\n\n        @Override\n        protected void afterExecute(Runnable r, Throwable t) {\n            mSubmittedCount.decrementAndGet();\n            super.afterExecute(r, t);\n        }\n\n        @Override\n        public void execute(@NonNull Runnable command) {\n            if (this.isShutdown()) return;\n            mSubmittedCount.incrementAndGet();\n            try {\n                super.execute(command);\n            } catch (RejectedExecutionException ignore) {\n                Log.e(\"ThreadUtils\", \"This will not happen!\");\n                mWorkQueue.offer(command);\n            } catch (Throwable t) {\n                mSubmittedCount.decrementAndGet();\n            }\n        }\n    }\n\n    private static final class LinkedBlockingQueue4Util extends LinkedBlockingQueue<Runnable> {\n\n        private volatile ThreadPoolExecutor4Util mPool;\n\n        private int mCapacity = Integer.MAX_VALUE;\n\n        LinkedBlockingQueue4Util() {\n            super();\n        }\n\n        LinkedBlockingQueue4Util(boolean isAddSubThreadFirstThenAddQueue) {\n            super();\n            if (isAddSubThreadFirstThenAddQueue) {\n                mCapacity = 0;\n            }\n        }\n\n        LinkedBlockingQueue4Util(int capacity) {\n            super();\n            mCapacity = capacity;\n        }\n\n        @Override\n        public boolean offer(@NonNull Runnable runnable) {\n            if (mCapacity <= size() &&\n                    mPool != null && mPool.getPoolSize() < mPool.getMaximumPoolSize()) {\n                // create a non-core thread\n                return false;\n            }\n            return super.offer(runnable);\n        }\n    }\n\n    static final class UtilsThreadFactory extends AtomicLong\n            implements ThreadFactory {\n        private static final AtomicInteger POOL_NUMBER      = new AtomicInteger(1);\n        private static final long          serialVersionUID = -9209200509960368598L;\n        private final        String        namePrefix;\n        private final        int           priority;\n        private final        boolean       isDaemon;\n\n        UtilsThreadFactory(String prefix, int priority) {\n            this(prefix, priority, false);\n        }\n\n        UtilsThreadFactory(String prefix, int priority, boolean isDaemon) {\n            namePrefix = prefix + \"-pool-\" +\n                    POOL_NUMBER.getAndIncrement() +\n                    \"-thread-\";\n            this.priority = priority;\n            this.isDaemon = isDaemon;\n        }\n\n        @Override\n        public Thread newThread(@NonNull Runnable r) {\n            Thread t = new Thread(r, namePrefix + getAndIncrement()) {\n                @Override\n                public void run() {\n                    try {\n                        super.run();\n                    } catch (Throwable t) {\n                        Log.e(\"ThreadUtils\", \"Request threw uncaught throwable\", t);\n                    }\n                }\n            };\n            t.setDaemon(isDaemon);\n            t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {\n                @Override\n                public void uncaughtException(Thread t, Throwable e) {\n                    System.out.println(e);\n                }\n            });\n            t.setPriority(priority);\n            return t;\n        }\n    }\n\n    public abstract static class SimpleTask<T> extends Task<T> {\n\n        @Override\n        public void onCancel() {\n            Log.e(\"ThreadUtils\", \"onCancel: \" + Thread.currentThread());\n        }\n\n        @Override\n        public void onFail(Throwable t) {\n            Log.e(\"ThreadUtils\", \"onFail: \", t);\n        }\n\n    }\n\n    public abstract static class Task<T> implements Runnable {\n\n        private static final int NEW         = 0;\n        private static final int RUNNING     = 1;\n        private static final int EXCEPTIONAL = 2;\n        private static final int COMPLETING  = 3;\n        private static final int CANCELLED   = 4;\n        private static final int INTERRUPTED = 5;\n        private static final int TIMEOUT     = 6;\n\n        private final AtomicInteger state = new AtomicInteger(NEW);\n\n        private volatile boolean isSchedule;\n        private volatile Thread  runner;\n\n        private Timer             mTimer;\n        private long              mTimeoutMillis;\n        private OnTimeoutListener mTimeoutListener;\n\n        private Executor deliver;\n\n        public abstract T doInBackground() throws Throwable;\n\n        public abstract void onSuccess(T result);\n\n        public abstract void onCancel();\n\n        public abstract void onFail(Throwable t);\n\n        @Override\n        public void run() {\n            if (isSchedule) {\n                if (runner == null) {\n                    if (!state.compareAndSet(NEW, RUNNING)) return;\n                    runner = Thread.currentThread();\n                    if (mTimeoutListener != null) {\n                        Log.w(\"ThreadUtils\", \"Scheduled task doesn't support timeout.\");\n                    }\n                } else {\n                    if (state.get() != RUNNING) return;\n                }\n            } else {\n                if (!state.compareAndSet(NEW, RUNNING)) return;\n                runner = Thread.currentThread();\n                if (mTimeoutListener != null) {\n                    mTimer = new Timer();\n                    mTimer.schedule(new TimerTask() {\n                        @Override\n                        public void run() {\n                            if (!isDone() && mTimeoutListener != null) {\n                                timeout();\n                                mTimeoutListener.onTimeout();\n                                onDone();\n                            }\n                        }\n                    }, mTimeoutMillis);\n                }\n            }\n            try {\n                final T result = doInBackground();\n                if (isSchedule) {\n                    if (state.get() != RUNNING) return;\n                    getDeliver().execute(new Runnable() {\n                        @Override\n                        public void run() {\n                            onSuccess(result);\n                        }\n                    });\n                } else {\n                    if (!state.compareAndSet(RUNNING, COMPLETING)) return;\n                    getDeliver().execute(new Runnable() {\n                        @Override\n                        public void run() {\n                            onSuccess(result);\n                            onDone();\n                        }\n                    });\n                }\n            } catch (InterruptedException ignore) {\n                state.compareAndSet(CANCELLED, INTERRUPTED);\n            } catch (final Throwable throwable) {\n                if (!state.compareAndSet(RUNNING, EXCEPTIONAL)) return;\n                getDeliver().execute(new Runnable() {\n                    @Override\n                    public void run() {\n                        onFail(throwable);\n                        onDone();\n                    }\n                });\n            }\n        }\n\n        public void cancel() {\n            cancel(true);\n        }\n\n        public void cancel(boolean mayInterruptIfRunning) {\n            synchronized (state) {\n                if (state.get() > RUNNING) return;\n                state.set(CANCELLED);\n            }\n            if (mayInterruptIfRunning) {\n                if (runner != null) {\n                    runner.interrupt();\n                }\n            }\n\n            getDeliver().execute(new Runnable() {\n                @Override\n                public void run() {\n                    onCancel();\n                    onDone();\n                }\n            });\n        }\n\n        private void timeout() {\n            synchronized (state) {\n                if (state.get() > RUNNING) return;\n                state.set(TIMEOUT);\n            }\n            if (runner != null) {\n                runner.interrupt();\n            }\n        }\n\n\n        public boolean isCanceled() {\n            return state.get() >= CANCELLED;\n        }\n\n        public boolean isDone() {\n            return state.get() > RUNNING;\n        }\n\n        public Task<T> setDeliver(Executor deliver) {\n            this.deliver = deliver;\n            return this;\n        }\n\n        /**\n         * Scheduled task doesn't support timeout.\n         */\n        public Task<T> setTimeout(final long timeoutMillis, final OnTimeoutListener listener) {\n            mTimeoutMillis = timeoutMillis;\n            mTimeoutListener = listener;\n            return this;\n        }\n\n        private void setSchedule(boolean isSchedule) {\n            this.isSchedule = isSchedule;\n        }\n\n        private Executor getDeliver() {\n            if (deliver == null) {\n                return getGlobalDeliver();\n            }\n            return deliver;\n        }\n\n        @CallSuper\n        protected void onDone() {\n            TASK_POOL_MAP.remove(this);\n            if (mTimer != null) {\n                mTimer.cancel();\n                mTimer = null;\n                mTimeoutListener = null;\n            }\n        }\n\n        public interface OnTimeoutListener {\n            void onTimeout();\n        }\n    }\n\n    public static class SyncValue<T> {\n\n        private CountDownLatch mLatch = new CountDownLatch(1);\n        private AtomicBoolean  mFlag  = new AtomicBoolean();\n        private T              mValue;\n\n        public void setValue(T value) {\n            if (mFlag.compareAndSet(false, true)) {\n                mValue = value;\n                mLatch.countDown();\n            }\n        }\n\n        public T getValue() {\n            if (!mFlag.get()) {\n                try {\n                    mLatch.await();\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                }\n            }\n            return mValue;\n        }\n\n        public T getValue(long timeout, TimeUnit unit, T defaultValue) {\n            if (!mFlag.get()) {\n                try {\n                    mLatch.await(timeout, unit);\n                } catch (InterruptedException e) {\n                    e.printStackTrace();\n                    return defaultValue;\n                }\n            }\n            return mValue;\n        }\n    }\n\n    private static Executor getGlobalDeliver() {\n        if (sDeliver == null) {\n            sDeliver = new Executor() {\n                @Override\n                public void execute(@NonNull Runnable command) {\n                    runOnUiThread(command);\n                }\n            };\n        }\n        return sDeliver;\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ThrowableUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport java.io.PrintWriter;\nimport java.io.StringWriter;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.StringTokenizer;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/02/12\n *     desc  : utils about exception\n * </pre>\n */\npublic class ThrowableUtils {\n\n    private static final String LINE_SEP = System.getProperty(\"line.separator\");\n\n    private ThrowableUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    public static String getFullStackTrace(Throwable throwable) {\n        final List<Throwable> throwableList = new ArrayList<>();\n        while (throwable != null && !throwableList.contains(throwable)) {\n            throwableList.add(throwable);\n            throwable = throwable.getCause();\n        }\n        final int size = throwableList.size();\n        final List<String> frames = new ArrayList<>();\n        List<String> nextTrace = getStackFrameList(throwableList.get(size - 1));\n        for (int i = size; --i >= 0; ) {\n            final List<String> trace = nextTrace;\n            if (i != 0) {\n                nextTrace = getStackFrameList(throwableList.get(i - 1));\n                removeCommonFrames(trace, nextTrace);\n            }\n            if (i == size - 1) {\n                frames.add(throwableList.get(i).toString());\n            } else {\n                frames.add(\" Caused by: \" + throwableList.get(i).toString());\n            }\n            frames.addAll(trace);\n        }\n        StringBuilder sb = new StringBuilder();\n        for (final String element : frames) {\n            sb.append(element).append(LINE_SEP);\n        }\n        return sb.toString();\n    }\n\n    private static List<String> getStackFrameList(final Throwable throwable) {\n        final StringWriter sw = new StringWriter();\n        final PrintWriter pw = new PrintWriter(sw, true);\n        throwable.printStackTrace(pw);\n        final String stackTrace = sw.toString();\n        final StringTokenizer frames = new StringTokenizer(stackTrace, LINE_SEP);\n        final List<String> list = new ArrayList<>();\n        boolean traceStarted = false;\n        while (frames.hasMoreTokens()) {\n            final String token = frames.nextToken();\n            // Determine if the line starts with <whitespace>at\n            final int at = token.indexOf(\"at\");\n            if (at != -1 && token.substring(0, at).trim().isEmpty()) {\n                traceStarted = true;\n                list.add(token);\n            } else if (traceStarted) {\n                break;\n            }\n        }\n        return list;\n    }\n\n    private static void removeCommonFrames(final List<String> causeFrames, final List<String> wrapperFrames) {\n        int causeFrameIndex = causeFrames.size() - 1;\n        int wrapperFrameIndex = wrapperFrames.size() - 1;\n        while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) {\n            // Remove the frame from the cause trace if it is the same\n            // as in the wrapper trace\n            final String causeFrame = causeFrames.get(causeFrameIndex);\n            final String wrapperFrame = wrapperFrames.get(wrapperFrameIndex);\n            if (causeFrame.equals(wrapperFrame)) {\n                causeFrames.remove(causeFrameIndex);\n            }\n            causeFrameIndex--;\n            wrapperFrameIndex--;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/TimeUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.annotation.SuppressLint;\nimport android.os.Build;\nimport android.provider.Settings;\n\nimport com.blankj.utilcode.constant.TimeConstants;\n\nimport java.text.DateFormat;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.GregorianCalendar;\nimport java.util.HashMap;\nimport java.util.Locale;\nimport java.util.Map;\n\nimport androidx.annotation.NonNull;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/02\n *     desc  : utils about time\n * </pre>\n */\npublic final class TimeUtils {\n\n    private static final ThreadLocal<Map<String, SimpleDateFormat>> SDF_THREAD_LOCAL\n            = new ThreadLocal<Map<String, SimpleDateFormat>>() {\n        @Override\n        protected Map<String, SimpleDateFormat> initialValue() {\n            return new HashMap<>();\n        }\n    };\n\n    private static SimpleDateFormat getDefaultFormat() {\n        return getSafeDateFormat(\"yyyy-MM-dd HH:mm:ss\");\n    }\n    /**\n     * Checks whether the device is using Network Provided Time or not.\n     *  Useful in situations where you want to verify that the device has a correct time set, to avoid fraud, or if you want to prevent the user from messing with the time and abusing your \"one-time\" and \"expiring\" features. \n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isUsingNetworkProvidedTime() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            return Settings.Global.getInt(Utils.getApp().getContentResolver(), Settings.Global.AUTO_TIME, 0) == 1;\n        } else {\n            return android.provider.Settings.System.getInt(Utils.getApp().getContentResolver(), android.provider.Settings.System.AUTO_TIME, 0) == 1;\n        }\n    }\n    \n\n    @SuppressLint(\"SimpleDateFormat\")\n    public static SimpleDateFormat getSafeDateFormat(String pattern) {\n        Map<String, SimpleDateFormat> sdfMap = SDF_THREAD_LOCAL.get();\n        //noinspection ConstantConditions\n        SimpleDateFormat simpleDateFormat = sdfMap.get(pattern);\n        if (simpleDateFormat == null) {\n            simpleDateFormat = new SimpleDateFormat(pattern);\n            sdfMap.put(pattern, simpleDateFormat);\n        }\n        return simpleDateFormat;\n    }\n\n    private TimeUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Milliseconds to the formatted time string.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param millis The milliseconds.\n     * @return the formatted time string\n     */\n    public static String millis2String(final long millis) {\n        return millis2String(millis, getDefaultFormat());\n    }\n\n    /**\n     * Milliseconds to the formatted time string.\n     *\n     * @param millis  The milliseconds.\n     * @param pattern The pattern of date format, such as yyyy/MM/dd HH:mm\n     * @return the formatted time string\n     */\n    public static String millis2String(long millis, @NonNull final String pattern) {\n        return millis2String(millis, getSafeDateFormat(pattern));\n    }\n\n    /**\n     * Milliseconds to the formatted time string.\n     *\n     * @param millis The milliseconds.\n     * @param format The format.\n     * @return the formatted time string\n     */\n    public static String millis2String(final long millis, @NonNull final DateFormat format) {\n        return format.format(new Date(millis));\n    }\n\n    /**\n     * Formatted time string to the milliseconds.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @return the milliseconds\n     */\n    public static long string2Millis(final String time) {\n        return string2Millis(time, getDefaultFormat());\n    }\n\n    /**\n     * Formatted time string to the milliseconds.\n     *\n     * @param time    The formatted time string.\n     * @param pattern The pattern of date format, such as yyyy/MM/dd HH:mm\n     * @return the milliseconds\n     */\n    public static long string2Millis(final String time, @NonNull final String pattern) {\n        return string2Millis(time, getSafeDateFormat(pattern));\n    }\n\n    /**\n     * Formatted time string to the milliseconds.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @return the milliseconds\n     */\n    public static long string2Millis(final String time, @NonNull final DateFormat format) {\n        try {\n            return format.parse(time).getTime();\n        } catch (ParseException e) {\n            e.printStackTrace();\n        }\n        return -1;\n    }\n\n    /**\n     * Formatted time string to the date.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @return the date\n     */\n    public static Date string2Date(final String time) {\n        return string2Date(time, getDefaultFormat());\n    }\n\n    /**\n     * Formatted time string to the date.\n     *\n     * @param time    The formatted time string.\n     * @param pattern The pattern of date format, such as yyyy/MM/dd HH:mm\n     * @return the date\n     */\n    public static Date string2Date(final String time, @NonNull final String pattern) {\n        return string2Date(time, getSafeDateFormat(pattern));\n    }\n\n    /**\n     * Formatted time string to the date.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @return the date\n     */\n    public static Date string2Date(final String time, @NonNull final DateFormat format) {\n        try {\n            return format.parse(time);\n        } catch (ParseException e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    /**\n     * Date to the formatted time string.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param date The date.\n     * @return the formatted time string\n     */\n    public static String date2String(final Date date) {\n        return date2String(date, getDefaultFormat());\n    }\n\n    /**\n     * Date to the formatted time string.\n     *\n     * @param date    The date.\n     * @param pattern The pattern of date format, such as yyyy/MM/dd HH:mm\n     * @return the formatted time string\n     */\n    public static String date2String(final Date date, @NonNull final String pattern) {\n        return getSafeDateFormat(pattern).format(date);\n    }\n\n    /**\n     * Date to the formatted time string.\n     *\n     * @param date   The date.\n     * @param format The format.\n     * @return the formatted time string\n     */\n    public static String date2String(final Date date, @NonNull final DateFormat format) {\n        return format.format(date);\n    }\n\n    /**\n     * Date to the milliseconds.\n     *\n     * @param date The date.\n     * @return the milliseconds\n     */\n    public static long date2Millis(final Date date) {\n        return date.getTime();\n    }\n\n    /**\n     * Milliseconds to the date.\n     *\n     * @param millis The milliseconds.\n     * @return the date\n     */\n    public static Date millis2Date(final long millis) {\n        return new Date(millis);\n    }\n\n    /**\n     * Return the time span, in unit.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time1 The first formatted time string.\n     * @param time2 The second formatted time string.\n     * @param unit  The unit of time span.\n     *              <ul>\n     *              <li>{@link TimeConstants#MSEC}</li>\n     *              <li>{@link TimeConstants#SEC }</li>\n     *              <li>{@link TimeConstants#MIN }</li>\n     *              <li>{@link TimeConstants#HOUR}</li>\n     *              <li>{@link TimeConstants#DAY }</li>\n     *              </ul>\n     * @return the time span, in unit\n     */\n    public static long getTimeSpan(final String time1,\n                                   final String time2,\n                                   @TimeConstants.Unit final int unit) {\n        return getTimeSpan(time1, time2, getDefaultFormat(), unit);\n    }\n\n    /**\n     * Return the time span, in unit.\n     *\n     * @param time1  The first formatted time string.\n     * @param time2  The second formatted time string.\n     * @param format The format.\n     * @param unit   The unit of time span.\n     *               <ul>\n     *               <li>{@link TimeConstants#MSEC}</li>\n     *               <li>{@link TimeConstants#SEC }</li>\n     *               <li>{@link TimeConstants#MIN }</li>\n     *               <li>{@link TimeConstants#HOUR}</li>\n     *               <li>{@link TimeConstants#DAY }</li>\n     *               </ul>\n     * @return the time span, in unit\n     */\n    public static long getTimeSpan(final String time1,\n                                   final String time2,\n                                   @NonNull final DateFormat format,\n                                   @TimeConstants.Unit final int unit) {\n        return millis2TimeSpan(string2Millis(time1, format) - string2Millis(time2, format), unit);\n    }\n\n    /**\n     * Return the time span, in unit.\n     *\n     * @param date1 The first date.\n     * @param date2 The second date.\n     * @param unit  The unit of time span.\n     *              <ul>\n     *              <li>{@link TimeConstants#MSEC}</li>\n     *              <li>{@link TimeConstants#SEC }</li>\n     *              <li>{@link TimeConstants#MIN }</li>\n     *              <li>{@link TimeConstants#HOUR}</li>\n     *              <li>{@link TimeConstants#DAY }</li>\n     *              </ul>\n     * @return the time span, in unit\n     */\n    public static long getTimeSpan(final Date date1,\n                                   final Date date2,\n                                   @TimeConstants.Unit final int unit) {\n        return millis2TimeSpan(date2Millis(date1) - date2Millis(date2), unit);\n    }\n\n    /**\n     * Return the time span, in unit.\n     *\n     * @param millis1 The first milliseconds.\n     * @param millis2 The second milliseconds.\n     * @param unit    The unit of time span.\n     *                <ul>\n     *                <li>{@link TimeConstants#MSEC}</li>\n     *                <li>{@link TimeConstants#SEC }</li>\n     *                <li>{@link TimeConstants#MIN }</li>\n     *                <li>{@link TimeConstants#HOUR}</li>\n     *                <li>{@link TimeConstants#DAY }</li>\n     *                </ul>\n     * @return the time span, in unit\n     */\n    public static long getTimeSpan(final long millis1,\n                                   final long millis2,\n                                   @TimeConstants.Unit final int unit) {\n        return millis2TimeSpan(millis1 - millis2, unit);\n    }\n\n    /**\n     * Return the fit time span.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time1     The first formatted time string.\n     * @param time2     The second formatted time string.\n     * @param precision The precision of time span.\n     *                  <ul>\n     *                  <li>precision = 0, return null</li>\n     *                  <li>precision = 1, return 天</li>\n     *                  <li>precision = 2, return 天, 小时</li>\n     *                  <li>precision = 3, return 天, 小时, 分钟</li>\n     *                  <li>precision = 4, return 天, 小时, 分钟, 秒</li>\n     *                  <li>precision &gt;= 5，return 天, 小时, 分钟, 秒, 毫秒</li>\n     *                  </ul>\n     * @return the fit time span\n     */\n    public static String getFitTimeSpan(final String time1,\n                                        final String time2,\n                                        final int precision) {\n        long delta = string2Millis(time1, getDefaultFormat()) - string2Millis(time2, getDefaultFormat());\n        return millis2FitTimeSpan(delta, precision);\n    }\n\n    /**\n     * Return the fit time span.\n     *\n     * @param time1     The first formatted time string.\n     * @param time2     The second formatted time string.\n     * @param format    The format.\n     * @param precision The precision of time span.\n     *                  <ul>\n     *                  <li>precision = 0, return null</li>\n     *                  <li>precision = 1, return 天</li>\n     *                  <li>precision = 2, return 天, 小时</li>\n     *                  <li>precision = 3, return 天, 小时, 分钟</li>\n     *                  <li>precision = 4, return 天, 小时, 分钟, 秒</li>\n     *                  <li>precision &gt;= 5，return 天, 小时, 分钟, 秒, 毫秒</li>\n     *                  </ul>\n     * @return the fit time span\n     */\n    public static String getFitTimeSpan(final String time1,\n                                        final String time2,\n                                        @NonNull final DateFormat format,\n                                        final int precision) {\n        long delta = string2Millis(time1, format) - string2Millis(time2, format);\n        return millis2FitTimeSpan(delta, precision);\n    }\n\n    /**\n     * Return the fit time span.\n     *\n     * @param date1     The first date.\n     * @param date2     The second date.\n     * @param precision The precision of time span.\n     *                  <ul>\n     *                  <li>precision = 0, return null</li>\n     *                  <li>precision = 1, return 天</li>\n     *                  <li>precision = 2, return 天, 小时</li>\n     *                  <li>precision = 3, return 天, 小时, 分钟</li>\n     *                  <li>precision = 4, return 天, 小时, 分钟, 秒</li>\n     *                  <li>precision &gt;= 5，return 天, 小时, 分钟, 秒, 毫秒</li>\n     *                  </ul>\n     * @return the fit time span\n     */\n    public static String getFitTimeSpan(final Date date1, final Date date2, final int precision) {\n        return millis2FitTimeSpan(date2Millis(date1) - date2Millis(date2), precision);\n    }\n\n    /**\n     * Return the fit time span.\n     *\n     * @param millis1   The first milliseconds.\n     * @param millis2   The second milliseconds.\n     * @param precision The precision of time span.\n     *                  <ul>\n     *                  <li>precision = 0, return null</li>\n     *                  <li>precision = 1, return 天</li>\n     *                  <li>precision = 2, return 天, 小时</li>\n     *                  <li>precision = 3, return 天, 小时, 分钟</li>\n     *                  <li>precision = 4, return 天, 小时, 分钟, 秒</li>\n     *                  <li>precision &gt;= 5，return 天, 小时, 分钟, 秒, 毫秒</li>\n     *                  </ul>\n     * @return the fit time span\n     */\n    public static String getFitTimeSpan(final long millis1,\n                                        final long millis2,\n                                        final int precision) {\n        return millis2FitTimeSpan(millis1 - millis2, precision);\n    }\n\n    /**\n     * Return the current time in milliseconds.\n     *\n     * @return the current time in milliseconds\n     */\n    public static long getNowMills() {\n        return System.currentTimeMillis();\n    }\n\n    /**\n     * Return the current formatted time string.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @return the current formatted time string\n     */\n    public static String getNowString() {\n        return millis2String(System.currentTimeMillis(), getDefaultFormat());\n    }\n\n    /**\n     * Return the current formatted time string.\n     *\n     * @param format The format.\n     * @return the current formatted time string\n     */\n    public static String getNowString(@NonNull final DateFormat format) {\n        return millis2String(System.currentTimeMillis(), format);\n    }\n\n    /**\n     * Return the current date.\n     *\n     * @return the current date\n     */\n    public static Date getNowDate() {\n        return new Date();\n    }\n\n    /**\n     * Return the time span by now, in unit.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @param unit The unit of time span.\n     *             <ul>\n     *             <li>{@link TimeConstants#MSEC}</li>\n     *             <li>{@link TimeConstants#SEC }</li>\n     *             <li>{@link TimeConstants#MIN }</li>\n     *             <li>{@link TimeConstants#HOUR}</li>\n     *             <li>{@link TimeConstants#DAY }</li>\n     *             </ul>\n     * @return the time span by now, in unit\n     */\n    public static long getTimeSpanByNow(final String time, @TimeConstants.Unit final int unit) {\n        return getTimeSpan(time, getNowString(), getDefaultFormat(), unit);\n    }\n\n    /**\n     * Return the time span by now, in unit.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @param unit   The unit of time span.\n     *               <ul>\n     *               <li>{@link TimeConstants#MSEC}</li>\n     *               <li>{@link TimeConstants#SEC }</li>\n     *               <li>{@link TimeConstants#MIN }</li>\n     *               <li>{@link TimeConstants#HOUR}</li>\n     *               <li>{@link TimeConstants#DAY }</li>\n     *               </ul>\n     * @return the time span by now, in unit\n     */\n    public static long getTimeSpanByNow(final String time,\n                                        @NonNull final DateFormat format,\n                                        @TimeConstants.Unit final int unit) {\n        return getTimeSpan(time, getNowString(format), format, unit);\n    }\n\n    /**\n     * Return the time span by now, in unit.\n     *\n     * @param date The date.\n     * @param unit The unit of time span.\n     *             <ul>\n     *             <li>{@link TimeConstants#MSEC}</li>\n     *             <li>{@link TimeConstants#SEC }</li>\n     *             <li>{@link TimeConstants#MIN }</li>\n     *             <li>{@link TimeConstants#HOUR}</li>\n     *             <li>{@link TimeConstants#DAY }</li>\n     *             </ul>\n     * @return the time span by now, in unit\n     */\n    public static long getTimeSpanByNow(final Date date, @TimeConstants.Unit final int unit) {\n        return getTimeSpan(date, new Date(), unit);\n    }\n\n    /**\n     * Return the time span by now, in unit.\n     *\n     * @param millis The milliseconds.\n     * @param unit   The unit of time span.\n     *               <ul>\n     *               <li>{@link TimeConstants#MSEC}</li>\n     *               <li>{@link TimeConstants#SEC }</li>\n     *               <li>{@link TimeConstants#MIN }</li>\n     *               <li>{@link TimeConstants#HOUR}</li>\n     *               <li>{@link TimeConstants#DAY }</li>\n     *               </ul>\n     * @return the time span by now, in unit\n     */\n    public static long getTimeSpanByNow(final long millis, @TimeConstants.Unit final int unit) {\n        return getTimeSpan(millis, System.currentTimeMillis(), unit);\n    }\n\n    /**\n     * Return the fit time span by now.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time      The formatted time string.\n     * @param precision The precision of time span.\n     *                  <ul>\n     *                  <li>precision = 0，返回 null</li>\n     *                  <li>precision = 1，返回天</li>\n     *                  <li>precision = 2，返回天和小时</li>\n     *                  <li>precision = 3，返回天、小时和分钟</li>\n     *                  <li>precision = 4，返回天、小时、分钟和秒</li>\n     *                  <li>precision &gt;= 5，返回天、小时、分钟、秒和毫秒</li>\n     *                  </ul>\n     * @return the fit time span by now\n     */\n    public static String getFitTimeSpanByNow(final String time, final int precision) {\n        return getFitTimeSpan(time, getNowString(), getDefaultFormat(), precision);\n    }\n\n    /**\n     * Return the fit time span by now.\n     *\n     * @param time      The formatted time string.\n     * @param format    The format.\n     * @param precision The precision of time span.\n     *                  <ul>\n     *                  <li>precision = 0，返回 null</li>\n     *                  <li>precision = 1，返回天</li>\n     *                  <li>precision = 2，返回天和小时</li>\n     *                  <li>precision = 3，返回天、小时和分钟</li>\n     *                  <li>precision = 4，返回天、小时、分钟和秒</li>\n     *                  <li>precision &gt;= 5，返回天、小时、分钟、秒和毫秒</li>\n     *                  </ul>\n     * @return the fit time span by now\n     */\n    public static String getFitTimeSpanByNow(final String time,\n                                             @NonNull final DateFormat format,\n                                             final int precision) {\n        return getFitTimeSpan(time, getNowString(format), format, precision);\n    }\n\n    /**\n     * Return the fit time span by now.\n     *\n     * @param date      The date.\n     * @param precision The precision of time span.\n     *                  <ul>\n     *                  <li>precision = 0，返回 null</li>\n     *                  <li>precision = 1，返回天</li>\n     *                  <li>precision = 2，返回天和小时</li>\n     *                  <li>precision = 3，返回天、小时和分钟</li>\n     *                  <li>precision = 4，返回天、小时、分钟和秒</li>\n     *                  <li>precision &gt;= 5，返回天、小时、分钟、秒和毫秒</li>\n     *                  </ul>\n     * @return the fit time span by now\n     */\n    public static String getFitTimeSpanByNow(final Date date, final int precision) {\n        return getFitTimeSpan(date, getNowDate(), precision);\n    }\n\n    /**\n     * Return the fit time span by now.\n     *\n     * @param millis    The milliseconds.\n     * @param precision The precision of time span.\n     *                  <ul>\n     *                  <li>precision = 0，返回 null</li>\n     *                  <li>precision = 1，返回天</li>\n     *                  <li>precision = 2，返回天和小时</li>\n     *                  <li>precision = 3，返回天、小时和分钟</li>\n     *                  <li>precision = 4，返回天、小时、分钟和秒</li>\n     *                  <li>precision &gt;= 5，返回天、小时、分钟、秒和毫秒</li>\n     *                  </ul>\n     * @return the fit time span by now\n     */\n    public static String getFitTimeSpanByNow(final long millis, final int precision) {\n        return getFitTimeSpan(millis, System.currentTimeMillis(), precision);\n    }\n\n    /**\n     * Return the friendly time span by now.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @return the friendly time span by now\n     * <ul>\n     * <li>如果小于 1 秒钟内，显示刚刚</li>\n     * <li>如果在 1 分钟内，显示 XXX秒前</li>\n     * <li>如果在 1 小时内，显示 XXX分钟前</li>\n     * <li>如果在 1 小时外的今天内，显示今天15:32</li>\n     * <li>如果是昨天的，显示昨天15:32</li>\n     * <li>其余显示，2016-10-15</li>\n     * <li>时间不合法的情况全部日期和时间信息，如星期六 十月 27 14:21:20 CST 2007</li>\n     * </ul>\n     */\n    public static String getFriendlyTimeSpanByNow(final String time) {\n        return getFriendlyTimeSpanByNow(time, getDefaultFormat());\n    }\n\n    /**\n     * Return the friendly time span by now.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @return the friendly time span by now\n     * <ul>\n     * <li>如果小于 1 秒钟内，显示刚刚</li>\n     * <li>如果在 1 分钟内，显示 XXX秒前</li>\n     * <li>如果在 1 小时内，显示 XXX分钟前</li>\n     * <li>如果在 1 小时外的今天内，显示今天15:32</li>\n     * <li>如果是昨天的，显示昨天15:32</li>\n     * <li>其余显示，2016-10-15</li>\n     * <li>时间不合法的情况全部日期和时间信息，如星期六 十月 27 14:21:20 CST 2007</li>\n     * </ul>\n     */\n    public static String getFriendlyTimeSpanByNow(final String time,\n                                                  @NonNull final DateFormat format) {\n        return getFriendlyTimeSpanByNow(string2Millis(time, format));\n    }\n\n    /**\n     * Return the friendly time span by now.\n     *\n     * @param date The date.\n     * @return the friendly time span by now\n     * <ul>\n     * <li>如果小于 1 秒钟内，显示刚刚</li>\n     * <li>如果在 1 分钟内，显示 XXX秒前</li>\n     * <li>如果在 1 小时内，显示 XXX分钟前</li>\n     * <li>如果在 1 小时外的今天内，显示今天15:32</li>\n     * <li>如果是昨天的，显示昨天15:32</li>\n     * <li>其余显示，2016-10-15</li>\n     * <li>时间不合法的情况全部日期和时间信息，如星期六 十月 27 14:21:20 CST 2007</li>\n     * </ul>\n     */\n    public static String getFriendlyTimeSpanByNow(final Date date) {\n        return getFriendlyTimeSpanByNow(date.getTime());\n    }\n\n    /**\n     * Return the friendly time span by now.\n     *\n     * @param millis The milliseconds.\n     * @return the friendly time span by now\n     * <ul>\n     * <li>如果小于 1 秒钟内，显示刚刚</li>\n     * <li>如果在 1 分钟内，显示 XXX秒前</li>\n     * <li>如果在 1 小时内，显示 XXX分钟前</li>\n     * <li>如果在 1 小时外的今天内，显示今天15:32</li>\n     * <li>如果是昨天的，显示昨天15:32</li>\n     * <li>其余显示，2016-10-15</li>\n     * <li>时间不合法的情况全部日期和时间信息，如星期六 十月 27 14:21:20 CST 2007</li>\n     * </ul>\n     */\n    public static String getFriendlyTimeSpanByNow(final long millis) {\n        long now = System.currentTimeMillis();\n        long span = now - millis;\n        if (span < 0)\n            // U can read http://www.apihome.cn/api/java/Formatter.html to understand it.\n            return String.format(\"%tc\", millis);\n        if (span < 1000) {\n            return \"刚刚\";\n        } else if (span < TimeConstants.MIN) {\n            return String.format(Locale.getDefault(), \"%d秒前\", span / TimeConstants.SEC);\n        } else if (span < TimeConstants.HOUR) {\n            return String.format(Locale.getDefault(), \"%d分钟前\", span / TimeConstants.MIN);\n        }\n        // 获取当天 00:00\n        long wee = getWeeOfToday();\n        if (millis >= wee) {\n            return String.format(\"今天%tR\", millis);\n        } else if (millis >= wee - TimeConstants.DAY) {\n            return String.format(\"昨天%tR\", millis);\n        } else {\n            return String.format(\"%tF\", millis);\n        }\n    }\n\n    private static long getWeeOfToday() {\n        Calendar cal = Calendar.getInstance();\n        cal.set(Calendar.HOUR_OF_DAY, 0);\n        cal.set(Calendar.SECOND, 0);\n        cal.set(Calendar.MINUTE, 0);\n        cal.set(Calendar.MILLISECOND, 0);\n        return cal.getTimeInMillis();\n    }\n\n    /**\n     * Return the milliseconds differ time span.\n     *\n     * @param millis   The milliseconds.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the milliseconds differ time span\n     */\n    public static long getMillis(final long millis,\n                                 final long timeSpan,\n                                 @TimeConstants.Unit final int unit) {\n        return millis + timeSpan2Millis(timeSpan, unit);\n    }\n\n    /**\n     * Return the milliseconds differ time span.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time     The formatted time string.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the milliseconds differ time span\n     */\n    public static long getMillis(final String time,\n                                 final long timeSpan,\n                                 @TimeConstants.Unit final int unit) {\n        return getMillis(time, getDefaultFormat(), timeSpan, unit);\n    }\n\n    /**\n     * Return the milliseconds differ time span.\n     *\n     * @param time     The formatted time string.\n     * @param format   The format.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the milliseconds differ time span.\n     */\n    public static long getMillis(final String time,\n                                 @NonNull final DateFormat format,\n                                 final long timeSpan,\n                                 @TimeConstants.Unit final int unit) {\n        return string2Millis(time, format) + timeSpan2Millis(timeSpan, unit);\n    }\n\n    /**\n     * Return the milliseconds differ time span.\n     *\n     * @param date     The date.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the milliseconds differ time span.\n     */\n    public static long getMillis(final Date date,\n                                 final long timeSpan,\n                                 @TimeConstants.Unit final int unit) {\n        return date2Millis(date) + timeSpan2Millis(timeSpan, unit);\n    }\n\n    /**\n     * Return the formatted time string differ time span.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param millis   The milliseconds.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the formatted time string differ time span\n     */\n    public static String getString(final long millis,\n                                   final long timeSpan,\n                                   @TimeConstants.Unit final int unit) {\n        return getString(millis, getDefaultFormat(), timeSpan, unit);\n    }\n\n    /**\n     * Return the formatted time string differ time span.\n     *\n     * @param millis   The milliseconds.\n     * @param format   The format.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the formatted time string differ time span\n     */\n    public static String getString(final long millis,\n                                   @NonNull final DateFormat format,\n                                   final long timeSpan,\n                                   @TimeConstants.Unit final int unit) {\n        return millis2String(millis + timeSpan2Millis(timeSpan, unit), format);\n    }\n\n    /**\n     * Return the formatted time string differ time span.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time     The formatted time string.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the formatted time string differ time span\n     */\n    public static String getString(final String time,\n                                   final long timeSpan,\n                                   @TimeConstants.Unit final int unit) {\n        return getString(time, getDefaultFormat(), timeSpan, unit);\n    }\n\n    /**\n     * Return the formatted time string differ time span.\n     *\n     * @param time     The formatted time string.\n     * @param format   The format.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the formatted time string differ time span\n     */\n    public static String getString(final String time,\n                                   @NonNull final DateFormat format,\n                                   final long timeSpan,\n                                   @TimeConstants.Unit final int unit) {\n        return millis2String(string2Millis(time, format) + timeSpan2Millis(timeSpan, unit), format);\n    }\n\n    /**\n     * Return the formatted time string differ time span.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param date     The date.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the formatted time string differ time span\n     */\n    public static String getString(final Date date,\n                                   final long timeSpan,\n                                   @TimeConstants.Unit final int unit) {\n        return getString(date, getDefaultFormat(), timeSpan, unit);\n    }\n\n    /**\n     * Return the formatted time string differ time span.\n     *\n     * @param date     The date.\n     * @param format   The format.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the formatted time string differ time span\n     */\n    public static String getString(final Date date,\n                                   @NonNull final DateFormat format,\n                                   final long timeSpan,\n                                   @TimeConstants.Unit final int unit) {\n        return millis2String(date2Millis(date) + timeSpan2Millis(timeSpan, unit), format);\n    }\n\n    /**\n     * Return the date differ time span.\n     *\n     * @param millis   The milliseconds.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the date differ time span\n     */\n    public static Date getDate(final long millis,\n                               final long timeSpan,\n                               @TimeConstants.Unit final int unit) {\n        return millis2Date(millis + timeSpan2Millis(timeSpan, unit));\n    }\n\n    /**\n     * Return the date differ time span.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time     The formatted time string.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the date differ time span\n     */\n    public static Date getDate(final String time,\n                               final long timeSpan,\n                               @TimeConstants.Unit final int unit) {\n        return getDate(time, getDefaultFormat(), timeSpan, unit);\n    }\n\n    /**\n     * Return the date differ time span.\n     *\n     * @param time     The formatted time string.\n     * @param format   The format.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the date differ time span\n     */\n    public static Date getDate(final String time,\n                               @NonNull final DateFormat format,\n                               final long timeSpan,\n                               @TimeConstants.Unit final int unit) {\n        return millis2Date(string2Millis(time, format) + timeSpan2Millis(timeSpan, unit));\n    }\n\n    /**\n     * Return the date differ time span.\n     *\n     * @param date     The date.\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the date differ time span\n     */\n    public static Date getDate(final Date date,\n                               final long timeSpan,\n                               @TimeConstants.Unit final int unit) {\n        return millis2Date(date2Millis(date) + timeSpan2Millis(timeSpan, unit));\n    }\n\n    /**\n     * Return the milliseconds differ time span by now.\n     *\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the milliseconds differ time span by now\n     */\n    public static long getMillisByNow(final long timeSpan, @TimeConstants.Unit final int unit) {\n        return getMillis(getNowMills(), timeSpan, unit);\n    }\n\n    /**\n     * Return the formatted time string differ time span by now.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the formatted time string differ time span by now\n     */\n    public static String getStringByNow(final long timeSpan, @TimeConstants.Unit final int unit) {\n        return getStringByNow(timeSpan, getDefaultFormat(), unit);\n    }\n\n    /**\n     * Return the formatted time string differ time span by now.\n     *\n     * @param timeSpan The time span.\n     * @param format   The format.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the formatted time string differ time span by now\n     */\n    public static String getStringByNow(final long timeSpan,\n                                        @NonNull final DateFormat format,\n                                        @TimeConstants.Unit final int unit) {\n        return getString(getNowMills(), format, timeSpan, unit);\n    }\n\n    /**\n     * Return the date differ time span by now.\n     *\n     * @param timeSpan The time span.\n     * @param unit     The unit of time span.\n     *                 <ul>\n     *                 <li>{@link TimeConstants#MSEC}</li>\n     *                 <li>{@link TimeConstants#SEC }</li>\n     *                 <li>{@link TimeConstants#MIN }</li>\n     *                 <li>{@link TimeConstants#HOUR}</li>\n     *                 <li>{@link TimeConstants#DAY }</li>\n     *                 </ul>\n     * @return the date differ time span by now\n     */\n    public static Date getDateByNow(final long timeSpan, @TimeConstants.Unit final int unit) {\n        return getDate(getNowMills(), timeSpan, unit);\n    }\n\n    /**\n     * Return whether it is today.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isToday(final String time) {\n        return isToday(string2Millis(time, getDefaultFormat()));\n    }\n\n    /**\n     * Return whether it is today.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isToday(final String time, @NonNull final DateFormat format) {\n        return isToday(string2Millis(time, format));\n    }\n\n    /**\n     * Return whether it is today.\n     *\n     * @param date The date.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isToday(final Date date) {\n        return isToday(date.getTime());\n    }\n\n    /**\n     * Return whether it is today.\n     *\n     * @param millis The milliseconds.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isToday(final long millis) {\n        long wee = getWeeOfToday();\n        return millis >= wee && millis < wee + TimeConstants.DAY;\n    }\n\n    /**\n     * Return whether it is leap year.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isLeapYear(final String time) {\n        return isLeapYear(string2Date(time, getDefaultFormat()));\n    }\n\n    /**\n     * Return whether it is leap year.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isLeapYear(final String time, @NonNull final DateFormat format) {\n        return isLeapYear(string2Date(time, format));\n    }\n\n    /**\n     * Return whether it is leap year.\n     *\n     * @param date The date.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isLeapYear(final Date date) {\n        Calendar cal = Calendar.getInstance();\n        cal.setTime(date);\n        int year = cal.get(Calendar.YEAR);\n        return isLeapYear(year);\n    }\n\n    /**\n     * Return whether it is leap year.\n     *\n     * @param millis The milliseconds.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isLeapYear(final long millis) {\n        return isLeapYear(millis2Date(millis));\n    }\n\n    /**\n     * Return whether it is leap year.\n     *\n     * @param year The year.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isLeapYear(final int year) {\n        return year % 4 == 0 && year % 100 != 0 || year % 400 == 0;\n    }\n\n    /**\n     * Return the day of week in Chinese.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @return the day of week in Chinese\n     */\n    public static String getChineseWeek(final String time) {\n        return getChineseWeek(string2Date(time, getDefaultFormat()));\n    }\n\n    /**\n     * Return the day of week in Chinese.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @return the day of week in Chinese\n     */\n    public static String getChineseWeek(final String time, @NonNull final DateFormat format) {\n        return getChineseWeek(string2Date(time, format));\n    }\n\n    /**\n     * Return the day of week in Chinese.\n     *\n     * @param date The date.\n     * @return the day of week in Chinese\n     */\n    public static String getChineseWeek(final Date date) {\n        return new SimpleDateFormat(\"E\", Locale.CHINA).format(date);\n    }\n\n    /**\n     * Return the day of week in Chinese.\n     *\n     * @param millis The milliseconds.\n     * @return the day of week in Chinese\n     */\n    public static String getChineseWeek(final long millis) {\n        return getChineseWeek(new Date(millis));\n    }\n\n    /**\n     * Return the day of week in US.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @return the day of week in US\n     */\n    public static String getUSWeek(final String time) {\n        return getUSWeek(string2Date(time, getDefaultFormat()));\n    }\n\n    /**\n     * Return the day of week in US.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @return the day of week in US\n     */\n    public static String getUSWeek(final String time, @NonNull final DateFormat format) {\n        return getUSWeek(string2Date(time, format));\n    }\n\n    /**\n     * Return the day of week in US.\n     *\n     * @param date The date.\n     * @return the day of week in US\n     */\n    public static String getUSWeek(final Date date) {\n        return new SimpleDateFormat(\"EEEE\", Locale.US).format(date);\n    }\n\n    /**\n     * Return the day of week in US.\n     *\n     * @param millis The milliseconds.\n     * @return the day of week in US\n     */\n    public static String getUSWeek(final long millis) {\n        return getUSWeek(new Date(millis));\n    }\n\n    /**\n     * Return whether it is am.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isAm() {\n        Calendar cal = Calendar.getInstance();\n        return cal.get(GregorianCalendar.AM_PM) == 0;\n    }\n\n    /**\n     * Return whether it is am.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isAm(final String time) {\n        return getValueByCalendarField(time, getDefaultFormat(), GregorianCalendar.AM_PM) == 0;\n    }\n\n    /**\n     * Return whether it is am.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isAm(final String time,\n                               @NonNull final DateFormat format) {\n        return getValueByCalendarField(time, format, GregorianCalendar.AM_PM) == 0;\n    }\n\n    /**\n     * Return whether it is am.\n     *\n     * @param date The date.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isAm(final Date date) {\n        return getValueByCalendarField(date, GregorianCalendar.AM_PM) == 0;\n    }\n\n    /**\n     * Return whether it is am.\n     *\n     * @param millis The milliseconds.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isAm(final long millis) {\n        return getValueByCalendarField(millis, GregorianCalendar.AM_PM) == 0;\n    }\n\n    /**\n     * Return whether it is am.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isPm() {\n        return !isAm();\n    }\n\n    /**\n     * Return whether it is am.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isPm(final String time) {\n        return !isAm(time);\n    }\n\n    /**\n     * Return whether it is am.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isPm(final String time,\n                               @NonNull final DateFormat format) {\n        return !isAm(time, format);\n    }\n\n    /**\n     * Return whether it is am.\n     *\n     * @param date The date.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isPm(final Date date) {\n        return !isAm(date);\n    }\n\n    /**\n     * Return whether it is am.\n     *\n     * @param millis The milliseconds.\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isPm(final long millis) {\n        return !isAm(millis);\n    }\n\n    /**\n     * Returns the value of the given calendar field.\n     *\n     * @param field The given calendar field.\n     *              <ul>\n     *              <li>{@link Calendar#ERA}</li>\n     *              <li>{@link Calendar#YEAR}</li>\n     *              <li>{@link Calendar#MONTH}</li>\n     *              <li>...</li>\n     *              <li>{@link Calendar#DST_OFFSET}</li>\n     *              </ul>\n     * @return the value of the given calendar field\n     */\n    public static int getValueByCalendarField(final int field) {\n        Calendar cal = Calendar.getInstance();\n        return cal.get(field);\n    }\n\n    /**\n     * Returns the value of the given calendar field.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time  The formatted time string.\n     * @param field The given calendar field.\n     *              <ul>\n     *              <li>{@link Calendar#ERA}</li>\n     *              <li>{@link Calendar#YEAR}</li>\n     *              <li>{@link Calendar#MONTH}</li>\n     *              <li>...</li>\n     *              <li>{@link Calendar#DST_OFFSET}</li>\n     *              </ul>\n     * @return the value of the given calendar field\n     */\n    public static int getValueByCalendarField(final String time, final int field) {\n        return getValueByCalendarField(string2Date(time, getDefaultFormat()), field);\n    }\n\n    /**\n     * Returns the value of the given calendar field.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @param field  The given calendar field.\n     *               <ul>\n     *               <li>{@link Calendar#ERA}</li>\n     *               <li>{@link Calendar#YEAR}</li>\n     *               <li>{@link Calendar#MONTH}</li>\n     *               <li>...</li>\n     *               <li>{@link Calendar#DST_OFFSET}</li>\n     *               </ul>\n     * @return the value of the given calendar field\n     */\n    public static int getValueByCalendarField(final String time,\n                                              @NonNull final DateFormat format,\n                                              final int field) {\n        return getValueByCalendarField(string2Date(time, format), field);\n    }\n\n    /**\n     * Returns the value of the given calendar field.\n     *\n     * @param date  The date.\n     * @param field The given calendar field.\n     *              <ul>\n     *              <li>{@link Calendar#ERA}</li>\n     *              <li>{@link Calendar#YEAR}</li>\n     *              <li>{@link Calendar#MONTH}</li>\n     *              <li>...</li>\n     *              <li>{@link Calendar#DST_OFFSET}</li>\n     *              </ul>\n     * @return the value of the given calendar field\n     */\n    public static int getValueByCalendarField(final Date date, final int field) {\n        Calendar cal = Calendar.getInstance();\n        cal.setTime(date);\n        return cal.get(field);\n    }\n\n    /**\n     * Returns the value of the given calendar field.\n     *\n     * @param millis The milliseconds.\n     * @param field  The given calendar field.\n     *               <ul>\n     *               <li>{@link Calendar#ERA}</li>\n     *               <li>{@link Calendar#YEAR}</li>\n     *               <li>{@link Calendar#MONTH}</li>\n     *               <li>...</li>\n     *               <li>{@link Calendar#DST_OFFSET}</li>\n     *               </ul>\n     * @return the value of the given calendar field\n     */\n    public static int getValueByCalendarField(final long millis, final int field) {\n        Calendar cal = Calendar.getInstance();\n        cal.setTimeInMillis(millis);\n        return cal.get(field);\n    }\n\n    private static final String[] CHINESE_ZODIAC =\n            {\"猴\", \"鸡\", \"狗\", \"猪\", \"鼠\", \"牛\", \"虎\", \"兔\", \"龙\", \"蛇\", \"马\", \"羊\"};\n\n    /**\n     * Return the Chinese zodiac.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @return the Chinese zodiac\n     */\n    public static String getChineseZodiac(final String time) {\n        return getChineseZodiac(string2Date(time, getDefaultFormat()));\n    }\n\n    /**\n     * Return the Chinese zodiac.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @return the Chinese zodiac\n     */\n    public static String getChineseZodiac(final String time, @NonNull final DateFormat format) {\n        return getChineseZodiac(string2Date(time, format));\n    }\n\n    /**\n     * Return the Chinese zodiac.\n     *\n     * @param date The date.\n     * @return the Chinese zodiac\n     */\n    public static String getChineseZodiac(final Date date) {\n        Calendar cal = Calendar.getInstance();\n        cal.setTime(date);\n        return CHINESE_ZODIAC[cal.get(Calendar.YEAR) % 12];\n    }\n\n    /**\n     * Return the Chinese zodiac.\n     *\n     * @param millis The milliseconds.\n     * @return the Chinese zodiac\n     */\n    public static String getChineseZodiac(final long millis) {\n        return getChineseZodiac(millis2Date(millis));\n    }\n\n    /**\n     * Return the Chinese zodiac.\n     *\n     * @param year The year.\n     * @return the Chinese zodiac\n     */\n    public static String getChineseZodiac(final int year) {\n        return CHINESE_ZODIAC[year % 12];\n    }\n\n    private static final int[]    ZODIAC_FLAGS = {20, 19, 21, 21, 21, 22, 23, 23, 23, 24, 23, 22};\n    private static final String[] ZODIAC       = {\n            \"水瓶座\", \"双鱼座\", \"白羊座\", \"金牛座\", \"双子座\", \"巨蟹座\",\n            \"狮子座\", \"处女座\", \"天秤座\", \"天蝎座\", \"射手座\", \"摩羯座\"\n    };\n\n    /**\n     * Return the zodiac.\n     * <p>The pattern is {@code yyyy-MM-dd HH:mm:ss}.</p>\n     *\n     * @param time The formatted time string.\n     * @return the zodiac\n     */\n    public static String getZodiac(final String time) {\n        return getZodiac(string2Date(time, getDefaultFormat()));\n    }\n\n    /**\n     * Return the zodiac.\n     *\n     * @param time   The formatted time string.\n     * @param format The format.\n     * @return the zodiac\n     */\n    public static String getZodiac(final String time, @NonNull final DateFormat format) {\n        return getZodiac(string2Date(time, format));\n    }\n\n    /**\n     * Return the zodiac.\n     *\n     * @param date The date.\n     * @return the zodiac\n     */\n    public static String getZodiac(final Date date) {\n        Calendar cal = Calendar.getInstance();\n        cal.setTime(date);\n        int month = cal.get(Calendar.MONTH) + 1;\n        int day = cal.get(Calendar.DAY_OF_MONTH);\n        return getZodiac(month, day);\n    }\n\n    /**\n     * Return the zodiac.\n     *\n     * @param millis The milliseconds.\n     * @return the zodiac\n     */\n    public static String getZodiac(final long millis) {\n        return getZodiac(millis2Date(millis));\n    }\n\n    /**\n     * Return the zodiac.\n     *\n     * @param month The month.\n     * @param day   The day.\n     * @return the zodiac\n     */\n    public static String getZodiac(final int month, final int day) {\n        return ZODIAC[day >= ZODIAC_FLAGS[month - 1]\n                ? month - 1\n                : (month + 10) % 12];\n    }\n\n    private static long timeSpan2Millis(final long timeSpan, @TimeConstants.Unit final int unit) {\n        return timeSpan * unit;\n    }\n\n    private static long millis2TimeSpan(final long millis, @TimeConstants.Unit final int unit) {\n        return millis / unit;\n    }\n\n    static String millis2FitTimeSpan(long millis, int precision) {\n        if (precision <= 0) return null;\n        precision = Math.min(precision, 5);\n        String[] units = {\"天\", \"小时\", \"分钟\", \"秒\", \"毫秒\"};\n        if (millis == 0) return 0 + units[precision - 1];\n        StringBuilder sb = new StringBuilder();\n        if (millis < 0) {\n            sb.append(\"-\");\n            millis = -millis;\n        }\n        int[] unitLen = {86400000, 3600000, 60000, 1000, 1};\n        for (int i = 0; i < precision; i++) {\n            if (millis >= unitLen[i]) {\n                long mode = millis / unitLen[i];\n                millis -= mode * unitLen[i];\n                sb.append(mode).append(units[i]);\n            }\n        }\n        return sb.toString();\n    }\n\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ToastUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.graphics.Bitmap;\nimport android.graphics.Color;\nimport android.graphics.PixelFormat;\nimport android.graphics.PorterDuff;\nimport android.graphics.PorterDuffColorFilter;\nimport android.graphics.drawable.Drawable;\nimport android.graphics.drawable.GradientDrawable;\nimport android.os.Build;\nimport android.os.Handler;\nimport android.os.Message;\nimport android.util.AttributeSet;\nimport android.view.Gravity;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.Window;\nimport android.view.WindowManager;\nimport android.widget.FrameLayout;\nimport android.widget.ImageView;\nimport android.widget.RelativeLayout;\nimport android.widget.TextView;\nimport android.widget.Toast;\n\nimport com.blankj.utilcode.R;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.ref.WeakReference;\nimport java.lang.reflect.Field;\n\nimport androidx.annotation.CallSuper;\nimport androidx.annotation.ColorInt;\nimport androidx.annotation.DrawableRes;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.StringDef;\nimport androidx.annotation.StringRes;\nimport androidx.core.app.NotificationManagerCompat;\nimport androidx.core.content.ContextCompat;\nimport androidx.core.view.ViewCompat;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/09/29\n *     desc  : utils about toast\n * </pre>\n */\npublic final class ToastUtils {\n\n    @StringDef({MODE.LIGHT, MODE.DARK})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface MODE {\n        String LIGHT = \"light\";\n        String DARK  = \"dark\";\n    }\n\n    private static final String     TAG_TOAST     = \"TAG_TOAST\";\n    private static final int        COLOR_DEFAULT = 0xFEFFFFFF;\n    private static final String     NULL          = \"toast null\";\n    private static final String     NOTHING       = \"toast nothing\";\n    private static final ToastUtils DEFAULT_MAKER = make();\n\n    private static WeakReference<IToast> sWeakToast;\n\n    private String     mMode;\n    private int        mGravity            = -1;\n    private int        mXOffset            = -1;\n    private int        mYOffset            = -1;\n    private int        mBgColor            = COLOR_DEFAULT;\n    private int        mBgResource         = -1;\n    private int        mTextColor          = COLOR_DEFAULT;\n    private int        mTextSize           = -1;\n    private boolean    isLong              = false;\n    private Drawable[] mIcons              = new Drawable[4];\n    private boolean    isNotUseSystemToast = false;\n\n    /**\n     * Make a toast.\n     *\n     * @return the single {@link ToastUtils} instance\n     */\n    @NonNull\n    public static ToastUtils make() {\n        return new ToastUtils();\n    }\n\n    /**\n     * @param mode The mode.\n     * @return the single {@link ToastUtils} instance\n     */\n    @NonNull\n    public final ToastUtils setMode(@MODE String mode) {\n        mMode = mode;\n        return this;\n    }\n\n    /**\n     * Set the gravity.\n     *\n     * @param gravity The gravity.\n     * @param xOffset X-axis offset, in pixel.\n     * @param yOffset Y-axis offset, in pixel.\n     * @return the single {@link ToastUtils} instance\n     */\n    @NonNull\n    public final ToastUtils setGravity(final int gravity, final int xOffset, final int yOffset) {\n        mGravity = gravity;\n        mXOffset = xOffset;\n        mYOffset = yOffset;\n        return this;\n    }\n\n    /**\n     * Set the color of background.\n     *\n     * @param backgroundColor The color of background.\n     * @return the single {@link ToastUtils} instance\n     */\n    @NonNull\n    public final ToastUtils setBgColor(@ColorInt final int backgroundColor) {\n        mBgColor = backgroundColor;\n        return this;\n    }\n\n    /**\n     * Set the resource of background.\n     *\n     * @param bgResource The resource of background.\n     * @return the single {@link ToastUtils} instance\n     */\n    @NonNull\n    public final ToastUtils setBgResource(@DrawableRes final int bgResource) {\n        mBgResource = bgResource;\n        return this;\n    }\n\n    /**\n     * Set the text color of toast.\n     *\n     * @param msgColor The text color of toast.\n     * @return the single {@link ToastUtils} instance\n     */\n    @NonNull\n    public final ToastUtils setTextColor(@ColorInt final int msgColor) {\n        mTextColor = msgColor;\n        return this;\n    }\n\n    /**\n     * Set the text size of toast.\n     *\n     * @param textSize The text size of toast.\n     * @return the single {@link ToastUtils} instance\n     */\n    @NonNull\n    public final ToastUtils setTextSize(final int textSize) {\n        mTextSize = textSize;\n        return this;\n    }\n\n    /**\n     * Set the toast for a long period of time.\n     *\n     * @return the single {@link ToastUtils} instance\n     */\n    @NonNull\n    public final ToastUtils setDurationIsLong(boolean isLong) {\n        this.isLong = isLong;\n        return this;\n    }\n\n    /**\n     * Set the left icon of toast.\n     *\n     * @param resId The left icon resource identifier.\n     * @return the single {@link ToastUtils} instance\n     */\n    @NonNull\n    public final ToastUtils setLeftIcon(@DrawableRes int resId) {\n        return setLeftIcon(ContextCompat.getDrawable(Utils.getApp(), resId));\n    }\n\n    /**\n     * Set the left icon of toast.\n     *\n     * @param drawable The left icon drawable.\n     * @return the single {@link ToastUtils} instance\n     */\n    @NonNull\n    public final ToastUtils setLeftIcon(@Nullable Drawable drawable) {\n        mIcons[0] = drawable;\n        return this;\n    }\n\n    /**\n     * Set the top icon of toast.\n     *\n     * @param resId The top icon resource identifier.\n     * @return the single {@link ToastUtils} instance\n     */\n    @NonNull\n    public final ToastUtils setTopIcon(@DrawableRes int resId) {\n        return setTopIcon(ContextCompat.getDrawable(Utils.getApp(), resId));\n    }\n\n    /**\n     * Set the top icon of toast.\n     *\n     * @param drawable The top icon drawable.\n     * @return the single {@link ToastUtils} instance\n     */\n    @NonNull\n    public final ToastUtils setTopIcon(@Nullable Drawable drawable) {\n        mIcons[1] = drawable;\n        return this;\n    }\n\n    /**\n     * Set the right icon of toast.\n     *\n     * @param resId The right icon resource identifier.\n     * @return the single {@link ToastUtils} instance\n     */\n    @NonNull\n    public final ToastUtils setRightIcon(@DrawableRes int resId) {\n        return setRightIcon(ContextCompat.getDrawable(Utils.getApp(), resId));\n    }\n\n    /**\n     * Set the right icon of toast.\n     *\n     * @param drawable The right icon drawable.\n     * @return the single {@link ToastUtils} instance\n     */\n    @NonNull\n    public final ToastUtils setRightIcon(@Nullable Drawable drawable) {\n        mIcons[2] = drawable;\n        return this;\n    }\n\n    /**\n     * Set the left bottom of toast.\n     *\n     * @param resId The bottom icon resource identifier.\n     * @return the single {@link ToastUtils} instance\n     */\n    @NonNull\n    public final ToastUtils setBottomIcon(int resId) {\n        return setBottomIcon(ContextCompat.getDrawable(Utils.getApp(), resId));\n    }\n\n    /**\n     * Set the bottom icon of toast.\n     *\n     * @param drawable The bottom icon drawable.\n     * @return the single {@link ToastUtils} instance\n     */\n    @NonNull\n    public final ToastUtils setBottomIcon(@Nullable Drawable drawable) {\n        mIcons[3] = drawable;\n        return this;\n    }\n\n    /**\n     * Set not use system toast.\n     *\n     * @return the single {@link ToastUtils} instance\n     */\n    @NonNull\n    public final ToastUtils setNotUseSystemToast() {\n        isNotUseSystemToast = true;\n        return this;\n    }\n\n    /**\n     * Return the default {@link ToastUtils} instance.\n     *\n     * @return the default {@link ToastUtils} instance\n     */\n    @NonNull\n    public static ToastUtils getDefaultMaker() {\n        return DEFAULT_MAKER;\n    }\n\n    /**\n     * Show the toast for a short period of time.\n     *\n     * @param text The text.\n     */\n    public final void show(@Nullable final CharSequence text) {\n        show(text, getDuration(), this);\n    }\n\n    /**\n     * Show the toast for a short period of time.\n     *\n     * @param resId The resource id for text.\n     */\n    public final void show(@StringRes final int resId) {\n        show(UtilsBridge.getString(resId), getDuration(), this);\n    }\n\n    /**\n     * Show the toast for a short period of time.\n     *\n     * @param resId The resource id for text.\n     * @param args  The args.\n     */\n    public final void show(@StringRes final int resId, final Object... args) {\n        show(UtilsBridge.getString(resId, args), getDuration(), this);\n    }\n\n    /**\n     * Show the toast for a short period of time.\n     *\n     * @param format The format.\n     * @param args   The args.\n     */\n    public final void show(@Nullable final String format, final Object... args) {\n        show(UtilsBridge.format(format, args), getDuration(), this);\n    }\n\n    /**\n     * Show custom toast.\n     */\n    public final void show(@NonNull final View view) {\n        show(view, getDuration(), this);\n    }\n\n    private int getDuration() {\n        return isLong ? Toast.LENGTH_LONG : Toast.LENGTH_SHORT;\n    }\n\n    private View tryApplyUtilsToastView(final CharSequence text) {\n        if (!MODE.DARK.equals(mMode) && !MODE.LIGHT.equals(mMode)\n                && mIcons[0] == null && mIcons[1] == null && mIcons[2] == null && mIcons[3] == null) {\n            return null;\n        }\n\n        View toastView = UtilsBridge.layoutId2View(R.layout.utils_toast_view);\n        TextView messageTv = toastView.findViewById(android.R.id.message);\n        if (MODE.DARK.equals(mMode)) {\n            GradientDrawable bg = (GradientDrawable) toastView.getBackground().mutate();\n            bg.setColor(Color.parseColor(\"#BB000000\"));\n            messageTv.setTextColor(Color.WHITE);\n        }\n        messageTv.setText(text);\n        if (mIcons[0] != null) {\n            View leftIconView = toastView.findViewById(R.id.utvLeftIconView);\n            ViewCompat.setBackground(leftIconView, mIcons[0]);\n            leftIconView.setVisibility(View.VISIBLE);\n        }\n        if (mIcons[1] != null) {\n            View topIconView = toastView.findViewById(R.id.utvTopIconView);\n            ViewCompat.setBackground(topIconView, mIcons[1]);\n            topIconView.setVisibility(View.VISIBLE);\n        }\n        if (mIcons[2] != null) {\n            View rightIconView = toastView.findViewById(R.id.utvRightIconView);\n            ViewCompat.setBackground(rightIconView, mIcons[2]);\n            rightIconView.setVisibility(View.VISIBLE);\n        }\n        if (mIcons[3] != null) {\n            View bottomIconView = toastView.findViewById(R.id.utvBottomIconView);\n            ViewCompat.setBackground(bottomIconView, mIcons[3]);\n            bottomIconView.setVisibility(View.VISIBLE);\n        }\n        return toastView;\n    }\n\n\n    /**\n     * Show the toast for a short period of time.\n     *\n     * @param text The text.\n     */\n    public static void showShort(@Nullable final CharSequence text) {\n        show(text, Toast.LENGTH_SHORT, DEFAULT_MAKER);\n    }\n\n    /**\n     * Show the toast for a short period of time.\n     *\n     * @param resId The resource id for text.\n     */\n    public static void showShort(@StringRes final int resId) {\n        show(UtilsBridge.getString(resId), Toast.LENGTH_SHORT, DEFAULT_MAKER);\n    }\n\n    /**\n     * Show the toast for a short period of time.\n     *\n     * @param resId The resource id for text.\n     * @param args  The args.\n     */\n    public static void showShort(@StringRes final int resId, final Object... args) {\n        show(UtilsBridge.getString(resId, args), Toast.LENGTH_SHORT, DEFAULT_MAKER);\n    }\n\n    /**\n     * Show the toast for a short period of time.\n     *\n     * @param format The format.\n     * @param args   The args.\n     */\n    public static void showShort(@Nullable final String format, final Object... args) {\n        show(UtilsBridge.format(format, args), Toast.LENGTH_SHORT, DEFAULT_MAKER);\n    }\n\n    /**\n     * Show the toast for a long period of time.\n     *\n     * @param text The text.\n     */\n    public static void showLong(@Nullable final CharSequence text) {\n        show(text, Toast.LENGTH_LONG, DEFAULT_MAKER);\n    }\n\n    /**\n     * Show the toast for a long period of time.\n     *\n     * @param resId The resource id for text.\n     */\n    public static void showLong(@StringRes final int resId) {\n        show(UtilsBridge.getString(resId), Toast.LENGTH_LONG, DEFAULT_MAKER);\n    }\n\n    /**\n     * Show the toast for a long period of time.\n     *\n     * @param resId The resource id for text.\n     * @param args  The args.\n     */\n    public static void showLong(@StringRes final int resId, final Object... args) {\n        show(UtilsBridge.getString(resId, args), Toast.LENGTH_LONG, DEFAULT_MAKER);\n    }\n\n    /**\n     * Show the toast for a long period of time.\n     *\n     * @param format The format.\n     * @param args   The args.\n     */\n    public static void showLong(@Nullable final String format, final Object... args) {\n        show(UtilsBridge.format(format, args), Toast.LENGTH_LONG, DEFAULT_MAKER);\n    }\n\n    /**\n     * Cancel the toast.\n     */\n    public static void cancel() {\n        UtilsBridge.runOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                if (sWeakToast != null) {\n                    final IToast iToast = ToastUtils.sWeakToast.get();\n                    if (iToast != null) {\n                        iToast.cancel();\n                    }\n                    sWeakToast = null;\n                }\n            }\n        });\n    }\n\n    private static void show(@Nullable final CharSequence text, final int duration, final ToastUtils utils) {\n        show(null, getToastFriendlyText(text), duration, utils);\n    }\n\n    private static void show(@NonNull final View view, final int duration, final ToastUtils utils) {\n        show(view, null, duration, utils);\n    }\n\n    private static void show(@Nullable final View view,\n                             @Nullable final CharSequence text,\n                             final int duration,\n                             @NonNull final ToastUtils utils) {\n        UtilsBridge.runOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                cancel();\n                IToast iToast = newToast(utils);\n                ToastUtils.sWeakToast = new WeakReference<>(iToast);\n                if (view != null) {\n                    iToast.setToastView(view);\n                } else {\n                    iToast.setToastView(text);\n                }\n                iToast.show(duration);\n            }\n        });\n    }\n\n    private static CharSequence getToastFriendlyText(CharSequence src) {\n        CharSequence text = src;\n        if (text == null) {\n            text = NULL;\n        } else if (text.length() == 0) {\n            text = NOTHING;\n        }\n        return text;\n    }\n\n    private static IToast newToast(ToastUtils toastUtils) {\n        if (!toastUtils.isNotUseSystemToast) {\n            if (NotificationManagerCompat.from(Utils.getApp()).areNotificationsEnabled()) {\n                if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {\n                    return new SystemToast(toastUtils);\n                }\n                if (!UtilsBridge.isGrantedDrawOverlays()) {\n                    return new SystemToast(toastUtils);\n                }\n            }\n        }\n\n        // not use system or notification disable\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) {\n            return new WindowManagerToast(toastUtils, WindowManager.LayoutParams.TYPE_TOAST);\n        } else if (UtilsBridge.isGrantedDrawOverlays()) {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n                return new WindowManagerToast(toastUtils, WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);\n            } else {\n                return new WindowManagerToast(toastUtils, WindowManager.LayoutParams.TYPE_PHONE);\n            }\n        }\n        return new ActivityToast(toastUtils);\n    }\n\n    static final class SystemToast extends AbsToast {\n\n        SystemToast(ToastUtils toastUtils) {\n            super(toastUtils);\n            if (Build.VERSION.SDK_INT == Build.VERSION_CODES.N_MR1) {\n                try {\n                    //noinspection JavaReflectionMemberAccess\n                    Field mTNField = Toast.class.getDeclaredField(\"mTN\");\n                    mTNField.setAccessible(true);\n                    Object mTN = mTNField.get(mToast);\n                    Field mTNmHandlerField = mTNField.getType().getDeclaredField(\"mHandler\");\n                    mTNmHandlerField.setAccessible(true);\n                    Handler tnHandler = (Handler) mTNmHandlerField.get(mTN);\n                    mTNmHandlerField.set(mTN, new SafeHandler(tnHandler));\n                } catch (Exception ignored) {/**/}\n            }\n        }\n\n        @Override\n        public void show(int duration) {\n            if (mToast == null) return;\n            mToast.setDuration(duration);\n            mToast.show();\n        }\n\n        static class SafeHandler extends Handler {\n            private Handler impl;\n\n            SafeHandler(Handler impl) {\n                this.impl = impl;\n            }\n\n            @Override\n            public void handleMessage(@NonNull Message msg) {\n                impl.handleMessage(msg);\n            }\n\n            @Override\n            public void dispatchMessage(@NonNull Message msg) {\n                try {\n                    impl.dispatchMessage(msg);\n                } catch (Exception e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n    }\n\n    static final class WindowManagerToast extends AbsToast {\n\n        private WindowManager mWM;\n\n        private WindowManager.LayoutParams mParams;\n\n        WindowManagerToast(ToastUtils toastUtils, int type) {\n            super(toastUtils);\n            mParams = new WindowManager.LayoutParams();\n            mWM = (WindowManager) Utils.getApp().getSystemService(Context.WINDOW_SERVICE);\n            mParams.type = type;\n        }\n\n        WindowManagerToast(ToastUtils toastUtils, WindowManager wm, int type) {\n            super(toastUtils);\n            mParams = new WindowManager.LayoutParams();\n            mWM = wm;\n            mParams.type = type;\n        }\n\n        @Override\n        public void show(final int duration) {\n            if (mToast == null) return;\n            mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;\n            mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;\n            mParams.format = PixelFormat.TRANSLUCENT;\n            mParams.windowAnimations = android.R.style.Animation_Toast;\n            mParams.setTitle(\"ToastWithoutNotification\");\n            mParams.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON\n                    | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE\n                    | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;\n            mParams.packageName = Utils.getApp().getPackageName();\n\n            mParams.gravity = mToast.getGravity();\n            if ((mParams.gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {\n                mParams.horizontalWeight = 1.0f;\n            }\n            if ((mParams.gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {\n                mParams.verticalWeight = 1.0f;\n            }\n\n            mParams.x = mToast.getXOffset();\n            mParams.y = mToast.getYOffset();\n            mParams.horizontalMargin = mToast.getHorizontalMargin();\n            mParams.verticalMargin = mToast.getVerticalMargin();\n\n            try {\n                if (mWM != null) {\n                    mWM.addView(mToastView, mParams);\n                }\n            } catch (Exception ignored) {/**/}\n\n            UtilsBridge.runOnUiThreadDelayed(new Runnable() {\n                @Override\n                public void run() {\n                    cancel();\n                }\n            }, duration == Toast.LENGTH_SHORT ? 2000 : 3500);\n        }\n\n        @Override\n        public void cancel() {\n            try {\n                if (mWM != null) {\n                    mWM.removeViewImmediate(mToastView);\n                    mWM = null;\n                }\n            } catch (Exception ignored) {/**/}\n            super.cancel();\n        }\n    }\n\n    static final class ActivityToast extends AbsToast {\n\n        private static int sShowingIndex = 0;\n\n        private Utils.ActivityLifecycleCallbacks mActivityLifecycleCallbacks;\n        private IToast                           iToast;\n\n        ActivityToast(ToastUtils toastUtils) {\n            super(toastUtils);\n        }\n\n        @Override\n        public void show(int duration) {\n            if (mToast == null) return;\n            if (!UtilsBridge.isAppForeground()) {\n                // try to use system toast\n                iToast = showSystemToast(duration);\n                return;\n            }\n            boolean hasAliveActivity = false;\n            for (final Activity activity : UtilsBridge.getActivityList()) {\n                if (!UtilsBridge.isActivityAlive(activity)) {\n                    continue;\n                }\n                if (!hasAliveActivity) {\n                    hasAliveActivity = true;\n                    iToast = showWithActivityWindow(activity, duration);\n                } else {\n                    showWithActivityView(activity, sShowingIndex, true);\n                }\n            }\n            if (hasAliveActivity) {\n                registerLifecycleCallback();\n                UtilsBridge.runOnUiThreadDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        cancel();\n                    }\n                }, duration == Toast.LENGTH_SHORT ? 2000 : 3500);\n\n                ++sShowingIndex;\n            } else {\n                // try to use system toast\n                iToast = showSystemToast(duration);\n            }\n        }\n\n        @Override\n        public void cancel() {\n            if (isShowing()) {\n                unregisterLifecycleCallback();\n                for (Activity activity : UtilsBridge.getActivityList()) {\n                    if (!UtilsBridge.isActivityAlive(activity)) {\n                        continue;\n                    }\n                    final Window window = activity.getWindow();\n                    if (window != null) {\n                        ViewGroup decorView = (ViewGroup) window.getDecorView();\n                        View toastView = decorView.findViewWithTag(TAG_TOAST + (sShowingIndex - 1));\n                        if (toastView != null) {\n                            try {\n                                decorView.removeView(toastView);\n                            } catch (Exception ignored) {/**/}\n                        }\n                    }\n                }\n            }\n            if (iToast != null) {\n                iToast.cancel();\n                iToast = null;\n            }\n            super.cancel();\n        }\n\n        private IToast showSystemToast(int duration) {\n            SystemToast systemToast = new SystemToast(mToastUtils);\n            systemToast.mToast = mToast;\n            systemToast.show(duration);\n            return systemToast;\n        }\n\n        private IToast showWithActivityWindow(Activity activity, int duration) {\n            WindowManagerToast wmToast = new WindowManagerToast(mToastUtils, activity.getWindowManager(), WindowManager.LayoutParams.LAST_APPLICATION_WINDOW);\n            wmToast.mToastView = getToastViewSnapshot(-1);\n            wmToast.mToast = mToast;\n            wmToast.show(duration);\n            return wmToast;\n        }\n\n        private void showWithActivityView(final Activity activity, final int index, boolean useAnim) {\n            final Window window = activity.getWindow();\n            if (window != null) {\n                final ViewGroup decorView = (ViewGroup) window.getDecorView();\n                FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(\n                        ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT\n                );\n                lp.gravity = mToast.getGravity();\n                lp.bottomMargin = mToast.getYOffset() + UtilsBridge.getNavBarHeight();\n                lp.topMargin = mToast.getYOffset() + UtilsBridge.getStatusBarHeight();\n                lp.leftMargin = mToast.getXOffset();\n                View toastViewSnapshot = getToastViewSnapshot(index);\n                if (useAnim) {\n                    toastViewSnapshot.setAlpha(0);\n                    toastViewSnapshot.animate().alpha(1).setDuration(200).start();\n                }\n                decorView.addView(toastViewSnapshot, lp);\n            }\n        }\n\n        private void registerLifecycleCallback() {\n            final int index = sShowingIndex;\n            mActivityLifecycleCallbacks = new Utils.ActivityLifecycleCallbacks() {\n                @Override\n                public void onActivityCreated(@NonNull Activity activity) {\n                    if (isShowing()) {\n                        showWithActivityView(activity, index, false);\n                    }\n                }\n            };\n            UtilsBridge.addActivityLifecycleCallbacks(mActivityLifecycleCallbacks);\n        }\n\n        private void unregisterLifecycleCallback() {\n            UtilsBridge.removeActivityLifecycleCallbacks(mActivityLifecycleCallbacks);\n            mActivityLifecycleCallbacks = null;\n        }\n\n        private boolean isShowing() {\n            return mActivityLifecycleCallbacks != null;\n        }\n    }\n\n    static abstract class AbsToast implements IToast {\n\n        protected Toast      mToast;\n        protected ToastUtils mToastUtils;\n        protected View       mToastView;\n\n        AbsToast(ToastUtils toastUtils) {\n            mToast = new Toast(Utils.getApp());\n            mToastUtils = toastUtils;\n\n            if (mToastUtils.mGravity != -1 || mToastUtils.mXOffset != -1 || mToastUtils.mYOffset != -1) {\n                mToast.setGravity(mToastUtils.mGravity, mToastUtils.mXOffset, mToastUtils.mYOffset);\n            }\n        }\n\n        @Override\n        public void setToastView(View view) {\n            mToastView = view;\n            mToast.setView(mToastView);\n        }\n\n        @Override\n        public void setToastView(CharSequence text) {\n            View utilsToastView = mToastUtils.tryApplyUtilsToastView(text);\n            if (utilsToastView != null) {\n                setToastView(utilsToastView);\n                processRtlIfNeed();\n                return;\n            }\n\n            mToastView = mToast.getView();\n            if (mToastView == null || mToastView.findViewById(android.R.id.message) == null) {\n                setToastView(UtilsBridge.layoutId2View(R.layout.utils_toast_view));\n            }\n\n            TextView messageTv = mToastView.findViewById(android.R.id.message);\n            messageTv.setText(text);\n            if (mToastUtils.mTextColor != COLOR_DEFAULT) {\n                messageTv.setTextColor(mToastUtils.mTextColor);\n            }\n            if (mToastUtils.mTextSize != -1) {\n                messageTv.setTextSize(mToastUtils.mTextSize);\n            }\n            setBg(messageTv);\n            processRtlIfNeed();\n        }\n\n        private void processRtlIfNeed() {\n            if (UtilsBridge.isLayoutRtl()) {\n                setToastView(getToastViewSnapshot(-1));\n            }\n        }\n\n        private void setBg(final TextView msgTv) {\n            if (mToastUtils.mBgResource != -1) {\n                mToastView.setBackgroundResource(mToastUtils.mBgResource);\n                msgTv.setBackgroundColor(Color.TRANSPARENT);\n            } else if (mToastUtils.mBgColor != COLOR_DEFAULT) {\n                Drawable toastBg = mToastView.getBackground();\n                Drawable msgBg = msgTv.getBackground();\n                if (toastBg != null && msgBg != null) {\n                    toastBg.mutate().setColorFilter(new PorterDuffColorFilter(mToastUtils.mBgColor, PorterDuff.Mode.SRC_IN));\n                    msgTv.setBackgroundColor(Color.TRANSPARENT);\n                } else if (toastBg != null) {\n                    toastBg.mutate().setColorFilter(new PorterDuffColorFilter(mToastUtils.mBgColor, PorterDuff.Mode.SRC_IN));\n                } else if (msgBg != null) {\n                    msgBg.mutate().setColorFilter(new PorterDuffColorFilter(mToastUtils.mBgColor, PorterDuff.Mode.SRC_IN));\n                } else {\n                    mToastView.setBackgroundColor(mToastUtils.mBgColor);\n                }\n            }\n        }\n\n        @Override\n        @CallSuper\n        public void cancel() {\n            if (mToast != null) {\n                mToast.cancel();\n            }\n            mToast = null;\n            mToastView = null;\n        }\n\n        View getToastViewSnapshot(final int index) {\n            Bitmap bitmap = UtilsBridge.view2Bitmap(mToastView);\n            ImageView toastIv = new ImageView(Utils.getApp());\n            toastIv.setTag(TAG_TOAST + index);\n            toastIv.setImageBitmap(bitmap);\n            return toastIv;\n        }\n    }\n\n    interface IToast {\n\n        void setToastView(View view);\n\n        void setToastView(CharSequence text);\n\n        void show(int duration);\n\n        void cancel();\n    }\n\n    public static final class UtilsMaxWidthRelativeLayout extends RelativeLayout {\n\n        private static final int SPACING = UtilsBridge.dp2px(80);\n\n        public UtilsMaxWidthRelativeLayout(Context context) {\n            super(context);\n        }\n\n        public UtilsMaxWidthRelativeLayout(Context context, AttributeSet attrs) {\n            super(context, attrs);\n        }\n\n        public UtilsMaxWidthRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {\n            super(context, attrs, defStyleAttr);\n        }\n\n        @Override\n        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n            int widthMaxSpec = MeasureSpec.makeMeasureSpec(UtilsBridge.getAppScreenWidth() - SPACING, MeasureSpec.AT_MOST);\n            super.onMeasure(widthMaxSpec, heightMeasureSpec);\n        }\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/TouchUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.view.MotionEvent;\nimport android.view.VelocityTracker;\nimport android.view.View;\nimport android.view.ViewConfiguration;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport androidx.annotation.IntDef;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/26\n *     desc  : utils about touch\n * </pre>\n */\npublic class TouchUtils {\n\n    public static final int UNKNOWN = 0;\n    public static final int LEFT    = 1;\n    public static final int UP      = 2;\n    public static final int RIGHT   = 4;\n    public static final int DOWN    = 8;\n\n    @IntDef({LEFT, UP, RIGHT, DOWN})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface Direction {\n    }\n\n    private TouchUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    public static void setOnTouchListener(final View v, final OnTouchUtilsListener listener) {\n        if (v == null || listener == null) {\n            return;\n        }\n        v.setOnTouchListener(listener);\n    }\n\n    public static abstract class OnTouchUtilsListener implements View.OnTouchListener {\n\n        private static final int STATE_DOWN = 0;\n        private static final int STATE_MOVE = 1;\n        private static final int STATE_STOP = 2;\n\n        private static final int MIN_TAP_TIME = 1000;\n\n        private int touchSlop;\n        private int downX, downY, lastX, lastY;\n        private int             state;\n        private int             direction;\n        private VelocityTracker velocityTracker;\n        private int             maximumFlingVelocity;\n        private int             minimumFlingVelocity;\n\n        public OnTouchUtilsListener() {\n            resetTouch(-1, -1);\n        }\n\n        private void resetTouch(int x, int y) {\n            downX = x;\n            downY = y;\n            lastX = x;\n            lastY = y;\n            state = STATE_DOWN;\n            direction = UNKNOWN;\n            if (velocityTracker != null) {\n                velocityTracker.clear();\n            }\n        }\n\n        public abstract boolean onDown(View view, int x, int y, MotionEvent event);\n\n        public abstract boolean onMove(View view,\n                                       @Direction int direction,\n                                       int x,\n                                       int y,\n                                       int dx,\n                                       int dy,\n                                       int totalX,\n                                       int totalY,\n                                       MotionEvent event);\n\n        public abstract boolean onStop(View view,\n                                       @Direction int direction,\n                                       int x,\n                                       int y,\n                                       int totalX,\n                                       int totalY,\n                                       int vx,\n                                       int vy,\n                                       MotionEvent event);\n\n        @Override\n        public boolean onTouch(View v, MotionEvent event) {\n            if (touchSlop == 0) {\n                touchSlop = ViewConfiguration.get(v.getContext()).getScaledTouchSlop();\n            }\n            if (maximumFlingVelocity == 0) {\n                maximumFlingVelocity =\n                        ViewConfiguration.get(v.getContext()).getScaledMaximumFlingVelocity();\n            }\n            if (minimumFlingVelocity == 0) {\n                minimumFlingVelocity =\n                        ViewConfiguration.get(v.getContext()).getScaledMinimumFlingVelocity();\n            }\n            if (velocityTracker == null) {\n                velocityTracker = VelocityTracker.obtain();\n            }\n            velocityTracker.addMovement(event);\n\n            switch (event.getAction()) {\n                case MotionEvent.ACTION_DOWN:\n                    return onUtilsDown(v, event);\n                case MotionEvent.ACTION_MOVE:\n                    return onUtilsMove(v, event);\n                case MotionEvent.ACTION_UP:\n                case MotionEvent.ACTION_CANCEL:\n                    return onUtilsStop(v, event);\n                default:\n                    break;\n            }\n            return false;\n        }\n\n        public boolean onUtilsDown(View view, MotionEvent event) {\n            int x = (int) event.getRawX();\n            int y = (int) event.getRawY();\n\n            resetTouch(x, y);\n            view.setPressed(true);\n            return onDown(view, x, y, event);\n        }\n\n        public boolean onUtilsMove(View view, MotionEvent event) {\n            int x = (int) event.getRawX();\n            int y = (int) event.getRawY();\n\n            if (downX == -1) {\n                // not receive down should reset\n                resetTouch(x, y);\n                view.setPressed(true);\n            }\n\n            if (state != STATE_MOVE) {\n                if (Math.abs(x - lastX) < touchSlop && Math.abs(y - lastY) < touchSlop) {\n                    return true;\n                }\n                state = STATE_MOVE;\n                if (Math.abs(x - lastX) >= Math.abs(y - lastY)) {\n                    if (x - lastX < 0) {\n                        direction = LEFT;\n                    } else {\n                        direction = RIGHT;\n                    }\n                } else {\n                    if (y - lastY < 0) {\n                        direction = UP;\n                    } else {\n                        direction = DOWN;\n                    }\n                }\n            }\n\n            boolean consumeMove =\n                    onMove(view, direction, x, y, x - lastX, y - lastY, x - downX, y - downY, event);\n            lastX = x;\n            lastY = y;\n            return consumeMove;\n        }\n\n        public boolean onUtilsStop(View view, MotionEvent event) {\n            int x = (int) event.getRawX();\n            int y = (int) event.getRawY();\n\n            int vx = 0, vy = 0;\n\n            if (velocityTracker != null) {\n                velocityTracker.computeCurrentVelocity(1000, maximumFlingVelocity);\n                vx = (int) velocityTracker.getXVelocity();\n                vy = (int) velocityTracker.getYVelocity();\n                velocityTracker.recycle();\n                if (Math.abs(vx) < minimumFlingVelocity) {\n                    vx = 0;\n                }\n                if (Math.abs(vy) < minimumFlingVelocity) {\n                    vy = 0;\n                }\n                velocityTracker = null;\n            }\n\n            view.setPressed(false);\n            boolean consumeStop = onStop(view, direction, x, y, x - downX, y - downY, vx, vy, event);\n\n            if (event.getAction() == MotionEvent.ACTION_UP) {\n                if (state == STATE_DOWN) {\n                    if (event.getEventTime() - event.getDownTime() <= MIN_TAP_TIME) {\n                        view.performClick();\n                    } else {\n                        view.performLongClick();\n                    }\n                }\n            }\n\n            resetTouch(-1, -1);\n\n            return consumeStop;\n        }\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/UiMessageUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.os.Message;\nimport android.util.Log;\nimport android.util.SparseArray;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport androidx.annotation.NonNull;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/10/20\n *     desc  : utils about ui message can replace LocalBroadcastManager\n * </pre>\n */\npublic final class UiMessageUtils implements Handler.Callback {\n\n    private static final String  TAG   = \"UiMessageUtils\";\n    private static final boolean DEBUG = UtilsBridge.isAppDebug();\n\n    private final Handler   mHandler = new Handler(Looper.getMainLooper(), this);\n    private final UiMessage mMessage = new UiMessage(null);\n\n    private final SparseArray<List<UiMessageCallback>> mListenersSpecific  = new SparseArray<>();\n    private final List<UiMessageCallback>              mListenersUniversal = new ArrayList<>();\n    private final List<UiMessageCallback>              mDefensiveCopyList  = new ArrayList<>();\n\n    public static UiMessageUtils getInstance() {\n        return LazyHolder.INSTANCE;\n    }\n\n    private UiMessageUtils() {\n    }\n\n    /**\n     * Sends an empty Message containing only the message ID.\n     *\n     * @param id The message ID.\n     */\n    public final void send(final int id) {\n        mHandler.sendEmptyMessage(id);\n    }\n\n    /**\n     * Sends a message containing the ID and an arbitrary object.\n     *\n     * @param id  The message ID.\n     * @param obj The object.\n     */\n    public final void send(final int id, @NonNull final Object obj) {\n        mHandler.sendMessage(mHandler.obtainMessage(id, obj));\n    }\n\n    /**\n     * Add listener for specific type of message by its ID.\n     * Don't forget to call {@link #removeListener(UiMessageCallback)} or\n     * {@link #removeListeners(int)}\n     *\n     * @param id       The ID of message that will be only notified to listener.\n     * @param listener The listener.\n     */\n    public void addListener(int id, @NonNull final UiMessageCallback listener) {\n        synchronized (mListenersSpecific) {\n            List<UiMessageCallback> idListeners = mListenersSpecific.get(id);\n            if (idListeners == null) {\n                idListeners = new ArrayList<>();\n                mListenersSpecific.put(id, idListeners);\n            }\n            if (!idListeners.contains(listener)) {\n                idListeners.add(listener);\n            }\n        }\n    }\n\n    /**\n     * Add listener for all messages.\n     *\n     * @param listener The listener.\n     */\n    public void addListener(@NonNull final UiMessageCallback listener) {\n        synchronized (mListenersUniversal) {\n            if (!mListenersUniversal.contains(listener)) {\n                mListenersUniversal.add(listener);\n            } else {\n                if (DEBUG) {\n                    Log.w(TAG, \"Listener is already added. \" + listener.toString());\n                }\n            }\n        }\n    }\n\n    /**\n     * Remove listener for all messages.\n     *\n     * @param listener The listener to remove.\n     */\n    public void removeListener(@NonNull final UiMessageCallback listener) {\n        synchronized (mListenersUniversal) {\n            if (DEBUG && !mListenersUniversal.contains(listener)) {\n                Log.w(TAG, \"Trying to remove a listener that is not registered. \" + listener.toString());\n            }\n            mListenersUniversal.remove(listener);\n        }\n    }\n\n    /**\n     * Remove all listeners for desired message ID.\n     *\n     * @param id The id of the message to stop listening to.\n     */\n    public void removeListeners(final int id) {\n        if (DEBUG) {\n            final List<UiMessageCallback> callbacks = mListenersSpecific.get(id);\n            if (callbacks == null || callbacks.size() == 0) {\n                Log.w(TAG, \"Trying to remove specific listeners that are not registered. ID \" + id);\n            }\n        }\n        synchronized (mListenersSpecific) {\n            mListenersSpecific.delete(id);\n        }\n    }\n\n    /**\n     * Remove the specific listener for desired message ID.\n     *\n     * @param id       The id of the message to stop listening to.\n     * @param listener The listener which should be removed.\n     */\n    public void removeListener(final int id, @NonNull final UiMessageCallback listener) {\n        synchronized (mListenersSpecific) {\n            final List<UiMessageCallback> callbacks = this.mListenersSpecific.get(id);\n            if (callbacks != null && !callbacks.isEmpty()) {\n                if (DEBUG) {\n                    if (!callbacks.contains(listener)) {\n                        Log.w(TAG, \"Trying to remove specific listener that is not registered. ID \" + id + \", \" + listener);\n                        return;\n                    }\n                }\n                callbacks.remove(listener);\n            } else {\n                if (DEBUG) {\n                    Log.w(TAG, \"Trying to remove specific listener that is not registered. ID \" + id + \", \" + listener);\n                }\n            }\n        }\n    }\n\n    @Override\n    public boolean handleMessage(Message msg) {\n        mMessage.setMessage(msg);\n        if (DEBUG) {\n            logMessageHandling(mMessage);\n        }\n\n        // process listeners for specified type of message what\n        synchronized (mListenersSpecific) {\n            final List<UiMessageCallback> idListeners = mListenersSpecific.get(msg.what);\n            if (idListeners != null) {\n                if (idListeners.size() == 0) {\n                    mListenersSpecific.remove(msg.what);\n                } else {\n                    mDefensiveCopyList.addAll(idListeners);\n                    for (final UiMessageCallback callback : mDefensiveCopyList) {\n                        callback.handleMessage(mMessage);\n                    }\n                    mDefensiveCopyList.clear();\n                }\n            }\n        }\n\n        // process universal listeners\n        synchronized (mListenersUniversal) {\n            if (mListenersUniversal.size() > 0) {\n                mDefensiveCopyList.addAll(mListenersUniversal);\n                for (final UiMessageCallback callback : mDefensiveCopyList) {\n                    callback.handleMessage(mMessage);\n                }\n                mDefensiveCopyList.clear();\n            }\n        }\n\n        mMessage.setMessage(null);\n\n        return true;\n    }\n\n    private void logMessageHandling(@NonNull final UiMessage msg) {\n        final List<UiMessageCallback> idListeners = mListenersSpecific.get(msg.getId());\n\n        if ((idListeners == null || idListeners.size() == 0) && mListenersUniversal.size() == 0) {\n            Log.w(TAG, \"Delivering FAILED for message ID \" + msg.getId() + \". No listeners. \" + msg.toString());\n        } else {\n            final StringBuilder stringBuilder = new StringBuilder();\n            stringBuilder.append(\"Delivering message ID \");\n            stringBuilder.append(msg.getId());\n            stringBuilder.append(\", Specific listeners: \");\n            if (idListeners == null || idListeners.size() == 0) {\n                stringBuilder.append(0);\n            } else {\n                stringBuilder.append(idListeners.size());\n                stringBuilder.append(\" [\");\n                for (int i = 0; i < idListeners.size(); i++) {\n                    stringBuilder.append(idListeners.get(i).getClass().getSimpleName());\n                    if (i < idListeners.size() - 1) {\n                        stringBuilder.append(\",\");\n                    }\n                }\n                stringBuilder.append(\"]\");\n            }\n\n            stringBuilder.append(\", Universal listeners: \");\n            synchronized (mListenersUniversal) {\n                if (mListenersUniversal.size() == 0) {\n                    stringBuilder.append(0);\n                } else {\n                    stringBuilder.append(mListenersUniversal.size());\n                    stringBuilder.append(\" [\");\n                    for (int i = 0; i < mListenersUniversal.size(); i++) {\n                        stringBuilder.append(mListenersUniversal.get(i).getClass().getSimpleName());\n                        if (i < mListenersUniversal.size() - 1) {\n                            stringBuilder.append(\",\");\n                        }\n                    }\n                    stringBuilder.append(\"], Message: \");\n                }\n            }\n            stringBuilder.append(msg.toString());\n\n            Log.v(TAG, stringBuilder.toString());\n        }\n    }\n\n    public static final class UiMessage {\n\n        private Message mMessage;\n\n        private UiMessage(final Message message) {\n            this.mMessage = message;\n        }\n\n        private void setMessage(final Message message) {\n            mMessage = message;\n        }\n\n        public int getId() {\n            return mMessage.what;\n        }\n\n        public Object getObject() {\n            return mMessage.obj;\n        }\n\n        @Override\n        public String toString() {\n            return \"{ \" +\n                    \"id=\" + getId() +\n                    \", obj=\" + getObject() +\n                    \" }\";\n        }\n    }\n\n    public interface UiMessageCallback {\n        void handleMessage(@NonNull UiMessage localMessage);\n    }\n\n    private static final class LazyHolder {\n        private static final UiMessageUtils INSTANCE = new UiMessageUtils();\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/UriUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.content.ContentResolver;\nimport android.content.ContentUris;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Environment;\nimport android.os.storage.StorageManager;\nimport android.provider.DocumentsContract;\nimport android.provider.MediaStore;\nimport android.text.TextUtils;\nimport android.util.Log;\n\nimport java.io.File;\nimport java.io.FileNotFoundException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.lang.reflect.Array;\nimport java.lang.reflect.Method;\n\nimport androidx.core.content.FileProvider;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/04/20\n *     desc  : utils about uri\n * </pre>\n */\npublic final class UriUtils {\n\n    private UriUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Resource to uri.\n     * <p>res2Uri([res type]/[res name]) -> res2Uri(drawable/icon), res2Uri(raw/icon)</p>\n     * <p>res2Uri([resource_id]) -> res2Uri(R.drawable.icon)</p>\n     *\n     * @param resPath The path of res.\n     * @return uri\n     */\n    public static Uri res2Uri(String resPath) {\n        return Uri.parse(\"android.resource://\" + Utils.getApp().getPackageName() + \"/\" + resPath);\n    }\n\n    /**\n     * File to uri.\n     *\n     * @param file The file.\n     * @return uri\n     */\n    public static Uri file2Uri(final File file) {\n        if (!UtilsBridge.isFileExists(file)) return null;\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n            String authority = Utils.getApp().getPackageName() + \".utilcode.fileprovider\";\n            return FileProvider.getUriForFile(Utils.getApp(), authority, file);\n        } else {\n            return Uri.fromFile(file);\n        }\n    }\n\n    /**\n     * Uri to file.\n     *\n     * @param uri The uri.\n     * @return file\n     */\n    public static File uri2File(final Uri uri) {\n        if (uri == null) return null;\n        File file = uri2FileReal(uri);\n        if (file != null) return file;\n        return copyUri2Cache(uri);\n    }\n\n    /**\n     * Uri to file, without creating the cache copy if the path cannot be resolved.\n     *\n     * @param uri The uri.\n     * @return file\n     */\n    public static File uri2FileNoCacheCopy(final Uri uri) {\n        if (uri == null) return null;\n        return uri2FileReal(uri);\n    }\n    \n    /**\n     * Uri to file.\n     *\n     * @param uri The uri.\n     * @return file\n     */\n    private static File uri2FileReal(final Uri uri) {\n        Log.d(\"UriUtils\", uri.toString());\n        String authority = uri.getAuthority();\n        String scheme = uri.getScheme();\n        String path = uri.getPath();\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && path != null) {\n            String[] externals = new String[]{\"/external/\", \"/external_path/\"};\n            File file = null;\n            for (String external : externals) {\n                if (path.startsWith(external)) {\n                    file = new File(Environment.getExternalStorageDirectory().getAbsolutePath()\n                            + path.replace(external, \"/\"));\n                    if (file.exists()) {\n                        Log.d(\"UriUtils\", uri.toString() + \" -> \" + external);\n                        return file;\n                    }\n                }\n            }\n            file = null;\n            if (path.startsWith(\"/files_path/\")) {\n                file = new File(Utils.getApp().getFilesDir().getAbsolutePath()\n                        + path.replace(\"/files_path/\", \"/\"));\n            } else if (path.startsWith(\"/cache_path/\")) {\n                file = new File(Utils.getApp().getCacheDir().getAbsolutePath()\n                        + path.replace(\"/cache_path/\", \"/\"));\n            } else if (path.startsWith(\"/external_files_path/\")) {\n                file = new File(Utils.getApp().getExternalFilesDir(null).getAbsolutePath()\n                        + path.replace(\"/external_files_path/\", \"/\"));\n            } else if (path.startsWith(\"/external_cache_path/\")) {\n                file = new File(Utils.getApp().getExternalCacheDir().getAbsolutePath()\n                        + path.replace(\"/external_cache_path/\", \"/\"));\n            }\n            if (file != null && file.exists()) {\n                Log.d(\"UriUtils\", uri.toString() + \" -> \" + path);\n                return file;\n            }\n        }\n        if (ContentResolver.SCHEME_FILE.equals(scheme)) {\n            if (path != null) return new File(path);\n            Log.d(\"UriUtils\", uri.toString() + \" parse failed. -> 0\");\n            return null;\n        }// end 0\n        else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT\n                && DocumentsContract.isDocumentUri(Utils.getApp(), uri)) {\n            if (\"com.android.externalstorage.documents\".equals(authority)) {\n                final String docId = DocumentsContract.getDocumentId(uri);\n                final String[] split = docId.split(\":\");\n                final String type = split[0];\n                if (\"primary\".equalsIgnoreCase(type)) {\n                    return new File(Environment.getExternalStorageDirectory() + \"/\" + split[1]);\n                } else {\n                    // Below logic is how External Storage provider build URI for documents\n                    // http://stackoverflow.com/questions/28605278/android-5-sd-card-label\n                    StorageManager mStorageManager = (StorageManager) Utils.getApp().getSystemService(Context.STORAGE_SERVICE);\n                    try {\n                        Class<?> storageVolumeClazz = Class.forName(\"android.os.storage.StorageVolume\");\n                        Method getVolumeList = mStorageManager.getClass().getMethod(\"getVolumeList\");\n                        Method getUuid = storageVolumeClazz.getMethod(\"getUuid\");\n                        Method getState = storageVolumeClazz.getMethod(\"getState\");\n                        Method getPath = storageVolumeClazz.getMethod(\"getPath\");\n                        Method isPrimary = storageVolumeClazz.getMethod(\"isPrimary\");\n                        Method isEmulated = storageVolumeClazz.getMethod(\"isEmulated\");\n\n                        Object result = getVolumeList.invoke(mStorageManager);\n\n                        final int length = Array.getLength(result);\n                        for (int i = 0; i < length; i++) {\n                            Object storageVolumeElement = Array.get(result, i);\n                            //String uuid = (String) getUuid.invoke(storageVolumeElement);\n\n                            final boolean mounted = Environment.MEDIA_MOUNTED.equals(getState.invoke(storageVolumeElement))\n                                    || Environment.MEDIA_MOUNTED_READ_ONLY.equals(getState.invoke(storageVolumeElement));\n\n                            //if the media is not mounted, we need not get the volume details\n                            if (!mounted) continue;\n\n                            //Primary storage is already handled.\n                            if ((Boolean) isPrimary.invoke(storageVolumeElement)\n                                    && (Boolean) isEmulated.invoke(storageVolumeElement)) {\n                                continue;\n                            }\n\n                            String uuid = (String) getUuid.invoke(storageVolumeElement);\n\n                            if (uuid != null && uuid.equals(type)) {\n                                return new File(getPath.invoke(storageVolumeElement) + \"/\" + split[1]);\n                            }\n                        }\n                    } catch (Exception ex) {\n                        Log.d(\"UriUtils\", uri.toString() + \" parse failed. \" + ex.toString() + \" -> 1_0\");\n                    }\n                }\n                Log.d(\"UriUtils\", uri.toString() + \" parse failed. -> 1_0\");\n                return null;\n            }// end 1_0\n            else if (\"com.android.providers.downloads.documents\".equals(authority)) {\n                String id = DocumentsContract.getDocumentId(uri);\n                if (TextUtils.isEmpty(id)) {\n                    Log.d(\"UriUtils\", uri.toString() + \" parse failed(id is null). -> 1_1\");\n                    return null;\n                }\n                if (id.startsWith(\"raw:\")) {\n                    return new File(id.substring(4));\n                } else if (id.startsWith(\"msf:\")) {\n                    id = id.split(\":\")[1];\n                }\n\n                long availableId = 0;\n                try {\n                    availableId = Long.parseLong(id);\n                } catch (Exception e) {\n                    return null;\n                }\n\n                String[] contentUriPrefixesToTry = new String[]{\n                        \"content://downloads/public_downloads\",\n                        \"content://downloads/all_downloads\",\n                        \"content://downloads/my_downloads\"\n                };\n\n                for (String contentUriPrefix : contentUriPrefixesToTry) {\n                    Uri contentUri = ContentUris.withAppendedId(Uri.parse(contentUriPrefix), availableId);\n                    try {\n                        File file = getFileFromUri(contentUri, \"1_1\");\n                        if (file != null) {\n                            return file;\n                        }\n                    } catch (Exception ignore) {\n                    }\n                }\n\n                Log.d(\"UriUtils\", uri.toString() + \" parse failed. -> 1_1\");\n                return null;\n            }// end 1_1\n            else if (\"com.android.providers.media.documents\".equals(authority)) {\n                final String docId = DocumentsContract.getDocumentId(uri);\n                final String[] split = docId.split(\":\");\n                final String type = split[0];\n                Uri contentUri;\n                if (\"image\".equals(type)) {\n                    contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;\n                } else if (\"video\".equals(type)) {\n                    contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;\n                } else if (\"audio\".equals(type)) {\n                    contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;\n                } else {\n                    Log.d(\"UriUtils\", uri.toString() + \" parse failed. -> 1_2\");\n                    return null;\n                }\n                final String selection = \"_id=?\";\n                final String[] selectionArgs = new String[]{split[1]};\n                return getFileFromUri(contentUri, selection, selectionArgs, \"1_2\");\n            }// end 1_2\n            else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {\n                return getFileFromUri(uri, \"1_3\");\n            }// end 1_3\n            else {\n                Log.d(\"UriUtils\", uri.toString() + \" parse failed. -> 1_4\");\n                return null;\n            }// end 1_4\n        }// end 1\n        else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {\n            return getFileFromUri(uri, \"2\");\n        }// end 2\n        else {\n            Log.d(\"UriUtils\", uri.toString() + \" parse failed. -> 3\");\n            return null;\n        }// end 3\n    }\n\n    private static File getFileFromUri(final Uri uri, final String code) {\n        return getFileFromUri(uri, null, null, code);\n    }\n\n    private static File getFileFromUri(final Uri uri,\n                                       final String selection,\n                                       final String[] selectionArgs,\n                                       final String code) {\n        if (\"com.google.android.apps.photos.content\".equals(uri.getAuthority())) {\n            if (!TextUtils.isEmpty(uri.getLastPathSegment())) {\n                return new File(uri.getLastPathSegment());\n            }\n        } else if (\"com.tencent.mtt.fileprovider\".equals(uri.getAuthority())) {\n            String path = uri.getPath();\n            if (!TextUtils.isEmpty(path)) {\n                File fileDir = Environment.getExternalStorageDirectory();\n                return new File(fileDir, path.substring(\"/QQBrowser\".length(), path.length()));\n            }\n        } else if (\"com.huawei.hidisk.fileprovider\".equals(uri.getAuthority())) {\n            String path = uri.getPath();\n            if (!TextUtils.isEmpty(path)) {\n                return new File(path.replace(\"/root\", \"\"));\n            }\n        }\n\n        final Cursor cursor = Utils.getApp().getContentResolver().query(\n                uri, new String[]{\"_data\"}, selection, selectionArgs, null);\n        if (cursor == null) {\n            Log.d(\"UriUtils\", uri.toString() + \" parse failed(cursor is null). -> \" + code);\n            return null;\n        }\n        try {\n            if (cursor.moveToFirst()) {\n                final int columnIndex = cursor.getColumnIndex(\"_data\");\n                if (columnIndex > -1) {\n                    return new File(cursor.getString(columnIndex));\n                } else {\n                    Log.d(\"UriUtils\", uri.toString() + \" parse failed(columnIndex: \" + columnIndex + \" is wrong). -> \" + code);\n                    return null;\n                }\n            } else {\n                Log.d(\"UriUtils\", uri.toString() + \" parse failed(moveToFirst return false). -> \" + code);\n                return null;\n            }\n        } catch (Exception e) {\n            Log.d(\"UriUtils\", uri.toString() + \" parse failed. -> \" + code);\n            return null;\n        } finally {\n            cursor.close();\n        }\n    }\n\n    private static File copyUri2Cache(Uri uri) {\n        Log.d(\"UriUtils\", \"copyUri2Cache() called\");\n        InputStream is = null;\n        try {\n            is = Utils.getApp().getContentResolver().openInputStream(uri);\n            File file = new File(Utils.getApp().getCacheDir(), \"\" + System.currentTimeMillis());\n            UtilsBridge.writeFileFromIS(file.getAbsolutePath(), is);\n            return file;\n        } catch (FileNotFoundException e) {\n            e.printStackTrace();\n            return null;\n        } finally {\n            if (is != null) {\n                try {\n                    is.close();\n                } catch (IOException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n    }\n\n    /**\n     * uri to input stream.\n     *\n     * @param uri The uri.\n     * @return the input stream\n     */\n    public static byte[] uri2Bytes(Uri uri) {\n        if (uri == null) return null;\n        InputStream is = null;\n        try {\n            is = Utils.getApp().getContentResolver().openInputStream(uri);\n            return UtilsBridge.inputStream2Bytes(is);\n        } catch (FileNotFoundException e) {\n            e.printStackTrace();\n            Log.d(\"UriUtils\", \"uri to bytes failed.\");\n            return null;\n        } finally {\n            if (is != null) {\n                try {\n                    is.close();\n                } catch (IOException e) {\n                    e.printStackTrace();\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/Utils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport android.app.Application;\nimport android.util.Log;\n\nimport androidx.annotation.NonNull;\nimport androidx.lifecycle.Lifecycle;\n\n/**\n * <pre>\n *     author:\n *                                      ___           ___           ___         ___\n *         _____                       /  /\\         /__/\\         /__/|       /  /\\\n *        /  /::\\                     /  /::\\        \\  \\:\\       |  |:|      /  /:/\n *       /  /:/\\:\\    ___     ___    /  /:/\\:\\        \\  \\:\\      |  |:|     /__/::\\\n *      /  /:/~/::\\  /__/\\   /  /\\  /  /:/~/::\\   _____\\__\\:\\   __|  |:|     \\__\\/\\:\\\n *     /__/:/ /:/\\:| \\  \\:\\ /  /:/ /__/:/ /:/\\:\\ /__/::::::::\\ /__/\\_|:|____    \\  \\:\\\n *     \\  \\:\\/:/~/:/  \\  \\:\\  /:/  \\  \\:\\/:/__\\/ \\  \\:\\~~\\~~\\/ \\  \\:\\/:::::/     \\__\\:\\\n *      \\  \\::/ /:/    \\  \\:\\/:/    \\  \\::/       \\  \\:\\  ~~~   \\  \\::/~~~~      /  /:/\n *       \\  \\:\\/:/      \\  \\::/      \\  \\:\\        \\  \\:\\        \\  \\:\\         /__/:/\n *        \\  \\::/        \\__\\/        \\  \\:\\        \\  \\:\\        \\  \\:\\        \\__\\/\n *         \\__\\/                       \\__\\/         \\__\\/         \\__\\/\n *     blog  : http://blankj.com\n *     time  : 16/12/08\n *     desc  : utils about initialization\n * </pre>\n */\npublic final class Utils {\n\n    @SuppressLint(\"StaticFieldLeak\")\n    private static Application sApp;\n\n    private Utils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Init utils.\n     * <p>Init it in the class of UtilsFileProvider.</p>\n     *\n     * @param app application\n     */\n    public static void init(final Application app) {\n        if (app == null) {\n            Log.e(\"Utils\", \"app is null.\");\n            return;\n        }\n        if (sApp == null) {\n            sApp = app;\n            UtilsBridge.init(sApp);\n            UtilsBridge.preLoad();\n            return;\n        }\n        if (sApp.equals(app)) return;\n        UtilsBridge.unInit(sApp);\n        sApp = app;\n        UtilsBridge.init(sApp);\n    }\n\n    /**\n     * Return the Application object.\n     * <p>Main process get app by UtilsFileProvider,\n     * and other process get app by reflect.</p>\n     *\n     * @return the Application object\n     */\n    public static Application getApp() {\n        if (sApp != null) return sApp;\n        init(UtilsBridge.getApplicationByReflect());\n        if (sApp == null) throw new NullPointerException(\"reflect failed.\");\n        Log.i(\"Utils\", UtilsBridge.getCurrentProcessName() + \" reflect app success.\");\n        return sApp;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // interface\n    ///////////////////////////////////////////////////////////////////////////\n\n    public abstract static class Task<Result> extends ThreadUtils.SimpleTask<Result> {\n\n        private Consumer<Result> mConsumer;\n\n        public Task(final Consumer<Result> consumer) {\n            mConsumer = consumer;\n        }\n\n        @Override\n        public void onSuccess(Result result) {\n            if (mConsumer != null) {\n                mConsumer.accept(result);\n            }\n        }\n    }\n\n    public interface OnAppStatusChangedListener {\n        void onForeground(Activity activity);\n\n        void onBackground(Activity activity);\n    }\n\n    public static class ActivityLifecycleCallbacks {\n\n        public void onActivityCreated(@NonNull Activity activity) {/**/}\n\n        public void onActivityStarted(@NonNull Activity activity) {/**/}\n\n        public void onActivityResumed(@NonNull Activity activity) {/**/}\n\n        public void onActivityPaused(@NonNull Activity activity) {/**/}\n\n        public void onActivityStopped(@NonNull Activity activity) {/**/}\n\n        public void onActivityDestroyed(@NonNull Activity activity) {/**/}\n\n        public void onLifecycleChanged(@NonNull Activity activity, Lifecycle.Event event) {/**/}\n    }\n\n    public interface Consumer<T> {\n        void accept(T t);\n    }\n\n    public interface Supplier<T> {\n        T get();\n    }\n\n    public interface Func1<Ret, Par> {\n        Ret call(Par param);\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/UtilsActivityLifecycleImpl.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.animation.ValueAnimator;\nimport android.app.Activity;\nimport android.app.Application;\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.util.Log;\nimport android.view.Window;\nimport android.view.WindowManager;\n\nimport java.lang.reflect.Field;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CopyOnWriteArrayList;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.lifecycle.Lifecycle;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2020/03/19\n *     desc  :\n * </pre>\n */\nfinal class UtilsActivityLifecycleImpl implements Application.ActivityLifecycleCallbacks {\n\n    static final UtilsActivityLifecycleImpl INSTANCE = new UtilsActivityLifecycleImpl();\n\n    private final LinkedList<Activity> mActivityList = new LinkedList<>();\n\n    private final List<Utils.OnAppStatusChangedListener>                mStatusListeners               = new CopyOnWriteArrayList<>();\n    private final Map<Activity, List<Utils.ActivityLifecycleCallbacks>> mActivityLifecycleCallbacksMap = new ConcurrentHashMap<>();\n\n    private static final Activity STUB = new Activity();\n\n    private int     mForegroundCount = 0;\n    private int     mConfigCount     = 0;\n    private boolean mIsBackground    = false;\n\n    void init(Application app) {\n        app.registerActivityLifecycleCallbacks(this);\n    }\n\n    void unInit(Application app) {\n        mActivityList.clear();\n        app.unregisterActivityLifecycleCallbacks(this);\n    }\n\n    Activity getTopActivity() {\n        List<Activity> activityList = getActivityList();\n        for (Activity activity : activityList) {\n            if (!UtilsBridge.isActivityAlive(activity)) {\n                continue;\n            }\n            return activity;\n        }\n        return null;\n    }\n\n    List<Activity> getActivityList() {\n        if (!mActivityList.isEmpty()) {\n            return new LinkedList<>(mActivityList);\n        }\n        List<Activity> reflectActivities = getActivitiesByReflect();\n        mActivityList.addAll(reflectActivities);\n        return new LinkedList<>(mActivityList);\n    }\n\n    void addOnAppStatusChangedListener(final Utils.OnAppStatusChangedListener listener) {\n        mStatusListeners.add(listener);\n    }\n\n    void removeOnAppStatusChangedListener(final Utils.OnAppStatusChangedListener listener) {\n        mStatusListeners.remove(listener);\n    }\n\n    void addActivityLifecycleCallbacks(final Utils.ActivityLifecycleCallbacks listener) {\n        addActivityLifecycleCallbacks(STUB, listener);\n    }\n\n    void addActivityLifecycleCallbacks(final Activity activity,\n                                       final Utils.ActivityLifecycleCallbacks listener) {\n        if (activity == null || listener == null) return;\n        UtilsBridge.runOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                addActivityLifecycleCallbacksInner(activity, listener);\n            }\n        });\n    }\n\n    boolean isAppForeground() {\n        return !mIsBackground;\n    }\n\n    private void addActivityLifecycleCallbacksInner(final Activity activity,\n                                                    final Utils.ActivityLifecycleCallbacks callbacks) {\n        List<Utils.ActivityLifecycleCallbacks> callbacksList = mActivityLifecycleCallbacksMap.get(activity);\n        if (callbacksList == null) {\n            callbacksList = new CopyOnWriteArrayList<>();\n            mActivityLifecycleCallbacksMap.put(activity, callbacksList);\n        } else {\n            if (callbacksList.contains(callbacks)) return;\n        }\n        callbacksList.add(callbacks);\n    }\n\n    void removeActivityLifecycleCallbacks(final Utils.ActivityLifecycleCallbacks callbacks) {\n        removeActivityLifecycleCallbacks(STUB, callbacks);\n    }\n\n    void removeActivityLifecycleCallbacks(final Activity activity) {\n        if (activity == null) return;\n        UtilsBridge.runOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                mActivityLifecycleCallbacksMap.remove(activity);\n            }\n        });\n    }\n\n    void removeActivityLifecycleCallbacks(final Activity activity,\n                                          final Utils.ActivityLifecycleCallbacks callbacks) {\n        if (activity == null || callbacks == null) return;\n        UtilsBridge.runOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                removeActivityLifecycleCallbacksInner(activity, callbacks);\n            }\n        });\n    }\n\n    private void removeActivityLifecycleCallbacksInner(final Activity activity,\n                                                       final Utils.ActivityLifecycleCallbacks callbacks) {\n        List<Utils.ActivityLifecycleCallbacks> callbacksList = mActivityLifecycleCallbacksMap.get(activity);\n        if (callbacksList != null && !callbacksList.isEmpty()) {\n            callbacksList.remove(callbacks);\n        }\n    }\n\n    private void consumeActivityLifecycleCallbacks(Activity activity, Lifecycle.Event event) {\n        consumeLifecycle(activity, event, mActivityLifecycleCallbacksMap.get(activity));\n        consumeLifecycle(activity, event, mActivityLifecycleCallbacksMap.get(STUB));\n    }\n\n    private void consumeLifecycle(Activity activity, Lifecycle.Event event, List<Utils.ActivityLifecycleCallbacks> listeners) {\n        if (listeners == null) return;\n        for (Utils.ActivityLifecycleCallbacks listener : listeners) {\n            listener.onLifecycleChanged(activity, event);\n            if (event.equals(Lifecycle.Event.ON_CREATE)) {\n                listener.onActivityCreated(activity);\n            } else if (event.equals(Lifecycle.Event.ON_START)) {\n                listener.onActivityStarted(activity);\n            } else if (event.equals(Lifecycle.Event.ON_RESUME)) {\n                listener.onActivityResumed(activity);\n            } else if (event.equals(Lifecycle.Event.ON_PAUSE)) {\n                listener.onActivityPaused(activity);\n            } else if (event.equals(Lifecycle.Event.ON_STOP)) {\n                listener.onActivityStopped(activity);\n            } else if (event.equals(Lifecycle.Event.ON_DESTROY)) {\n                listener.onActivityDestroyed(activity);\n            }\n        }\n        if (event.equals(Lifecycle.Event.ON_DESTROY)) {\n            mActivityLifecycleCallbacksMap.remove(activity);\n        }\n    }\n\n    Application getApplicationByReflect() {\n        try {\n            Class activityThreadClass = Class.forName(\"android.app.ActivityThread\");\n            Object thread = getActivityThread();\n            if (thread == null) return null;\n            Object app = activityThreadClass.getMethod(\"getApplication\").invoke(thread);\n            if (app == null) return null;\n            return (Application) app;\n        } catch (Exception e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // lifecycle start\n    ///////////////////////////////////////////////////////////////////////////\n    @Override\n    public void onActivityPreCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {/**/}\n\n    @Override\n    public void onActivityCreated(@NonNull Activity activity, Bundle savedInstanceState) {\n        if (mActivityList.size() == 0) {\n            postStatus(activity, true);\n        }\n        LanguageUtils.applyLanguage(activity);\n        setAnimatorsEnabled();\n        setTopActivity(activity);\n        consumeActivityLifecycleCallbacks(activity, Lifecycle.Event.ON_CREATE);\n    }\n\n    @Override\n    public void onActivityPostCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) {/**/}\n\n    @Override\n    public void onActivityPreStarted(@NonNull Activity activity) {/**/}\n\n    @Override\n    public void onActivityStarted(@NonNull Activity activity) {\n        if (!mIsBackground) {\n            setTopActivity(activity);\n        }\n        if (mConfigCount < 0) {\n            ++mConfigCount;\n        } else {\n            ++mForegroundCount;\n        }\n        consumeActivityLifecycleCallbacks(activity, Lifecycle.Event.ON_START);\n    }\n\n    @Override\n    public void onActivityPostStarted(@NonNull Activity activity) {/**/}\n\n    @Override\n    public void onActivityPreResumed(@NonNull Activity activity) {/**/}\n\n    @Override\n    public void onActivityResumed(@NonNull final Activity activity) {\n        setTopActivity(activity);\n        if (mIsBackground) {\n            mIsBackground = false;\n            postStatus(activity, true);\n        }\n        processHideSoftInputOnActivityDestroy(activity, false);\n        consumeActivityLifecycleCallbacks(activity, Lifecycle.Event.ON_RESUME);\n    }\n\n    @Override\n    public void onActivityPostResumed(@NonNull Activity activity) {/**/}\n\n    @Override\n    public void onActivityPrePaused(@NonNull Activity activity) {/**/}\n\n    @Override\n    public void onActivityPaused(@NonNull Activity activity) {\n        consumeActivityLifecycleCallbacks(activity, Lifecycle.Event.ON_PAUSE);\n    }\n\n    @Override\n    public void onActivityPostPaused(@NonNull Activity activity) {/**/}\n\n    @Override\n    public void onActivityPreStopped(@NonNull Activity activity) {/**/}\n\n    @Override\n    public void onActivityStopped(Activity activity) {\n        if (activity.isChangingConfigurations()) {\n            --mConfigCount;\n        } else {\n            --mForegroundCount;\n            if (mForegroundCount <= 0) {\n                mIsBackground = true;\n                postStatus(activity, false);\n            }\n        }\n        processHideSoftInputOnActivityDestroy(activity, true);\n        consumeActivityLifecycleCallbacks(activity, Lifecycle.Event.ON_STOP);\n    }\n\n    @Override\n    public void onActivityPostStopped(@NonNull Activity activity) {/**/}\n\n    @Override\n    public void onActivityPreSaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {/**/}\n\n    @Override\n    public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {/**/}\n\n    @Override\n    public void onActivityPostSaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {/**/}\n\n    @Override\n    public void onActivityPreDestroyed(@NonNull Activity activity) {/**/}\n\n    @Override\n    public void onActivityDestroyed(@NonNull Activity activity) {\n        mActivityList.remove(activity);\n        UtilsBridge.fixSoftInputLeaks(activity);\n        consumeActivityLifecycleCallbacks(activity, Lifecycle.Event.ON_DESTROY);\n    }\n\n    @Override\n    public void onActivityPostDestroyed(@NonNull Activity activity) {/**/}\n    ///////////////////////////////////////////////////////////////////////////\n    // lifecycle end\n    ///////////////////////////////////////////////////////////////////////////\n\n    /**\n     * To solve close keyboard when activity onDestroy.\n     * The preActivity set windowSoftInputMode will prevent\n     * the keyboard from closing when curActivity onDestroy.\n     */\n    private void processHideSoftInputOnActivityDestroy(final Activity activity, boolean isSave) {\n        try {\n            if (isSave) {\n                Window window = activity.getWindow();\n                final WindowManager.LayoutParams attrs = window.getAttributes();\n                final int softInputMode = attrs.softInputMode;\n                window.getDecorView().setTag(-123, softInputMode);\n                window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);\n            } else {\n                final Object tag = activity.getWindow().getDecorView().getTag(-123);\n                if (!(tag instanceof Integer)) return;\n                UtilsBridge.runOnUiThreadDelayed(new Runnable() {\n                    @Override\n                    public void run() {\n                        try {\n                            Window window = activity.getWindow();\n                            if (window != null) {\n                                window.setSoftInputMode(((Integer) tag));\n                            }\n                        } catch (Exception ignore) {\n                        }\n                    }\n                }, 100);\n            }\n        } catch (Exception ignore) {\n        }\n    }\n\n    private void postStatus(final Activity activity, final boolean isForeground) {\n        if (mStatusListeners.isEmpty()) return;\n        for (Utils.OnAppStatusChangedListener statusListener : mStatusListeners) {\n            if (isForeground) {\n                statusListener.onForeground(activity);\n            } else {\n                statusListener.onBackground(activity);\n            }\n        }\n    }\n\n    private void setTopActivity(final Activity activity) {\n        if (mActivityList.contains(activity)) {\n            if (!mActivityList.getFirst().equals(activity)) {\n                mActivityList.remove(activity);\n                mActivityList.addFirst(activity);\n            }\n        } else {\n            mActivityList.addFirst(activity);\n        }\n    }\n\n    /**\n     * @return the activities which topActivity is first position\n     */\n    private List<Activity> getActivitiesByReflect() {\n        LinkedList<Activity> list = new LinkedList<>();\n        Activity topActivity = null;\n        try {\n            Object activityThread = getActivityThread();\n            if (activityThread == null) return list;\n            Field mActivitiesField = activityThread.getClass().getDeclaredField(\"mActivities\");\n            mActivitiesField.setAccessible(true);\n            Object mActivities = mActivitiesField.get(activityThread);\n            if (!(mActivities instanceof Map)) {\n                return list;\n            }\n            Map<Object, Object> binder_activityClientRecord_map = (Map<Object, Object>) mActivities;\n            for (Object activityRecord : binder_activityClientRecord_map.values()) {\n                Class activityClientRecordClass = activityRecord.getClass();\n                Field activityField = activityClientRecordClass.getDeclaredField(\"activity\");\n                activityField.setAccessible(true);\n                Activity activity = (Activity) activityField.get(activityRecord);\n                if (topActivity == null) {\n                    Field pausedField = activityClientRecordClass.getDeclaredField(\"paused\");\n                    pausedField.setAccessible(true);\n                    if (!pausedField.getBoolean(activityRecord)) {\n                        topActivity = activity;\n                    } else {\n                        list.addFirst(activity);\n                    }\n                } else {\n                    list.addFirst(activity);\n                }\n            }\n        } catch (Exception e) {\n            Log.e(\"UtilsActivityLifecycle\", \"getActivitiesByReflect: \" + e.getMessage());\n        }\n        if (topActivity != null) {\n            list.addFirst(topActivity);\n        }\n        return list;\n    }\n\n    private Object getActivityThread() {\n        Object activityThread = getActivityThreadInActivityThreadStaticField();\n        if (activityThread != null) return activityThread;\n        return getActivityThreadInActivityThreadStaticMethod();\n    }\n\n    private Object getActivityThreadInActivityThreadStaticField() {\n        try {\n            Class activityThreadClass = Class.forName(\"android.app.ActivityThread\");\n            Field sCurrentActivityThreadField = activityThreadClass.getDeclaredField(\"sCurrentActivityThread\");\n            sCurrentActivityThreadField.setAccessible(true);\n            return sCurrentActivityThreadField.get(null);\n        } catch (Exception e) {\n            Log.e(\"UtilsActivityLifecycle\", \"getActivityThreadInActivityThreadStaticField: \" + e.getMessage());\n            return null;\n        }\n    }\n\n    private Object getActivityThreadInActivityThreadStaticMethod() {\n        try {\n            Class activityThreadClass = Class.forName(\"android.app.ActivityThread\");\n            return activityThreadClass.getMethod(\"currentActivityThread\").invoke(null);\n        } catch (Exception e) {\n            Log.e(\"UtilsActivityLifecycle\", \"getActivityThreadInActivityThreadStaticMethod: \" + e.getMessage());\n            return null;\n        }\n    }\n\n    /**\n     * Set animators enabled.\n     */\n    private static void setAnimatorsEnabled() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && ValueAnimator.areAnimatorsEnabled()) {\n            return;\n        }\n        try {\n            //noinspection JavaReflectionMemberAccess\n            Field sDurationScaleField = ValueAnimator.class.getDeclaredField(\"sDurationScale\");\n            sDurationScaleField.setAccessible(true);\n            //noinspection ConstantConditions\n            float sDurationScale = (Float) sDurationScaleField.get(null);\n            if (sDurationScale == 0f) {\n                sDurationScaleField.set(null, 1f);\n                Log.i(\"UtilsActivityLifecycle\", \"setAnimatorsEnabled: Animators are enabled now!\");\n            }\n        } catch (NoSuchFieldException e) {\n            e.printStackTrace();\n        } catch (IllegalAccessException e) {\n            e.printStackTrace();\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/UtilsBridge.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.app.Activity;\nimport android.app.Application;\nimport android.app.Notification;\nimport android.content.Context;\nimport android.content.Intent;\nimport android.graphics.Bitmap;\nimport android.graphics.drawable.Drawable;\nimport android.net.Uri;\nimport android.os.Build;\nimport android.os.Parcelable;\nimport android.text.TextUtils;\nimport android.view.View;\n\nimport com.google.gson.Gson;\n\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.File;\nimport java.io.InputStream;\nimport java.io.Serializable;\nimport java.lang.reflect.Type;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport androidx.annotation.LayoutRes;\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.annotation.RequiresApi;\nimport androidx.annotation.RequiresPermission;\nimport androidx.annotation.StringRes;\nimport androidx.core.app.NotificationCompat;\n\nimport static android.Manifest.permission.CALL_PHONE;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2020/03/19\n *     desc  :\n * </pre>\n */\nclass UtilsBridge {\n\n    static void init(Application app) {\n        UtilsActivityLifecycleImpl.INSTANCE.init(app);\n    }\n\n    static void unInit(Application app) {\n        UtilsActivityLifecycleImpl.INSTANCE.unInit(app);\n    }\n\n    static void preLoad() {\n        preLoad(AdaptScreenUtils.getPreLoadRunnable());\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // UtilsActivityLifecycleImpl\n    ///////////////////////////////////////////////////////////////////////////\n    static Activity getTopActivity() {\n        return UtilsActivityLifecycleImpl.INSTANCE.getTopActivity();\n    }\n\n    static void addOnAppStatusChangedListener(final Utils.OnAppStatusChangedListener listener) {\n        UtilsActivityLifecycleImpl.INSTANCE.addOnAppStatusChangedListener(listener);\n    }\n\n    static void removeOnAppStatusChangedListener(final Utils.OnAppStatusChangedListener listener) {\n        UtilsActivityLifecycleImpl.INSTANCE.removeOnAppStatusChangedListener(listener);\n    }\n\n    static void addActivityLifecycleCallbacks(final Utils.ActivityLifecycleCallbacks callbacks) {\n        UtilsActivityLifecycleImpl.INSTANCE.addActivityLifecycleCallbacks(callbacks);\n    }\n\n    static void removeActivityLifecycleCallbacks(final Utils.ActivityLifecycleCallbacks callbacks) {\n        UtilsActivityLifecycleImpl.INSTANCE.removeActivityLifecycleCallbacks(callbacks);\n    }\n\n    static void addActivityLifecycleCallbacks(final Activity activity,\n                                              final Utils.ActivityLifecycleCallbacks callbacks) {\n        UtilsActivityLifecycleImpl.INSTANCE.addActivityLifecycleCallbacks(activity, callbacks);\n    }\n\n    static void removeActivityLifecycleCallbacks(final Activity activity) {\n        UtilsActivityLifecycleImpl.INSTANCE.removeActivityLifecycleCallbacks(activity);\n    }\n\n    static void removeActivityLifecycleCallbacks(final Activity activity,\n                                                 final Utils.ActivityLifecycleCallbacks callbacks) {\n        UtilsActivityLifecycleImpl.INSTANCE.removeActivityLifecycleCallbacks(activity, callbacks);\n    }\n\n    static List<Activity> getActivityList() {\n        return UtilsActivityLifecycleImpl.INSTANCE.getActivityList();\n    }\n\n    static Application getApplicationByReflect() {\n        return UtilsActivityLifecycleImpl.INSTANCE.getApplicationByReflect();\n    }\n\n    static boolean isAppForeground() {\n        return UtilsActivityLifecycleImpl.INSTANCE.isAppForeground();\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // ActivityUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static boolean isActivityAlive(final Activity activity) {\n        return ActivityUtils.isActivityAlive(activity);\n    }\n\n    static String getLauncherActivity(final String pkg) {\n        return ActivityUtils.getLauncherActivity(pkg);\n    }\n\n    static Activity getActivityByContext(Context context) {\n        return ActivityUtils.getActivityByContext(context);\n    }\n\n    static void startHomeActivity() {\n        ActivityUtils.startHomeActivity();\n    }\n\n    static void finishAllActivities() {\n        ActivityUtils.finishAllActivities();\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // AppUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static boolean isAppRunning(@NonNull final String pkgName) {\n        return AppUtils.isAppRunning(pkgName);\n    }\n\n    static boolean isAppInstalled(final String pkgName) {\n        return AppUtils.isAppInstalled(pkgName);\n    }\n\n    static boolean isAppDebug() {\n        return AppUtils.isAppDebug();\n    }\n\n    static void relaunchApp() {\n        AppUtils.relaunchApp();\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // BarUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static int getStatusBarHeight() {\n        return BarUtils.getStatusBarHeight();\n    }\n\n    static int getNavBarHeight() {\n        return BarUtils.getNavBarHeight();\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // ConvertUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static String bytes2HexString(final byte[] bytes) {\n        return ConvertUtils.bytes2HexString(bytes);\n    }\n\n    static byte[] hexString2Bytes(String hexString) {\n        return ConvertUtils.hexString2Bytes(hexString);\n    }\n\n    static byte[] string2Bytes(final String string) {\n        return ConvertUtils.string2Bytes(string);\n    }\n\n    static String bytes2String(final byte[] bytes) {\n        return ConvertUtils.bytes2String(bytes);\n    }\n\n    static byte[] jsonObject2Bytes(final JSONObject jsonObject) {\n        return ConvertUtils.jsonObject2Bytes(jsonObject);\n    }\n\n    static JSONObject bytes2JSONObject(final byte[] bytes) {\n        return ConvertUtils.bytes2JSONObject(bytes);\n    }\n\n    static byte[] jsonArray2Bytes(final JSONArray jsonArray) {\n        return ConvertUtils.jsonArray2Bytes(jsonArray);\n    }\n\n    static JSONArray bytes2JSONArray(final byte[] bytes) {\n        return ConvertUtils.bytes2JSONArray(bytes);\n    }\n\n    static byte[] parcelable2Bytes(final Parcelable parcelable) {\n        return ConvertUtils.parcelable2Bytes(parcelable);\n    }\n\n    static <T> T bytes2Parcelable(final byte[] bytes,\n                                  final Parcelable.Creator<T> creator) {\n        return ConvertUtils.bytes2Parcelable(bytes, creator);\n    }\n\n    static byte[] serializable2Bytes(final Serializable serializable) {\n        return ConvertUtils.serializable2Bytes(serializable);\n    }\n\n    static Object bytes2Object(final byte[] bytes) {\n        return ConvertUtils.bytes2Object(bytes);\n    }\n\n    static String byte2FitMemorySize(final long byteSize) {\n        return ConvertUtils.byte2FitMemorySize(byteSize);\n    }\n\n    static byte[] inputStream2Bytes(final InputStream is) {\n        return ConvertUtils.inputStream2Bytes(is);\n    }\n\n    static ByteArrayOutputStream input2OutputStream(final InputStream is) {\n        return ConvertUtils.input2OutputStream(is);\n    }\n\n    static List<String> inputStream2Lines(final InputStream is, final String charsetName) {\n        return ConvertUtils.inputStream2Lines(is, charsetName);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // DebouncingUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static boolean isValid(@NonNull final View view, final long duration) {\n        return DebouncingUtils.isValid(view, duration);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // EncodeUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static byte[] base64Encode(final byte[] input) {\n        return EncodeUtils.base64Encode(input);\n    }\n\n    static byte[] base64Decode(final byte[] input) {\n        return EncodeUtils.base64Decode(input);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // EncryptUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static byte[] hashTemplate(final byte[] data, final String algorithm) {\n        return EncryptUtils.hashTemplate(data, algorithm);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // FileIOUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static boolean writeFileFromBytes(final File file,\n                                      final byte[] bytes) {\n        return FileIOUtils.writeFileFromBytesByChannel(file, bytes, true);\n    }\n\n    static byte[] readFile2Bytes(final File file) {\n        return FileIOUtils.readFile2BytesByChannel(file);\n    }\n\n    static boolean writeFileFromString(final String filePath, final String content, final boolean append) {\n        return FileIOUtils.writeFileFromString(filePath, content, append);\n    }\n\n    static boolean writeFileFromIS(final String filePath, final InputStream is) {\n        return FileIOUtils.writeFileFromIS(filePath, is);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // FileUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static boolean isFileExists(final File file) {\n        return FileUtils.isFileExists(file);\n    }\n\n    static File getFileByPath(final String filePath) {\n        return FileUtils.getFileByPath(filePath);\n    }\n\n    static boolean deleteAllInDir(final File dir) {\n        return FileUtils.deleteAllInDir(dir);\n    }\n\n    static boolean createOrExistsFile(final File file) {\n        return FileUtils.createOrExistsFile(file);\n    }\n\n    static boolean createOrExistsDir(final File file) {\n        return FileUtils.createOrExistsDir(file);\n    }\n\n    static boolean createFileByDeleteOldFile(final File file) {\n        return FileUtils.createFileByDeleteOldFile(file);\n    }\n\n    static long getFsTotalSize(String path) {\n        return FileUtils.getFsTotalSize(path);\n    }\n\n    static long getFsAvailableSize(String path) {\n        return FileUtils.getFsAvailableSize(path);\n    }\n\n    static void notifySystemToScan(File file) {\n        FileUtils.notifySystemToScan(file);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // GsonUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static String toJson(final Object object) {\n        return GsonUtils.toJson(object);\n    }\n\n    static <T> T fromJson(final String json, final Type type) {\n        return GsonUtils.fromJson(json, type);\n    }\n\n    static Gson getGson4LogUtils() {\n        return GsonUtils.getGson4LogUtils();\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // ImageUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static byte[] bitmap2Bytes(final Bitmap bitmap) {\n        return ImageUtils.bitmap2Bytes(bitmap);\n    }\n\n    static byte[] bitmap2Bytes(final Bitmap bitmap, final Bitmap.CompressFormat format, int quality) {\n        return ImageUtils.bitmap2Bytes(bitmap, format, quality);\n    }\n\n    static Bitmap bytes2Bitmap(final byte[] bytes) {\n        return ImageUtils.bytes2Bitmap(bytes);\n    }\n\n    static byte[] drawable2Bytes(final Drawable drawable) {\n        return ImageUtils.drawable2Bytes(drawable);\n    }\n\n    static byte[] drawable2Bytes(final Drawable drawable, final Bitmap.CompressFormat format, int quality) {\n        return ImageUtils.drawable2Bytes(drawable, format, quality);\n    }\n\n    static Drawable bytes2Drawable(final byte[] bytes) {\n        return ImageUtils.bytes2Drawable(bytes);\n    }\n\n    static Bitmap view2Bitmap(final View view) {\n        return ImageUtils.view2Bitmap(view);\n    }\n\n    static Bitmap drawable2Bitmap(final Drawable drawable) {\n        return ImageUtils.drawable2Bitmap(drawable);\n    }\n\n    static Drawable bitmap2Drawable(final Bitmap bitmap) {\n        return ImageUtils.bitmap2Drawable(bitmap);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // IntentUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static boolean isIntentAvailable(final Intent intent) {\n        return IntentUtils.isIntentAvailable(intent);\n    }\n\n    static Intent getLaunchAppIntent(final String pkgName) {\n        return IntentUtils.getLaunchAppIntent(pkgName);\n    }\n\n    static Intent getInstallAppIntent(final File file) {\n        return IntentUtils.getInstallAppIntent(file);\n    }\n\n    static Intent getInstallAppIntent(final Uri uri) {\n        return IntentUtils.getInstallAppIntent(uri);\n    }\n\n    static Intent getUninstallAppIntent(final String pkgName) {\n        return IntentUtils.getUninstallAppIntent(pkgName);\n    }\n\n    static Intent getDialIntent(final String phoneNumber) {\n        return IntentUtils.getDialIntent(phoneNumber);\n    }\n\n    @RequiresPermission(CALL_PHONE)\n    static Intent getCallIntent(final String phoneNumber) {\n        return IntentUtils.getCallIntent(phoneNumber);\n    }\n\n    static Intent getSendSmsIntent(final String phoneNumber, final String content) {\n        return IntentUtils.getSendSmsIntent(phoneNumber, content);\n    }\n\n    static Intent getLaunchAppDetailsSettingsIntent(final String pkgName, final boolean isNewTask) {\n        return IntentUtils.getLaunchAppDetailsSettingsIntent(pkgName, isNewTask);\n    }\n\n\n    ///////////////////////////////////////////////////////////////////////////\n    // JsonUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static String formatJson(String json) {\n        return JsonUtils.formatJson(json);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // KeyboardUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static void fixSoftInputLeaks(final Activity activity) {\n        KeyboardUtils.fixSoftInputLeaks(activity);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // NotificationUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static Notification getNotification(NotificationUtils.ChannelConfig channelConfig,\n                                        Utils.Consumer<NotificationCompat.Builder> consumer) {\n        return NotificationUtils.getNotification(channelConfig, consumer);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // PermissionUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static boolean isGranted(final String... permissions) {\n        return PermissionUtils.isGranted(permissions);\n    }\n\n    @RequiresApi(api = Build.VERSION_CODES.M)\n    static boolean isGrantedDrawOverlays() {\n        return PermissionUtils.isGrantedDrawOverlays();\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // ProcessUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static boolean isMainProcess() {\n        return ProcessUtils.isMainProcess();\n    }\n\n    static String getForegroundProcessName() {\n        return ProcessUtils.getForegroundProcessName();\n    }\n\n    static String getCurrentProcessName() {\n        return ProcessUtils.getCurrentProcessName();\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // RomUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static boolean isSamsung() {\n        return RomUtils.isSamsung();\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // ScreenUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static int getAppScreenWidth() {\n        return ScreenUtils.getAppScreenWidth();\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // SDCardUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static boolean isSDCardEnableByEnvironment() {\n        return SDCardUtils.isSDCardEnableByEnvironment();\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // ServiceUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static boolean isServiceRunning(final String className) {\n        return ServiceUtils.isServiceRunning(className);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // ShellUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static ShellUtils.CommandResult execCmd(final String command, final boolean isRooted) {\n        return ShellUtils.execCmd(command, isRooted);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // SizeUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static int dp2px(final float dpValue) {\n        return SizeUtils.dp2px(dpValue);\n    }\n\n    static int px2dp(final float pxValue) {\n        return SizeUtils.px2dp(pxValue);\n    }\n\n    static int sp2px(final float spValue) {\n        return SizeUtils.sp2px(spValue);\n    }\n\n    static int px2sp(final float pxValue) {\n        return SizeUtils.px2sp(pxValue);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // SpUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static SPUtils getSpUtils4Utils() {\n        return SPUtils.getInstance(\"Utils\");\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // StringUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static boolean isSpace(final String s) {\n        return StringUtils.isSpace(s);\n    }\n\n    static boolean equals(final CharSequence s1, final CharSequence s2) {\n        return StringUtils.equals(s1, s2);\n    }\n\n    static String getString(@StringRes int id) {\n        return StringUtils.getString(id);\n    }\n\n    static String getString(@StringRes int id, Object... formatArgs) {\n        return StringUtils.getString(id, formatArgs);\n    }\n\n    static String format(@Nullable String str, Object... args) {\n        return StringUtils.format(str, args);\n    }\n\n\n    ///////////////////////////////////////////////////////////////////////////\n    // ThreadUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static <T> Utils.Task<T> doAsync(final Utils.Task<T> task) {\n        ThreadUtils.getCachedPool().execute(task);\n        return task;\n    }\n\n    static void runOnUiThread(final Runnable runnable) {\n        ThreadUtils.runOnUiThread(runnable);\n    }\n\n    static void runOnUiThreadDelayed(final Runnable runnable, long delayMillis) {\n        ThreadUtils.runOnUiThreadDelayed(runnable, delayMillis);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // ThrowableUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static String getFullStackTrace(Throwable throwable) {\n        return ThrowableUtils.getFullStackTrace(throwable);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // TimeUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static String millis2FitTimeSpan(long millis, int precision) {\n        return TimeUtils.millis2FitTimeSpan(millis, precision);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // ToastUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static void toastShowShort(final CharSequence text) {\n        ToastUtils.showShort(text);\n    }\n\n    static void toastCancel() {\n        ToastUtils.cancel();\n    }\n\n    private static void preLoad(final Runnable... runs) {\n        for (final Runnable r : runs) {\n            ThreadUtils.getCachedPool().execute(r);\n        }\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // UriUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static Uri file2Uri(final File file) {\n        return UriUtils.file2Uri(file);\n    }\n\n    static File uri2File(final Uri uri) {\n        return UriUtils.uri2File(uri);\n    }\n\n\n    ///////////////////////////////////////////////////////////////////////////\n    // ViewUtils\n    ///////////////////////////////////////////////////////////////////////////\n    static View layoutId2View(@LayoutRes final int layoutId) {\n        return ViewUtils.layoutId2View(layoutId);\n    }\n\n    static boolean isLayoutRtl() {\n        return ViewUtils.isLayoutRtl();\n    }\n\n\n    ///////////////////////////////////////////////////////////////////////////\n    // Common\n    ///////////////////////////////////////////////////////////////////////////\n    static final class FileHead {\n\n        private String                        mName;\n        private LinkedHashMap<String, String> mFirst = new LinkedHashMap<>();\n        private LinkedHashMap<String, String> mLast  = new LinkedHashMap<>();\n\n        FileHead(String name) {\n            mName = name;\n        }\n\n        void addFirst(String key, String value) {\n            append2Host(mFirst, key, value);\n        }\n\n        void append(Map<String, String> extra) {\n            append2Host(mLast, extra);\n        }\n\n        void append(String key, String value) {\n            append2Host(mLast, key, value);\n        }\n\n        private void append2Host(Map<String, String> host, Map<String, String> extra) {\n            if (extra == null || extra.isEmpty()) {\n                return;\n            }\n            for (Map.Entry<String, String> entry : extra.entrySet()) {\n                append2Host(host, entry.getKey(), entry.getValue());\n            }\n        }\n\n        private void append2Host(Map<String, String> host, String key, String value) {\n            if (TextUtils.isEmpty(key) || TextUtils.isEmpty(value)) {\n                return;\n            }\n            int delta = 19 - key.length(); // 19 is length of \"Device Manufacturer\"\n            if (delta > 0) {\n                key = key + \"                   \".substring(0, delta);\n            }\n            host.put(key, value);\n        }\n\n        public String getAppended() {\n            StringBuilder sb = new StringBuilder();\n            for (Map.Entry<String, String> entry : mLast.entrySet()) {\n                sb.append(entry.getKey()).append(\": \").append(entry.getValue()).append(\"\\n\");\n            }\n            return sb.toString();\n        }\n\n        @Override\n        public String toString() {\n            StringBuilder sb = new StringBuilder();\n            String border = \"************* \" + mName + \" Head ****************\\n\";\n            sb.append(border);\n            for (Map.Entry<String, String> entry : mFirst.entrySet()) {\n                sb.append(entry.getKey()).append(\": \").append(entry.getValue()).append(\"\\n\");\n            }\n\n            sb.append(\"Rom Info           : \").append(RomUtils.getRomInfo()).append(\"\\n\");\n            sb.append(\"Device Manufacturer: \").append(Build.MANUFACTURER).append(\"\\n\");\n            sb.append(\"Device Model       : \").append(Build.MODEL).append(\"\\n\");\n            sb.append(\"Android Version    : \").append(Build.VERSION.RELEASE).append(\"\\n\");\n            sb.append(\"Android SDK        : \").append(Build.VERSION.SDK_INT).append(\"\\n\");\n            sb.append(\"App VersionName    : \").append(AppUtils.getAppVersionName()).append(\"\\n\");\n            sb.append(\"App VersionCode    : \").append(AppUtils.getAppVersionCode()).append(\"\\n\");\n\n            sb.append(getAppended());\n            return sb.append(border).append(\"\\n\").toString();\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/UtilsFileProvider.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.app.Application;\n\nimport androidx.core.content.FileProvider;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2020/03/19\n *     desc  :\n * </pre>\n */\npublic class UtilsFileProvider extends FileProvider {\n\n    @Override\n    public boolean onCreate() {\n        //noinspection ConstantConditions\n        Utils.init((Application) getContext().getApplicationContext());\n        return true;\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/UtilsTransActivity.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.view.MotionEvent;\n\nimport java.io.Serializable;\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\nimport androidx.appcompat.app.AppCompatActivity;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2020/03/19\n *     desc  :\n * </pre>\n */\npublic class UtilsTransActivity extends AppCompatActivity {\n\n    private static final Map<UtilsTransActivity, TransActivityDelegate> CALLBACK_MAP = new HashMap<>();\n\n    protected static final String EXTRA_DELEGATE = \"extra_delegate\";\n\n    public static void start(final TransActivityDelegate delegate) {\n        start(null, null, delegate, UtilsTransActivity.class);\n    }\n\n    public static void start(final Utils.Consumer<Intent> consumer,\n                             final TransActivityDelegate delegate) {\n        start(null, consumer, delegate, UtilsTransActivity.class);\n    }\n\n    public static void start(final Activity activity,\n                             final TransActivityDelegate delegate) {\n        start(activity, null, delegate, UtilsTransActivity.class);\n    }\n\n    public static void start(final Activity activity,\n                             final Utils.Consumer<Intent> consumer,\n                             final TransActivityDelegate delegate) {\n        start(activity, consumer, delegate, UtilsTransActivity.class);\n    }\n\n    protected static void start(final Activity activity,\n                                final Utils.Consumer<Intent> consumer,\n                                final TransActivityDelegate delegate,\n                                final Class<?> cls) {\n        if (delegate == null) return;\n        Intent starter = new Intent(Utils.getApp(), cls);\n        starter.putExtra(EXTRA_DELEGATE, delegate);\n        if (consumer != null) {\n            consumer.accept(starter);\n        }\n        if (activity == null) {\n            starter.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n            Utils.getApp().startActivity(starter);\n        } else {\n            activity.startActivity(starter);\n        }\n    }\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        overridePendingTransition(0, 0);\n        Serializable extra = getIntent().getSerializableExtra(EXTRA_DELEGATE);\n        if (!(extra instanceof TransActivityDelegate)) {\n            super.onCreate(savedInstanceState);\n            finish();\n            return;\n        }\n        TransActivityDelegate delegate = (TransActivityDelegate) extra;\n        CALLBACK_MAP.put(this, delegate);\n        delegate.onCreateBefore(this, savedInstanceState);\n        super.onCreate(savedInstanceState);\n        delegate.onCreated(this, savedInstanceState);\n    }\n\n    @Override\n    protected void onStart() {\n        super.onStart();\n        TransActivityDelegate callback = CALLBACK_MAP.get(this);\n        if (callback == null) return;\n        callback.onStarted(this);\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n        TransActivityDelegate callback = CALLBACK_MAP.get(this);\n        if (callback == null) return;\n        callback.onResumed(this);\n    }\n\n    @Override\n    protected void onPause() {\n        overridePendingTransition(0, 0);\n        super.onPause();\n        TransActivityDelegate callback = CALLBACK_MAP.get(this);\n        if (callback == null) return;\n        callback.onPaused(this);\n    }\n\n    @Override\n    protected void onStop() {\n        super.onStop();\n        TransActivityDelegate callback = CALLBACK_MAP.get(this);\n        if (callback == null) return;\n        callback.onStopped(this);\n    }\n\n    @Override\n    protected void onSaveInstanceState(Bundle outState) {\n        super.onSaveInstanceState(outState);\n        TransActivityDelegate callback = CALLBACK_MAP.get(this);\n        if (callback == null) return;\n        callback.onSaveInstanceState(this, outState);\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        TransActivityDelegate callback = CALLBACK_MAP.get(this);\n        if (callback == null) return;\n        callback.onDestroy(this);\n        CALLBACK_MAP.remove(this);\n    }\n\n    @Override\n    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {\n        super.onRequestPermissionsResult(requestCode, permissions, grantResults);\n        TransActivityDelegate callback = CALLBACK_MAP.get(this);\n        if (callback == null) return;\n        callback.onRequestPermissionsResult(this, requestCode, permissions, grantResults);\n    }\n\n    @Override\n    protected void onActivityResult(int requestCode, int resultCode, Intent data) {\n        super.onActivityResult(requestCode, resultCode, data);\n        TransActivityDelegate callback = CALLBACK_MAP.get(this);\n        if (callback == null) return;\n        callback.onActivityResult(this, requestCode, resultCode, data);\n    }\n\n    @Override\n    public boolean dispatchTouchEvent(MotionEvent ev) {\n        TransActivityDelegate callback = CALLBACK_MAP.get(this);\n        if (callback == null) return super.dispatchTouchEvent(ev);\n        if (callback.dispatchTouchEvent(this, ev)) {\n            return true;\n        }\n        return super.dispatchTouchEvent(ev);\n    }\n\n    public abstract static class TransActivityDelegate implements Serializable {\n        public void onCreateBefore(@NonNull UtilsTransActivity activity, @Nullable Bundle savedInstanceState) {/**/}\n\n        public void onCreated(@NonNull UtilsTransActivity activity, @Nullable Bundle savedInstanceState) {/**/}\n\n        public void onStarted(@NonNull UtilsTransActivity activity) {/**/}\n\n        public void onDestroy(@NonNull UtilsTransActivity activity) {/**/}\n\n        public void onResumed(@NonNull UtilsTransActivity activity) {/**/}\n\n        public void onPaused(@NonNull UtilsTransActivity activity) {/**/}\n\n        public void onStopped(@NonNull UtilsTransActivity activity) {/**/}\n\n        public void onSaveInstanceState(@NonNull UtilsTransActivity activity, Bundle outState) {/**/}\n\n        public void onRequestPermissionsResult(@NonNull UtilsTransActivity activity, int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {/**/}\n\n        public void onActivityResult(@NonNull UtilsTransActivity activity, int requestCode, int resultCode, Intent data) {/**/}\n\n        public boolean dispatchTouchEvent(@NonNull UtilsTransActivity activity, MotionEvent ev) {\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/UtilsTransActivity4MainProcess.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.app.Activity;\nimport android.content.Intent;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2020/03/19\n *     desc  :\n * </pre>\n */\npublic class UtilsTransActivity4MainProcess extends UtilsTransActivity {\n\n    public static void start(final TransActivityDelegate delegate) {\n        start(null, null, delegate, UtilsTransActivity4MainProcess.class);\n    }\n\n    public static void start(final Utils.Consumer<Intent> consumer,\n                             final TransActivityDelegate delegate) {\n        start(null, consumer, delegate, UtilsTransActivity4MainProcess.class);\n    }\n\n    public static void start(final Activity activity,\n                             final TransActivityDelegate delegate) {\n        start(activity, null, delegate, UtilsTransActivity4MainProcess.class);\n    }\n\n    public static void start(final Activity activity,\n                             final Utils.Consumer<Intent> consumer,\n                             final TransActivityDelegate delegate) {\n        start(activity, consumer, delegate, UtilsTransActivity4MainProcess.class);\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/VibrateUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.content.Context;\nimport android.media.AudioAttributes;\nimport android.os.Build;\nimport android.os.Vibrator;\n\nimport androidx.annotation.RequiresApi;\nimport androidx.annotation.RequiresPermission;\n\nimport static android.Manifest.permission.VIBRATE;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/09/29\n *     desc  : utils about vibrate\n * </pre>\n */\npublic final class VibrateUtils {\n\n    private static Vibrator vibrator;\n\n    private VibrateUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Vibrate.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.VIBRATE\" />}</p>\n     *\n     * @param milliseconds The number of milliseconds to vibrate.\n     */\n    @RequiresPermission(VIBRATE)\n    public static void vibrate(final long milliseconds) {\n        Vibrator vibrator = getVibrator();\n        if (vibrator == null) return;\n        vibrator.vibrate(milliseconds);\n    }\n\n    /**\n     * Vibrate.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.VIBRATE\" />}</p>\n     *\n     * @param milliseconds The number of milliseconds to vibrate.\n     * @param attributes   {@link AudioAttributes} corresponding to the vibration. For example,\n     *                     specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or\n     *                     {@link AudioAttributes#USAGE_NOTIFICATION_RINGTONE} for\n     *                     vibrations associated with incoming calls.\n     */\n    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)\n    @RequiresPermission(VIBRATE)\n    public static void vibrate(final long milliseconds, AudioAttributes attributes) {\n        Vibrator vibrator = getVibrator();\n        if (vibrator == null) return;\n        vibrator.vibrate(milliseconds, attributes);\n    }\n\n    /**\n     * VibrateCompat - Can vibrate in background\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.VIBRATE\" />}</p>\n     *\n     * @param milliseconds he number of milliseconds to vibrate.\n     */\n    @RequiresPermission(VIBRATE)\n    public static void vibrateCompat(final long milliseconds) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            vibrate(milliseconds, getAudioAttributes());\n        } else {\n            vibrate(milliseconds);\n        }\n    }\n\n    /**\n     * Vibrate.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.VIBRATE\" />}</p>\n     *\n     * @param pattern An array of longs of times for which to turn the vibrator on or off.\n     * @param repeat  The index into pattern at which to repeat, or -1 if you don't want to repeat.\n     */\n    @RequiresPermission(VIBRATE)\n    public static void vibrate(final long[] pattern, final int repeat) {\n        Vibrator vibrator = getVibrator();\n        if (vibrator == null) return;\n        vibrator.vibrate(pattern, repeat);\n    }\n\n    /**\n     * Vibrate.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.VIBRATE\" />}</p>\n     *\n     * @param pattern    An array of longs of times for which to turn the vibrator on or off.\n     * @param repeat     The index into pattern at which to repeat, or -1 if you don't want to repeat.\n     * @param attributes {@link AudioAttributes} corresponding to the vibration. For example,\n     *                   specify {@link AudioAttributes#USAGE_ALARM} for alarm vibrations or\n     *                   {@link AudioAttributes#USAGE_NOTIFICATION_RINGTONE} for\n     *                   vibrations associated with incoming calls.\n     */\n    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)\n    @RequiresPermission(VIBRATE)\n    public static void vibrate(final long[] pattern, final int repeat, AudioAttributes attributes) {\n        Vibrator vibrator = getVibrator();\n        if (vibrator == null) return;\n        vibrator.vibrate(pattern, repeat, attributes);\n    }\n\n    /**\n     * VibrateCompat - Can vibrate in background\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.VIBRATE\" />}</p>\n     *\n     * @param pattern An array of longs of times for which to turn the vibrator on or off.\n     * @param repeat  The index into pattern at which to repeat, or -1 if you don't want to repeat.\n     */\n    @RequiresPermission(VIBRATE)\n    public static void vibrateCompat(final long[] pattern, final int repeat) {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {\n            vibrate(pattern, repeat, getAudioAttributes());\n        } else {\n            vibrate(pattern, repeat);\n        }\n    }\n\n    /**\n     * Cancel vibrate.\n     * <p>Must hold {@code <uses-permission android:name=\"android.permission.VIBRATE\" />}</p>\n     */\n    @RequiresPermission(VIBRATE)\n    public static void cancel() {\n        Vibrator vibrator = getVibrator();\n        if (vibrator == null) return;\n        vibrator.cancel();\n    }\n\n    private static Vibrator getVibrator() {\n        if (vibrator == null) {\n            vibrator = (Vibrator) Utils.getApp().getSystemService(Context.VIBRATOR_SERVICE);\n        }\n        return vibrator;\n    }\n\n    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)\n    private static AudioAttributes getAudioAttributes() {\n        return new AudioAttributes.Builder()\n                .setUsage(AudioAttributes.USAGE_ALARM)\n                .build();\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ViewUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.content.Context;\nimport android.os.Build;\nimport android.text.TextUtils;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport java.util.Locale;\n\nimport androidx.annotation.LayoutRes;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/06/18\n *     desc  : utils about view\n * </pre>\n */\npublic class ViewUtils {\n\n    /**\n     * Set the enabled state of this view.\n     *\n     * @param view    The view.\n     * @param enabled True to enabled, false otherwise.\n     */\n    public static void setViewEnabled(View view, boolean enabled) {\n        setViewEnabled(view, enabled, (View) null);\n    }\n\n    /**\n     * Set the enabled state of this view.\n     *\n     * @param view     The view.\n     * @param enabled  True to enabled, false otherwise.\n     * @param excludes The excludes.\n     */\n    public static void setViewEnabled(View view, boolean enabled, View... excludes) {\n        if (view == null) return;\n        if (excludes != null) {\n            for (View exclude : excludes) {\n                if (view == exclude) return;\n            }\n        }\n        if (view instanceof ViewGroup) {\n            ViewGroup viewGroup = (ViewGroup) view;\n            int childCount = viewGroup.getChildCount();\n            for (int i = 0; i < childCount; i++) {\n                setViewEnabled(viewGroup.getChildAt(i), enabled, excludes);\n            }\n        }\n        view.setEnabled(enabled);\n    }\n\n    /**\n     * @param runnable The runnable\n     */\n    public static void runOnUiThread(final Runnable runnable) {\n        UtilsBridge.runOnUiThread(runnable);\n    }\n\n    /**\n     * @param runnable    The runnable.\n     * @param delayMillis The delay (in milliseconds) until the Runnable will be executed.\n     */\n    public static void runOnUiThreadDelayed(final Runnable runnable, long delayMillis) {\n        UtilsBridge.runOnUiThreadDelayed(runnable, delayMillis);\n    }\n\n    /**\n     * Return whether horizontal layout direction of views are from Right to Left.\n     *\n     * @return {@code true}: yes<br>{@code false}: no\n     */\n    public static boolean isLayoutRtl() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            Locale primaryLocale;\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n                primaryLocale = Utils.getApp().getResources().getConfiguration().getLocales().get(0);\n            } else {\n                primaryLocale = Utils.getApp().getResources().getConfiguration().locale;\n            }\n            return TextUtils.getLayoutDirectionFromLocale(primaryLocale) == View.LAYOUT_DIRECTION_RTL;\n        }\n        return false;\n    }\n\n    /**\n     * Fix the problem of topping the ScrollView nested ListView/GridView/WebView/RecyclerView.\n     *\n     * @param view The root view inner of ScrollView.\n     */\n    public static void fixScrollViewTopping(View view) {\n        view.setFocusable(false);\n        ViewGroup viewGroup = null;\n        if (view instanceof ViewGroup) {\n            viewGroup = (ViewGroup) view;\n        }\n        if (viewGroup == null) {\n            return;\n        }\n        for (int i = 0, n = viewGroup.getChildCount(); i < n; i++) {\n            View childAt = viewGroup.getChildAt(i);\n            childAt.setFocusable(false);\n            if (childAt instanceof ViewGroup) {\n                fixScrollViewTopping(childAt);\n            }\n        }\n    }\n\n    public static View layoutId2View(@LayoutRes final int layoutId) {\n        LayoutInflater inflate =\n                (LayoutInflater) Utils.getApp().getSystemService(Context.LAYOUT_INFLATER_SERVICE);\n        return inflate.inflate(layoutId, null);\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/VolumeUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.content.Context;\nimport android.media.AudioManager;\nimport android.os.Build;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2020/09/08\n *     desc  : utils about volume\n * </pre>\n */\npublic class VolumeUtils {\n\n    /**\n     * Return the volume.\n     *\n     * @param streamType The stream type.\n     *                   <ul>\n     *                   <li>{@link AudioManager#STREAM_VOICE_CALL}</li>\n     *                   <li>{@link AudioManager#STREAM_SYSTEM}</li>\n     *                   <li>{@link AudioManager#STREAM_RING}</li>\n     *                   <li>{@link AudioManager#STREAM_MUSIC}</li>\n     *                   <li>{@link AudioManager#STREAM_ALARM}</li>\n     *                   <li>{@link AudioManager#STREAM_NOTIFICATION}</li>\n     *                   <li>{@link AudioManager#STREAM_DTMF}</li>\n     *                   <li>{@link AudioManager#STREAM_ACCESSIBILITY}</li>\n     *                   </ul>\n     * @return the volume\n     */\n    public static int getVolume(int streamType) {\n        AudioManager am = (AudioManager) Utils.getApp().getSystemService(Context.AUDIO_SERVICE);\n        //noinspection ConstantConditions\n        return am.getStreamVolume(streamType);\n    }\n\n    /**\n     * Sets media volume.<br>\n     * When setting the value of parameter 'volume' greater than the maximum value of the media volume will not either cause error or throw exception but maximize the media volume.<br>\n     * Setting the value of volume lower than 0 will minimize the media volume.\n     *\n     * @param streamType The stream type.\n     *                   <ul>\n     *                   <li>{@link AudioManager#STREAM_VOICE_CALL}</li>\n     *                   <li>{@link AudioManager#STREAM_SYSTEM}</li>\n     *                   <li>{@link AudioManager#STREAM_RING}</li>\n     *                   <li>{@link AudioManager#STREAM_MUSIC}</li>\n     *                   <li>{@link AudioManager#STREAM_ALARM}</li>\n     *                   <li>{@link AudioManager#STREAM_NOTIFICATION}</li>\n     *                   <li>{@link AudioManager#STREAM_DTMF}</li>\n     *                   <li>{@link AudioManager#STREAM_ACCESSIBILITY}</li>\n     *                   </ul>\n     * @param volume     The volume.\n     * @param flags      The flags.\n     *                   <ul>\n     *                   <li>{@link AudioManager#FLAG_SHOW_UI}</li>\n     *                   <li>{@link AudioManager#FLAG_ALLOW_RINGER_MODES}</li>\n     *                   <li>{@link AudioManager#FLAG_PLAY_SOUND}</li>\n     *                   <li>{@link AudioManager#FLAG_REMOVE_SOUND_AND_VIBRATE}</li>\n     *                   <li>{@link AudioManager#FLAG_VIBRATE}</li>\n     *                   </ul>\n     */\n    public static void setVolume(int streamType, int volume, int flags) {\n        AudioManager am = (AudioManager) Utils.getApp().getSystemService(Context.AUDIO_SERVICE);\n        try {\n            //noinspection ConstantConditions\n            am.setStreamVolume(streamType, volume, flags);\n        } catch (SecurityException ignore) {\n        }\n    }\n\n    /**\n     * Return the maximum volume.\n     *\n     * @param streamType The stream type.\n     *                   <ul>\n     *                   <li>{@link AudioManager#STREAM_VOICE_CALL}</li>\n     *                   <li>{@link AudioManager#STREAM_SYSTEM}</li>\n     *                   <li>{@link AudioManager#STREAM_RING}</li>\n     *                   <li>{@link AudioManager#STREAM_MUSIC}</li>\n     *                   <li>{@link AudioManager#STREAM_ALARM}</li>\n     *                   <li>{@link AudioManager#STREAM_NOTIFICATION}</li>\n     *                   <li>{@link AudioManager#STREAM_DTMF}</li>\n     *                   <li>{@link AudioManager#STREAM_ACCESSIBILITY}</li>\n     *                   </ul>\n     * @return the maximum volume\n     */\n    public static int getMaxVolume(int streamType) {\n        AudioManager am = (AudioManager) Utils.getApp().getSystemService(Context.AUDIO_SERVICE);\n        //noinspection ConstantConditions\n        return am.getStreamMaxVolume(streamType);\n    }\n\n    /**\n     * Return the minimum volume.\n     *\n     * @param streamType The stream type.\n     *                   <ul>\n     *                   <li>{@link AudioManager#STREAM_VOICE_CALL}</li>\n     *                   <li>{@link AudioManager#STREAM_SYSTEM}</li>\n     *                   <li>{@link AudioManager#STREAM_RING}</li>\n     *                   <li>{@link AudioManager#STREAM_MUSIC}</li>\n     *                   <li>{@link AudioManager#STREAM_ALARM}</li>\n     *                   <li>{@link AudioManager#STREAM_NOTIFICATION}</li>\n     *                   <li>{@link AudioManager#STREAM_DTMF}</li>\n     *                   <li>{@link AudioManager#STREAM_ACCESSIBILITY}</li>\n     *                   </ul>\n     * @return the minimum volume\n     */\n    public static int getMinVolume(int streamType) {\n        AudioManager am = (AudioManager) Utils.getApp().getSystemService(Context.AUDIO_SERVICE);\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n            //noinspection ConstantConditions\n            return am.getStreamMinVolume(streamType);\n        }\n        return 0;\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/java/com/blankj/utilcode/util/ZipUtils.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.util.Log;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\nimport java.util.zip.ZipOutputStream;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/27\n *     desc  : utils about zip\n * </pre>\n */\npublic final class ZipUtils {\n\n    private static final int BUFFER_LEN = 8192;\n\n    private ZipUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Zip the files.\n     *\n     * @param srcFiles    The source of files.\n     * @param zipFilePath The path of ZIP file.\n     * @return {@code true}: success<br>{@code false}: fail\n     * @throws IOException if an I/O error has occurred\n     */\n    public static boolean zipFiles(final Collection<String> srcFiles,\n                                   final String zipFilePath)\n            throws IOException {\n        return zipFiles(srcFiles, zipFilePath, null);\n    }\n\n    /**\n     * Zip the files.\n     *\n     * @param srcFilePaths The paths of source files.\n     * @param zipFilePath  The path of ZIP file.\n     * @param comment      The comment.\n     * @return {@code true}: success<br>{@code false}: fail\n     * @throws IOException if an I/O error has occurred\n     */\n    public static boolean zipFiles(final Collection<String> srcFilePaths,\n                                   final String zipFilePath,\n                                   final String comment)\n            throws IOException {\n        if (srcFilePaths == null || zipFilePath == null) return false;\n        ZipOutputStream zos = null;\n        try {\n            zos = new ZipOutputStream(new FileOutputStream(zipFilePath));\n            for (String srcFile : srcFilePaths) {\n                if (!zipFile(UtilsBridge.getFileByPath(srcFile), \"\", zos, comment)) return false;\n            }\n            return true;\n        } finally {\n            if (zos != null) {\n                zos.finish();\n                zos.close();\n            }\n        }\n    }\n\n    /**\n     * Zip the files.\n     *\n     * @param srcFiles The source of files.\n     * @param zipFile  The ZIP file.\n     * @return {@code true}: success<br>{@code false}: fail\n     * @throws IOException if an I/O error has occurred\n     */\n    public static boolean zipFiles(final Collection<File> srcFiles, final File zipFile)\n            throws IOException {\n        return zipFiles(srcFiles, zipFile, null);\n    }\n\n    /**\n     * Zip the files.\n     *\n     * @param srcFiles The source of files.\n     * @param zipFile  The ZIP file.\n     * @param comment  The comment.\n     * @return {@code true}: success<br>{@code false}: fail\n     * @throws IOException if an I/O error has occurred\n     */\n    public static boolean zipFiles(final Collection<File> srcFiles,\n                                   final File zipFile,\n                                   final String comment)\n            throws IOException {\n        if (srcFiles == null || zipFile == null) return false;\n        ZipOutputStream zos = null;\n        try {\n            zos = new ZipOutputStream(new FileOutputStream(zipFile));\n            for (File srcFile : srcFiles) {\n                if (!zipFile(srcFile, \"\", zos, comment)) return false;\n            }\n            return true;\n        } finally {\n            if (zos != null) {\n                zos.finish();\n                zos.close();\n            }\n        }\n    }\n\n    /**\n     * Zip the file.\n     *\n     * @param srcFilePath The path of source file.\n     * @param zipFilePath The path of ZIP file.\n     * @return {@code true}: success<br>{@code false}: fail\n     * @throws IOException if an I/O error has occurred\n     */\n    public static boolean zipFile(final String srcFilePath,\n                                  final String zipFilePath)\n            throws IOException {\n        return zipFile(UtilsBridge.getFileByPath(srcFilePath), UtilsBridge.getFileByPath(zipFilePath), null);\n    }\n\n    /**\n     * Zip the file.\n     *\n     * @param srcFilePath The path of source file.\n     * @param zipFilePath The path of ZIP file.\n     * @param comment     The comment.\n     * @return {@code true}: success<br>{@code false}: fail\n     * @throws IOException if an I/O error has occurred\n     */\n    public static boolean zipFile(final String srcFilePath,\n                                  final String zipFilePath,\n                                  final String comment)\n            throws IOException {\n        return zipFile(UtilsBridge.getFileByPath(srcFilePath), UtilsBridge.getFileByPath(zipFilePath), comment);\n    }\n\n    /**\n     * Zip the file.\n     *\n     * @param srcFile The source of file.\n     * @param zipFile The ZIP file.\n     * @return {@code true}: success<br>{@code false}: fail\n     * @throws IOException if an I/O error has occurred\n     */\n    public static boolean zipFile(final File srcFile,\n                                  final File zipFile)\n            throws IOException {\n        return zipFile(srcFile, zipFile, null);\n    }\n\n    /**\n     * Zip the file.\n     *\n     * @param srcFile The source of file.\n     * @param zipFile The ZIP file.\n     * @param comment The comment.\n     * @return {@code true}: success<br>{@code false}: fail\n     * @throws IOException if an I/O error has occurred\n     */\n    public static boolean zipFile(final File srcFile,\n                                  final File zipFile,\n                                  final String comment)\n            throws IOException {\n        if (srcFile == null || zipFile == null) return false;\n        ZipOutputStream zos = null;\n        try {\n            zos = new ZipOutputStream(new FileOutputStream(zipFile));\n            return zipFile(srcFile, \"\", zos, comment);\n        } finally {\n            if (zos != null) {\n                zos.close();\n            }\n        }\n    }\n\n    private static boolean zipFile(final File srcFile,\n                                   String rootPath,\n                                   final ZipOutputStream zos,\n                                   final String comment)\n            throws IOException {\n        rootPath = rootPath + (UtilsBridge.isSpace(rootPath) ? \"\" : File.separator) + srcFile.getName();\n        if (srcFile.isDirectory()) {\n            File[] fileList = srcFile.listFiles();\n            if (fileList == null || fileList.length <= 0) {\n                ZipEntry entry = new ZipEntry(rootPath + '/');\n                entry.setComment(comment);\n                zos.putNextEntry(entry);\n                zos.closeEntry();\n            } else {\n                for (File file : fileList) {\n                    if (!zipFile(file, rootPath, zos, comment)) return false;\n                }\n            }\n        } else {\n            InputStream is = null;\n            try {\n                is = new BufferedInputStream(new FileInputStream(srcFile));\n                ZipEntry entry = new ZipEntry(rootPath);\n                entry.setComment(comment);\n                zos.putNextEntry(entry);\n                byte buffer[] = new byte[BUFFER_LEN];\n                int len;\n                while ((len = is.read(buffer, 0, BUFFER_LEN)) != -1) {\n                    zos.write(buffer, 0, len);\n                }\n                zos.closeEntry();\n            } finally {\n                if (is != null) {\n                    is.close();\n                }\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Unzip the file.\n     *\n     * @param zipFilePath The path of ZIP file.\n     * @param destDirPath The path of destination directory.\n     * @return the unzipped files\n     * @throws IOException if unzip unsuccessfully\n     */\n    public static List<File> unzipFile(final String zipFilePath,\n                                       final String destDirPath)\n            throws IOException {\n        return unzipFileByKeyword(zipFilePath, destDirPath, null);\n    }\n\n    /**\n     * Unzip the file.\n     *\n     * @param zipFile The ZIP file.\n     * @param destDir The destination directory.\n     * @return the unzipped files\n     * @throws IOException if unzip unsuccessfully\n     */\n    public static List<File> unzipFile(final File zipFile,\n                                       final File destDir)\n            throws IOException {\n        return unzipFileByKeyword(zipFile, destDir, null);\n    }\n\n    /**\n     * Unzip the file by keyword.\n     *\n     * @param zipFilePath The path of ZIP file.\n     * @param destDirPath The path of destination directory.\n     * @param keyword     The keyboard.\n     * @return the unzipped files\n     * @throws IOException if unzip unsuccessfully\n     */\n    public static List<File> unzipFileByKeyword(final String zipFilePath,\n                                                final String destDirPath,\n                                                final String keyword)\n            throws IOException {\n        return unzipFileByKeyword(UtilsBridge.getFileByPath(zipFilePath), UtilsBridge.getFileByPath(destDirPath), keyword);\n    }\n\n    /**\n     * Unzip the file by keyword.\n     *\n     * @param zipFile The ZIP file.\n     * @param destDir The destination directory.\n     * @param keyword The keyboard.\n     * @return the unzipped files\n     * @throws IOException if unzip unsuccessfully\n     */\n    public static List<File> unzipFileByKeyword(final File zipFile,\n                                                final File destDir,\n                                                final String keyword)\n            throws IOException {\n        if (zipFile == null || destDir == null) return null;\n        List<File> files = new ArrayList<>();\n        ZipFile zip = new ZipFile(zipFile);\n        Enumeration<?> entries = zip.entries();\n        try {\n            if (UtilsBridge.isSpace(keyword)) {\n                while (entries.hasMoreElements()) {\n                    ZipEntry entry = ((ZipEntry) entries.nextElement());\n                    String entryName = entry.getName().replace(\"\\\\\", \"/\");\n                    if (entryName.contains(\"../\")) {\n                        Log.e(\"ZipUtils\", \"entryName: \" + entryName + \" is dangerous!\");\n                        continue;\n                    }\n                    if (!unzipChildFile(destDir, files, zip, entry, entryName)) return files;\n                }\n            } else {\n                while (entries.hasMoreElements()) {\n                    ZipEntry entry = ((ZipEntry) entries.nextElement());\n                    String entryName = entry.getName().replace(\"\\\\\", \"/\");\n                    if (entryName.contains(\"../\")) {\n                        Log.e(\"ZipUtils\", \"entryName: \" + entryName + \" is dangerous!\");\n                        continue;\n                    }\n                    if (entryName.contains(keyword)) {\n                        if (!unzipChildFile(destDir, files, zip, entry, entryName)) return files;\n                    }\n                }\n            }\n        } finally {\n            zip.close();\n        }\n        return files;\n    }\n\n    private static boolean unzipChildFile(final File destDir,\n                                          final List<File> files,\n                                          final ZipFile zip,\n                                          final ZipEntry entry,\n                                          final String name) throws IOException {\n        File file = new File(destDir, name);\n        files.add(file);\n        if (entry.isDirectory()) {\n            return UtilsBridge.createOrExistsDir(file);\n        } else {\n            if (!UtilsBridge.createOrExistsFile(file)) return false;\n            InputStream in = null;\n            OutputStream out = null;\n            try {\n                in = new BufferedInputStream(zip.getInputStream(entry));\n                out = new BufferedOutputStream(new FileOutputStream(file));\n                byte buffer[] = new byte[BUFFER_LEN];\n                int len;\n                while ((len = in.read(buffer)) != -1) {\n                    out.write(buffer, 0, len);\n                }\n            } finally {\n                if (in != null) {\n                    in.close();\n                }\n                if (out != null) {\n                    out.close();\n                }\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Return the files' path in ZIP file.\n     *\n     * @param zipFilePath The path of ZIP file.\n     * @return the files' path in ZIP file\n     * @throws IOException if an I/O error has occurred\n     */\n    public static List<String> getFilesPath(final String zipFilePath)\n            throws IOException {\n        return getFilesPath(UtilsBridge.getFileByPath(zipFilePath));\n    }\n\n    /**\n     * Return the files' path in ZIP file.\n     *\n     * @param zipFile The ZIP file.\n     * @return the files' path in ZIP file\n     * @throws IOException if an I/O error has occurred\n     */\n    public static List<String> getFilesPath(final File zipFile)\n            throws IOException {\n        if (zipFile == null) return null;\n        List<String> paths = new ArrayList<>();\n        ZipFile zip = new ZipFile(zipFile);\n        Enumeration<?> entries = zip.entries();\n        while (entries.hasMoreElements()) {\n            String entryName = ((ZipEntry) entries.nextElement()).getName().replace(\"\\\\\", \"/\");\n            if (entryName.contains(\"../\")) {\n                Log.e(\"ZipUtils\", \"entryName: \" + entryName + \" is dangerous!\");\n                paths.add(entryName);\n            } else {\n                paths.add(entryName);\n            }\n        }\n        zip.close();\n        return paths;\n    }\n\n    /**\n     * Return the files' comment in ZIP file.\n     *\n     * @param zipFilePath The path of ZIP file.\n     * @return the files' comment in ZIP file\n     * @throws IOException if an I/O error has occurred\n     */\n    public static List<String> getComments(final String zipFilePath)\n            throws IOException {\n        return getComments(UtilsBridge.getFileByPath(zipFilePath));\n    }\n\n    /**\n     * Return the files' comment in ZIP file.\n     *\n     * @param zipFile The ZIP file.\n     * @return the files' comment in ZIP file\n     * @throws IOException if an I/O error has occurred\n     */\n    public static List<String> getComments(final File zipFile)\n            throws IOException {\n        if (zipFile == null) return null;\n        List<String> comments = new ArrayList<>();\n        ZipFile zip = new ZipFile(zipFile);\n        Enumeration<?> entries = zip.entries();\n        while (entries.hasMoreElements()) {\n            ZipEntry entry = ((ZipEntry) entries.nextElement());\n            comments.add(entry.getComment());\n        }\n        zip.close();\n        return comments;\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/main/res/drawable/utils_toast_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <solid android:color=\"#E6EEEEEE\" />\n    <corners android:radius=\"16dp\" />\n</shape>\n\n"
  },
  {
    "path": "lib/utilcode/src/main/res/layout/utils_toast_view.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<view xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    class=\"com.blankj.utilcode.util.ToastUtils$UtilsMaxWidthRelativeLayout\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@drawable/utils_toast_bg\"\n    android:paddingLeft=\"16dp\"\n    android:paddingTop=\"12dp\"\n    android:paddingRight=\"16dp\"\n    android:paddingBottom=\"12dp\">\n\n    <View\n        android:id=\"@+id/utvLeftIconView\"\n        android:layout_width=\"22dp\"\n        android:layout_height=\"22dp\"\n        android:layout_alignParentLeft=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:layout_marginRight=\"8dp\"\n        android:visibility=\"gone\"\n        tools:background=\"#00ff00\"\n        tools:visibility=\"gone\" />\n\n    <View\n        android:id=\"@+id/utvTopIconView\"\n        android:layout_width=\"22dp\"\n        android:layout_height=\"22dp\"\n        android:layout_alignParentTop=\"true\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_marginBottom=\"4dp\"\n        android:visibility=\"gone\"\n        tools:background=\"#00ff00\"\n        tools:visibility=\"gone\" />\n\n    <View\n        android:id=\"@+id/utvRightIconView\"\n        android:layout_width=\"22dp\"\n        android:layout_height=\"22dp\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:layout_marginLeft=\"8dp\"\n        android:visibility=\"gone\"\n        tools:background=\"#00ff00\"\n        tools:visibility=\"gone\" />\n\n    <View\n        android:id=\"@+id/utvBottomIconView\"\n        android:layout_width=\"22dp\"\n        android:layout_height=\"22dp\"\n        android:layout_alignParentBottom=\"true\"\n        android:layout_centerHorizontal=\"true\"\n        android:layout_marginTop=\"4dp\"\n        android:visibility=\"gone\"\n        tools:background=\"#00ff00\"\n        tools:visibility=\"gone\" />\n\n    <TextView\n        android:id=\"@android:id/message\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_above=\"@id/utvBottomIconView\"\n        android:layout_below=\"@id/utvTopIconView\"\n        android:layout_centerInParent=\"true\"\n        android:layout_toLeftOf=\"@id/utvRightIconView\"\n        android:layout_toRightOf=\"@id/utvLeftIconView\"\n        android:fontFamily=\"sans-serif\"\n        android:gravity=\"center\"\n        android:lineSpacingExtra=\"2dp\"\n        android:textColor=\"#DE000000\"\n        android:textSize=\"14sp\"\n        tools:text=\"This is long text.\" />\n</view>"
  },
  {
    "path": "lib/utilcode/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"ActivityTranslucent\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n        <item name=\"android:background\">@android:color/transparent</item>\n        <item name=\"android:colorBackgroundCacheHint\">@null</item>\n        <item name=\"android:windowActionBar\">false</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:backgroundDimEnabled\">false</item>\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:windowContentOverlay\">@null</item>\n        <item name=\"android:windowIsTranslucent\">true</item>\n        <item name=\"android:windowAnimationStyle\">@null</item>\n        <item name=\"android:windowDisablePreview\">true</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "lib/utilcode/src/main/res/values-v21/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"ActivityTranslucent\" parent=\"Theme.AppCompat.Light.NoActionBar\">\n        <item name=\"android:background\">@android:color/transparent</item>\n        <item name=\"android:colorBackgroundCacheHint\">@null</item>\n        <item name=\"android:windowActionBar\">false</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n        <item name=\"android:backgroundDimEnabled\">false</item>\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:windowContentOverlay\">@null</item>\n        <item name=\"android:windowIsTranslucent\">true</item>\n        <item name=\"android:windowAnimationStyle\">@null</item>\n        <item name=\"android:windowDisablePreview\">true</item>\n    </style>\n\n</resources>"
  },
  {
    "path": "lib/utilcode/src/main/res/xml/util_code_provider_paths.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<paths>\n    <files-path\n        name=\"files_path\"\n        path=\".\" />\n\n    <cache-path\n        name=\"cache_path\"\n        path=\".\" />\n\n    <external-path\n        name=\"external_path\"\n        path=\".\" />\n\n    <external-files-path\n        name=\"external_files_path\"\n        path=\".\" />\n\n    <external-cache-path\n        name=\"external_cache_path\"\n        path=\".\" />\n\n    <external-media-path\n        name=\"external_media_path\"\n        path=\".\" />\n      <!--配置root-path。这样子可以读取到sd卡和一些应用分身的目录，否则微信分身保存的图片，就会导致 java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/999/tencent/MicroMsg/WeiXin/export1544062754693.jpg，在小米6的手机上微信分身有这个crash，华为没有\n-->\n    <root-path\n        name=\"root-path\"\n        path=\"\" />\n</paths>\n"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/ApiUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/11\n *     desc  :\n * </pre>\n */\npublic class ApiUtilsTest extends BaseTest {\n\n    @Before\n    public void setUp() throws Exception {\n        ApiUtils.register(TestApiImpl.class);\n    }\n\n    @Test\n    public void getApi() {\n        System.out.println(\"ApiUtils.getApi(TestApi.class).test(\\\"hehe\\\") = \" + ApiUtils.getApi(TestApi.class).test(\"hehe\"));\n    }\n\n    @Test\n    public void toString_() {\n        System.out.println(\"ApiUtils.toString_() = \" + ApiUtils.toString_());\n    }\n\n    @ApiUtils.Api\n    public static class TestApiImpl extends TestApi {\n\n        @Override\n        public String test(String param) {\n            System.out.println(\"param = \" + param);\n            return \"haha\";\n        }\n\n    }\n\n    public static abstract class TestApi extends ApiUtils.BaseApi {\n\n        public abstract String test(String name);\n\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/ArrayUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.List;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/12\n *     desc  : test ArrayUtils\n * </pre>\n */\npublic class ArrayUtilsTest extends BaseTest {\n\n    @Test\n    public void newArray() {\n//        System.out.println(ArrayUtils.toString(ArrayUtils.newArray(null)));\n        System.out.println(ArrayUtils.toString(ArrayUtils.newArray((String) null)));\n        System.out.println(ArrayUtils.toString(ArrayUtils.newArray(0, 1, 2, 3)));\n        System.out.println(ArrayUtils.toString(ArrayUtils.newLongArray(0, 1, 2, 3)));\n        System.out.println(ArrayUtils.toString(ArrayUtils.newIntArray(0, 1, 2, 3)));\n        System.out.println(ArrayUtils.toString(ArrayUtils.newShortArray((short) 0, (short) 1, (short) 2, (short) 3)));\n        System.out.println(ArrayUtils.toString(ArrayUtils.newCharArray('0', '1', '2', '3')));\n        System.out.println(ArrayUtils.toString(ArrayUtils.newByteArray((byte) 0, (byte) 1, (byte) 2, (byte) 3)));\n        System.out.println(ArrayUtils.toString(ArrayUtils.newDoubleArray(0, 1, 2, 3)));\n        System.out.println(ArrayUtils.toString(ArrayUtils.newFloatArray(0, 1, 2, 3)));\n        System.out.println(ArrayUtils.toString(ArrayUtils.newBooleanArray(false, true, false, true)));\n    }\n\n    @Test\n    public void isEmpty() {\n        Object nullArr = null;\n        Object emptyArr = new int[]{};\n        Assert.assertTrue(ArrayUtils.isEmpty(nullArr));\n        Assert.assertTrue(ArrayUtils.isEmpty(emptyArr));\n        Assert.assertFalse(ArrayUtils.isEmpty(new int[]{1}));\n    }\n\n    @Test\n    public void getLength() {\n        Object nullArr = null;\n        Object emptyArr = new int[]{};\n        Assert.assertEquals(0, ArrayUtils.getLength(nullArr));\n        Assert.assertEquals(0, ArrayUtils.getLength(emptyArr));\n        Assert.assertEquals(1, ArrayUtils.getLength(new int[]{1}));\n    }\n\n    @Test\n    public void isSameLength() {\n        Object emptyArr1 = new int[]{};\n        Assert.assertTrue(ArrayUtils.isSameLength(null, emptyArr1));\n        Assert.assertTrue(ArrayUtils.isSameLength(new boolean[0], emptyArr1));\n    }\n\n    @Test\n    public void get() {\n        Object emptyArr1 = new int[]{0, 1, 2};\n        Assert.assertEquals(0, ArrayUtils.get(emptyArr1, 0));\n        Assert.assertEquals(1, ArrayUtils.get(emptyArr1, 1));\n        ArrayUtils.get(emptyArr1, 4);\n    }\n\n    @Test\n    public void getOrDefault() {\n        Object array = new int[]{0, 1, 2};\n        Assert.assertEquals(0, ArrayUtils.get(array, 0));\n        Assert.assertEquals(1, ArrayUtils.get(array, 1));\n        Assert.assertEquals(-1, ArrayUtils.get(array, 4, -1));\n        Assert.assertNull(ArrayUtils.get(array, 4));\n    }\n\n    @Test\n    public void set() {\n        int[] array = new int[]{0, -1, 2};\n        ArrayUtils.set(array, 1, 1);\n        Assert.assertArrayEquals(new int[]{0, 1, 2}, array);\n    }\n\n    @Test\n    public void equals() {\n        Assert.assertTrue(ArrayUtils.equals(null, (Object[]) null));\n    }\n\n    @Test\n    public void reverse() {\n        int[] array = new int[]{0, 1, 2, 3};\n        ArrayUtils.reverse(array);\n        Assert.assertArrayEquals(new int[]{3, 2, 1, 0}, array);\n    }\n\n    @Test\n    public void copy() {\n        int[] array = new int[]{0, 1, 2, 3};\n        int[] copy = ArrayUtils.copy(array);\n        Assert.assertArrayEquals(array, copy);\n        Assert.assertNotSame(array, copy);\n    }\n\n    @Test\n    public void subArray() {\n        int[] array = new int[]{0, 1, 2, 3};\n        int[] subArray = ArrayUtils.subArray(array, 1, 3);\n        Assert.assertArrayEquals(new int[]{1, 2}, subArray);\n    }\n\n    @Test\n    public void add() {\n        int[] array = new int[]{0, 1, 2, 3};\n        int[] addLastOne = ArrayUtils.add(array, 4);\n        int[] addFirstOne = ArrayUtils.add(array, 0, -1);\n        int[] addArr = ArrayUtils.add(array, new int[]{4, 5});\n        int[] addFirstArr = ArrayUtils.add(array, 0, new int[]{-2, -1});\n        int[] addMidArr = ArrayUtils.add(array, 2, new int[]{1, 2});\n        Assert.assertArrayEquals(new int[]{0, 1, 2, 3, 4}, addLastOne);\n        Assert.assertArrayEquals(new int[]{-1, 0, 1, 2, 3}, addFirstOne);\n        Assert.assertArrayEquals(new int[]{0, 1, 2, 3, 4, 5}, addArr);\n        Assert.assertArrayEquals(new int[]{-2, -1, 0, 1, 2, 3}, addFirstArr);\n        Assert.assertArrayEquals(new int[]{0, 1, 1, 2, 2, 3}, addMidArr);\n    }\n\n    @Test\n    public void remove() {\n        int[] array = new int[]{0, 1, 2, 3};\n        int[] remove = ArrayUtils.remove(array, 0);\n        Assert.assertArrayEquals(new int[]{1, 2, 3}, remove);\n    }\n\n    @Test\n    public void removeElement() {\n        int[] array = new int[]{0, 1, 2, 3};\n        int[] remove = ArrayUtils.removeElement(array, 0);\n        Assert.assertArrayEquals(new int[]{1, 2, 3}, remove);\n    }\n\n    @Test\n    public void indexOf() {\n        int[] array = new int[]{0, 1, 2, 3};\n        int i = ArrayUtils.indexOf(array, 0);\n        int i1 = ArrayUtils.indexOf(array, -1);\n        int i2 = ArrayUtils.indexOf(array, 0, 1);\n        Assert.assertEquals(0, i);\n        Assert.assertEquals(-1, i1);\n        Assert.assertEquals(-1, i2);\n    }\n\n    @Test\n    public void lastIndexOf() {\n        int[] array = new int[]{0, 1, 2, 3, 0};\n        int i = ArrayUtils.lastIndexOf(array, 0);\n        int i1 = ArrayUtils.lastIndexOf(array, -1);\n        int i2 = ArrayUtils.lastIndexOf(array, 0, 1);\n        Assert.assertEquals(4, i);\n        Assert.assertEquals(-1, i1);\n        Assert.assertEquals(0, i2);\n    }\n\n    @Test\n    public void contains() {\n        int[] array = new int[]{0, 1, 2, 3};\n        Assert.assertTrue(ArrayUtils.contains(array, 0));\n        Assert.assertFalse(ArrayUtils.contains(array, 4));\n    }\n\n    @Test\n    public void toPrimitive() {\n        int[] primitiveArray = new int[]{0, 1, 2, 3};\n        Integer[] array = new Integer[]{0, 1, 2, 3};\n        Assert.assertArrayEquals(primitiveArray, ArrayUtils.toPrimitive(array));\n        int[] primitiveArray1 = new int[]{0, 1, 2, 3, 0};\n        Integer[] array1 = new Integer[]{0, 1, 2, 3, null};\n        Assert.assertArrayEquals(primitiveArray1, ArrayUtils.toPrimitive(array1, 0));\n    }\n\n    @Test\n    public void toObject() {\n        int[] primitiveArray = new int[]{0, 1, 2, 3};\n        Integer[] array = new Integer[]{0, 1, 2, 3};\n        Assert.assertArrayEquals(array, ArrayUtils.toObject(primitiveArray));\n    }\n\n    @Test\n    public void asList() {\n//        Assert.assertEquals(0, ArrayUtils.asList(null).size());\n\n        List<Integer> list = ArrayUtils.asList(1, 2, 3, 4);\n        System.out.println(list);\n    }\n\n    @Test\n    public void sort() {\n        int[] ints = {2, 1, 0, 1, 2, 3};\n        ArrayUtils.sort(ints);\n        System.out.println(ArrayUtils.toString(ints));\n    }\n\n    @Test\n    public void forAllDo() {\n        ArrayUtils.forAllDo(null, null);\n        int[] array = new int[]{0, 1, 2, 3};\n        ArrayUtils.forAllDo(array, new ArrayUtils.Closure<Integer>() {\n            @Override\n            public void execute(int index, Integer item) {\n                System.out.println(index + \", \" + item);\n            }\n        });\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/BaseTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.robolectric.RobolectricTestRunner;\nimport org.robolectric.RuntimeEnvironment;\nimport org.robolectric.annotation.Config;\nimport org.robolectric.shadows.ShadowLog;\n\nimport java.util.concurrent.Executor;\n\nimport androidx.annotation.NonNull;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/08/03\n *     desc  :\n * </pre>\n */\n@RunWith(RobolectricTestRunner.class)\n@Config(manifest = Config.NONE, shadows = {ShadowLog.class})\npublic class BaseTest {\n\n    @BusUtils.Bus(tag = \"base\")\n    public void noParamFun(int i) {\n        System.out.println(\"base\" + i);\n    }\n\n    public BaseTest() {\n        ShadowLog.stream = System.out;\n        ThreadUtils.setDeliver(new Executor() {\n            @Override\n            public void execute(@NonNull Runnable command) {\n                command.run();\n            }\n        });\n        Utils.init(RuntimeEnvironment.application);\n    }\n\n    @Test\n    public void test() throws Exception {\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/BusUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport org.greenrobot.eventbus.EventBus;\nimport org.greenrobot.eventbus.Subscribe;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.util.concurrent.CountDownLatch;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/11\n *     desc  :\n * </pre>\n */\npublic class BusUtilsTest extends BaseTest {\n\n    private static final String TAG_NO_PARAM         = \"TagNoParam\";\n    private static final String TAG_ONE_PARAM        = \"TagOneParam\";\n    private static final String TAG_NO_PARAM_STICKY  = \"TagNoParamSticky\";\n    private static final String TAG_ONE_PARAM_STICKY = \"TagOneParamSticky\";\n\n    private static final String TAG_IO     = \"TAG_IO\";\n    private static final String TAG_CPU    = \"TAG_CPU\";\n    private static final String TAG_CACHED = \"TAG_CACHED\";\n    private static final String TAG_SINGLE = \"TAG_SINGLE\";\n\n    @BusUtils.Bus(tag = TAG_NO_PARAM)\n    public void noParamFun() {\n        System.out.println(\"noParam\");\n    }\n\n    @BusUtils.Bus(tag = TAG_NO_PARAM)\n    public void noParamSameTagFun() {\n        System.out.println(\"sameTag: noParam\");\n    }\n\n    @BusUtils.Bus(tag = TAG_ONE_PARAM)\n    public void oneParamFun(String param) {\n        System.out.println(param);\n    }\n\n    @BusUtils.Bus(tag = TAG_NO_PARAM_STICKY, sticky = true)\n    public void foo() {\n        System.out.println(\"foo\");\n    }\n\n    @BusUtils.Bus(tag = TAG_ONE_PARAM_STICKY, sticky = true)\n    public void oneParamStickyFun(Callback callback) {\n        if (callback != null) {\n            System.out.println(callback.call());\n        }\n    }\n\n    @BusUtils.Bus(tag = TAG_IO, threadMode = BusUtils.ThreadMode.IO)\n    public void ioFun(CountDownLatch latch) {\n        System.out.println(\"Thread.currentThread() = \" + Thread.currentThread());\n        latch.countDown();\n    }\n\n    @BusUtils.Bus(tag = TAG_CPU, threadMode = BusUtils.ThreadMode.CPU)\n    public void cpuFun(CountDownLatch latch) {\n        System.out.println(\"Thread.currentThread() = \" + Thread.currentThread());\n        latch.countDown();\n    }\n\n    @BusUtils.Bus(tag = TAG_CACHED, threadMode = BusUtils.ThreadMode.CACHED)\n    public void cachedFun(CountDownLatch latch) {\n        System.out.println(\"Thread.currentThread() = \" + Thread.currentThread());\n        latch.countDown();\n    }\n\n    @BusUtils.Bus(tag = TAG_SINGLE, threadMode = BusUtils.ThreadMode.SINGLE)\n    public void singleFun(CountDownLatch latch) {\n        System.out.println(\"Thread.currentThread() = \" + Thread.currentThread());\n        latch.countDown();\n    }\n\n    @Before\n    public void setUp() throws Exception {\n        BusUtils.registerBus4Test(TAG_NO_PARAM, BusUtilsTest.class.getName(), \"noParamFun\", \"\", \"\", false, \"POSTING\", 0);\n        BusUtils.registerBus4Test(TAG_ONE_PARAM, BusUtilsTest.class.getName(), \"oneParamFun\", String.class.getName(), \"param\", false, \"POSTING\", 0);\n        BusUtils.registerBus4Test(TAG_NO_PARAM_STICKY, BusUtilsTest.class.getName(), \"noParamStickyFun\", \"\", \"\", true, \"POSTING\", 0);\n        BusUtils.registerBus4Test(TAG_NO_PARAM_STICKY, BusUtilsTest.class.getName(), \"foo\", \"\", \"\", true, \"POSTING\", 0);\n        BusUtils.registerBus4Test(TAG_ONE_PARAM_STICKY, BusUtilsTest.class.getName(), \"oneParamStickyFun\", Callback.class.getName(), \"callback\", true, \"POSTING\", 0);\n\n        BusUtils.registerBus4Test(TAG_IO, BusUtilsTest.class.getName(), \"ioFun\", CountDownLatch.class.getName(), \"latch\", false, \"IO\", 0);\n        BusUtils.registerBus4Test(TAG_CPU, BusUtilsTest.class.getName(), \"cpuFun\", CountDownLatch.class.getName(), \"latch\", false, \"CPU\", 0);\n        BusUtils.registerBus4Test(TAG_CACHED, BusUtilsTest.class.getName(), \"cachedFun\", CountDownLatch.class.getName(), \"latch\", false, \"CACHED\", 0);\n        BusUtils.registerBus4Test(TAG_SINGLE, BusUtilsTest.class.getName(), \"singleFun\", CountDownLatch.class.getName(), \"latch\", false, \"SINGLE\", 0);\n    }\n\n    @BusUtils.Bus(tag = TAG_NO_PARAM_STICKY, sticky = true)\n    public void noParamStickyFun() {\n//        BusUtils.removeSticky(TAG_NO_PARAM_STICKY);\n        System.out.println(\"noParamSticky\");\n    }\n\n    @Subscribe(sticky = true)\n    public void eventBusFun(String param) {\n        System.out.println(param);\n    }\n\n    @Subscribe(sticky = true)\n    public void eventBusFun1(String param) {\n        System.out.println(\"foo\");\n    }\n\n    @Test\n    public void testEventBusSticky() {\n        EventBus.getDefault().postSticky(\"test\");\n        System.out.println(\"----\");\n\n        BusUtilsTest test = new BusUtilsTest();\n        EventBus.getDefault().register(new BusUtilsTest());\n        EventBus.getDefault().register(new BusUtilsTest());\n        EventBus.getDefault().register(new BusUtilsTest());\n\n        System.out.println(\"----\");\n\n        EventBus.getDefault().postSticky(\"test\");\n        EventBus.getDefault().postSticky(\"test\");\n    }\n\n    @Test\n    public void testSticky() {\n        BusUtils.postSticky(TAG_NO_PARAM_STICKY);\n        System.out.println(\"----\");\n\n        BusUtilsTest test = new BusUtilsTest();\n        BusUtils.register(new BusUtilsTest());\n        BusUtils.register(new BusUtilsTest());\n        BusUtils.register(new BusUtilsTest());\n\n        System.out.println(\"----\");\n\n        BusUtils.post(TAG_NO_PARAM_STICKY);\n//        BusUtils.post(TAG_NO_PARAM_STICKY);\n    }\n\n    @Test\n    public void testMultiThread() {\n        final BusUtilsTest test = new BusUtilsTest();\n//        for (int i = 0; i < 100; i++) {\n//            new Thread(new Runnable() {\n//                @Override\n//                public void run() {\n//                    BusUtils.register(test);\n//                }\n//            }).start();\n//        }\n//        CountDownLatch countDownLatch = new CountDownLatch(1);\n//        BusUtils.register(test);\n//        for (int i = 0; i < 100; i++) {\n//            new Thread(new Runnable() {\n//                @Override\n//                public void run() {\n//                    BusUtils.post(TAG_NO_PARAM);\n//                }\n//            }).start();\n//        }\n//        try {\n//            countDownLatch.await(1, TimeUnit.SECONDS);\n//        } catch (InterruptedException e) {\n//            e.printStackTrace();\n//        }\n//        BusUtils.unregister(test);\n//        for (int i = 0; i < 100; i++) {\n//            new Thread(new Runnable() {\n//                @Override\n//                public void run() {\n//                    BusUtils.unregister(test);\n//                }\n//            }).start();\n//        }\n//        for (int i = 0; i < 100; i++) {\n//            final int finalI = i;\n//            new Thread(new Runnable() {\n//                @Override\n//                public void run() {\n//                    BusUtils.register(test);\n//                    BusUtils.post(TAG_ONE_PARAM, \"\" + finalI);\n//                    BusUtils.unregister(test);\n//                }\n//            }).start();\n//        }\n    }\n\n    @Test\n    public void registerAndUnregister() {\n        BusUtilsTest test = new BusUtilsTest();\n\n        BusUtils.post(TAG_NO_PARAM);\n        BusUtils.post(TAG_ONE_PARAM, \"post to one param fun.\");\n\n        BusUtils.register(test);\n\n        BusUtils.post(TAG_NO_PARAM);\n        BusUtils.post(TAG_ONE_PARAM, \"Post to one param fun.\");\n\n        BusUtils.unregister(test);\n\n        BusUtils.post(TAG_NO_PARAM);\n        BusUtils.post(TAG_ONE_PARAM, \"Post to one param fun.\");\n    }\n\n    @Test\n    public void post() {\n        BusUtilsTest test0 = new BusUtilsTest();\n        BusUtilsTest test1 = new BusUtilsTest();\n\n        BusUtils.register(test0);\n        BusUtils.register(test1);\n\n        BusUtils.post(TAG_NO_PARAM);\n        BusUtils.post(TAG_ONE_PARAM, \"post to one param fun.\");\n\n        BusUtils.unregister(test0);\n        BusUtils.unregister(test1);\n    }\n\n    @Test\n    public void postSticky() {\n        System.out.println(\"-----not sticky bus postSticky will be failed.-----\");\n        BusUtils.postSticky(TAG_NO_PARAM);\n        BusUtils.postSticky(TAG_ONE_PARAM, \"post to one param fun.\");\n\n        System.out.println(\"\\n-----sticky bus postSticky will be successful.-----\");\n        BusUtils.postSticky(TAG_NO_PARAM_STICKY);\n        BusUtils.postSticky(TAG_ONE_PARAM_STICKY, new Callback() {\n            @Override\n            public String call() {\n                return \"post to one param sticky fun.\";\n            }\n        });\n        BusUtilsTest test = new BusUtilsTest();\n        System.out.println(\"\\n-----register.-----\");\n        BusUtils.register(test);\n\n        System.out.println(\"\\n-----sticky post.-----\");\n        BusUtils.postSticky(TAG_NO_PARAM);\n        BusUtils.postSticky(TAG_ONE_PARAM, \"post to one param fun.\");\n        BusUtils.postSticky(TAG_NO_PARAM_STICKY);\n        BusUtils.postSticky(TAG_ONE_PARAM_STICKY, new Callback() {\n            @Override\n            public String call() {\n                return \"post to one param sticky fun.\";\n            }\n        });\n\n        BusUtils.removeSticky(TAG_NO_PARAM_STICKY);\n        BusUtils.removeSticky(TAG_ONE_PARAM_STICKY);\n        BusUtils.unregister(test);\n    }\n\n    @Test\n    public void removeSticky() {\n        BusUtils.postSticky(TAG_NO_PARAM_STICKY);\n        BusUtils.postSticky(TAG_ONE_PARAM_STICKY, new Callback() {\n            @Override\n            public String call() {\n                return \"post to one param sticky fun.\";\n            }\n        });\n        BusUtilsTest test = new BusUtilsTest();\n        System.out.println(\"-----register.-----\");\n        BusUtils.register(test);\n\n        BusUtils.unregister(test);\n        System.out.println(\"\\n-----register.-----\");\n        BusUtils.register(test);\n\n        System.out.println(\"\\n-----remove sticky bus.-----\");\n        BusUtils.removeSticky(TAG_NO_PARAM_STICKY);\n        BusUtils.removeSticky(TAG_ONE_PARAM_STICKY);\n        BusUtils.unregister(test);\n\n        System.out.println(\"\\n-----register.-----\");\n        BusUtils.register(test);\n        BusUtils.unregister(test);\n    }\n\n    @Test\n    public void testThreadMode() throws InterruptedException {\n        BusUtilsTest test = new BusUtilsTest();\n        CountDownLatch latch = new CountDownLatch(4);\n        BusUtils.register(test);\n\n        BusUtils.post(TAG_IO, latch);\n        BusUtils.post(TAG_CPU, latch);\n        BusUtils.post(TAG_CACHED, latch);\n        BusUtils.post(TAG_SINGLE, latch);\n\n        latch.await();\n        BusUtils.unregister(test);\n    }\n\n    @Test\n    public void toString_() {\n        System.out.println(\"BusUtils.toString_() = \" + BusUtils.toString_());\n    }\n\n    @Test\n    public void testBase() {\n        BusUtils.registerBus4Test(\"base\", BaseTest.class.getName(), \"noParamFun\", \"int\", \"i\", false, \"POSTING\", 0);\n\n        BaseTest t = new BusUtilsTest();\n        BusUtils.register(t);\n        BusUtils.post(\"base\", 1);\n        BusUtils.unregister(t);\n    }\n\n    @Test\n    public void testSameTag() {\n        BusUtils.registerBus4Test(TAG_NO_PARAM, BusUtilsTest.class.getName(), \"noParamSameTagFun\", \"\", \"\", false, \"POSTING\", 2);\n\n        BusUtilsTest test = new BusUtilsTest();\n        BusUtils.register(test);\n        BusUtils.post(TAG_NO_PARAM);\n        BusUtils.unregister(test);\n    }\n\n    public interface Callback {\n        String call();\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/BusUtilsVsEventBusTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport org.greenrobot.eventbus.EventBus;\nimport org.greenrobot.eventbus.Subscribe;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/14\n *     desc  :\n * </pre>\n */\npublic class BusUtilsVsEventBusTest extends BaseTest {\n\n    @Subscribe\n    public void eventBusFun(String param) {\n    }\n\n    @BusUtils.Bus(tag = \"busUtilsFun\")\n    public void busUtilsFun(String param) {\n    }\n\n    @Before\n    public void setUp() throws Exception {\n        // 这一步是在 AOP 的时候注入的，这里通过反射来注入 busUtilsFun 事件，效果是一样的\n        ReflectUtils getInstance = ReflectUtils.reflect(BusUtils.class).method(\"getInstance\");\n        getInstance.method(\"registerBus\", \"busUtilsFun\", BusUtilsVsEventBusTest.class.getName(), \"busUtilsFun\", String.class.getName(), \"param\", false, \"POSTING\");\n    }\n\n    /**\n     * 注册 10000 个订阅者，共执行 10 次取平均值\n     */\n//    @Test\n    public void compareRegister10000Times() {\n        final List<BusUtilsVsEventBusTest> eventBusTests = new ArrayList<>();\n        final List<BusUtilsVsEventBusTest> busUtilsTests = new ArrayList<>();\n\n        compareWithEventBus(\"Register 10000 times.\", 10, 10000, new CompareCallback() {\n            @Override\n            public void runEventBus() {\n                BusUtilsVsEventBusTest test = new BusUtilsVsEventBusTest();\n                EventBus.getDefault().register(test);\n                eventBusTests.add(test);\n            }\n\n            @Override\n            public void runBusUtils() {\n                BusUtilsVsEventBusTest test = new BusUtilsVsEventBusTest();\n                BusUtils.register(test);\n                busUtilsTests.add(test);\n            }\n\n            @Override\n            public void restState() {\n                for (BusUtilsVsEventBusTest test : eventBusTests) {\n                    EventBus.getDefault().unregister(test);\n                }\n                eventBusTests.clear();\n\n                for (BusUtilsVsEventBusTest test : busUtilsTests) {\n                    BusUtils.unregister(test);\n                }\n                busUtilsTests.clear();\n            }\n        });\n    }\n\n    /**\n     * 向 1 个订阅者发送 * 1000000 次，共执行 10 次取平均值\n     */\n//    @Test\n    public void comparePostTo1Subscriber1000000Times() {\n        comparePostTemplate(\"Post to 1 subscriber 1000000 times.\", 1, 1000000);\n    }\n\n    /**\n     * 向 100 个订阅者发送 * 100000 次，共执行 10 次取平均值\n     */\n//    @Test\n    public void comparePostTo100Subscribers100000Times() {\n        comparePostTemplate(\"Post to 100 subscribers 100000 times.\", 100, 100000);\n    }\n\n    private void comparePostTemplate(String name, int subscribeNum, int postTimes) {\n        final List<BusUtilsVsEventBusTest> tests = new ArrayList<>();\n        for (int i = 0; i < subscribeNum; i++) {\n            BusUtilsVsEventBusTest test = new BusUtilsVsEventBusTest();\n            EventBus.getDefault().register(test);\n            BusUtils.register(test);\n            tests.add(test);\n        }\n\n        compareWithEventBus(name, 10, postTimes, new CompareCallback() {\n            @Override\n            public void runEventBus() {\n                EventBus.getDefault().post(\"EventBus\");\n            }\n\n            @Override\n            public void runBusUtils() {\n                BusUtils.post(\"busUtilsFun\", \"BusUtils\");\n            }\n\n            @Override\n            public void restState() {\n\n            }\n        });\n        for (BusUtilsVsEventBusTest test : tests) {\n            EventBus.getDefault().unregister(test);\n            BusUtils.unregister(test);\n        }\n    }\n\n    /**\n     * 注销 10000 个订阅者，共执行 10 次取平均值\n     */\n//    @Test\n    public void compareUnregister10000Times() {\n        final List<BusUtilsVsEventBusTest> tests = new ArrayList<>();\n        for (int i = 0; i < 10000; i++) {\n            BusUtilsVsEventBusTest test = new BusUtilsVsEventBusTest();\n            EventBus.getDefault().register(test);\n            BusUtils.register(test);\n            tests.add(test);\n        }\n\n        compareWithEventBus(\"Unregister 10000 times.\", 10, 1, new CompareCallback() {\n            @Override\n            public void runEventBus() {\n                for (BusUtilsVsEventBusTest test : tests) {\n                    EventBus.getDefault().unregister(test);\n                }\n            }\n\n            @Override\n            public void runBusUtils() {\n                for (BusUtilsVsEventBusTest test : tests) {\n                    BusUtils.unregister(test);\n                }\n            }\n\n            @Override\n            public void restState() {\n                for (BusUtilsVsEventBusTest test : tests) {\n                    EventBus.getDefault().register(test);\n                    BusUtils.register(test);\n                }\n            }\n        });\n\n        for (BusUtilsVsEventBusTest test : tests) {\n            EventBus.getDefault().unregister(test);\n            BusUtils.unregister(test);\n        }\n    }\n\n    /**\n     * @param name       传入的测试函数名\n     * @param sampleSize 样本的数量\n     * @param times      每次执行的次数\n     * @param callback   比较的回调函数\n     */\n    private void compareWithEventBus(String name, int sampleSize, int times, CompareCallback callback) {\n        long[][] dur = new long[2][sampleSize];\n        for (int i = 0; i < sampleSize; i++) {\n            long cur = System.currentTimeMillis();\n            for (int j = 0; j < times; j++) {\n                callback.runEventBus();\n            }\n            dur[0][i] = System.currentTimeMillis() - cur;\n\n            cur = System.currentTimeMillis();\n            for (int j = 0; j < times; j++) {\n                callback.runBusUtils();\n            }\n            dur[1][i] = System.currentTimeMillis() - cur;\n            callback.restState();\n        }\n        long eventBusAverageTime = 0;\n        long busUtilsAverageTime = 0;\n        for (int i = 0; i < sampleSize; i++) {\n            eventBusAverageTime += dur[0][i];\n            busUtilsAverageTime += dur[1][i];\n        }\n        System.out.println(\n                name +\n                \"\\nEventBusCostTime: \" + eventBusAverageTime / sampleSize +\n                \"\\nBusUtilsCostTime: \" + busUtilsAverageTime / sampleSize\n        );\n    }\n\n    public interface CompareCallback {\n        void runEventBus();\n\n        void runBusUtils();\n\n        void restState();\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/CacheDiskStaticUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.graphics.Bitmap;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.Serializable;\n\nimport static com.blankj.utilcode.util.TestConfig.FILE_SEP;\nimport static com.blankj.utilcode.util.TestConfig.PATH_CACHE;\nimport static org.junit.Assert.assertArrayEquals;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertNull;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/01/04\n *     desc  : test CacheDiskStaticUtils\n * </pre>\n */\npublic class CacheDiskStaticUtilsTest extends BaseTest {\n\n    private static final String           DISK1_PATH        = PATH_CACHE + \"disk1\" + FILE_SEP;\n    private static final String           DISK2_PATH        = PATH_CACHE + \"disk2\" + FILE_SEP;\n    private static final File             DISK1_FILE        = new File(DISK1_PATH);\n    private static final File             DISK2_FILE        = new File(DISK2_PATH);\n    private static final CacheDiskUtils   CACHE_DISK_UTILS1 = CacheDiskUtils.getInstance(DISK1_FILE);\n    private static final CacheDiskUtils   CACHE_DISK_UTILS2 = CacheDiskUtils.getInstance(DISK2_FILE);\n    private static final byte[]           BYTES             = \"CacheDiskUtils\".getBytes();\n    private static final String           STRING            = \"CacheDiskUtils\";\n    private static final JSONObject       JSON_OBJECT       = new JSONObject();\n    private static final JSONArray        JSON_ARRAY        = new JSONArray();\n    private static final ParcelableTest   PARCELABLE_TEST   = new ParcelableTest(\"Blankj\", \"CacheDiskUtils\");\n    private static final SerializableTest SERIALIZABLE_TEST = new SerializableTest(\"Blankj\", \"CacheDiskUtils\");\n    private static final Bitmap           BITMAP            = Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565);\n    private static final Drawable         DRAWABLE          = new BitmapDrawable(Utils.getApp().getResources(), BITMAP);\n\n    static {\n        try {\n            JSON_OBJECT.put(\"class\", \"CacheDiskUtils\");\n            JSON_OBJECT.put(\"author\", \"Blankj\");\n            JSON_ARRAY.put(0, JSON_OBJECT);\n        } catch (JSONException e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Before\n    public void setUp() {\n        CacheDiskStaticUtils.put(\"bytes1\", BYTES, 60 * CacheDiskUtils.SEC, CACHE_DISK_UTILS1);\n        CacheDiskStaticUtils.put(\"string1\", STRING, 60 * CacheDiskUtils.MIN, CACHE_DISK_UTILS1);\n        CacheDiskStaticUtils.put(\"jsonObject1\", JSON_OBJECT, 24 * CacheDiskUtils.HOUR, CACHE_DISK_UTILS1);\n        CacheDiskStaticUtils.put(\"jsonArray1\", JSON_ARRAY, 365 * CacheDiskUtils.DAY, CACHE_DISK_UTILS1);\n        CacheDiskStaticUtils.put(\"bitmap1\", BITMAP, 60 * CacheDiskUtils.SEC, CACHE_DISK_UTILS1);\n        CacheDiskStaticUtils.put(\"drawable1\", DRAWABLE, 60 * CacheDiskUtils.SEC, CACHE_DISK_UTILS1);\n        CacheDiskStaticUtils.put(\"parcelable1\", PARCELABLE_TEST, 60 * CacheDiskUtils.SEC, CACHE_DISK_UTILS1);\n        CacheDiskStaticUtils.put(\"serializable1\", SERIALIZABLE_TEST, 60 * CacheDiskUtils.SEC, CACHE_DISK_UTILS1);\n\n        CacheDiskStaticUtils.put(\"bytes2\", BYTES, CACHE_DISK_UTILS2);\n        CacheDiskStaticUtils.put(\"string2\", STRING, CACHE_DISK_UTILS2);\n        CacheDiskStaticUtils.put(\"jsonObject2\", JSON_OBJECT, CACHE_DISK_UTILS2);\n        CacheDiskStaticUtils.put(\"jsonArray2\", JSON_ARRAY, CACHE_DISK_UTILS2);\n        CacheDiskStaticUtils.put(\"bitmap2\", BITMAP, CACHE_DISK_UTILS2);\n        CacheDiskStaticUtils.put(\"drawable2\", DRAWABLE, CACHE_DISK_UTILS2);\n        CacheDiskStaticUtils.put(\"parcelable2\", PARCELABLE_TEST, CACHE_DISK_UTILS2);\n        CacheDiskStaticUtils.put(\"serializable2\", SERIALIZABLE_TEST, CACHE_DISK_UTILS2);\n    }\n\n    @Test\n    public void getBytes() {\n        assertEquals(STRING, new String(CacheDiskStaticUtils.getBytes(\"bytes1\", CACHE_DISK_UTILS1)));\n        assertEquals(STRING, new String(CacheDiskStaticUtils.getBytes(\"bytes1\", null, CACHE_DISK_UTILS1)));\n        assertNull(CacheDiskStaticUtils.getBytes(\"bytes2\", null, CACHE_DISK_UTILS1));\n\n        assertEquals(STRING, new String(CacheDiskStaticUtils.getBytes(\"bytes2\", CACHE_DISK_UTILS2)));\n        assertEquals(STRING, new String(CacheDiskStaticUtils.getBytes(\"bytes2\", null, CACHE_DISK_UTILS2)));\n        assertNull(CacheDiskStaticUtils.getBytes(\"bytes1\", null, CACHE_DISK_UTILS2));\n    }\n\n    @Test\n    public void getString() {\n        assertEquals(STRING, CacheDiskStaticUtils.getString(\"string1\", CACHE_DISK_UTILS1));\n        assertEquals(STRING, CacheDiskStaticUtils.getString(\"string1\", null, CACHE_DISK_UTILS1));\n        assertNull(CacheDiskStaticUtils.getString(\"string2\", null, CACHE_DISK_UTILS1));\n\n        assertEquals(STRING, CacheDiskStaticUtils.getString(\"string2\", CACHE_DISK_UTILS2));\n        assertEquals(STRING, CacheDiskStaticUtils.getString(\"string2\", null, CACHE_DISK_UTILS2));\n        assertNull(CacheDiskStaticUtils.getString(\"string1\", null, CACHE_DISK_UTILS2));\n    }\n\n    @Test\n    public void getJSONObject() {\n        assertEquals(JSON_OBJECT.toString(), CacheDiskStaticUtils.getJSONObject(\"jsonObject1\", CACHE_DISK_UTILS1).toString());\n        assertEquals(JSON_OBJECT.toString(), CacheDiskStaticUtils.getJSONObject(\"jsonObject1\", null, CACHE_DISK_UTILS1).toString());\n        assertNull(CacheDiskStaticUtils.getJSONObject(\"jsonObject2\", null, CACHE_DISK_UTILS1));\n\n        assertEquals(JSON_OBJECT.toString(), CacheDiskStaticUtils.getJSONObject(\"jsonObject2\", CACHE_DISK_UTILS2).toString());\n        assertEquals(JSON_OBJECT.toString(), CacheDiskStaticUtils.getJSONObject(\"jsonObject2\", null, CACHE_DISK_UTILS2).toString());\n        assertNull(CacheDiskStaticUtils.getJSONObject(\"jsonObject1\", null, CACHE_DISK_UTILS2));\n    }\n\n    @Test\n    public void getJSONArray() {\n        assertEquals(JSON_ARRAY.toString(), CacheDiskStaticUtils.getJSONArray(\"jsonArray1\", CACHE_DISK_UTILS1).toString());\n        assertEquals(JSON_ARRAY.toString(), CacheDiskStaticUtils.getJSONArray(\"jsonArray1\", null, CACHE_DISK_UTILS1).toString());\n        assertNull(CacheDiskStaticUtils.getJSONArray(\"jsonArray2\", null, CACHE_DISK_UTILS1));\n\n        assertEquals(JSON_ARRAY.toString(), CacheDiskStaticUtils.getJSONArray(\"jsonArray2\", CACHE_DISK_UTILS2).toString());\n        assertEquals(JSON_ARRAY.toString(), CacheDiskStaticUtils.getJSONArray(\"jsonArray2\", null, CACHE_DISK_UTILS2).toString());\n        assertNull(CacheDiskStaticUtils.getJSONArray(\"jsonArray1\", null, CACHE_DISK_UTILS2));\n    }\n\n    @Test\n    public void getBitmap() {\n        assertArrayEquals(\n                ImageUtils.bitmap2Bytes(BITMAP, Bitmap.CompressFormat.PNG, 100),\n                ImageUtils.bitmap2Bytes(CacheDiskStaticUtils.getBitmap(\"bitmap1\", CACHE_DISK_UTILS1), Bitmap.CompressFormat.PNG, 100)\n        );\n        assertArrayEquals(\n                ImageUtils.bitmap2Bytes(BITMAP, Bitmap.CompressFormat.PNG, 100),\n                ImageUtils.bitmap2Bytes(CacheDiskStaticUtils.getBitmap(\"bitmap1\", null, CACHE_DISK_UTILS1), Bitmap.CompressFormat.PNG, 100)\n        );\n        assertNull(CacheDiskStaticUtils.getBitmap(\"bitmap2\", null, CACHE_DISK_UTILS1));\n\n        assertArrayEquals(\n                ImageUtils.bitmap2Bytes(BITMAP, Bitmap.CompressFormat.PNG, 100),\n                ImageUtils.bitmap2Bytes(CacheDiskStaticUtils.getBitmap(\"bitmap2\", CACHE_DISK_UTILS2), Bitmap.CompressFormat.PNG, 100)\n        );\n        assertArrayEquals(\n                ImageUtils.bitmap2Bytes(BITMAP, Bitmap.CompressFormat.PNG, 100),\n                ImageUtils.bitmap2Bytes(CacheDiskStaticUtils.getBitmap(\"bitmap2\", null, CACHE_DISK_UTILS2), Bitmap.CompressFormat.PNG, 100)\n        );\n        assertNull(CacheDiskStaticUtils.getBitmap(\"bitmap1\", null, CACHE_DISK_UTILS2));\n    }\n\n    @Test\n    public void getDrawable() {\n        assertArrayEquals(\n                ImageUtils.bitmap2Bytes(BITMAP, Bitmap.CompressFormat.PNG, 100),\n                ImageUtils.drawable2Bytes(CacheDiskStaticUtils.getDrawable(\"drawable1\", CACHE_DISK_UTILS1), Bitmap.CompressFormat.PNG, 100)\n        );\n        assertArrayEquals(\n                ImageUtils.bitmap2Bytes(BITMAP, Bitmap.CompressFormat.PNG, 100),\n                ImageUtils.drawable2Bytes(CacheDiskStaticUtils.getDrawable(\"drawable1\", null, CACHE_DISK_UTILS1), Bitmap.CompressFormat.PNG, 100)\n        );\n        assertNull(CacheDiskStaticUtils.getDrawable(\"drawable2\", null, CACHE_DISK_UTILS1));\n\n        assertArrayEquals(\n                ImageUtils.bitmap2Bytes(BITMAP, Bitmap.CompressFormat.PNG, 100),\n                ImageUtils.drawable2Bytes(CacheDiskStaticUtils.getDrawable(\"drawable2\", CACHE_DISK_UTILS2), Bitmap.CompressFormat.PNG, 100)\n        );\n        assertArrayEquals(\n                ImageUtils.bitmap2Bytes(BITMAP, Bitmap.CompressFormat.PNG, 100),\n                ImageUtils.drawable2Bytes(CacheDiskStaticUtils.getDrawable(\"drawable2\", null, CACHE_DISK_UTILS2), Bitmap.CompressFormat.PNG, 100)\n        );\n        assertNull(CacheDiskStaticUtils.getDrawable(\"drawable1\", null, CACHE_DISK_UTILS2));\n    }\n\n    @Test\n    public void getParcel() {\n        assertEquals(PARCELABLE_TEST, CacheDiskStaticUtils.getParcelable(\"parcelable1\", ParcelableTest.CREATOR, CACHE_DISK_UTILS1));\n        assertEquals(PARCELABLE_TEST, CacheDiskStaticUtils.getParcelable(\"parcelable1\", ParcelableTest.CREATOR, null, CACHE_DISK_UTILS1));\n        assertNull(CacheDiskStaticUtils.getParcelable(\"parcelable2\", ParcelableTest.CREATOR, null, CACHE_DISK_UTILS1));\n\n        assertEquals(PARCELABLE_TEST, CacheDiskStaticUtils.getParcelable(\"parcelable2\", ParcelableTest.CREATOR, CACHE_DISK_UTILS2));\n        assertEquals(PARCELABLE_TEST, CacheDiskStaticUtils.getParcelable(\"parcelable2\", ParcelableTest.CREATOR, null, CACHE_DISK_UTILS2));\n        assertNull(CacheDiskStaticUtils.getParcelable(\"parcelable1\", ParcelableTest.CREATOR, null, CACHE_DISK_UTILS2));\n    }\n\n    @Test\n    public void getSerializable() {\n        assertEquals(SERIALIZABLE_TEST, CacheDiskStaticUtils.getSerializable(\"serializable1\", CACHE_DISK_UTILS1));\n        assertEquals(SERIALIZABLE_TEST, CacheDiskStaticUtils.getSerializable(\"serializable1\", null, CACHE_DISK_UTILS1));\n        assertNull(CacheDiskStaticUtils.getSerializable(\"parcelable2\", null, CACHE_DISK_UTILS1));\n\n        assertEquals(SERIALIZABLE_TEST, CacheDiskStaticUtils.getSerializable(\"serializable2\", CACHE_DISK_UTILS2));\n        assertEquals(SERIALIZABLE_TEST, CacheDiskStaticUtils.getSerializable(\"serializable2\", null, CACHE_DISK_UTILS2));\n        assertNull(CacheDiskStaticUtils.getSerializable(\"parcelable1\", null, CACHE_DISK_UTILS2));\n    }\n\n    @Test\n    public void getCacheSize() {\n        assertEquals(FileUtils.getLength(DISK1_FILE), CacheDiskStaticUtils.getCacheSize(CACHE_DISK_UTILS1));\n\n        assertEquals(FileUtils.getLength(DISK2_FILE), CacheDiskStaticUtils.getCacheSize(CACHE_DISK_UTILS2));\n    }\n\n    @Test\n    public void getCacheCount() {\n        assertEquals(8, CacheDiskStaticUtils.getCacheCount(CACHE_DISK_UTILS1));\n\n        assertEquals(8, CacheDiskStaticUtils.getCacheCount(CACHE_DISK_UTILS2));\n    }\n\n    @Test\n    public void remove() {\n        assertNotNull(CacheDiskStaticUtils.getString(\"string1\", CACHE_DISK_UTILS1));\n        CacheDiskStaticUtils.remove(\"string1\", CACHE_DISK_UTILS1);\n        assertNull(CacheDiskStaticUtils.getString(\"string1\", CACHE_DISK_UTILS1));\n\n        assertNotNull(CacheDiskStaticUtils.getString(\"string2\", CACHE_DISK_UTILS2));\n        CacheDiskStaticUtils.remove(\"string2\", CACHE_DISK_UTILS2);\n        assertNull(CacheDiskStaticUtils.getString(\"string2\", CACHE_DISK_UTILS2));\n    }\n\n    @Test\n    public void clear() {\n        assertNotNull(CacheDiskStaticUtils.getBytes(\"bytes1\", CACHE_DISK_UTILS1));\n        assertNotNull(CacheDiskStaticUtils.getString(\"string1\", CACHE_DISK_UTILS1));\n        assertNotNull(CacheDiskStaticUtils.getJSONObject(\"jsonObject1\", CACHE_DISK_UTILS1));\n        assertNotNull(CacheDiskStaticUtils.getJSONArray(\"jsonArray1\", CACHE_DISK_UTILS1));\n        assertNotNull(CacheDiskStaticUtils.getBitmap(\"bitmap1\", CACHE_DISK_UTILS1));\n        assertNotNull(CacheDiskStaticUtils.getDrawable(\"drawable1\", CACHE_DISK_UTILS1));\n        assertNotNull(CacheDiskStaticUtils.getParcelable(\"parcelable1\", ParcelableTest.CREATOR, CACHE_DISK_UTILS1));\n        assertNotNull(CacheDiskStaticUtils.getSerializable(\"serializable1\", CACHE_DISK_UTILS1));\n        CacheDiskStaticUtils.clear(CACHE_DISK_UTILS1);\n        assertNull(CacheDiskStaticUtils.getBytes(\"bytes1\", CACHE_DISK_UTILS1));\n        assertNull(CacheDiskStaticUtils.getString(\"string1\", CACHE_DISK_UTILS1));\n        assertNull(CacheDiskStaticUtils.getJSONObject(\"jsonObject1\", CACHE_DISK_UTILS1));\n        assertNull(CacheDiskStaticUtils.getJSONArray(\"jsonArray1\", CACHE_DISK_UTILS1));\n        assertNull(CacheDiskStaticUtils.getBitmap(\"bitmap1\", CACHE_DISK_UTILS1));\n        assertNull(CacheDiskStaticUtils.getDrawable(\"drawable1\", CACHE_DISK_UTILS1));\n        assertNull(CacheDiskStaticUtils.getParcelable(\"parcelable1\", ParcelableTest.CREATOR, CACHE_DISK_UTILS1));\n        assertNull(CacheDiskStaticUtils.getSerializable(\"serializable1\", CACHE_DISK_UTILS1));\n        assertEquals(0, CacheDiskStaticUtils.getCacheSize(CACHE_DISK_UTILS1));\n        assertEquals(0, CacheDiskStaticUtils.getCacheCount(CACHE_DISK_UTILS1));\n\n\n        assertNotNull(CacheDiskStaticUtils.getBytes(\"bytes2\", CACHE_DISK_UTILS2));\n        assertNotNull(CacheDiskStaticUtils.getString(\"string2\", CACHE_DISK_UTILS2));\n        assertNotNull(CacheDiskStaticUtils.getJSONObject(\"jsonObject2\", CACHE_DISK_UTILS2));\n        assertNotNull(CacheDiskStaticUtils.getJSONArray(\"jsonArray2\", CACHE_DISK_UTILS2));\n        assertNotNull(CacheDiskStaticUtils.getBitmap(\"bitmap2\", CACHE_DISK_UTILS2));\n        assertNotNull(CacheDiskStaticUtils.getDrawable(\"drawable2\", CACHE_DISK_UTILS2));\n        assertNotNull(CacheDiskStaticUtils.getParcelable(\"parcelable2\", ParcelableTest.CREATOR, CACHE_DISK_UTILS2));\n        assertNotNull(CacheDiskStaticUtils.getSerializable(\"serializable2\", CACHE_DISK_UTILS2));\n        CacheDiskStaticUtils.clear(CACHE_DISK_UTILS2);\n        assertNull(CacheDiskStaticUtils.getBytes(\"bytes2\", CACHE_DISK_UTILS2));\n        assertNull(CacheDiskStaticUtils.getString(\"string2\", CACHE_DISK_UTILS2));\n        assertNull(CacheDiskStaticUtils.getJSONObject(\"jsonObject2\", CACHE_DISK_UTILS2));\n        assertNull(CacheDiskStaticUtils.getJSONArray(\"jsonArray2\", CACHE_DISK_UTILS2));\n        assertNull(CacheDiskStaticUtils.getBitmap(\"bitmap2\", CACHE_DISK_UTILS2));\n        assertNull(CacheDiskStaticUtils.getDrawable(\"drawable2\", CACHE_DISK_UTILS2));\n        assertNull(CacheDiskStaticUtils.getParcelable(\"parcelable2\", ParcelableTest.CREATOR, CACHE_DISK_UTILS2));\n        assertNull(CacheDiskStaticUtils.getSerializable(\"serializable2\", CACHE_DISK_UTILS2));\n        assertEquals(0, CacheDiskStaticUtils.getCacheSize(CACHE_DISK_UTILS2));\n        assertEquals(0, CacheDiskStaticUtils.getCacheCount(CACHE_DISK_UTILS2));\n    }\n\n    @After\n    public void tearDown() {\n        CacheDiskStaticUtils.clear(CACHE_DISK_UTILS1);\n        CacheDiskStaticUtils.clear(CACHE_DISK_UTILS2);\n    }\n\n    static class ParcelableTest implements Parcelable {\n        String author;\n        String className;\n\n        public String getAuthor() {\n            return author;\n        }\n\n        public void setAuthor(String author) {\n            this.author = author;\n        }\n\n        public String getClassName() {\n            return className;\n        }\n\n        public void setClassName(String className) {\n            this.className = className;\n        }\n\n        ParcelableTest(String author, String className) {\n            this.author = author;\n            this.className = className;\n        }\n\n        ParcelableTest(Parcel in) {\n            author = in.readString();\n            className = in.readString();\n        }\n\n        @Override\n        public void writeToParcel(Parcel dest, int flags) {\n            dest.writeString(author);\n            dest.writeString(className);\n        }\n\n        @Override\n        public int describeContents() {\n            return 0;\n        }\n\n        public static final Creator<ParcelableTest> CREATOR = new Creator<ParcelableTest>() {\n            @Override\n            public ParcelableTest createFromParcel(Parcel in) {\n                return new ParcelableTest(in);\n            }\n\n            @Override\n            public ParcelableTest[] newArray(int size) {\n                return new ParcelableTest[size];\n            }\n        };\n\n        @Override\n        public boolean equals(Object obj) {\n            return obj instanceof ParcelableTest\n                    && ((ParcelableTest) obj).author.equals(author)\n                    && ((ParcelableTest) obj).className.equals(className);\n        }\n    }\n\n    static class SerializableTest implements Serializable {\n\n        private static final long serialVersionUID = -5806706668736895024L;\n\n        String author;\n        String className;\n\n        SerializableTest(String author, String className) {\n            this.author = author;\n            this.className = className;\n        }\n\n        public String getAuthor() {\n            return author;\n        }\n\n        public void setAuthor(String author) {\n            this.author = author;\n        }\n\n        public String getClassName() {\n            return className;\n        }\n\n        public void setClassName(String className) {\n            this.className = className;\n        }\n\n        @Override\n        public boolean equals(Object obj) {\n            return obj instanceof SerializableTest\n                    && ((SerializableTest) obj).author.equals(author)\n                    && ((SerializableTest) obj).className.equals(className);\n        }\n    }\n}\n\n"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/CacheDiskUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.graphics.Bitmap;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.Serializable;\n\nimport static com.blankj.utilcode.util.TestConfig.FILE_SEP;\nimport static com.blankj.utilcode.util.TestConfig.PATH_CACHE;\nimport static org.junit.Assert.assertArrayEquals;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertNull;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/05/26\n *     desc  : test CacheDiskUtils\n * </pre>\n */\npublic class CacheDiskUtilsTest extends BaseTest {\n\n    private static final String           DISK1_PATH        = PATH_CACHE + \"disk1\" + FILE_SEP;\n    private static final String           DISK2_PATH        = PATH_CACHE + \"disk2\" + FILE_SEP;\n    private static final File             DISK1_FILE        = new File(DISK1_PATH);\n    private static final File             DISK2_FILE        = new File(DISK2_PATH);\n    private static final CacheDiskUtils   CACHE_DISK_UTILS1 = CacheDiskUtils.getInstance(DISK1_FILE);\n    private static final CacheDiskUtils   CACHE_DISK_UTILS2 = CacheDiskUtils.getInstance(DISK2_FILE);\n    private static final byte[]           BYTES             = \"CacheDiskUtils\".getBytes();\n    private static final String           STRING            = \"CacheDiskUtils\";\n    private static final JSONObject       JSON_OBJECT       = new JSONObject();\n    private static final JSONArray        JSON_ARRAY        = new JSONArray();\n    private static final ParcelableTest   PARCELABLE_TEST   = new ParcelableTest(\"Blankj\", \"CacheDiskUtils\");\n    private static final SerializableTest SERIALIZABLE_TEST = new SerializableTest(\"Blankj\", \"CacheDiskUtils\");\n    private static final Bitmap           BITMAP            = Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565);\n    private static final Drawable         DRAWABLE          = new BitmapDrawable(Utils.getApp().getResources(), BITMAP);\n\n    static {\n        try {\n            JSON_OBJECT.put(\"class\", \"CacheDiskUtils\");\n            JSON_OBJECT.put(\"author\", \"Blankj\");\n            JSON_ARRAY.put(0, JSON_OBJECT);\n        } catch (JSONException e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Before\n    public void setUp() {\n        CACHE_DISK_UTILS1.put(\"bytes1\", BYTES, 60 * CacheDiskUtils.SEC);\n        CACHE_DISK_UTILS1.put(\"string1\", STRING, 60 * CacheDiskUtils.MIN);\n        CACHE_DISK_UTILS1.put(\"jsonObject1\", JSON_OBJECT, 24 * CacheDiskUtils.HOUR);\n        CACHE_DISK_UTILS1.put(\"jsonArray1\", JSON_ARRAY, 365 * CacheDiskUtils.DAY);\n        CACHE_DISK_UTILS1.put(\"bitmap1\", BITMAP, 60 * CacheDiskUtils.SEC);\n        CACHE_DISK_UTILS1.put(\"drawable1\", DRAWABLE, 60 * CacheDiskUtils.SEC);\n        CACHE_DISK_UTILS1.put(\"parcelable1\", PARCELABLE_TEST, 60 * CacheDiskUtils.SEC);\n        CACHE_DISK_UTILS1.put(\"serializable1\", SERIALIZABLE_TEST, 60 * CacheDiskUtils.SEC);\n\n        CACHE_DISK_UTILS2.put(\"bytes2\", BYTES);\n        CACHE_DISK_UTILS2.put(\"string2\", STRING);\n        CACHE_DISK_UTILS2.put(\"jsonObject2\", JSON_OBJECT);\n        CACHE_DISK_UTILS2.put(\"jsonArray2\", JSON_ARRAY);\n        CACHE_DISK_UTILS2.put(\"bitmap2\", BITMAP);\n        CACHE_DISK_UTILS2.put(\"drawable2\", DRAWABLE);\n        CACHE_DISK_UTILS2.put(\"parcelable2\", PARCELABLE_TEST);\n        CACHE_DISK_UTILS2.put(\"serializable2\", SERIALIZABLE_TEST);\n    }\n\n    @Test\n    public void getBytes() {\n        assertEquals(STRING, new String(CACHE_DISK_UTILS1.getBytes(\"bytes1\")));\n        assertEquals(STRING, new String(CACHE_DISK_UTILS1.getBytes(\"bytes1\", null)));\n        assertNull(CACHE_DISK_UTILS1.getBytes(\"bytes2\", null));\n\n        assertEquals(STRING, new String(CACHE_DISK_UTILS2.getBytes(\"bytes2\")));\n        assertEquals(STRING, new String(CACHE_DISK_UTILS2.getBytes(\"bytes2\", null)));\n        assertNull(CACHE_DISK_UTILS2.getBytes(\"bytes1\", null));\n    }\n\n    @Test\n    public void getString() {\n        assertEquals(STRING, CACHE_DISK_UTILS1.getString(\"string1\"));\n        assertEquals(STRING, CACHE_DISK_UTILS1.getString(\"string1\", null));\n        assertNull(CACHE_DISK_UTILS1.getString(\"string2\", null));\n\n        assertEquals(STRING, CACHE_DISK_UTILS2.getString(\"string2\"));\n        assertEquals(STRING, CACHE_DISK_UTILS2.getString(\"string2\", null));\n        assertNull(CACHE_DISK_UTILS2.getString(\"string1\", null));\n    }\n\n    @Test\n    public void getJSONObject() {\n        assertEquals(JSON_OBJECT.toString(), CACHE_DISK_UTILS1.getJSONObject(\"jsonObject1\").toString());\n        assertEquals(JSON_OBJECT.toString(), CACHE_DISK_UTILS1.getJSONObject(\"jsonObject1\", null).toString());\n        assertNull(CACHE_DISK_UTILS1.getJSONObject(\"jsonObject2\", null));\n\n        assertEquals(JSON_OBJECT.toString(), CACHE_DISK_UTILS2.getJSONObject(\"jsonObject2\").toString());\n        assertEquals(JSON_OBJECT.toString(), CACHE_DISK_UTILS2.getJSONObject(\"jsonObject2\", null).toString());\n        assertNull(CACHE_DISK_UTILS2.getJSONObject(\"jsonObject1\", null));\n    }\n\n    @Test\n    public void getJSONArray() {\n        assertEquals(JSON_ARRAY.toString(), CACHE_DISK_UTILS1.getJSONArray(\"jsonArray1\").toString());\n        assertEquals(JSON_ARRAY.toString(), CACHE_DISK_UTILS1.getJSONArray(\"jsonArray1\", null).toString());\n        assertNull(CACHE_DISK_UTILS1.getJSONArray(\"jsonArray2\", null));\n\n        assertEquals(JSON_ARRAY.toString(), CACHE_DISK_UTILS2.getJSONArray(\"jsonArray2\").toString());\n        assertEquals(JSON_ARRAY.toString(), CACHE_DISK_UTILS2.getJSONArray(\"jsonArray2\", null).toString());\n        assertNull(CACHE_DISK_UTILS2.getJSONArray(\"jsonArray1\", null));\n    }\n\n    @Test\n    public void getBitmap() {\n        assertArrayEquals(\n                ImageUtils.bitmap2Bytes(BITMAP, Bitmap.CompressFormat.PNG, 100),\n                ImageUtils.bitmap2Bytes(CACHE_DISK_UTILS1.getBitmap(\"bitmap1\"), Bitmap.CompressFormat.PNG, 100)\n        );\n        assertArrayEquals(\n                ImageUtils.bitmap2Bytes(BITMAP, Bitmap.CompressFormat.PNG, 100),\n                ImageUtils.bitmap2Bytes(CACHE_DISK_UTILS1.getBitmap(\"bitmap1\", null), Bitmap.CompressFormat.PNG, 100)\n        );\n        assertNull(CACHE_DISK_UTILS1.getBitmap(\"bitmap2\", null));\n\n        assertArrayEquals(\n                ImageUtils.bitmap2Bytes(BITMAP, Bitmap.CompressFormat.PNG, 100),\n                ImageUtils.bitmap2Bytes(CACHE_DISK_UTILS2.getBitmap(\"bitmap2\"), Bitmap.CompressFormat.PNG, 100)\n        );\n        assertArrayEquals(\n                ImageUtils.bitmap2Bytes(BITMAP, Bitmap.CompressFormat.PNG, 100),\n                ImageUtils.bitmap2Bytes(CACHE_DISK_UTILS2.getBitmap(\"bitmap2\", null), Bitmap.CompressFormat.PNG, 100)\n        );\n        assertNull(CACHE_DISK_UTILS2.getBitmap(\"bitmap1\", null));\n    }\n\n    @Test\n    public void getDrawable() {\n        String bitmapString = \"Bitmap (100 x 100) compressed as PNG with quality 100\";\n        assertArrayEquals(\n                ImageUtils.bitmap2Bytes(BITMAP, Bitmap.CompressFormat.PNG, 100),\n                ImageUtils.drawable2Bytes(CACHE_DISK_UTILS1.getDrawable(\"drawable1\"), Bitmap.CompressFormat.PNG, 100)\n        );\n        assertArrayEquals(\n                ImageUtils.bitmap2Bytes(BITMAP, Bitmap.CompressFormat.PNG, 100),\n                ImageUtils.drawable2Bytes(CACHE_DISK_UTILS1.getDrawable(\"drawable1\", null), Bitmap.CompressFormat.PNG, 100)\n        );\n        assertNull(CACHE_DISK_UTILS1.getDrawable(\"drawable2\", null));\n\n        assertArrayEquals(\n                ImageUtils.bitmap2Bytes(BITMAP, Bitmap.CompressFormat.PNG, 100),\n                ImageUtils.drawable2Bytes(CACHE_DISK_UTILS2.getDrawable(\"drawable2\"), Bitmap.CompressFormat.PNG, 100)\n        );\n        assertArrayEquals(\n                ImageUtils.bitmap2Bytes(BITMAP, Bitmap.CompressFormat.PNG, 100),\n                ImageUtils.drawable2Bytes(CACHE_DISK_UTILS2.getDrawable(\"drawable2\", null), Bitmap.CompressFormat.PNG, 100)\n        );\n        assertNull(CACHE_DISK_UTILS2.getDrawable(\"drawable1\", null));\n    }\n\n    @Test\n    public void getParcel() {\n        assertEquals(PARCELABLE_TEST, CACHE_DISK_UTILS1.getParcelable(\"parcelable1\", ParcelableTest.CREATOR));\n        assertEquals(PARCELABLE_TEST, CACHE_DISK_UTILS1.getParcelable(\"parcelable1\", ParcelableTest.CREATOR, null));\n        assertNull(CACHE_DISK_UTILS1.getParcelable(\"parcelable2\", ParcelableTest.CREATOR, null));\n\n        assertEquals(PARCELABLE_TEST, CACHE_DISK_UTILS2.getParcelable(\"parcelable2\", ParcelableTest.CREATOR));\n        assertEquals(PARCELABLE_TEST, CACHE_DISK_UTILS2.getParcelable(\"parcelable2\", ParcelableTest.CREATOR, null));\n        assertNull(CACHE_DISK_UTILS2.getParcelable(\"parcelable1\", ParcelableTest.CREATOR, null));\n    }\n\n    @Test\n    public void getSerializable() {\n        assertEquals(SERIALIZABLE_TEST, CACHE_DISK_UTILS1.getSerializable(\"serializable1\"));\n        assertEquals(SERIALIZABLE_TEST, CACHE_DISK_UTILS1.getSerializable(\"serializable1\", null));\n        assertNull(CACHE_DISK_UTILS1.getSerializable(\"parcelable2\", null));\n\n        assertEquals(SERIALIZABLE_TEST, CACHE_DISK_UTILS2.getSerializable(\"serializable2\"));\n        assertEquals(SERIALIZABLE_TEST, CACHE_DISK_UTILS2.getSerializable(\"serializable2\", null));\n        assertNull(CACHE_DISK_UTILS2.getSerializable(\"parcelable1\", null));\n    }\n\n    @Test\n    public void getCacheSize() {\n        System.out.println(FileUtils.getLength(DISK1_FILE));\n        assertEquals(FileUtils.getLength(DISK1_FILE), CACHE_DISK_UTILS1.getCacheSize());\n\n        System.out.println(FileUtils.getLength(DISK2_FILE));\n        assertEquals(FileUtils.getLength(DISK2_FILE), CACHE_DISK_UTILS2.getCacheSize());\n    }\n\n    @Test\n    public void getCacheCount() {\n        assertEquals(8, CACHE_DISK_UTILS1.getCacheCount());\n\n        assertEquals(8, CACHE_DISK_UTILS2.getCacheCount());\n    }\n\n    @Test\n    public void remove() {\n        assertNotNull(CACHE_DISK_UTILS1.getString(\"string1\"));\n        assertTrue(CACHE_DISK_UTILS1.remove(\"string1\"));\n        assertNull(CACHE_DISK_UTILS1.getString(\"string1\"));\n\n        assertNotNull(CACHE_DISK_UTILS2.getString(\"string2\"));\n        CACHE_DISK_UTILS2.remove(\"string2\");\n        assertNull(CACHE_DISK_UTILS2.getString(\"string2\"));\n    }\n\n    @Test\n    public void clear() {\n        assertNotNull(CACHE_DISK_UTILS1.getBytes(\"bytes1\"));\n        assertNotNull(CACHE_DISK_UTILS1.getString(\"string1\"));\n        assertNotNull(CACHE_DISK_UTILS1.getJSONObject(\"jsonObject1\"));\n        assertNotNull(CACHE_DISK_UTILS1.getJSONArray(\"jsonArray1\"));\n        assertNotNull(CACHE_DISK_UTILS1.getBitmap(\"bitmap1\"));\n        assertNotNull(CACHE_DISK_UTILS1.getDrawable(\"drawable1\"));\n        assertNotNull(CACHE_DISK_UTILS1.getParcelable(\"parcelable1\", ParcelableTest.CREATOR));\n        assertNotNull(CACHE_DISK_UTILS1.getSerializable(\"serializable1\"));\n        CACHE_DISK_UTILS1.clear();\n        assertNull(CACHE_DISK_UTILS1.getBytes(\"bytes1\"));\n        assertNull(CACHE_DISK_UTILS1.getString(\"string1\"));\n        assertNull(CACHE_DISK_UTILS1.getJSONObject(\"jsonObject1\"));\n        assertNull(CACHE_DISK_UTILS1.getJSONArray(\"jsonArray1\"));\n        assertNull(CACHE_DISK_UTILS1.getBitmap(\"bitmap1\"));\n        assertNull(CACHE_DISK_UTILS1.getDrawable(\"drawable1\"));\n        assertNull(CACHE_DISK_UTILS1.getParcelable(\"parcelable1\", ParcelableTest.CREATOR));\n        assertNull(CACHE_DISK_UTILS1.getSerializable(\"serializable1\"));\n        assertEquals(0, CACHE_DISK_UTILS1.getCacheSize());\n        assertEquals(0, CACHE_DISK_UTILS1.getCacheCount());\n\n\n        assertNotNull(CACHE_DISK_UTILS2.getBytes(\"bytes2\"));\n        assertNotNull(CACHE_DISK_UTILS2.getString(\"string2\"));\n        assertNotNull(CACHE_DISK_UTILS2.getJSONObject(\"jsonObject2\"));\n        assertNotNull(CACHE_DISK_UTILS2.getJSONArray(\"jsonArray2\"));\n        assertNotNull(CACHE_DISK_UTILS2.getBitmap(\"bitmap2\"));\n        assertNotNull(CACHE_DISK_UTILS2.getDrawable(\"drawable2\"));\n        assertNotNull(CACHE_DISK_UTILS2.getParcelable(\"parcelable2\", ParcelableTest.CREATOR));\n        assertNotNull(CACHE_DISK_UTILS2.getSerializable(\"serializable2\"));\n        CACHE_DISK_UTILS2.clear();\n        assertNull(CACHE_DISK_UTILS2.getBytes(\"bytes2\"));\n        assertNull(CACHE_DISK_UTILS2.getString(\"string2\"));\n        assertNull(CACHE_DISK_UTILS2.getJSONObject(\"jsonObject2\"));\n        assertNull(CACHE_DISK_UTILS2.getJSONArray(\"jsonArray2\"));\n        assertNull(CACHE_DISK_UTILS2.getBitmap(\"bitmap2\"));\n        assertNull(CACHE_DISK_UTILS2.getDrawable(\"drawable2\"));\n        assertNull(CACHE_DISK_UTILS2.getParcelable(\"parcelable2\", ParcelableTest.CREATOR));\n        assertNull(CACHE_DISK_UTILS2.getSerializable(\"serializable2\"));\n        assertEquals(0, CACHE_DISK_UTILS2.getCacheSize());\n        assertEquals(0, CACHE_DISK_UTILS2.getCacheCount());\n    }\n\n    @After\n    public void tearDown() {\n        CACHE_DISK_UTILS1.clear();\n        CACHE_DISK_UTILS2.clear();\n    }\n\n    static class ParcelableTest implements Parcelable {\n        String author;\n        String className;\n\n        public String getAuthor() {\n            return author;\n        }\n\n        public void setAuthor(String author) {\n            this.author = author;\n        }\n\n        public String getClassName() {\n            return className;\n        }\n\n        public void setClassName(String className) {\n            this.className = className;\n        }\n\n        ParcelableTest(String author, String className) {\n            this.author = author;\n            this.className = className;\n        }\n\n        ParcelableTest(Parcel in) {\n            author = in.readString();\n            className = in.readString();\n        }\n\n        @Override\n        public void writeToParcel(Parcel dest, int flags) {\n            dest.writeString(author);\n            dest.writeString(className);\n        }\n\n        @Override\n        public int describeContents() {\n            return 0;\n        }\n\n        public static final Creator<ParcelableTest> CREATOR = new Creator<ParcelableTest>() {\n            @Override\n            public ParcelableTest createFromParcel(Parcel in) {\n                return new ParcelableTest(in);\n            }\n\n            @Override\n            public ParcelableTest[] newArray(int size) {\n                return new ParcelableTest[size];\n            }\n        };\n\n        @Override\n        public boolean equals(Object obj) {\n            return obj instanceof ParcelableTest\n                    && ((ParcelableTest) obj).author.equals(author)\n                    && ((ParcelableTest) obj).className.equals(className);\n        }\n    }\n\n    static class SerializableTest implements Serializable {\n\n        private static final long serialVersionUID = -5806706668736895024L;\n\n        String author;\n        String className;\n\n        SerializableTest(String author, String className) {\n            this.author = author;\n            this.className = className;\n        }\n\n        public String getAuthor() {\n            return author;\n        }\n\n        public void setAuthor(String author) {\n            this.author = author;\n        }\n\n        public String getClassName() {\n            return className;\n        }\n\n        public void setClassName(String className) {\n            this.className = className;\n        }\n\n        @Override\n        public boolean equals(Object obj) {\n            return obj instanceof SerializableTest\n                    && ((SerializableTest) obj).author.equals(author)\n                    && ((SerializableTest) obj).className.equals(className);\n        }\n    }\n}\n\n"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/CacheDoubleStaticUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.graphics.Bitmap;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.Serializable;\n\nimport static com.blankj.utilcode.util.TestConfig.FILE_SEP;\nimport static com.blankj.utilcode.util.TestConfig.PATH_CACHE;\nimport static org.junit.Assert.assertArrayEquals;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertNull;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/06/13\n *     desc  : test CacheDoubleStaticUtils\n * </pre>\n */\npublic class CacheDoubleStaticUtilsTest extends BaseTest {\n\n    private static final String           CACHE_PATH         = PATH_CACHE + \"double\" + FILE_SEP;\n    private static final File             CACHE_FILE         = new File(CACHE_PATH);\n    private static final byte[]           BYTES              = \"CacheDoubleUtils\".getBytes();\n    private static final String           STRING             = \"CacheDoubleUtils\";\n    private static final JSONObject       JSON_OBJECT        = new JSONObject();\n    private static final JSONArray        JSON_ARRAY         = new JSONArray();\n    private static final ParcelableTest   PARCELABLE_TEST    = new ParcelableTest(\"Blankj\", \"CacheDoubleUtils\");\n    private static final SerializableTest SERIALIZABLE_TEST  = new SerializableTest(\"Blankj\", \"CacheDoubleUtils\");\n    private static final Bitmap           BITMAP             = Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565);\n    private static final Drawable         DRAWABLE           = new BitmapDrawable(Utils.getApp().getResources(), BITMAP);\n    private static final CacheMemoryUtils CACHE_MEMORY_UTILS = CacheMemoryUtils.getInstance();\n    private static final CacheDiskUtils   CACHE_DISK_UTILS   = CacheDiskUtils.getInstance(CACHE_FILE);\n    private static final CacheDoubleUtils CACHE_DOUBLE_UTILS = CacheDoubleUtils.getInstance(CACHE_MEMORY_UTILS, CACHE_DISK_UTILS);\n\n    static {\n        try {\n            JSON_OBJECT.put(\"class\", \"CacheDoubleUtils\");\n            JSON_OBJECT.put(\"author\", \"Blankj\");\n            JSON_ARRAY.put(0, JSON_OBJECT);\n        } catch (JSONException e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Before\n    public void setUp() {\n        CacheDoubleStaticUtils.setDefaultCacheDoubleUtils(CACHE_DOUBLE_UTILS);\n        CacheDoubleStaticUtils.put(\"bytes\", BYTES);\n        CacheDoubleStaticUtils.put(\"string\", STRING);\n        CacheDoubleStaticUtils.put(\"jsonObject\", JSON_OBJECT);\n        CacheDoubleStaticUtils.put(\"jsonArray\", JSON_ARRAY);\n        CacheDoubleStaticUtils.put(\"bitmap\", BITMAP);\n        CacheDoubleStaticUtils.put(\"drawable\", DRAWABLE);\n        CacheDoubleStaticUtils.put(\"parcelable\", PARCELABLE_TEST);\n        CacheDoubleStaticUtils.put(\"serializable\", SERIALIZABLE_TEST);\n    }\n\n    @Test\n    public void getBytes() {\n        assertEquals(STRING, new String(CacheDoubleStaticUtils.getBytes(\"bytes\")));\n        CACHE_MEMORY_UTILS.remove(\"bytes\");\n        assertEquals(STRING, new String(CacheDoubleStaticUtils.getBytes(\"bytes\")));\n        CACHE_DISK_UTILS.remove(\"bytes\");\n        assertNull(CacheDoubleStaticUtils.getBytes(\"bytes\"));\n    }\n\n    @Test\n    public void getString() {\n        assertEquals(STRING, CacheDoubleStaticUtils.getString(\"string\"));\n        CACHE_MEMORY_UTILS.remove(\"string\");\n        assertEquals(STRING, CacheDoubleStaticUtils.getString(\"string\"));\n        CACHE_DISK_UTILS.remove(\"string\");\n        assertNull(CacheDoubleStaticUtils.getString(\"string\"));\n    }\n\n    @Test\n    public void getJSONObject() {\n        assertEquals(JSON_OBJECT.toString(), CacheDoubleStaticUtils.getJSONObject(\"jsonObject\").toString());\n        CACHE_MEMORY_UTILS.remove(\"jsonObject\");\n        assertEquals(JSON_OBJECT.toString(), CacheDoubleStaticUtils.getJSONObject(\"jsonObject\").toString());\n        CACHE_DISK_UTILS.remove(\"jsonObject\");\n        assertNull(CacheDoubleStaticUtils.getJSONObject(\"jsonObject\"));\n    }\n\n    @Test\n    public void getJSONArray() {\n        assertEquals(JSON_ARRAY.toString(), CacheDoubleStaticUtils.getJSONArray(\"jsonArray\").toString());\n        CACHE_MEMORY_UTILS.remove(\"jsonArray\");\n        assertEquals(JSON_ARRAY.toString(), CacheDoubleStaticUtils.getJSONArray(\"jsonArray\").toString());\n        CACHE_DISK_UTILS.remove(\"jsonArray\");\n        assertNull(CacheDoubleStaticUtils.getJSONArray(\"jsonArray\"));\n    }\n\n    @Test\n    public void getBitmap() {\n        assertEquals(BITMAP, CacheDoubleStaticUtils.getBitmap(\"bitmap\"));\n        CACHE_MEMORY_UTILS.remove(\"bitmap\");\n        assertArrayEquals(\n                ImageUtils.bitmap2Bytes(BITMAP, Bitmap.CompressFormat.PNG, 100),\n                ImageUtils.bitmap2Bytes(CacheDoubleStaticUtils.getBitmap(\"bitmap\"), Bitmap.CompressFormat.PNG, 100)\n        );\n        CACHE_DISK_UTILS.remove(\"bitmap\");\n        assertNull(CacheDoubleStaticUtils.getBitmap(\"bitmap\"));\n    }\n\n    @Test\n    public void getDrawable() {\n        assertEquals(DRAWABLE, CacheDoubleStaticUtils.getDrawable(\"drawable\"));\n        CACHE_MEMORY_UTILS.remove(\"drawable\");\n        assertArrayEquals(\n                ImageUtils.bitmap2Bytes(BITMAP, Bitmap.CompressFormat.PNG, 100),\n                ImageUtils.drawable2Bytes(CacheDoubleStaticUtils.getDrawable(\"drawable\"), Bitmap.CompressFormat.PNG, 100)\n        );\n        CACHE_DISK_UTILS.remove(\"drawable\");\n        assertNull(CacheDoubleStaticUtils.getDrawable(\"drawable\"));\n    }\n\n    @Test\n    public void getParcel() {\n        assertEquals(PARCELABLE_TEST, CacheDoubleStaticUtils.getParcelable(\"parcelable\", ParcelableTest.CREATOR));\n        CACHE_MEMORY_UTILS.remove(\"parcelable\");\n        assertEquals(PARCELABLE_TEST, CacheDoubleStaticUtils.getParcelable(\"parcelable\", ParcelableTest.CREATOR));\n        CACHE_DISK_UTILS.remove(\"parcelable\");\n        assertNull(CacheDoubleStaticUtils.getParcelable(\"parcelable\", PARCELABLE_TEST.CREATOR));\n    }\n\n    @Test\n    public void getSerializable() {\n        assertEquals(SERIALIZABLE_TEST, CacheDoubleStaticUtils.getSerializable(\"serializable\"));\n        CACHE_MEMORY_UTILS.remove(\"serializable\");\n        assertEquals(SERIALIZABLE_TEST, CacheDoubleStaticUtils.getSerializable(\"serializable\"));\n        CACHE_DISK_UTILS.remove(\"serializable\");\n        assertNull(CacheDoubleStaticUtils.getSerializable(\"serializable\"));\n    }\n\n    @Test\n    public void getCacheDiskSize() {\n        assertEquals(FileUtils.getLength(CACHE_FILE), CacheDoubleStaticUtils.getCacheDiskSize());\n    }\n\n    @Test\n    public void getCacheDiskCount() {\n        assertEquals(8, CacheDoubleStaticUtils.getCacheDiskCount());\n    }\n\n    @Test\n    public void getCacheMemoryCount() {\n        assertEquals(8, CacheDoubleStaticUtils.getCacheMemoryCount());\n    }\n\n    @Test\n    public void remove() {\n        assertNotNull(CacheDoubleStaticUtils.getString(\"string\"));\n        CacheDoubleStaticUtils.remove(\"string\");\n        assertNull(CacheDoubleStaticUtils.getString(\"string\"));\n    }\n\n    @Test\n    public void clear() {\n        assertNotNull(CacheDoubleStaticUtils.getBytes(\"bytes\"));\n        assertNotNull(CacheDoubleStaticUtils.getString(\"string\"));\n        assertNotNull(CacheDoubleStaticUtils.getJSONObject(\"jsonObject\"));\n        assertNotNull(CacheDoubleStaticUtils.getJSONArray(\"jsonArray\"));\n        assertNotNull(CacheDoubleStaticUtils.getBitmap(\"bitmap\"));\n        assertNotNull(CacheDoubleStaticUtils.getDrawable(\"drawable\"));\n        assertNotNull(CacheDoubleStaticUtils.getParcelable(\"parcelable\", ParcelableTest.CREATOR));\n        assertNotNull(CacheDoubleStaticUtils.getSerializable(\"serializable\"));\n        CacheDoubleStaticUtils.clear();\n        assertNull(CacheDoubleStaticUtils.getBytes(\"bytes\"));\n        assertNull(CacheDoubleStaticUtils.getString(\"string\"));\n        assertNull(CacheDoubleStaticUtils.getJSONObject(\"jsonObject\"));\n        assertNull(CacheDoubleStaticUtils.getJSONArray(\"jsonArray\"));\n        assertNull(CacheDoubleStaticUtils.getBitmap(\"bitmap\"));\n        assertNull(CacheDoubleStaticUtils.getDrawable(\"drawable\"));\n        assertNull(CacheDoubleStaticUtils.getParcelable(\"parcelable\", ParcelableTest.CREATOR));\n        assertNull(CacheDoubleStaticUtils.getSerializable(\"serializable\"));\n        assertEquals(0, CacheDoubleStaticUtils.getCacheDiskSize());\n        assertEquals(0, CacheDoubleStaticUtils.getCacheDiskCount());\n        assertEquals(0, CacheDoubleStaticUtils.getCacheMemoryCount());\n    }\n\n    @After\n    public void tearDown() {\n        CacheDoubleStaticUtils.clear();\n    }\n\n    static class ParcelableTest implements Parcelable {\n        String author;\n        String className;\n\n        public String getAuthor() {\n            return author;\n        }\n\n        public void setAuthor(String author) {\n            this.author = author;\n        }\n\n        public String getClassName() {\n            return className;\n        }\n\n        public void setClassName(String className) {\n            this.className = className;\n        }\n\n        ParcelableTest(String author, String className) {\n            this.author = author;\n            this.className = className;\n        }\n\n        ParcelableTest(Parcel in) {\n            author = in.readString();\n            className = in.readString();\n        }\n\n        @Override\n        public void writeToParcel(Parcel dest, int flags) {\n            dest.writeString(author);\n            dest.writeString(className);\n        }\n\n        @Override\n        public int describeContents() {\n            return 0;\n        }\n\n        public static final Creator<ParcelableTest> CREATOR = new Creator<ParcelableTest>() {\n            @Override\n            public ParcelableTest createFromParcel(Parcel in) {\n                return new ParcelableTest(in);\n            }\n\n            @Override\n            public ParcelableTest[] newArray(int size) {\n                return new ParcelableTest[size];\n            }\n        };\n\n        @Override\n        public boolean equals(Object obj) {\n            return obj instanceof ParcelableTest\n                    && ((ParcelableTest) obj).author.equals(author)\n                    && ((ParcelableTest) obj).className.equals(className);\n        }\n    }\n\n    static class SerializableTest implements Serializable {\n\n        private static final long serialVersionUID = -5806706668736895024L;\n\n        String author;\n        String className;\n\n        SerializableTest(String author, String className) {\n            this.author = author;\n            this.className = className;\n        }\n\n        public String getAuthor() {\n            return author;\n        }\n\n        public void setAuthor(String author) {\n            this.author = author;\n        }\n\n        public String getClassName() {\n            return className;\n        }\n\n        public void setClassName(String className) {\n            this.className = className;\n        }\n\n        @Override\n        public boolean equals(Object obj) {\n            return obj instanceof SerializableTest\n                    && ((SerializableTest) obj).author.equals(author)\n                    && ((SerializableTest) obj).className.equals(className);\n        }\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/CacheDoubleUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.graphics.Bitmap;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.graphics.drawable.Drawable;\nimport android.os.Parcel;\nimport android.os.Parcelable;\n\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.Serializable;\n\nimport static com.blankj.utilcode.util.TestConfig.FILE_SEP;\nimport static com.blankj.utilcode.util.TestConfig.PATH_CACHE;\nimport static org.junit.Assert.assertArrayEquals;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertNull;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/06/13\n *     desc  : test CacheDoubleUtils\n * </pre>\n */\npublic class CacheDoubleUtilsTest extends BaseTest {\n\n    private static final String           CACHE_PATH         = PATH_CACHE + \"double\" + FILE_SEP;\n    private static final File             CACHE_FILE         = new File(CACHE_PATH);\n    private static final byte[]           BYTES              = \"CacheDoubleUtils\".getBytes();\n    private static final String           STRING             = \"CacheDoubleUtils\";\n    private static final JSONObject       JSON_OBJECT        = new JSONObject();\n    private static final JSONArray        JSON_ARRAY         = new JSONArray();\n    private static final ParcelableTest   PARCELABLE_TEST    = new ParcelableTest(\"Blankj\", \"CacheDoubleUtils\");\n    private static final SerializableTest SERIALIZABLE_TEST  = new SerializableTest(\"Blankj\", \"CacheDoubleUtils\");\n    private static final Bitmap           BITMAP             = Bitmap.createBitmap(100, 100, Bitmap.Config.RGB_565);\n    private static final Drawable         DRAWABLE           = new BitmapDrawable(Utils.getApp().getResources(), BITMAP);\n    private static final CacheMemoryUtils CACHE_MEMORY_UTILS = CacheMemoryUtils.getInstance();\n    private static final CacheDiskUtils   CACHE_DISK_UTILS   = CacheDiskUtils.getInstance(CACHE_FILE);\n    private static final CacheDoubleUtils CACHE_DOUBLE_UTILS = CacheDoubleUtils.getInstance(CACHE_MEMORY_UTILS, CACHE_DISK_UTILS);\n\n    static {\n        try {\n            JSON_OBJECT.put(\"class\", \"CacheDiskUtils\");\n            JSON_OBJECT.put(\"author\", \"Blankj\");\n            JSON_ARRAY.put(0, JSON_OBJECT);\n        } catch (JSONException e) {\n            e.printStackTrace();\n        }\n    }\n\n    @Before\n    public void setUp() {\n        CACHE_DOUBLE_UTILS.put(\"bytes\", BYTES);\n        CACHE_DOUBLE_UTILS.put(\"string\", STRING);\n        CACHE_DOUBLE_UTILS.put(\"jsonObject\", JSON_OBJECT);\n        CACHE_DOUBLE_UTILS.put(\"jsonArray\", JSON_ARRAY);\n        CACHE_DOUBLE_UTILS.put(\"bitmap\", BITMAP);\n        CACHE_DOUBLE_UTILS.put(\"drawable\", DRAWABLE);\n        CACHE_DOUBLE_UTILS.put(\"parcelable\", PARCELABLE_TEST);\n        CACHE_DOUBLE_UTILS.put(\"serializable\", SERIALIZABLE_TEST);\n    }\n\n    @Test\n    public void getBytes() {\n        assertEquals(STRING, new String(CACHE_DOUBLE_UTILS.getBytes(\"bytes\")));\n        CACHE_MEMORY_UTILS.remove(\"bytes\");\n        assertEquals(STRING, new String(CACHE_DOUBLE_UTILS.getBytes(\"bytes\")));\n        CACHE_DISK_UTILS.remove(\"bytes\");\n        assertNull(CACHE_DOUBLE_UTILS.getBytes(\"bytes\"));\n    }\n\n    @Test\n    public void getString() {\n        assertEquals(STRING, CACHE_DOUBLE_UTILS.getString(\"string\"));\n        CACHE_MEMORY_UTILS.remove(\"string\");\n        assertEquals(STRING, CACHE_DOUBLE_UTILS.getString(\"string\"));\n        CACHE_DISK_UTILS.remove(\"string\");\n        assertNull(CACHE_DOUBLE_UTILS.getString(\"string\"));\n    }\n\n    @Test\n    public void getJSONObject() {\n        assertEquals(JSON_OBJECT.toString(), CACHE_DOUBLE_UTILS.getJSONObject(\"jsonObject\").toString());\n        CACHE_MEMORY_UTILS.remove(\"jsonObject\");\n        assertEquals(JSON_OBJECT.toString(), CACHE_DOUBLE_UTILS.getJSONObject(\"jsonObject\").toString());\n        CACHE_DISK_UTILS.remove(\"jsonObject\");\n        assertNull(CACHE_DOUBLE_UTILS.getJSONObject(\"jsonObject\"));\n    }\n\n    @Test\n    public void getJSONArray() {\n        assertEquals(JSON_ARRAY.toString(), CACHE_DOUBLE_UTILS.getJSONArray(\"jsonArray\").toString());\n        CACHE_MEMORY_UTILS.remove(\"jsonArray\");\n        assertEquals(JSON_ARRAY.toString(), CACHE_DOUBLE_UTILS.getJSONArray(\"jsonArray\").toString());\n        CACHE_DISK_UTILS.remove(\"jsonArray\");\n        assertNull(CACHE_DOUBLE_UTILS.getJSONArray(\"jsonArray\"));\n    }\n\n    @Test\n    public void getBitmap() {\n        String bitmapString = \"Bitmap (100 x 100) compressed as PNG with quality 100\";\n        assertEquals(BITMAP, CACHE_DOUBLE_UTILS.getBitmap(\"bitmap\"));\n        CACHE_MEMORY_UTILS.remove(\"bitmap\");\n        assertArrayEquals(\n                ImageUtils.bitmap2Bytes(BITMAP, Bitmap.CompressFormat.PNG, 100),\n                ImageUtils.bitmap2Bytes(CACHE_DOUBLE_UTILS.getBitmap(\"bitmap\"), Bitmap.CompressFormat.PNG, 100)\n        );\n        CACHE_DISK_UTILS.remove(\"bitmap\");\n        assertNull(CACHE_DOUBLE_UTILS.getBitmap(\"bitmap\"));\n    }\n\n    @Test\n    public void getDrawable() {\n        String bitmapString = \"Bitmap (100 x 100) compressed as PNG with quality 100\";\n        assertEquals(DRAWABLE, CACHE_DOUBLE_UTILS.getDrawable(\"drawable\"));\n        CACHE_MEMORY_UTILS.remove(\"drawable\");\n        assertArrayEquals(\n                ImageUtils.bitmap2Bytes(BITMAP, Bitmap.CompressFormat.PNG, 100),\n                ImageUtils.drawable2Bytes(CACHE_DOUBLE_UTILS.getDrawable(\"drawable\"), Bitmap.CompressFormat.PNG, 100)\n        );\n        CACHE_DISK_UTILS.remove(\"drawable\");\n        assertNull(CACHE_DOUBLE_UTILS.getDrawable(\"drawable\"));\n    }\n\n    @Test\n    public void getParcel() {\n        assertEquals(PARCELABLE_TEST, CACHE_DOUBLE_UTILS.getParcelable(\"parcelable\", ParcelableTest.CREATOR));\n        CACHE_MEMORY_UTILS.remove(\"parcelable\");\n        assertEquals(PARCELABLE_TEST, CACHE_DOUBLE_UTILS.getParcelable(\"parcelable\", ParcelableTest.CREATOR));\n        CACHE_DISK_UTILS.remove(\"parcelable\");\n        assertNull(CACHE_DOUBLE_UTILS.getParcelable(\"parcelable\", ParcelableTest.CREATOR));\n    }\n\n    @Test\n    public void getSerializable() {\n        assertEquals(SERIALIZABLE_TEST, CACHE_DOUBLE_UTILS.getSerializable(\"serializable\"));\n        CACHE_MEMORY_UTILS.remove(\"serializable\");\n        assertEquals(SERIALIZABLE_TEST, CACHE_DOUBLE_UTILS.getSerializable(\"serializable\"));\n        CACHE_DISK_UTILS.remove(\"serializable\");\n        assertNull(CACHE_DOUBLE_UTILS.getSerializable(\"serializable\"));\n    }\n\n    @Test\n    public void getCacheDiskSize() {\n        assertEquals(FileUtils.getLength(CACHE_FILE), CACHE_DOUBLE_UTILS.getCacheDiskSize());\n    }\n\n    @Test\n    public void getCacheDiskCount() {\n        assertEquals(8, CACHE_DOUBLE_UTILS.getCacheDiskCount());\n    }\n\n    @Test\n    public void getCacheMemoryCount() {\n        assertEquals(8, CACHE_DOUBLE_UTILS.getCacheMemoryCount());\n    }\n\n    @Test\n    public void remove() {\n        assertNotNull(CACHE_DOUBLE_UTILS.getString(\"string\"));\n        CACHE_DOUBLE_UTILS.remove(\"string\");\n        assertNull(CACHE_DOUBLE_UTILS.getString(\"string\"));\n    }\n\n    @Test\n    public void clear() {\n        assertNotNull(CACHE_DOUBLE_UTILS.getBytes(\"bytes\"));\n        assertNotNull(CACHE_DOUBLE_UTILS.getString(\"string\"));\n        assertNotNull(CACHE_DOUBLE_UTILS.getJSONObject(\"jsonObject\"));\n        assertNotNull(CACHE_DOUBLE_UTILS.getJSONArray(\"jsonArray\"));\n        assertNotNull(CACHE_DOUBLE_UTILS.getBitmap(\"bitmap\"));\n        assertNotNull(CACHE_DOUBLE_UTILS.getDrawable(\"drawable\"));\n        assertNotNull(CACHE_DOUBLE_UTILS.getParcelable(\"parcelable\", ParcelableTest.CREATOR));\n        assertNotNull(CACHE_DOUBLE_UTILS.getSerializable(\"serializable\"));\n        CACHE_DOUBLE_UTILS.clear();\n        assertNull(CACHE_DOUBLE_UTILS.getBytes(\"bytes\"));\n        assertNull(CACHE_DOUBLE_UTILS.getString(\"string\"));\n        assertNull(CACHE_DOUBLE_UTILS.getJSONObject(\"jsonObject\"));\n        assertNull(CACHE_DOUBLE_UTILS.getJSONArray(\"jsonArray\"));\n        assertNull(CACHE_DOUBLE_UTILS.getBitmap(\"bitmap\"));\n        assertNull(CACHE_DOUBLE_UTILS.getDrawable(\"drawable\"));\n        assertNull(CACHE_DOUBLE_UTILS.getParcelable(\"parcelable\", ParcelableTest.CREATOR));\n        assertNull(CACHE_DOUBLE_UTILS.getSerializable(\"serializable\"));\n        assertEquals(0, CACHE_DOUBLE_UTILS.getCacheDiskSize());\n        assertEquals(0, CACHE_DOUBLE_UTILS.getCacheDiskCount());\n        assertEquals(0, CACHE_DOUBLE_UTILS.getCacheMemoryCount());\n    }\n\n    @After\n    public void tearDown() {\n        CACHE_DOUBLE_UTILS.clear();\n    }\n\n    static class ParcelableTest implements Parcelable {\n        String author;\n        String className;\n\n        public String getAuthor() {\n            return author;\n        }\n\n        public void setAuthor(String author) {\n            this.author = author;\n        }\n\n        public String getClassName() {\n            return className;\n        }\n\n        public void setClassName(String className) {\n            this.className = className;\n        }\n\n        ParcelableTest(String author, String className) {\n            this.author = author;\n            this.className = className;\n        }\n\n        ParcelableTest(Parcel in) {\n            author = in.readString();\n            className = in.readString();\n        }\n\n        @Override\n        public void writeToParcel(Parcel dest, int flags) {\n            dest.writeString(author);\n            dest.writeString(className);\n        }\n\n        @Override\n        public int describeContents() {\n            return 0;\n        }\n\n        public static final Creator<ParcelableTest> CREATOR = new Creator<ParcelableTest>() {\n            @Override\n            public ParcelableTest createFromParcel(Parcel in) {\n                return new ParcelableTest(in);\n            }\n\n            @Override\n            public ParcelableTest[] newArray(int size) {\n                return new ParcelableTest[size];\n            }\n        };\n\n        @Override\n        public boolean equals(Object obj) {\n            return obj instanceof ParcelableTest\n                    && ((ParcelableTest) obj).author.equals(author)\n                    && ((ParcelableTest) obj).className.equals(className);\n        }\n    }\n\n    static class SerializableTest implements Serializable {\n\n        private static final long serialVersionUID = -5806706668736895024L;\n\n        String author;\n        String className;\n\n        SerializableTest(String author, String className) {\n            this.author = author;\n            this.className = className;\n        }\n\n        public String getAuthor() {\n            return author;\n        }\n\n        public void setAuthor(String author) {\n            this.author = author;\n        }\n\n        public String getClassName() {\n            return className;\n        }\n\n        public void setClassName(String className) {\n            this.className = className;\n        }\n\n        @Override\n        public boolean equals(Object obj) {\n            return obj instanceof SerializableTest\n                    && ((SerializableTest) obj).author.equals(author)\n                    && ((SerializableTest) obj).className.equals(className);\n        }\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/CacheMemoryStaticUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNull;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/01/04\n *     desc  : test CacheMemoryStaticUtils\n * </pre>\n */\npublic class CacheMemoryStaticUtilsTest extends BaseTest {\n\n    private CacheMemoryUtils mCacheMemoryUtils = CacheMemoryUtils.getInstance(3);\n\n    @Before\n    public void setUp() {\n        for (int i = 0; i < 10; i++) {\n            CacheMemoryStaticUtils.put(String.valueOf(i), i);\n        }\n        for (int i = 0; i < 10; i++) {\n            CacheMemoryStaticUtils.put(String.valueOf(i), i, mCacheMemoryUtils);\n        }\n    }\n\n    @Test\n    public void get() {\n        for (int i = 0; i < 10; i++) {\n            assertEquals(i, CacheMemoryStaticUtils.get(String.valueOf(i)));\n        }\n        for (int i = 0; i < 10; i++) {\n            if (i < 7) {\n                assertNull(CacheMemoryStaticUtils.get(String.valueOf(i), mCacheMemoryUtils));\n            } else {\n                assertEquals(i, CacheMemoryStaticUtils.get(String.valueOf(i), mCacheMemoryUtils));\n            }\n        }\n    }\n\n    @Test\n    public void getExpired() throws Exception {\n        CacheMemoryStaticUtils.put(\"10\", 10, 2 * CacheMemoryUtils.SEC);\n\n        assertEquals(10, CacheMemoryStaticUtils.get(\"10\"));\n        Thread.sleep(1500);\n        assertEquals(10, CacheMemoryStaticUtils.get(\"10\"));\n        Thread.sleep(1500);\n        assertNull(CacheMemoryStaticUtils.get(\"10\"));\n    }\n\n    @Test\n    public void getDefault() {\n        assertNull(CacheMemoryStaticUtils.get(\"10\"));\n        assertEquals(\"10\", CacheMemoryStaticUtils.get(\"10\", \"10\"));\n    }\n\n    @Test\n    public void getCacheCount() {\n        assertEquals(10, CacheMemoryStaticUtils.getCacheCount());\n        assertEquals(3, CacheMemoryStaticUtils.getCacheCount(mCacheMemoryUtils));\n    }\n\n    @Test\n    public void remove() {\n        assertEquals(0, CacheMemoryStaticUtils.remove(\"0\"));\n        assertNull(CacheMemoryStaticUtils.get(\"0\"));\n        assertNull(CacheMemoryStaticUtils.remove(\"0\"));\n    }\n\n    @Test\n    public void clear() {\n        CacheMemoryStaticUtils.clear();\n        CacheMemoryStaticUtils.clear(mCacheMemoryUtils);\n\n        for (int i = 0; i < 10; i++) {\n            assertNull(CacheMemoryStaticUtils.get(String.valueOf(i)));\n        }\n        for (int i = 0; i < 10; i++) {\n            assertNull(CacheMemoryStaticUtils.get(String.valueOf(i), mCacheMemoryUtils));\n        }\n        assertEquals(0, CacheMemoryStaticUtils.getCacheCount());\n        assertEquals(0, CacheMemoryStaticUtils.getCacheCount(mCacheMemoryUtils));\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/CacheMemoryUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport org.junit.Before;\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNull;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/06/13\n *     desc  : test CacheMemoryUtils\n * </pre>\n */\npublic class CacheMemoryUtilsTest extends BaseTest {\n\n    private CacheMemoryUtils mCacheMemoryUtils1 = CacheMemoryUtils.getInstance();\n    private CacheMemoryUtils mCacheMemoryUtils2 = CacheMemoryUtils.getInstance(3);\n\n    @Before\n    public void setUp() {\n        for (int i = 0; i < 10; i++) {\n            mCacheMemoryUtils1.put(String.valueOf(i), i);\n        }\n        for (int i = 0; i < 10; i++) {\n            mCacheMemoryUtils2.put(String.valueOf(i), i);\n        }\n    }\n\n    @Test\n    public void get() {\n        for (int i = 0; i < 10; i++) {\n            assertEquals(i, mCacheMemoryUtils1.get(String.valueOf(i)));\n        }\n        for (int i = 0; i < 10; i++) {\n            if (i < 7) {\n                assertNull(mCacheMemoryUtils2.get(String.valueOf(i)));\n            } else {\n                assertEquals(i, mCacheMemoryUtils2.get(String.valueOf(i)));\n            }\n        }\n    }\n\n    @Test\n    public void getExpired() throws Exception {\n        mCacheMemoryUtils1.put(\"10\", 10, 2 * CacheMemoryUtils.SEC);\n\n        assertEquals(10, mCacheMemoryUtils1.get(\"10\"));\n        Thread.sleep(1500);\n        assertEquals(10, mCacheMemoryUtils1.get(\"10\"));\n        Thread.sleep(1500);\n        assertNull(mCacheMemoryUtils1.get(\"10\"));\n    }\n\n    @Test\n    public void getDefault() {\n        assertNull(mCacheMemoryUtils1.get(\"10\"));\n        assertEquals(\"10\", mCacheMemoryUtils1.get(\"10\", \"10\"));\n    }\n\n    @Test\n    public void getCacheCount() {\n        assertEquals(10, mCacheMemoryUtils1.getCacheCount());\n        assertEquals(3, mCacheMemoryUtils2.getCacheCount());\n    }\n\n    @Test\n    public void remove() {\n        assertEquals(0, mCacheMemoryUtils1.remove(\"0\"));\n        assertNull(mCacheMemoryUtils1.get(\"0\"));\n        assertNull(mCacheMemoryUtils1.remove(\"0\"));\n    }\n\n    @Test\n    public void clear() {\n        mCacheMemoryUtils1.clear();\n        mCacheMemoryUtils2.clear();\n\n        for (int i = 0; i < 10; i++) {\n            assertNull(mCacheMemoryUtils1.get(String.valueOf(i)));\n        }\n        for (int i = 0; i < 10; i++) {\n            assertNull(mCacheMemoryUtils2.get(String.valueOf(i)));\n        }\n        assertEquals(0, mCacheMemoryUtils1.getCacheCount());\n        assertEquals(0, mCacheMemoryUtils2.getCacheCount());\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/CloneUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\r\n\r\nimport org.junit.Assert;\r\nimport org.junit.Test;\r\n\r\n/**\r\n * <pre>\r\n *     author: Blankj\r\n *     blog  : http://blankj.com\r\n *     time  : 2018/04/08\r\n *     desc  : test CloneUtils\r\n * </pre>\r\n */\r\npublic class CloneUtilsTest extends BaseTest {\r\n\r\n    @Test\r\n    public void deepClone() {\r\n        Result<Person> result = new Result<>(new Person(\"Blankj\"));\r\n        Result<Person> cloneResult = CloneUtils.deepClone(result, GsonUtils.getType(Result.class, Person.class));\r\n        System.out.println(result);\r\n        System.out.println(cloneResult);\r\n        Assert.assertNotEquals(result, cloneResult);\r\n    }\r\n\r\n    static class Result<T> {\r\n        int    code;\r\n        String message;\r\n        T      data;\r\n\r\n        Result(T data) {\r\n            this.code = 200;\r\n            this.message = \"success\";\r\n            this.data = data;\r\n        }\r\n\r\n        @Override\r\n        public String toString() {\r\n            return \"{\\\"code\\\":\" + primitive2String(code) +\r\n                    \",\\\"message\\\":\" + primitive2String(message) +\r\n                    \",\\\"data\\\":\" + primitive2String(data) + \"}\";\r\n        }\r\n    }\r\n\r\n    static class Person {\r\n\r\n        String name;\r\n        int    gender;\r\n        String address;\r\n\r\n        Person(String name) {\r\n            this.name = name;\r\n        }\r\n\r\n        @Override\r\n        public String toString() {\r\n            return \"{\\\"name\\\":\" + primitive2String(name) +\r\n                    \",\\\"gender\\\":\" + primitive2String(gender) +\r\n                    \",\\\"address\\\":\" + primitive2String(address) + \"}\";\r\n        }\r\n    }\r\n\r\n    private static String primitive2String(final Object obj) {\r\n        if (obj == null) return \"null\";\r\n        if (obj instanceof CharSequence) return \"\\\"\" + obj.toString() + \"\\\"\";\r\n        return obj.toString();\r\n    }\r\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/CollectionUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Enumeration;\nimport java.util.Iterator;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/12\n *     desc  : test CollectionUtils\n * </pre>\n */\npublic class CollectionUtilsTest extends BaseTest {\n\n    @Test\n    public void union() {\n        ArrayList<String> l0 = CollectionUtils.newArrayList(\"00\", \"01\", \"02\");\n        ArrayList<String> l1 = CollectionUtils.newArrayList(\"00\", \"11\", \"12\");\n\n        Collection unionNullNull = CollectionUtils.union(null, null);\n        Collection unionL0Null = CollectionUtils.union(l0, null);\n        Collection unionNullL1 = CollectionUtils.union(null, l1);\n        Collection unionL0L1 = CollectionUtils.union(l0, l1);\n\n        System.out.println(unionNullNull);\n        System.out.println(unionL0Null);\n        System.out.println(unionNullL1);\n        System.out.println(unionL0L1);\n\n        Assert.assertNotSame(l0, unionL0Null);\n        Assert.assertNotSame(l1, unionNullL1);\n    }\n\n    @Test\n    public void intersection() {\n        ArrayList<String> l0 = CollectionUtils.newArrayList(\"00\", \"01\", \"02\");\n        ArrayList<String> l1 = CollectionUtils.newArrayList(\"00\", \"11\", \"12\");\n\n        Collection intersectionNullNull = CollectionUtils.intersection(null, null);\n        Collection intersectionL0Null = CollectionUtils.intersection(l0, null);\n        Collection intersectionNullL1 = CollectionUtils.intersection(null, l1);\n        Collection intersectionL0L1 = CollectionUtils.intersection(l0, l1);\n\n        System.out.println(intersectionNullNull);\n        System.out.println(intersectionL0Null);\n        System.out.println(intersectionNullL1);\n        System.out.println(intersectionL0L1);\n    }\n\n    @Test\n    public void disjunction() {\n        ArrayList<String> l0 = CollectionUtils.newArrayList(\"00\", \"01\", \"02\");\n        ArrayList<String> l1 = CollectionUtils.newArrayList(\"00\", \"11\", \"12\");\n\n        Collection disjunctionNullNull = CollectionUtils.disjunction(null, null);\n        Collection disjunctionL0Null = CollectionUtils.disjunction(l0, null);\n        Collection disjunctionNullL1 = CollectionUtils.disjunction(null, l1);\n        Collection disjunctionL0L1 = CollectionUtils.disjunction(l0, l1);\n\n        System.out.println(disjunctionNullNull);\n        System.out.println(disjunctionL0Null);\n        System.out.println(disjunctionNullL1);\n        System.out.println(disjunctionL0L1);\n\n        Assert.assertNotSame(l0, disjunctionL0Null);\n        Assert.assertNotSame(l1, disjunctionNullL1);\n    }\n\n    @Test\n    public void subtract() {\n        ArrayList<String> l0 = CollectionUtils.newArrayList(\"00\", \"01\", \"02\");\n        ArrayList<String> l1 = CollectionUtils.newArrayList(\"00\", \"11\", \"12\");\n\n        Collection subtractNullNull = CollectionUtils.subtract(null, null);\n        Collection subtractL0Null = CollectionUtils.subtract(l0, null);\n        Collection subtractNullL1 = CollectionUtils.subtract(null, l1);\n        Collection subtractL0L1 = CollectionUtils.subtract(l0, l1);\n\n        System.out.println(subtractNullNull);\n        System.out.println(subtractL0Null);\n        System.out.println(subtractNullL1);\n        System.out.println(subtractL0L1);\n\n        Assert.assertNotSame(l0, subtractL0Null);\n    }\n\n    @Test\n    public void containsAny() {\n        ArrayList<String> l0 = CollectionUtils.newArrayList(\"00\", \"01\", \"02\");\n        ArrayList<String> l1 = CollectionUtils.newArrayList(\"00\", \"11\", \"12\");\n\n        Assert.assertFalse(CollectionUtils.containsAny(null, null));\n        Assert.assertFalse(CollectionUtils.containsAny(l0, null));\n        Assert.assertFalse(CollectionUtils.containsAny(null, l1));\n        Assert.assertTrue(CollectionUtils.containsAny(l0, l1));\n    }\n\n    @Test\n    public void getCardinalityMap() {\n        System.out.println(CollectionUtils.getCardinalityMap(null));\n\n        ArrayList<String> l0 = CollectionUtils.newArrayList(\"0\", \"0\", \"1\", \"1\", \"2\");\n        System.out.println(CollectionUtils.getCardinalityMap(l0));\n    }\n\n    @Test\n    public void isSubCollection() {\n        ArrayList<String> l0 = CollectionUtils.newArrayList(\"0\", \"1\", \"2\");\n        ArrayList<String> l1 = CollectionUtils.newArrayList(\"0\", \"0\", \"1\", \"1\", \"2\");\n\n        Assert.assertFalse(CollectionUtils.isSubCollection(null, null));\n        Assert.assertFalse(CollectionUtils.isSubCollection(l0, null));\n        Assert.assertFalse(CollectionUtils.isSubCollection(null, l0));\n        Assert.assertTrue(CollectionUtils.isSubCollection(l0, l1));\n        Assert.assertTrue(CollectionUtils.isSubCollection(l0, l0));\n        Assert.assertFalse(CollectionUtils.isSubCollection(l1, l0));\n    }\n\n    @Test\n    public void isProperSubCollection() {\n        ArrayList<String> l0 = CollectionUtils.newArrayList(\"0\", \"1\", \"2\");\n        ArrayList<String> l1 = CollectionUtils.newArrayList(\"0\", \"0\", \"1\", \"1\", \"2\");\n\n        Assert.assertFalse(CollectionUtils.isProperSubCollection(null, null));\n        Assert.assertFalse(CollectionUtils.isProperSubCollection(l0, null));\n        Assert.assertFalse(CollectionUtils.isProperSubCollection(null, l0));\n        Assert.assertTrue(CollectionUtils.isProperSubCollection(l0, l1));\n        Assert.assertFalse(CollectionUtils.isProperSubCollection(l0, l0));\n        Assert.assertFalse(CollectionUtils.isProperSubCollection(l1, l0));\n    }\n\n    @Test\n    public void isEqualCollection() {\n        ArrayList<String> l0 = CollectionUtils.newArrayList(\"0\", \"1\", \"2\");\n        ArrayList<String> l1 = CollectionUtils.newArrayList(\"0\", \"0\", \"1\", \"1\", \"2\");\n\n        Assert.assertFalse(CollectionUtils.isEqualCollection(null, null));\n        Assert.assertFalse(CollectionUtils.isEqualCollection(l0, null));\n        Assert.assertFalse(CollectionUtils.isEqualCollection(null, l0));\n        Assert.assertTrue(CollectionUtils.isEqualCollection(l0, l0));\n        Assert.assertFalse(CollectionUtils.isEqualCollection(l0, l1));\n    }\n\n    @Test\n    public void cardinality() {\n        ArrayList<String> list = CollectionUtils.newArrayList(\"0\", \"1\", \"1\", \"2\");\n        Assert.assertEquals(0, CollectionUtils.cardinality(null, null));\n        Assert.assertEquals(0, CollectionUtils.cardinality(null, list));\n        list.add(null);\n        Assert.assertEquals(1, CollectionUtils.cardinality(null, list));\n        Assert.assertEquals(2, CollectionUtils.cardinality(\"1\", list));\n    }\n\n    @Test\n    public void find() {\n        ArrayList<String> list = CollectionUtils.newArrayList(\"0\", \"1\", \"1\", \"2\");\n\n        Assert.assertNull(CollectionUtils.find(null, null));\n        Assert.assertNull(CollectionUtils.find(list, null));\n        Assert.assertNull(CollectionUtils.find(null, new CollectionUtils.Predicate<String>() {\n            @Override\n            public boolean evaluate(String item) {\n                return true;\n            }\n        }));\n        Assert.assertEquals(\"1\", CollectionUtils.find(list, new CollectionUtils.Predicate<String>() {\n            @Override\n            public boolean evaluate(String item) {\n                return \"1\".equals(item);\n            }\n        }));\n    }\n\n    @Test\n    public void forAllDo() {\n        ArrayList<String> list = CollectionUtils.newArrayList(\"0\", \"1\", \"1\", \"2\");\n\n        CollectionUtils.forAllDo(null, null);\n        CollectionUtils.forAllDo(list, null);\n        CollectionUtils.forAllDo(null, new CollectionUtils.Closure<Object>() {\n            @Override\n            public void execute(int index, Object item) {\n                System.out.println(index + \": \" + index);\n            }\n        });\n\n        CollectionUtils.forAllDo(list, new CollectionUtils.Closure<String>() {\n            @Override\n            public void execute(int index, String item) {\n                System.out.println(index + \": \" + index);\n            }\n        });\n    }\n\n    @Test\n    public void filter() {\n        ArrayList<Integer> l0 = null;\n        ArrayList<Integer> l1 = CollectionUtils.newArrayList(0, 1, 2, 3);\n\n        CollectionUtils.filter(l0, null);\n        Assert.assertNull(l0);\n\n        CollectionUtils.filter(l0, new CollectionUtils.Predicate<Integer>() {\n            @Override\n            public boolean evaluate(Integer item) {\n                return item > 1;\n            }\n        });\n        Assert.assertNull(l0);\n\n        CollectionUtils.filter(l1, null);\n        Assert.assertEquals(l1, l1);\n\n        CollectionUtils.filter(l1, new CollectionUtils.Predicate<Integer>() {\n            @Override\n            public boolean evaluate(Integer item) {\n                return item > 1;\n            }\n        });\n        Assert.assertTrue(CollectionUtils.isEqualCollection(CollectionUtils.newArrayList(2, 3), l1));\n    }\n\n    @Test\n    public void select() {\n        ArrayList<Integer> list = CollectionUtils.newArrayList(0, 1, 2, 3);\n\n        Assert.assertEquals(0, CollectionUtils.select(null, null).size());\n        Assert.assertEquals(0, CollectionUtils.select(list, null).size());\n        Assert.assertEquals(0, CollectionUtils.select(null, new CollectionUtils.Predicate<Object>() {\n            @Override\n            public boolean evaluate(Object item) {\n                return true;\n            }\n        }).size());\n\n        Assert.assertTrue(CollectionUtils.isEqualCollection(\n                CollectionUtils.newArrayList(2, 3),\n                CollectionUtils.select(list, new CollectionUtils.Predicate<Integer>() {\n                    @Override\n                    public boolean evaluate(Integer item) {\n                        return item > 1;\n                    }\n                })));\n\n        Collection<Integer> list1 = CollectionUtils.select(list, new CollectionUtils.Predicate<Integer>() {\n            @Override\n            public boolean evaluate(Integer item) {\n                return true;\n            }\n        });\n\n        Assert.assertTrue(CollectionUtils.isEqualCollection(list, list1));\n        Assert.assertNotSame(list, list1);\n    }\n\n\n    @Test\n    public void selectRejected() {\n        ArrayList<Integer> list = CollectionUtils.newArrayList(0, 1, 2, 3);\n\n        Assert.assertEquals(0, CollectionUtils.selectRejected(null, null).size());\n        Assert.assertEquals(0, CollectionUtils.selectRejected(list, null).size());\n        Assert.assertEquals(0, CollectionUtils.selectRejected(null, new CollectionUtils.Predicate<Object>() {\n            @Override\n            public boolean evaluate(Object item) {\n                return true;\n            }\n        }).size());\n\n        Assert.assertTrue(CollectionUtils.isEqualCollection(\n                CollectionUtils.newArrayList(0, 1),\n                CollectionUtils.selectRejected(list, new CollectionUtils.Predicate<Integer>() {\n                    @Override\n                    public boolean evaluate(Integer item) {\n                        return item > 1;\n                    }\n                })));\n\n\n        Collection<Integer> list1 = CollectionUtils.selectRejected(list, new CollectionUtils.Predicate<Integer>() {\n            @Override\n            public boolean evaluate(Integer item) {\n                return false;\n            }\n        });\n\n        Assert.assertTrue(CollectionUtils.isEqualCollection(list, list1));\n        Assert.assertNotSame(list, list1);\n    }\n\n    @Test\n    public void transform() {\n        ArrayList<Integer> l0 = null;\n        ArrayList<Integer> l1 = CollectionUtils.newArrayList(0, 1, 2, 3);\n\n        CollectionUtils.transform(l0, null);\n        Assert.assertNull(l0);\n\n        CollectionUtils.transform(l0, new CollectionUtils.Transformer<Integer, Object>() {\n            @Override\n            public Object transform(Integer input) {\n                return \"int: \" + input;\n            }\n        });\n        Assert.assertNull(l0);\n\n        CollectionUtils.transform(l1, null);\n        Assert.assertEquals(l1, l1);\n\n        CollectionUtils.transform(l1, new CollectionUtils.Transformer<Integer, String>() {\n            @Override\n            public String transform(Integer input) {\n                return String.valueOf(input);\n            }\n        });\n        Assert.assertTrue(CollectionUtils.isEqualCollection(CollectionUtils.newArrayList(\"0\", \"1\", \"2\", \"3\"), l1));\n    }\n\n    @Test\n    public void collect() {\n        ArrayList<Integer> list = CollectionUtils.newArrayList(0, 1, 2, 3);\n\n        Assert.assertTrue(CollectionUtils.isEmpty(CollectionUtils.collect(null, null)));\n        Assert.assertTrue(CollectionUtils.isEmpty(CollectionUtils.collect(list, null)));\n        Assert.assertTrue(CollectionUtils.isEmpty(CollectionUtils.collect(null, new CollectionUtils.Transformer() {\n            @Override\n            public Object transform(Object input) {\n                return null;\n            }\n        })));\n\n        Assert.assertTrue(CollectionUtils.isEqualCollection(\n                CollectionUtils.newArrayList(\"0\", \"1\", \"2\", \"3\"),\n                CollectionUtils.collect(list, new CollectionUtils.Transformer<Integer, String>() {\n                    @Override\n                    public String transform(Integer input) {\n                        return String.valueOf(input);\n                    }\n                })));\n    }\n\n    @Test\n    public void countMatches() {\n        ArrayList<Integer> list = CollectionUtils.newArrayList(0, 1, 2, 3);\n\n        Assert.assertEquals(0, CollectionUtils.countMatches(null, null));\n        Assert.assertEquals(0, CollectionUtils.countMatches(list, null));\n        Assert.assertEquals(0, CollectionUtils.countMatches(null, new CollectionUtils.Predicate<Object>() {\n            @Override\n            public boolean evaluate(Object item) {\n                return false;\n            }\n        }));\n        Assert.assertEquals(2, CollectionUtils.countMatches(list, new CollectionUtils.Predicate<Integer>() {\n            @Override\n            public boolean evaluate(Integer item) {\n                return item > 1;\n            }\n        }));\n    }\n\n    @Test\n    public void exists() {\n        ArrayList<Integer> list = CollectionUtils.newArrayList(0, 1, 2, 3);\n\n        Assert.assertFalse(CollectionUtils.exists(null, null));\n        Assert.assertFalse(CollectionUtils.exists(list, null));\n        Assert.assertFalse(CollectionUtils.exists(null, new CollectionUtils.Predicate<Object>() {\n            @Override\n            public boolean evaluate(Object item) {\n                return false;\n            }\n        }));\n        Assert.assertTrue(CollectionUtils.exists(list, new CollectionUtils.Predicate<Integer>() {\n            @Override\n            public boolean evaluate(Integer item) {\n                return item > 1;\n            }\n        }));\n    }\n\n    @Test\n    public void addIgnoreNull() {\n        ArrayList<Integer> list = CollectionUtils.newArrayList(0, 1, 2, 3);\n\n        Assert.assertFalse(CollectionUtils.addIgnoreNull(null, null));\n        Assert.assertFalse(CollectionUtils.addIgnoreNull(null, 1));\n\n        CollectionUtils.addIgnoreNull(list, null);\n        Assert.assertEquals(CollectionUtils.newArrayList(0, 1, 2, 3), list);\n\n        CollectionUtils.addIgnoreNull(list, 4);\n        Assert.assertEquals(CollectionUtils.newArrayList(0, 1, 2, 3, 4), list);\n    }\n\n    @Test\n    public void addAll() {\n        ArrayList<Integer> l0 = null;\n        ArrayList<Integer> l1 = CollectionUtils.newArrayList(0, 1, 2, 3);\n        CollectionUtils.addAll(l0, (Iterator<Integer>) null);\n        Assert.assertNull(l0);\n        CollectionUtils.addAll(l0, (Enumeration<Integer>) null);\n        Assert.assertNull(l0);\n        CollectionUtils.addAll(l0, (Integer[]) null);\n        Assert.assertNull(l0);\n\n        CollectionUtils.addAll(l1, (Iterator<Integer>) null);\n        Assert.assertTrue(CollectionUtils.isEqualCollection(CollectionUtils.newArrayList(0, 1, 2, 3), l1));\n        CollectionUtils.addAll(l1, (Enumeration<Integer>) null);\n        Assert.assertTrue(CollectionUtils.isEqualCollection(CollectionUtils.newArrayList(0, 1, 2, 3), l1));\n        CollectionUtils.addAll(l1, (Integer[]) null);\n        Assert.assertTrue(CollectionUtils.isEqualCollection(CollectionUtils.newArrayList(0, 1, 2, 3), l1));\n\n        CollectionUtils.addAll(l1, CollectionUtils.newArrayList(4, 5).iterator());\n        Assert.assertTrue(CollectionUtils.isEqualCollection(CollectionUtils.newArrayList(0, 1, 2, 3, 4, 5), l1));\n    }\n\n    @Test\n    public void get() {\n        ArrayList<Integer> l0 = null;\n        ArrayList<Integer> l1 = CollectionUtils.newArrayList(0, 1, 2, 3);\n\n        Assert.assertNull(CollectionUtils.get(l0, 0));\n        Assert.assertEquals(0, CollectionUtils.get(l1, 0));\n    }\n\n    @Test\n    public void size() {\n        ArrayList<Integer> l0 = null;\n        ArrayList<Integer> l1 = CollectionUtils.newArrayList(0, 1, 2, 3);\n\n        Assert.assertEquals(0, CollectionUtils.size(l0));\n        Assert.assertEquals(4, CollectionUtils.size(l1));\n    }\n\n    @Test\n    public void sizeIsEmpty() {\n        ArrayList<Integer> l0 = null;\n        ArrayList<Integer> l1 = CollectionUtils.newArrayList(0, 1, 2, 3);\n\n        Assert.assertTrue(CollectionUtils.sizeIsEmpty(l0));\n        Assert.assertFalse(CollectionUtils.sizeIsEmpty(l1));\n    }\n\n    @Test\n    public void isEmpty() {\n        ArrayList<Integer> l0 = null;\n        ArrayList<Integer> l1 = CollectionUtils.newArrayList(0, 1, 2, 3);\n\n        Assert.assertTrue(CollectionUtils.isEmpty(l0));\n        Assert.assertFalse(CollectionUtils.isEmpty(l1));\n    }\n\n    @Test\n    public void isNotEmpty() {\n        ArrayList<Integer> l0 = null;\n        ArrayList<Integer> l1 = CollectionUtils.newArrayList(0, 1, 2, 3);\n\n        Assert.assertFalse(CollectionUtils.isNotEmpty(l0));\n        Assert.assertTrue(CollectionUtils.isNotEmpty(l1));\n    }\n\n    @Test\n    public void retainAll() {\n        ArrayList<Integer> list = CollectionUtils.newArrayList(0, 1, 2, 3);\n\n        Assert.assertEquals(0, CollectionUtils.retainAll(null, null).size());\n        Assert.assertEquals(0, CollectionUtils.retainAll(list, null).size());\n        Assert.assertEquals(0, CollectionUtils.retainAll(null, list).size());\n\n        Assert.assertTrue(CollectionUtils.isEqualCollection(\n                CollectionUtils.newArrayList(0, 1, 2, 3),\n                CollectionUtils.retainAll(list, list)\n        ));\n\n        Assert.assertTrue(CollectionUtils.isEqualCollection(\n                CollectionUtils.newArrayList(0, 1),\n                CollectionUtils.retainAll(list, CollectionUtils.newArrayList(0, 1, 6))\n        ));\n    }\n\n    @Test\n    public void removeAll() {\n        ArrayList<Integer> list = CollectionUtils.newArrayList(0, 1, 2, 3);\n\n        Assert.assertEquals(0, CollectionUtils.removeAll(null, null).size());\n        Assert.assertTrue(CollectionUtils.isEqualCollection(\n                CollectionUtils.newArrayList(0, 1, 2, 3),\n                CollectionUtils.removeAll(list, null)\n        ));\n        Assert.assertEquals(0, CollectionUtils.removeAll(null, list).size());\n\n        Assert.assertTrue(CollectionUtils.isEqualCollection(\n                CollectionUtils.newArrayList(2, 3),\n                CollectionUtils.removeAll(list, CollectionUtils.newArrayList(0, 1, 6))\n        ));\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/ColorUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/01/15\n *     desc  : test ColorUtils\n * </pre>\n */\npublic class ColorUtilsTest extends BaseTest {\n\n    @Test\n    public void setAlphaComponent() {\n        assertEquals(0x80ffffff, ColorUtils.setAlphaComponent(0xffffffff, 0x80));\n        assertEquals(0x80ffffff, ColorUtils.setAlphaComponent(0xffffffff, 0.5f));\n    }\n\n    @Test\n    public void setRedComponent() {\n        assertEquals(0xff80ffff, ColorUtils.setRedComponent(0xffffffff, 0x80));\n        assertEquals(0xff80ffff, ColorUtils.setRedComponent(0xffffffff, 0.5f));\n    }\n\n    @Test\n    public void setGreenComponent() {\n        assertEquals(0xffff80ff, ColorUtils.setGreenComponent(0xffffffff, 0x80));\n        assertEquals(0xffff80ff, ColorUtils.setGreenComponent(0xffffffff, 0.5f));\n    }\n\n    @Test\n    public void setBlueComponent() {\n        assertEquals(0xffffff80, ColorUtils.setBlueComponent(0xffffffff, 0x80));\n        assertEquals(0xffffff80, ColorUtils.setBlueComponent(0xffffffff, 0.5f));\n    }\n\n    @Test\n    public void string2Int() {\n        assertEquals(0xffffffff, ColorUtils.string2Int(\"#ffffff\"));\n        assertEquals(0xffffffff, ColorUtils.string2Int(\"#ffffffff\"));\n        assertEquals(0xffffffff, ColorUtils.string2Int(\"white\"));\n    }\n\n    @Test\n    public void int2RgbString() {\n        assertEquals(\"#000001\", ColorUtils.int2RgbString(1));\n        assertEquals(\"#ffffff\", ColorUtils.int2RgbString(0xffffff));\n    }\n\n    @Test\n    public void int2ArgbString() {\n        assertEquals(\"#ff000001\", ColorUtils.int2ArgbString(1));\n        assertEquals(\"#ffffffff\", ColorUtils.int2ArgbString(0xffffff));\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/ConvertUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\r\n\r\n\r\nimport com.blankj.utilcode.constant.MemoryConstants;\r\nimport com.blankj.utilcode.constant.TimeConstants;\r\n\r\nimport org.junit.Test;\r\n\r\nimport java.util.Arrays;\r\n\r\nimport static org.junit.Assert.assertEquals;\r\nimport static org.junit.Assert.assertTrue;\r\n\r\n\r\n/**\r\n * <pre>\r\n *     author: Blankj\r\n *     blog  : http://blankj.com\r\n *     time  : 2016/08/13\r\n *     desc  : test ConvertUtils\r\n * </pre>\r\n */\r\npublic class ConvertUtilsTest extends BaseTest {\r\n\r\n    private byte[] mBytes    = new byte[]{0x00, 0x08, (byte) 0xdb, 0x33, 0x45, (byte) 0xab, 0x02, 0x23};\r\n    private String hexString = \"0008DB3345AB0223\";\r\n\r\n    private char[] mChars1 = new char[]{'0', '1', '2'};\r\n    private byte[] mBytes1 = new byte[]{48, 49, 50};\r\n\r\n    @Test\r\n    public void bytes2HexString() {\r\n        assertEquals(\r\n                hexString,\r\n                ConvertUtils.bytes2HexString(mBytes)\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void hexString2Bytes() {\r\n        assertTrue(\r\n                Arrays.equals(\r\n                        mBytes,\r\n                        ConvertUtils.hexString2Bytes(hexString)\r\n                )\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void chars2Bytes() {\r\n        assertTrue(\r\n                Arrays.equals(\r\n                        mBytes1,\r\n                        ConvertUtils.chars2Bytes(mChars1)\r\n                )\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void bytes2Chars() {\r\n        assertTrue(\r\n                Arrays.equals(\r\n                        mChars1,\r\n                        ConvertUtils.bytes2Chars(mBytes1)\r\n                )\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void byte2MemorySize() {\r\n        assertEquals(\r\n                1024,\r\n                ConvertUtils.byte2MemorySize(MemoryConstants.GB, MemoryConstants.MB),\r\n                0.001\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void byte2FitMemorySize() {\r\n        assertEquals(\r\n                \"3.098MB\",\r\n                ConvertUtils.byte2FitMemorySize(1024 * 1024 * 3 + 1024 * 100)\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void millis2FitTimeSpan() {\r\n        long millis = 6 * TimeConstants.DAY\r\n                + 6 * TimeConstants.HOUR\r\n                + 6 * TimeConstants.MIN\r\n                + 6 * TimeConstants.SEC\r\n                + 6;\r\n        assertEquals(\r\n                \"6天6小时6分钟6秒6毫秒\",\r\n                ConvertUtils.millis2FitTimeSpan(millis, 7)\r\n        );\r\n        assertEquals(\r\n                \"6天6小时6分钟6秒\",\r\n                ConvertUtils.millis2FitTimeSpan(millis, 4)\r\n        );\r\n        assertEquals(\r\n                \"6天6小时6分钟\",\r\n                ConvertUtils.millis2FitTimeSpan(millis, 3)\r\n        );\r\n        assertEquals(\r\n                \"25天24分钟24秒24毫秒\",\r\n                ConvertUtils.millis2FitTimeSpan(millis * 4, 5)\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void bytes2Bits_bits2Bytes() {\r\n        assertEquals(\r\n                \"0111111111111010\",\r\n                ConvertUtils.bytes2Bits(new byte[]{0x7F, (byte) 0xFA})\r\n        );\r\n        assertEquals(\r\n                \"0111111111111010\",\r\n                ConvertUtils.bytes2Bits(ConvertUtils.bits2Bytes(\"111111111111010\"))\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void inputStream2Bytes_bytes2InputStream() throws Exception {\r\n        String string = \"this is test string\";\r\n        assertTrue(\r\n                Arrays.equals(\r\n                        string.getBytes(\"UTF-8\"),\r\n                        ConvertUtils.inputStream2Bytes(ConvertUtils.bytes2InputStream(string.getBytes(\"UTF-8\")))\r\n                )\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void inputStream2String_string2InputStream() {\r\n        String string = \"this is test string\";\r\n        assertEquals(\r\n                string,\r\n                ConvertUtils.inputStream2String(ConvertUtils.string2InputStream(string, \"UTF-8\"), \"UTF-8\")\r\n        );\r\n    }\r\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/EncodeUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\r\n\r\nimport org.junit.Test;\r\n\r\nimport static org.junit.Assert.assertArrayEquals;\r\nimport static org.junit.Assert.assertEquals;\r\n\r\n/**\r\n * <pre>\r\n *     author: Blankj\r\n *     blog  : http://blankj.com\r\n *     time  : 2016/08/12\r\n *     desc  : test EncodeUtils\r\n * </pre>\r\n */\r\npublic class EncodeUtilsTest extends BaseTest {\r\n\r\n    @Test\r\n    public void urlEncode_urlDecode() {\r\n        String urlEncodeString = \"%E5%93%88%E5%93%88%E5%93%88\";\r\n        assertEquals(urlEncodeString, EncodeUtils.urlEncode(\"哈哈哈\"));\r\n        assertEquals(urlEncodeString, EncodeUtils.urlEncode(\"哈哈哈\", \"UTF-8\"));\r\n\r\n        assertEquals(\"哈哈哈\", EncodeUtils.urlDecode(urlEncodeString));\r\n        assertEquals(\"哈哈哈\", EncodeUtils.urlDecode(urlEncodeString, \"UTF-8\"));\r\n    }\r\n\r\n    @Test\r\n    public void base64Decode_base64Encode() {\r\n        assertArrayEquals(\"blankj\".getBytes(), EncodeUtils.base64Decode(EncodeUtils.base64Encode(\"blankj\")));\r\n        assertArrayEquals(\"blankj\".getBytes(), EncodeUtils.base64Decode(EncodeUtils.base64Encode2String(\"blankj\".getBytes())));\r\n        assertEquals(\r\n                \"Ymxhbmtq\",\r\n                EncodeUtils.base64Encode2String(\"blankj\".getBytes())\r\n        );\r\n        assertArrayEquals(\"Ymxhbmtq\".getBytes(), EncodeUtils.base64Encode(\"blankj\".getBytes()));\r\n    }\r\n\r\n    @Test\r\n    public void htmlEncode_htmlDecode() {\r\n        String html = \"<html>\" +\r\n                \"<head>\" +\r\n                \"<title>我的第一个 HTML 页面</title>\" +\r\n                \"</head>\" +\r\n                \"<body>\" +\r\n                \"<p>body 元素的内容会显示在浏览器中。</p>\" +\r\n                \"<p>title 元素的内容会显示在浏览器的标题栏中。</p>\" +\r\n                \"</body>\" +\r\n                \"</html>\";\r\n        String encodeHtml = \"&lt;html&gt;&lt;head&gt;&lt;title&gt;我的第一个 HTML 页面&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&lt;p&gt;body 元素的内容会显示在浏览器中。&lt;/p&gt;&lt;p&gt;title 元素的内容会显示在浏览器的标题栏中。&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;\";\r\n\r\n        assertEquals(encodeHtml, EncodeUtils.htmlEncode(html));\r\n\r\n        assertEquals(html, EncodeUtils.htmlDecode(encodeHtml).toString());\r\n    }\r\n\r\n    @Test\r\n    public void binEncode_binDecode() {\r\n        String test = \"test\";\r\n        String binary = EncodeUtils.binaryEncode(test);\r\n        assertEquals(\"test\", EncodeUtils.binaryDecode(binary));\r\n    }\r\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/EncryptUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\r\n\r\nimport android.util.Pair;\r\n\r\nimport org.junit.Assert;\r\nimport org.junit.Test;\r\n\r\nimport java.io.File;\r\nimport java.security.Key;\r\nimport java.security.KeyPair;\r\nimport java.security.KeyPairGenerator;\r\nimport java.security.NoSuchAlgorithmException;\r\nimport java.security.SecureRandom;\r\n\r\nimport static com.blankj.utilcode.util.TestConfig.PATH_ENCRYPT;\r\nimport static org.junit.Assert.assertArrayEquals;\r\nimport static org.junit.Assert.assertEquals;\r\n\r\n\r\n/**\r\n * <pre>\r\n *     author: Blankj\r\n *     blog  : http://blankj.com\r\n *     time  : 2016/08/06\r\n *     desc  : test EncryptUtils\r\n * </pre>\r\n */\r\npublic class EncryptUtilsTest extends BaseTest {\r\n    @Test\r\n    public void encryptMD2() {\r\n        String blankjMD2 = \"15435017570D8A73449E25C4622E17A4\";\r\n        Assert.assertEquals(\r\n                blankjMD2,\r\n                EncryptUtils.encryptMD2ToString(\"blankj\")\r\n        );\r\n        assertEquals(\r\n                blankjMD2,\r\n                EncryptUtils.encryptMD2ToString(\"blankj\".getBytes())\r\n        );\r\n        assertArrayEquals(\r\n                UtilsBridge.hexString2Bytes(blankjMD2),\r\n                EncryptUtils.encryptMD2(\"blankj\".getBytes())\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void encryptMD5() {\r\n        String blankjMD5 = \"AAC25CD336E01C8655F4EC7875445A60\";\r\n        assertEquals(\r\n                blankjMD5,\r\n                EncryptUtils.encryptMD5ToString(\"blankj\")\r\n        );\r\n        assertEquals(\r\n                blankjMD5,\r\n                EncryptUtils.encryptMD5ToString(\"blankj\".getBytes())\r\n        );\r\n        assertArrayEquals(\r\n                UtilsBridge.hexString2Bytes(blankjMD5),\r\n                EncryptUtils.encryptMD5(\"blankj\".getBytes())\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void encryptMD5File() {\r\n        String fileMd5 = \"7f138a09169b250e9dcb378140907378\";\r\n        assertEquals(\r\n                fileMd5.toUpperCase(),\r\n                EncryptUtils.encryptMD5File2String(new File(PATH_ENCRYPT + \"MD5.txt\"))\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void encryptSHA1() {\r\n        String blankjSHA1 = \"C606ACCB1FEB669E19D080ADDDDBB8E6CDA5F43C\";\r\n        assertEquals(\r\n                blankjSHA1,\r\n                EncryptUtils.encryptSHA1ToString(\"blankj\")\r\n        );\r\n        assertEquals(\r\n                blankjSHA1,\r\n                EncryptUtils.encryptSHA1ToString(\"blankj\".getBytes())\r\n        );\r\n        assertArrayEquals(\r\n                UtilsBridge.hexString2Bytes(blankjSHA1),\r\n                EncryptUtils.encryptSHA1(\"blankj\".getBytes())\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void encryptSHA224() {\r\n        String blankjSHA224 = \"F4C5C0E8CF56CAC4D06DB6B523F67621859A9D79BDA4B2AC03097D5F\";\r\n        assertEquals(\r\n                blankjSHA224,\r\n                EncryptUtils.encryptSHA224ToString(\"blankj\")\r\n        );\r\n        assertEquals(\r\n                blankjSHA224,\r\n                EncryptUtils.encryptSHA224ToString(\"blankj\".getBytes())\r\n        );\r\n        assertArrayEquals(\r\n                UtilsBridge.hexString2Bytes(blankjSHA224),\r\n                EncryptUtils.encryptSHA224(\"blankj\".getBytes())\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void encryptSHA256() {\r\n        String blankjSHA256 = \"8BD80AE90DFBA112786367BEBDDEE60A638EF5B82682EDF8F3D3CA8E6BFEF648\";\r\n        assertEquals(\r\n                blankjSHA256,\r\n                EncryptUtils.encryptSHA256ToString(\"blankj\")\r\n        );\r\n        assertEquals(\r\n                blankjSHA256,\r\n                EncryptUtils.encryptSHA256ToString(\"blankj\".getBytes())\r\n        );\r\n        assertArrayEquals(\r\n                UtilsBridge.hexString2Bytes(blankjSHA256),\r\n                EncryptUtils.encryptSHA256(\"blankj\".getBytes()));\r\n\r\n    }\r\n\r\n    @Test\r\n    public void encryptSHA384() {\r\n        String blankjSHA384 = \"BF831E5221FC108D6A72ACB888BA3EB0C030A5F01BA2F739856BE70681D86F992B85E0D461101C74BAEDA895BD422557\";\r\n        assertEquals(\r\n                blankjSHA384,\r\n                EncryptUtils.encryptSHA384ToString(\"blankj\")\r\n        );\r\n        assertEquals(\r\n                blankjSHA384,\r\n                EncryptUtils.encryptSHA384ToString(\"blankj\".getBytes())\r\n        );\r\n        assertArrayEquals(\r\n                UtilsBridge.hexString2Bytes(blankjSHA384),\r\n                EncryptUtils.encryptSHA384(\"blankj\".getBytes())\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void encryptSHA512() {\r\n        String blankjSHA512 = \"D59D31067F614ED3586F85A31FEFDB7F33096316DA26EBE0FF440B241C8560D96650F100D78C512560C976949EFA89CB5D5589DCF68C7FAADE98F03BCFEC2B45\";\r\n        assertEquals(\r\n                blankjSHA512,\r\n                EncryptUtils.encryptSHA512ToString(\"blankj\")\r\n        );\r\n        assertEquals(\r\n                blankjSHA512,\r\n                EncryptUtils.encryptSHA512ToString(\"blankj\".getBytes())\r\n        );\r\n        assertArrayEquals(\r\n                UtilsBridge.hexString2Bytes(blankjSHA512),\r\n                EncryptUtils.encryptSHA512(\"blankj\".getBytes())\r\n        );\r\n    }\r\n\r\n    private String blankjHmacSHA512 =\r\n            \"FC55AD54B95F55A8E32EA1BAD7748C157F80679F5561EC95A3EAD975316BA85363CB4AF6462D695F742F469EDC2D577272BE359A7F9E9C7018FDF4C921E1B3CF\";\r\n    private String blankjHmackey    = \"blankj\";\r\n\r\n    @Test\r\n    public void encryptHmacMD5() {\r\n        String blankjHmacMD5 = \"2BA3FDABEE222522044BEC0CE5D6B490\";\r\n        assertEquals(\r\n                blankjHmacMD5,\r\n                EncryptUtils.encryptHmacMD5ToString(\"blankj\", blankjHmackey)\r\n        );\r\n        assertEquals(\r\n                blankjHmacMD5,\r\n                EncryptUtils.encryptHmacMD5ToString(\"blankj\".getBytes(), blankjHmackey.getBytes())\r\n        );\r\n        assertArrayEquals(\r\n                UtilsBridge.hexString2Bytes(blankjHmacMD5),\r\n                EncryptUtils.encryptHmacMD5(\"blankj\".getBytes(), blankjHmackey.getBytes())\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void encryptHmacSHA1() {\r\n        String blankjHmacSHA1 = \"88E83EFD915496860C83739BE2CF4752B2AC105F\";\r\n        assertEquals(\r\n                blankjHmacSHA1,\r\n                EncryptUtils.encryptHmacSHA1ToString(\"blankj\", blankjHmackey)\r\n        );\r\n        assertEquals(\r\n                blankjHmacSHA1,\r\n                EncryptUtils.encryptHmacSHA1ToString(\"blankj\".getBytes(), blankjHmackey.getBytes())\r\n        );\r\n        assertArrayEquals(\r\n                UtilsBridge.hexString2Bytes(blankjHmacSHA1),\r\n                EncryptUtils.encryptHmacSHA1(\"blankj\".getBytes(), blankjHmackey.getBytes())\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void encryptHmacSHA224() {\r\n        String blankjHmacSHA224 = \"E392D83D1030323FB2E062E8165A3AD38366E53DF19EA3290961E153\";\r\n        assertEquals(\r\n                blankjHmacSHA224,\r\n                EncryptUtils.encryptHmacSHA224ToString(\"blankj\", blankjHmackey)\r\n        );\r\n        assertEquals(\r\n                blankjHmacSHA224,\r\n                EncryptUtils.encryptHmacSHA224ToString(\"blankj\".getBytes(), blankjHmackey.getBytes())\r\n        );\r\n        assertArrayEquals(\r\n                UtilsBridge.hexString2Bytes(blankjHmacSHA224),\r\n                EncryptUtils.encryptHmacSHA224(\"blankj\".getBytes(), blankjHmackey.getBytes())\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void encryptHmacSHA256() {\r\n        String blankjHmacSHA256 = \"A59675F13FC9A6E06D8DC90D4DC01DB9C991B0B95749D2471E588BF311DA2C67\";\r\n        assertEquals(\r\n                blankjHmacSHA256,\r\n                EncryptUtils.encryptHmacSHA256ToString(\"blankj\", blankjHmackey)\r\n        );\r\n        assertEquals(\r\n                blankjHmacSHA256,\r\n                EncryptUtils.encryptHmacSHA256ToString(\"blankj\".getBytes(), blankjHmackey.getBytes())\r\n        );\r\n        assertArrayEquals(\r\n                UtilsBridge.hexString2Bytes(blankjHmacSHA256),\r\n                EncryptUtils.encryptHmacSHA256(\"blankj\".getBytes(), blankjHmackey.getBytes())\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void encryptHmacSHA384() {\r\n        String blankjHmacSHA384 = \"9FC2F49C7EDE698EA59645B3BEFBBE67DCC7D6623E03D4D03CDA1324F7B6445BC428AB42F6A962CF79AFAD1302C3223D\";\r\n        assertEquals(\r\n                blankjHmacSHA384,\r\n                EncryptUtils.encryptHmacSHA384ToString(\"blankj\", blankjHmackey)\r\n        );\r\n        assertEquals(\r\n                blankjHmacSHA384,\r\n                EncryptUtils.encryptHmacSHA384ToString(\"blankj\".getBytes(), blankjHmackey.getBytes())\r\n        );\r\n        assertArrayEquals(\r\n                UtilsBridge.hexString2Bytes(blankjHmacSHA384),\r\n                EncryptUtils.encryptHmacSHA384(\"blankj\".getBytes(), blankjHmackey.getBytes())\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void encryptHmacSHA512() {\r\n        assertEquals(\r\n                blankjHmacSHA512,\r\n                EncryptUtils.encryptHmacSHA512ToString(\"blankj\", blankjHmackey)\r\n        );\r\n        assertEquals(\r\n                blankjHmacSHA512,\r\n                EncryptUtils.encryptHmacSHA512ToString(\"blankj\".getBytes(), blankjHmackey.getBytes())\r\n        );\r\n        assertArrayEquals(\r\n                UtilsBridge.hexString2Bytes(blankjHmacSHA512),\r\n                EncryptUtils.encryptHmacSHA512(\"blankj\".getBytes(), blankjHmackey.getBytes())\r\n        );\r\n    }\r\n\r\n\r\n    private String dataDES      = \"0008DB3345AB0223\";\r\n    private String keyDES       = \"6801020304050607\";\r\n    private String resDES       = \"1F7962581118F360\";\r\n    private byte[] bytesDataDES = UtilsBridge.hexString2Bytes(dataDES);\r\n    private byte[] bytesKeyDES  = UtilsBridge.hexString2Bytes(keyDES);\r\n    private byte[] bytesResDES  = UtilsBridge.hexString2Bytes(resDES);\r\n\r\n    @Test\r\n    public void encryptDES() {\r\n        assertArrayEquals(\r\n                bytesResDES,\r\n                EncryptUtils.encryptDES(bytesDataDES, bytesKeyDES, \"DES/ECB/NoPadding\", null)\r\n        );\r\n        assertEquals(\r\n                resDES,\r\n                EncryptUtils.encryptDES2HexString(bytesDataDES, bytesKeyDES, \"DES/ECB/NoPadding\", null)\r\n        );\r\n        assertArrayEquals(\r\n                UtilsBridge.base64Encode(bytesResDES),\r\n                EncryptUtils.encryptDES2Base64(bytesDataDES, bytesKeyDES, \"DES/ECB/NoPadding\", null)\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void decryptDES() {\r\n        assertArrayEquals(\r\n                bytesDataDES,\r\n                EncryptUtils.decryptDES(bytesResDES, bytesKeyDES, \"DES/ECB/NoPadding\", null)\r\n        );\r\n        assertArrayEquals(\r\n                bytesDataDES,\r\n                EncryptUtils.decryptHexStringDES(resDES, bytesKeyDES, \"DES/ECB/NoPadding\", null)\r\n        );\r\n        assertArrayEquals(\r\n                bytesDataDES,\r\n                EncryptUtils.decryptBase64DES(UtilsBridge.base64Encode(bytesResDES), bytesKeyDES, \"DES/ECB/NoPadding\", null)\r\n        );\r\n    }\r\n\r\n    private String data3DES      = \"1111111111111111\";\r\n    private String key3DES       = \"111111111111111111111111111111111111111111111111\";\r\n    private String res3DES       = \"F40379AB9E0EC533\";\r\n    private byte[] bytesDataDES3 = UtilsBridge.hexString2Bytes(data3DES);\r\n    private byte[] bytesKeyDES3  = UtilsBridge.hexString2Bytes(key3DES);\r\n    private byte[] bytesResDES3  = UtilsBridge.hexString2Bytes(res3DES);\r\n\r\n    @Test\r\n    public void encrypt3DES() {\r\n        assertArrayEquals(\r\n                bytesResDES3,\r\n                EncryptUtils.encrypt3DES(bytesDataDES3, bytesKeyDES3, \"DESede/ECB/NoPadding\", null)\r\n        );\r\n        assertEquals(\r\n                res3DES,\r\n                EncryptUtils.encrypt3DES2HexString(bytesDataDES3, bytesKeyDES3, \"DESede/ECB/NoPadding\", null)\r\n        );\r\n        assertArrayEquals(\r\n                UtilsBridge.base64Encode(bytesResDES3),\r\n                EncryptUtils.encrypt3DES2Base64(bytesDataDES3, bytesKeyDES3, \"DESede/ECB/NoPadding\", null)\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void decrypt3DES() {\r\n        assertArrayEquals(\r\n                bytesDataDES3,\r\n                EncryptUtils.decrypt3DES(bytesResDES3, bytesKeyDES3, \"DESede/ECB/NoPadding\", null)\r\n        );\r\n        assertArrayEquals(\r\n                bytesDataDES3,\r\n                EncryptUtils.decryptHexString3DES(res3DES, bytesKeyDES3, \"DESede/ECB/NoPadding\", null)\r\n        );\r\n        assertArrayEquals(\r\n                bytesDataDES3,\r\n                EncryptUtils.decryptBase64_3DES(UtilsBridge.base64Encode(bytesResDES3), bytesKeyDES3, \"DESede/ECB/NoPadding\", null)\r\n        );\r\n    }\r\n\r\n    private String dataAES      = \"111111111111111111111111111111111\";\r\n    private String keyAES       = \"11111111111111111111111111111111\";\r\n    private String resAES       = \"393FBBBC2C774BE50A106A50393E623AC3790781D015BB854359587256581F6D\";\r\n    private byte[] bytesDataAES = UtilsBridge.hexString2Bytes(dataAES);\r\n    private byte[] bytesKeyAES  = UtilsBridge.hexString2Bytes(keyAES);\r\n    private byte[] bytesResAES  = UtilsBridge.hexString2Bytes(resAES);\r\n\r\n    @Test\r\n    public void encryptAES() {\r\n        assertArrayEquals(\r\n                bytesResAES,\r\n                EncryptUtils.encryptAES(bytesDataAES, bytesKeyAES, \"AES/ECB/PKCS5Padding\", null)\r\n        );\r\n        assertEquals(\r\n                resAES,\r\n                EncryptUtils.encryptAES2HexString(bytesDataAES, bytesKeyAES, \"AES/ECB/PKCS5Padding\", null)\r\n        );\r\n        assertArrayEquals(\r\n                UtilsBridge.base64Encode(bytesResAES),\r\n                EncryptUtils.encryptAES2Base64(bytesDataAES, bytesKeyAES, \"AES/ECB/PKCS5Padding\", null)\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void decryptAES() {\r\n        assertArrayEquals(\r\n                bytesDataAES,\r\n                EncryptUtils.decryptAES(bytesResAES, bytesKeyAES, \"AES/ECB/PKCS5Padding\", null)\r\n        );\r\n        assertArrayEquals(\r\n                bytesDataAES,\r\n                EncryptUtils.decryptHexStringAES(resAES, bytesKeyAES, \"AES/ECB/PKCS5Padding\", null)\r\n        );\r\n        assertArrayEquals(bytesDataAES,\r\n                EncryptUtils.decryptBase64AES(UtilsBridge.base64Encode(bytesResAES), bytesKeyAES, \"AES/ECB/PKCS5Padding\", null)\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void encryptDecryptRSA() throws Exception {\r\n        int keySize = 1024;\r\n        Pair<String, String> publicPrivateKey = genKeyPair(keySize);\r\n\r\n        String publicKey = publicPrivateKey.first;\r\n        String privateKey = publicPrivateKey.second;\r\n        String dataRSA = \"BlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBlankjBl\";\r\n        System.out.println(\"publicKeyBase64:\" + publicKey);\r\n        System.out.println(\"privateKeyBase64:\" + privateKey);\r\n\r\n        System.out.println(EncryptUtils.encryptRSA2HexString(\r\n                dataRSA.getBytes(),\r\n                UtilsBridge.base64Decode(publicKey.getBytes()),\r\n                keySize,\r\n                \"RSA/None/PKCS1Padding\"\r\n        ));\r\n\r\n        assertArrayEquals(EncryptUtils.decryptRSA(\r\n                EncryptUtils.encryptRSA(\r\n                        dataRSA.getBytes(),\r\n                        UtilsBridge.base64Decode(publicKey.getBytes()),\r\n                        keySize,\r\n                        \"RSA/None/PKCS1Padding\"\r\n                ),\r\n                UtilsBridge.base64Decode(privateKey.getBytes()),\r\n                keySize,\r\n                \"RSA/None/PKCS1Padding\"\r\n        ), dataRSA.getBytes());\r\n    }\r\n\r\n    private String dataRc4 = \"111111111111111111111\";\r\n    private String keyRc4  = \"111111111111\";\r\n\r\n    @Test\r\n    public void rc4() throws Exception {\r\n        System.out.println(new String(EncryptUtils.rc4(EncryptUtils.rc4(dataRc4.getBytes(), keyRc4.getBytes()), keyRc4.getBytes())));\r\n    }\r\n\r\n    private Pair<String, String> genKeyPair(int size) throws NoSuchAlgorithmException {\r\n\r\n        if (size == 1024) {\r\n            return Pair.create(\r\n                    \"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCYHGvdORdwsK5i+s9rKaMPL1O5eDK2XwNHRUWaxmGB/cxLxeinJrrqdAN+mME7XtGN9bklnOR3MUBQLVnWIn/IU0pnIJY9DpPTVc7x+1zFb8UUq1N0BBo/NpUG5olxuQULuAAHZOg28pnP/Pcb5XVEvpNKL0HaWjN8pu/Dzf8gZwIDAQAB\",\r\n                    \"MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAJgca905F3CwrmL6z2spow8vU7l4MrZfA0dFRZrGYYH9zEvF6Kcmuup0A36YwTte0Y31uSWc5HcxQFAtWdYif8hTSmcglj0Ok9NVzvH7XMVvxRSrU3QEGj82lQbmiXG5BQu4AAdk6Dbymc/89xvldUS+k0ovQdpaM3ym78PN/yBnAgMBAAECgYAFdX+pgNMGiFC53KZ1AhmIAfrPPTEUunQzqpjE5Tm6oJEkZwXiedFbeK5nbLQCnXSH07nBT9AjNvFH71i6BqLvT1l3/ezPq9pmRPriHfWQQ3/J3ASf1O9F9CkYbq/s/qqkXEFcl8PdYQV0xU/kS4jZPP+60Lv3sPkLg2DpkhM+AQJBANTl+/v6sBqqQSS0Anl5nE15Ck3XGBcq0nvATHfFkJYtG9rrXz3ZoRATLxF1iJYwGSAtirhev9W7qFayjci0ztcCQQC25/kkFbeMEWT6/kyV8wcPIog1mKy8RVB9+2l6C8AzbWBPZYtLlB7uaGSJeZBTEGfvRYzpFm5xO0JqwCfDddjxAkBmxtgM3wqg9MwaAeSn6/Nu2x4EUfBJTtzp7P19XJzeQsyNtM73ttYwQnKYhRr5FiMrC5FKTENj1QIBSJV17QNlAkAL5cUAAuWgl9UQuo/yxQ81fdKMYfUCfiPBPiRbSv5imf/Eyl8oOGdWrLW1d5HaxVttZgHHe60NcoRce0la3oSRAkAe8OqLsm9ryXNvBtZxSG+1JUvePVxpRSlJdZIAUKxN6XQE0S9aEe/IkNDBgVeiUEtop76R2NkkGtGTwzbzl0gm\"\r\n            );\r\n        } else if (size == 2048) {\r\n            return Pair.create(\r\n                    \"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAjLLeJZIO7dfQKb6tHE+TlhvD1m3UdTefKvl4uNQboDXy2ztgPcksjLDXxsT+znxMBh4RpXxfVPgnrcSLewGVhTb3uXh9sWo6tvvshNaMKBTebaZePhE7grq+LHH3NILscVssK24rDSvIquZ4nUbDipF/Iscge4LwnypcCuun/3RCn4HYzXW+0YFFZC8Vq4zabIxtzzkvgZlAlvuD6tT76Uuo5kD8b36yYNALI+ZStOj283wlL8PgyyitRGaqCH+MjWYqDb5C0DN31kcoSU7ARTGWgNNAoexAdNujkBvVRFyR2cH9FpjJDu18Oa8v9uSjlRftVWPj0OQXE7vRUsrrawIDAQAB\",\r\n                    \"MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCMst4lkg7t19Apvq0cT5OWG8PWbdR1N58q+Xi41BugNfLbO2A9ySyMsNfGxP7OfEwGHhGlfF9U+CetxIt7AZWFNve5eH2xajq2++yE1owoFN5tpl4+ETuCur4scfc0guxxWywrbisNK8iq5nidRsOKkX8ixyB7gvCfKlwK66f/dEKfgdjNdb7RgUVkLxWrjNpsjG3POS+BmUCW+4Pq1PvpS6jmQPxvfrJg0Asj5lK06PbzfCUvw+DLKK1EZqoIf4yNZioNvkLQM3fWRyhJTsBFMZaA00Ch7EB026OQG9VEXJHZwf0WmMkO7Xw5ry/25KOVF+1VY+PQ5BcTu9FSyutrAgMBAAECggEAHJQ4i2kfnzA3GEOi5h1D3TnGjcfBYA3sRs5ltyVedyx+KAnngqVaZzmEmtto5ohY6OUysGqS8q91X9aMfm/T7zs7FnFjFqZ9Rq3lXRY3YezbQWqJuhHGBMfp2R1NGV1+qYfbcPbvx70dBZnK5id5kKv9JxNLhcsTFUGFcLJtbXXixY2CGiS/dIbFvFHGMbAz3+9l9HXaL4AS7KQXvnauwJW1a5vIAVFYZVBj0qY9Viy2vq6ShH+9pdxOSsWBt08WpxIhjkTr+ZkFck67la2Jn0SBlClB0FIygTqbAmsM3p1nqcR55jdx3hfs31rIfM1Rx5epMm48KYErb2ktowngAQKBgQDL9FEumMMagPy4+EjR1puFHNvADlAi8tIUNt1W5zKKnd+T6gYGn8nqiiy5pvwLLUp8JISmq50tMC3cgAPw+G4kIe5zoBO2EU9X6aPhMd/ScUlVdk0IzEMXa3kMAOjOInWvoevJ4cwWcBPH2aRuDg5wZdh3TpB9LQP4uQ0QHwmE3wKBgQCwmkL6rJDrNo1GNUsjw+WIsXkuS3PYJahbg/uhRdGSsX2BRIPQVCRJP7MkgaUMhZRilt1ROfQy4d2BPxTxvUiGJcKfpsW8xi39PrYWZC5TvEA839q39Uak+ISCsYtZaHk5dvzmE9nF5gv0ivjCr81N2/1KwXO8VmNofzWUqNd+9QKBgQCs39QICRgm2Ppd1qXyp1N/SuzBJ+CpHuUOmUqXpLRkZljiSVT+PGar1J8AZhfxaVxfSZzeoUxCxzm4UxIEKK9DFTfG7gKHKrj0LWfpM5siB0A/nlzBflHIAiLCF+s8/lx+mGMB5dBVnH5HwaTsXCHFB66pwgAa+hMJueDmr0gkRQKBgDKhd1Rwxvd4Y1ZejxVI43SmFOzt2t98JGFgXHLnFmdtFWNLJlNC3EhXx99Of+gwH9OIFxljeRxhXuTgFfwcXT+AceTdplExrBuvr/qJbDK7hNsu/oDBBCjlyu/BQQc4CZEtCOJZjJTNGF5avWjrh/urd1nITosPZV6fIdhl86pFAoGAfOwK0Wte6gO5glAHP9RNktDeyFJCfFH1KUFiAG7XUww6bRpL2fEAqBIcDVgsS565ihxDSbUjgQgg/Ckh2+iBrwf1K9ViO4XUuwWqRS26rn4Is/W5kbPtnC4HS5cQIH1aWi3xUMJcWxV4ZrwiMVdw91leYWC0IbXC/yrc/PBW+sE=\"\r\n            );\r\n        }\r\n\r\n        SecureRandom secureRandom = new SecureRandom();\r\n\r\n        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(\"RSA\");\r\n\r\n        keyPairGenerator.initialize(size, secureRandom);\r\n\r\n        KeyPair keyPair = keyPairGenerator.generateKeyPair();\r\n\r\n        Key publicKey = keyPair.getPublic();\r\n\r\n        Key privateKey = keyPair.getPrivate();\r\n\r\n        byte[] publicKeyBytes = publicKey.getEncoded();\r\n        byte[] privateKeyBytes = privateKey.getEncoded();\r\n\r\n        String publicKeyBase64 = EncodeUtils.base64Encode2String(publicKeyBytes);\r\n        String privateKeyBase64 = EncodeUtils.base64Encode2String(privateKeyBytes);\r\n\r\n        return Pair.create(publicKeyBase64, privateKeyBase64);\r\n    }\r\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/FileIOUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport org.junit.After;\nimport org.junit.Test;\n\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\n\nimport static com.blankj.utilcode.util.TestConfig.PATH_TEMP;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/05/24\n *     desc  : test FileIOUtils\n * </pre>\n */\npublic class FileIOUtilsTest extends BaseTest {\n\n    @Test\n    public void writeFileFromIS() throws Exception {\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < 100000; i++) {\n            sb.append(String.format(\"%5dFileIOUtilsTest\\n\", i));\n        }\n        InputStream is = ConvertUtils.string2InputStream(sb.toString(), \"UTF-8\");\n\n        FileIOUtils.writeFileFromIS(PATH_TEMP + \"writeFileFromIS.txt\", is, new FileIOUtils.OnProgressUpdateListener() {\n            @Override\n            public void onProgressUpdate(double progress) {\n                System.out.println(String.format(\"%.2f\", progress));\n            }\n        });\n    }\n\n    @Test\n    public void writeFileFromBytesByStream() throws Exception {\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < 100000; i++) {\n            sb.append(String.format(\"%5dFileIOUtilsTest\\n\", i));\n        }\n        byte[] bytes = sb.toString().getBytes(StandardCharsets.UTF_8);\n\n        FileIOUtils.writeFileFromBytesByStream(PATH_TEMP + \"writeFileFromBytesByStream.txt\", bytes, new FileIOUtils.OnProgressUpdateListener() {\n            @Override\n            public void onProgressUpdate(double progress) {\n                System.out.println(String.format(\"%.2f\", progress));\n            }\n        });\n    }\n\n    @Test\n    public void writeFileFromString() {\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < 10; i++) {\n            sb.append(i).append(\"FileIOUtilsTest\\n\");\n        }\n        FileIOUtils.writeFileFromString(PATH_TEMP + \"writeFileFromString.txt\", sb.toString());\n    }\n\n    @Test\n    public void readFile2List() {\n        writeFileFromString();\n        for (String s : FileIOUtils.readFile2List(PATH_TEMP + \"writeFileFromString.txt\")) {\n            System.out.println(s);\n        }\n    }\n\n    @Test\n    public void readFile2String() {\n        writeFileFromString();\n        System.out.println(FileIOUtils.readFile2String(PATH_TEMP + \"writeFileFromString.txt\"));\n    }\n\n    @Test\n    public void readFile2Bytes() throws Exception {\n        writeFileFromBytesByStream();\n        FileIOUtils.readFile2BytesByStream(PATH_TEMP + \"writeFileFromIS.txt\", new FileIOUtils.OnProgressUpdateListener() {\n            @Override\n            public void onProgressUpdate(double progress) {\n                System.out.println(String.format(\"%.2f\", progress));\n            }\n        });\n    }\n\n    @After\n    public void tearDown() {\n        FileUtils.deleteAllInDir(PATH_TEMP);\n    }\n\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/FileUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport org.junit.Test;\n\nimport java.io.File;\nimport java.io.FileFilter;\n\nimport static com.blankj.utilcode.util.TestConfig.FILE_SEP;\nimport static com.blankj.utilcode.util.TestConfig.PATH_FILE;\nimport static com.blankj.utilcode.util.TestConfig.PATH_TEMP;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertNotNull;\nimport static org.junit.Assert.assertNull;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/19\n *     desc  : test FileUtils\n * </pre>\n */\npublic class FileUtilsTest extends BaseTest {\n\n    private FileFilter mFilter = new FileFilter() {\n        @Override\n        public boolean accept(File pathname) {\n            return pathname.getName().endsWith(\"8.txt\");\n        }\n    };\n\n    private FileUtils.OnReplaceListener mListener = new FileUtils.OnReplaceListener() {\n        @Override\n        public boolean onReplace(File srcFile, File destFile) {\n            return true;\n        }\n    };\n\n\n    @Test\n    public void getFileByPath() {\n        assertNull(FileUtils.getFileByPath(\" \"));\n        assertNotNull(FileUtils.getFileByPath(PATH_FILE));\n    }\n\n    @Test\n    public void isFileExists() {\n        assertTrue(FileUtils.isFileExists(PATH_FILE + \"UTF8.txt\"));\n        assertFalse(FileUtils.isFileExists(PATH_FILE + \"UTF8\"));\n    }\n\n    @Test\n    public void rename() {\n        assertTrue(FileUtils.rename(PATH_FILE + \"GBK.txt\", \"GBK1.txt\"));\n        assertTrue(FileUtils.rename(PATH_FILE + \"GBK1.txt\", \"GBK.txt\"));\n    }\n\n    @Test\n    public void isDir() {\n        assertFalse(FileUtils.isDir(PATH_FILE + \"UTF8.txt\"));\n        assertTrue(FileUtils.isDir(PATH_FILE));\n    }\n\n    @Test\n    public void isFile() {\n        assertTrue(FileUtils.isFile(PATH_FILE + \"UTF8.txt\"));\n        assertFalse(FileUtils.isFile(PATH_FILE));\n    }\n\n    @Test\n    public void createOrExistsDir() {\n        assertTrue(FileUtils.createOrExistsDir(PATH_FILE + \"new Dir\"));\n        assertTrue(FileUtils.createOrExistsDir(PATH_FILE));\n        assertTrue(FileUtils.delete(PATH_FILE + \"new Dir\"));\n    }\n\n    @Test\n    public void createOrExistsFile() {\n        assertTrue(FileUtils.createOrExistsFile(PATH_FILE + \"new File\"));\n        assertFalse(FileUtils.createOrExistsFile(PATH_FILE));\n        assertTrue(FileUtils.delete(PATH_FILE + \"new File\"));\n    }\n\n    @Test\n    public void createFileByDeleteOldFile() {\n        assertTrue(FileUtils.createFileByDeleteOldFile(PATH_FILE + \"new File\"));\n        assertFalse(FileUtils.createFileByDeleteOldFile(PATH_FILE));\n        assertTrue(FileUtils.delete(PATH_FILE + \"new File\"));\n    }\n\n    @Test\n    public void copyDir() {\n        assertFalse(FileUtils.copy(PATH_FILE, PATH_FILE, mListener));\n        assertFalse(FileUtils.copy(PATH_FILE, PATH_FILE + \"new Dir\", mListener));\n        assertTrue(FileUtils.copy(PATH_FILE, PATH_TEMP, mListener));\n        assertTrue(FileUtils.delete(PATH_TEMP));\n    }\n\n    @Test\n    public void copyFile() {\n        assertFalse(FileUtils.copy(PATH_FILE + \"GBK.txt\", PATH_FILE + \"GBK.txt\", mListener));\n        assertTrue(FileUtils.copy(PATH_FILE + \"GBK.txt\", PATH_FILE + \"new Dir\" + FILE_SEP + \"GBK.txt\", mListener));\n        assertTrue(FileUtils.copy(PATH_FILE + \"GBK.txt\", PATH_TEMP + \"GBK.txt\", mListener));\n        assertTrue(FileUtils.delete(PATH_FILE + \"new Dir\"));\n        assertTrue(FileUtils.delete(PATH_TEMP));\n    }\n\n    @Test\n    public void moveDir() {\n        assertFalse(FileUtils.move(PATH_FILE, PATH_FILE, mListener));\n        assertFalse(FileUtils.move(PATH_FILE, PATH_FILE + \"new Dir\", mListener));\n        assertTrue(FileUtils.move(PATH_FILE, PATH_TEMP, mListener));\n        assertTrue(FileUtils.move(PATH_TEMP, PATH_FILE, mListener));\n    }\n\n    @Test\n    public void moveFile() {\n        assertFalse(FileUtils.move(PATH_FILE + \"GBK.txt\", PATH_FILE + \"GBK.txt\", mListener));\n        assertTrue(FileUtils.move(PATH_FILE + \"GBK.txt\", PATH_TEMP + \"GBK.txt\", mListener));\n        assertTrue(FileUtils.move(PATH_TEMP + \"GBK.txt\", PATH_FILE + \"GBK.txt\", mListener));\n        FileUtils.delete(PATH_TEMP);\n    }\n\n    @Test\n    public void listFilesInDir() {\n        System.out.println(FileUtils.listFilesInDir(PATH_FILE, false).toString());\n        System.out.println(FileUtils.listFilesInDir(PATH_FILE, true).toString());\n    }\n\n    @Test\n    public void listFilesInDirWithFilter() {\n        System.out.println(FileUtils.listFilesInDirWithFilter(PATH_FILE, mFilter, false).toString());\n        System.out.println(FileUtils.listFilesInDirWithFilter(PATH_FILE, mFilter, true).toString());\n    }\n\n    @Test\n    public void getFileLastModified() {\n        System.out.println(TimeUtils.millis2String(FileUtils.getFileLastModified(PATH_FILE)));\n    }\n\n    @Test\n    public void getFileCharsetSimple() {\n        assertEquals(\"GBK\", FileUtils.getFileCharsetSimple(PATH_FILE + \"GBK.txt\"));\n        assertEquals(\"Unicode\", FileUtils.getFileCharsetSimple(PATH_FILE + \"Unicode.txt\"));\n        assertEquals(\"UTF-8\", FileUtils.getFileCharsetSimple(PATH_FILE + \"UTF8.txt\"));\n        assertEquals(\"UTF-16BE\", FileUtils.getFileCharsetSimple(PATH_FILE + \"UTF16BE.txt\"));\n    }\n\n    @Test\n    public void isUtf8() {\n        assertTrue(FileUtils.isUtf8(PATH_FILE + \"UTF8.txt\"));\n        assertFalse(FileUtils.isUtf8(PATH_FILE + \"UTF16BE.txt\"));\n        assertFalse(FileUtils.isUtf8(PATH_FILE + \"Unicode.txt\"));\n    }\n\n    @Test\n    public void getFileLines() {\n        assertEquals(7, FileUtils.getFileLines(PATH_FILE + \"UTF8.txt\"));\n    }\n\n    @Test\n    public void getDirSize() {\n        System.out.println(FileUtils.getSize(PATH_FILE));\n    }\n\n    @Test\n    public void getFileSize() {\n        System.out.println(FileUtils.getSize(PATH_FILE + \"UTF8.txt\"));\n    }\n\n    @Test\n    public void getDirLength() {\n        System.out.println(FileUtils.getLength(PATH_FILE));\n    }\n\n    @Test\n    public void getFileLength() {\n        System.out.println(FileUtils.getFileLength(PATH_FILE + \"UTF8.txt\"));\n//        System.out.println(FileUtils.getFileLength(\"https://raw.githubusercontent.com/Blankj/AndroidUtilCode/85bc44d1c8adb31027870ea4cb7a931700c80cad/LICENSE\"));\n    }\n\n    @Test\n    public void getFileMD5ToString() {\n        assertEquals(\"249D3E76851DCC56C945994DE9DAC406\", FileUtils.getFileMD5ToString(PATH_FILE + \"UTF8.txt\"));\n    }\n\n    @Test\n    public void getDirName() {\n        assertEquals(PATH_FILE, FileUtils.getDirName(new File(PATH_FILE + \"UTF8.txt\")));\n        assertEquals(PATH_FILE, FileUtils.getDirName(PATH_FILE + \"UTF8.txt\"));\n    }\n\n    @Test\n    public void getFileName() {\n        assertEquals(\"UTF8.txt\", FileUtils.getFileName(PATH_FILE + \"UTF8.txt\"));\n        assertEquals(\"UTF8.txt\", FileUtils.getFileName(new File(PATH_FILE + \"UTF8.txt\")));\n    }\n\n    @Test\n    public void getFileNameNoExtension() {\n        assertEquals(\"UTF8\", FileUtils.getFileNameNoExtension(PATH_FILE + \"UTF8.txt\"));\n        assertEquals(\"UTF8\", FileUtils.getFileNameNoExtension(new File(PATH_FILE + \"UTF8.txt\")));\n    }\n\n    @Test\n    public void getFileExtension() {\n        assertEquals(\"txt\", FileUtils.getFileExtension(new File(PATH_FILE + \"UTF8.txt\")));\n        assertEquals(\"txt\", FileUtils.getFileExtension(PATH_FILE + \"UTF8.txt\"));\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/GsonUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\r\n\r\nimport org.junit.Assert;\r\nimport org.junit.Test;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\n\r\n\r\n/**\r\n * <pre>\r\n *     author: Blankj\r\n *     blog  : http://blankj.com\r\n *     time  : 2016/09/26\r\n *     desc  : test GsonUtils\r\n * </pre>\r\n */\r\npublic class GsonUtilsTest extends BaseTest {\r\n\r\n    @Test\r\n    public void getGson() {\r\n        Assert.assertNotNull(GsonUtils.getGson());\r\n    }\r\n\r\n    @Test\r\n    public void toJson() {\r\n        Result<Person> result = new Result<>(new Person(\"Blankj\"));\r\n        Assert.assertEquals(\r\n                \"{\\\"code\\\":200,\\\"message\\\":\\\"success\\\",\\\"data\\\":{\\\"name\\\":\\\"Blankj\\\",\\\"gender\\\":0,\\\"address\\\":null}}\",\r\n                GsonUtils.toJson(result)\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void fromJson() {\r\n        List<Person> people = new ArrayList<>();\r\n        people.add(new Person(\"Blankj\"));\r\n        people.add(new Person(\"Ming\"));\r\n        Result<List<Person>> result = new Result<>(people);\r\n\r\n        Assert.assertEquals(\r\n                GsonUtils.toJson(result),\r\n                GsonUtils.toJson(\r\n                        GsonUtils.fromJson(\r\n                                GsonUtils.toJson(result),\r\n                                GsonUtils.getType(Result.class, GsonUtils.getListType(Person.class))\r\n                        )\r\n                )\r\n        );\r\n    }\r\n\r\n    @Test\r\n    public void getType() {\r\n        Assert.assertEquals(\r\n                \"java.util.List<java.lang.String>\",\r\n                GsonUtils.getListType(String.class).toString()\r\n        );\r\n        Assert.assertEquals(\r\n                \"java.util.Map<java.lang.String, java.lang.Integer>\",\r\n                GsonUtils.getMapType(String.class, Integer.class).toString()\r\n        );\r\n        Assert.assertEquals(\r\n                \"java.lang.String[]\",\r\n                GsonUtils.getArrayType(String.class).toString()\r\n        );\r\n        Assert.assertEquals(\r\n                \"com.blankj.utilcode.util.GsonUtilsTest$Result<java.lang.String>\",\r\n                GsonUtils.getType(Result.class, String.class).toString()\r\n        );\r\n        Assert.assertEquals(\r\n                \"java.util.Map<java.lang.String, java.util.List<java.lang.String>>\",\r\n                GsonUtils.getMapType(String.class, GsonUtils.getListType(String.class)).toString()\r\n        );\r\n    }\r\n\r\n    static class Result<T> {\r\n        int    code;\r\n        String message;\r\n        T      data;\r\n\r\n        Result(T data) {\r\n            this.code = 200;\r\n            this.message = \"success\";\r\n            this.data = data;\r\n        }\r\n    }\r\n\r\n    static class Person {\r\n\r\n        String name;\r\n        int    gender;\r\n        String address;\r\n\r\n        Person(String name) {\r\n            this.name = name;\r\n        }\r\n    }\r\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/ImageUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/25\n *     desc  : test ImageUtils\n * </pre>\n */\npublic class ImageUtilsTest extends BaseTest {\n\n    @Test\n    public void getImageType() {\n        Assert.assertEquals(ImageUtils.ImageType.TYPE_JPG, ImageUtils.getImageType(TestConfig.PATH_IMAGE + \"ic_launcher.jpg\"));\n        Assert.assertEquals(ImageUtils.ImageType.TYPE_PNG, ImageUtils.getImageType(TestConfig.PATH_IMAGE + \"ic_launcher.png\"));\n        Assert.assertEquals(ImageUtils.ImageType.TYPE_GIF, ImageUtils.getImageType(TestConfig.PATH_IMAGE + \"ic_launcher.gif\"));\n        Assert.assertEquals(ImageUtils.ImageType.TYPE_TIFF, ImageUtils.getImageType(TestConfig.PATH_IMAGE + \"ic_launcher.tif\"));\n        Assert.assertEquals(ImageUtils.ImageType.TYPE_BMP, ImageUtils.getImageType(TestConfig.PATH_IMAGE + \"ic_launcher.bmp\"));\n        Assert.assertEquals(ImageUtils.ImageType.TYPE_WEBP, ImageUtils.getImageType(TestConfig.PATH_IMAGE + \"ic_launcher.webp\"));\n        Assert.assertEquals(ImageUtils.ImageType.TYPE_ICO, ImageUtils.getImageType(TestConfig.PATH_IMAGE + \"ic_launcher.ico\"));\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/LogUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport org.junit.Test;\n\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/09/26\n *     desc  : test LogUtils\n * </pre>\n */\npublic class LogUtilsTest extends BaseTest {\n\n    private static final String              JSON        = \"\\r\\n{\\\"tools\\\": [{ \\\"name\\\":\\\"css format\\\" , \\\"site\\\":\\\"http://tools.w3cschool.cn/code/css\\\" },{ \\\"name\\\":\\\"JSON format\\\" , \\\"site\\\":\\\"http://tools.w3cschool.cn/code/JSON\\\" },{ \\\"name\\\":\\\"pwd check\\\" , \\\"site\\\":\\\"http://tools.w3cschool.cn/password/my_password_safe\\\" }]}\";\n    private static final String              XML         = \"<books><book><author>Jack Herrington</author><title>PHP Hacks</title><publisher>O'Reilly</publisher></book><book><author>Jack Herrington</author><title>Podcasting Hacks</title><publisher>O'Reilly</publisher></book></books>\";\n    private static final int[]               ONE_D_ARRAY = new int[]{1, 2, 3};\n    private static final int[][]             TWO_D_ARRAY = new int[][]{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};\n    private static final ArrayList<String>   LIST        = new ArrayList<>();\n    private static final Map<String, Object> MAP         = new HashMap<>();\n\n    @Test\n    public void testV() {\n        LogUtils.v();\n        LogUtils.v(\"\");\n        LogUtils.v((Object) null);\n        LogUtils.v(\"hello\");\n        LogUtils.v(\"hello\\nworld\");\n        LogUtils.v(\"hello\", \"world\");\n    }\n\n    @Test\n    public void testVTag() {\n        LogUtils.vTag(\"\");\n        LogUtils.vTag(\"\", \"\");\n        LogUtils.vTag(\"TAG\", (Object) null);\n        LogUtils.vTag(\"TAG\", \"hello\");\n        LogUtils.vTag(\"TAG\", \"hello\\nworld\");\n        LogUtils.vTag(\"TAG\", \"hello\", \"world\");\n    }\n\n    @Test\n    public void testD() {\n        LogUtils.d();\n        LogUtils.d(\"\");\n        LogUtils.d((Object) null);\n        LogUtils.d(\"hello\");\n        LogUtils.d(\"hello\\nworld\");\n        LogUtils.d(\"hello\", \"world\");\n    }\n\n    @Test\n    public void testDTag() {\n        LogUtils.dTag(\"\");\n        LogUtils.dTag(\"\", \"\");\n        LogUtils.dTag(\"TAG\", (Object) null);\n        LogUtils.dTag(\"TAG\", \"hello\");\n        LogUtils.dTag(\"TAG\", \"hello\\nworld\");\n        LogUtils.dTag(\"TAG\", \"hello\", \"world\");\n    }\n\n    @Test\n    public void testI() {\n        LogUtils.i();\n        LogUtils.i(\"\");\n        LogUtils.i((Object) null);\n        LogUtils.i(\"hello\");\n        LogUtils.i(\"hello\\nworld\");\n        LogUtils.i(\"hello\", \"world\");\n    }\n\n    @Test\n    public void testITag() {\n        LogUtils.iTag(\"\");\n        LogUtils.iTag(\"\", \"\");\n        LogUtils.iTag(\"TAG\", (Object) null);\n        LogUtils.iTag(\"TAG\", \"hello\");\n        LogUtils.iTag(\"TAG\", \"hello\\nworld\");\n        LogUtils.iTag(\"TAG\", \"hello\", \"world\");\n    }\n\n    @Test\n    public void testW() {\n        LogUtils.w();\n        LogUtils.w(\"\");\n        LogUtils.w((Object) null);\n        LogUtils.w(\"hello\");\n        LogUtils.w(\"hello\\nworld\");\n        LogUtils.w(\"hello\", \"world\");\n    }\n\n    @Test\n    public void testWTag() {\n        LogUtils.wTag(\"\");\n        LogUtils.wTag(\"\", \"\");\n        LogUtils.wTag(\"TAG\", (Object) null);\n        LogUtils.wTag(\"TAG\", \"hello\");\n        LogUtils.wTag(\"TAG\", \"hello\\nworld\");\n        LogUtils.wTag(\"TAG\", \"hello\", \"world\");\n    }\n\n    @Test\n    public void testE() {\n        LogUtils.e();\n        LogUtils.e(\"\");\n        LogUtils.e((Object) null);\n        LogUtils.e(\"hello\");\n        LogUtils.e(\"hello\\nworld\");\n        LogUtils.e(\"hello\", \"world\");\n    }\n\n    @Test\n    public void testETag() {\n        LogUtils.eTag(\"\");\n        LogUtils.eTag(\"\", \"\");\n        LogUtils.eTag(\"TAG\", (Object) null);\n        LogUtils.eTag(\"TAG\", \"hello\");\n        LogUtils.eTag(\"TAG\", \"hello\\nworld\");\n        LogUtils.eTag(\"TAG\", \"hello\", \"world\");\n    }\n\n    @Test\n    public void testA() {\n        LogUtils.a();\n        LogUtils.a(\"\");\n        LogUtils.a((Object) null);\n        LogUtils.a(\"hello\");\n        LogUtils.a(\"hello\\nworld\");\n        LogUtils.a(\"hello\", \"world\");\n    }\n\n    @Test\n    public void testATag() {\n        LogUtils.aTag(\"\");\n        LogUtils.aTag(\"\", \"\");\n        LogUtils.aTag(\"TAG\", (Object) null);\n        LogUtils.aTag(\"TAG\", \"hello\");\n        LogUtils.aTag(\"TAG\", \"hello\\nworld\");\n        LogUtils.aTag(\"TAG\", \"hello\", \"world\");\n    }\n\n    @Test\n    public void testJson() {\n        LogUtils.json(JSON);\n        LogUtils.json(new Person(\"Blankj\"));\n    }\n\n    @Test\n    public void testXml() {\n        LogUtils.xml(XML);\n    }\n\n    @Test\n    public void testObject() {\n        LIST.add(\"hello\");\n        LIST.add(\"log\");\n        LIST.add(\"utils\");\n\n        MAP.put(\"name\", \"AndroidUtilCode\");\n        MAP.put(\"class\", \"LogUtils\");\n        LogUtils.d((Object) ONE_D_ARRAY);\n        LogUtils.d((Object) TWO_D_ARRAY);\n        LogUtils.d(LIST);\n        LogUtils.d(MAP);\n    }\n\n    static class Person {\n\n        String name;\n        int    gender;\n        String address;\n\n        public Person(String name) {\n            this.name = name;\n        }\n\n        @Override\n        public boolean equals(Object obj) {\n            if (obj == this) return true;\n            if (!(obj instanceof Person)) return false;\n            Person p = (Person) obj;\n            return equals(name, p.name) && p.gender == gender && equals(address, p.address);\n        }\n\n        private static boolean equals(final Object o1, final Object o2) {\n            return o1 == o2 || (o1 != null && o1.equals(o2));\n        }\n\n        @Override\n        public String toString() {\n            return \"{\\\"name\\\":\" + primitive2String(name) +\n                    \",\\\"gender\\\":\" + primitive2String(gender) +\n                    \",\\\"address\\\":\" + primitive2String(address) + \"}\";\n        }\n    }\n\n    private static String primitive2String(final Object obj) {\n        if (obj == null) return \"null\";\n        if (obj instanceof CharSequence) return \"\\\"\" + obj.toString() + \"\\\"\";\n        return obj.toString();\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/MapUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.util.Pair;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\nimport java.util.Comparator;\nimport java.util.Map;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/14\n *     desc  : test MapUtils\n * </pre>\n */\npublic class MapUtilsTest extends BaseTest {\n\n    @Test\n    public void newUnmodifiableMap() {\n        System.out.println(MapUtils.newUnmodifiableMap(\n                Pair.create(0, \"0\"),\n                Pair.create(1, \"1\"),\n                Pair.create(2, \"2\"),\n                Pair.create(3, \"3\")\n        ));\n    }\n\n    @Test\n    public void newHashMap() {\n        System.out.println(MapUtils.newHashMap(\n                Pair.create(0, \"0\"),\n                Pair.create(1, \"1\"),\n                Pair.create(2, \"2\"),\n                Pair.create(3, \"3\")\n        ));\n    }\n\n    @Test\n    public void newLinkedHashMap() {\n        System.out.println(MapUtils.newLinkedHashMap(\n                Pair.create(0, \"0\"),\n                Pair.create(1, \"1\"),\n                Pair.create(2, \"2\"),\n                Pair.create(3, \"3\")\n        ));\n    }\n\n    @Test\n    public void newTreeMap() {\n        System.out.println(MapUtils.newTreeMap(\n                new Comparator<Integer>() {\n                    @Override\n                    public int compare(Integer o1, Integer o2) {\n                        return o2 - o1;\n                    }\n                },\n                Pair.create(0, \"0\"),\n                Pair.create(1, \"1\"),\n                Pair.create(2, \"2\"),\n                Pair.create(3, \"3\")\n        ));\n    }\n\n    @Test\n    public void newHashTable() {\n        System.out.println(MapUtils.newHashTable(\n                Pair.create(0, \"0\"),\n                Pair.create(1, \"1\"),\n                Pair.create(2, \"2\"),\n                Pair.create(3, \"3\")\n        ));\n    }\n\n    @Test\n    public void isEmpty() {\n        Assert.assertTrue(MapUtils.isEmpty(null));\n        Assert.assertTrue(MapUtils.isEmpty(MapUtils.newHashMap()));\n        Assert.assertFalse(MapUtils.isEmpty(MapUtils.newHashMap(Pair.create(0, 0))));\n    }\n\n    @Test\n    public void isNotEmpty() {\n        Assert.assertFalse(MapUtils.isNotEmpty(null));\n        Assert.assertFalse(MapUtils.isNotEmpty(MapUtils.newHashMap()));\n        Assert.assertTrue(MapUtils.isNotEmpty(MapUtils.newHashMap(Pair.create(0, 0))));\n    }\n\n    @Test\n    public void size() {\n        Assert.assertEquals(0, MapUtils.size(null));\n        Assert.assertEquals(0, MapUtils.size(MapUtils.newHashMap()));\n        Assert.assertEquals(1, MapUtils.size(MapUtils.newHashMap(Pair.create(0, 0))));\n    }\n\n    @Test\n    public void forAllDo() {\n        MapUtils.forAllDo(\n                MapUtils.newHashMap(\n                        Pair.create(0, \"0\"),\n                        Pair.create(1, \"1\"),\n                        Pair.create(2, \"2\"),\n                        Pair.create(3, \"3\")\n                ), new MapUtils.Closure<Integer, String>() {\n                    @Override\n                    public void execute(Integer key, String value) {\n                        System.out.println(key + \"=\" + value);\n                    }\n                });\n    }\n\n    @Test\n    public void transform() {\n        Map<String, String> transform = MapUtils.transform(\n                MapUtils.newHashMap(\n                        Pair.create(0, \"0\"),\n                        Pair.create(1, \"1\"),\n                        Pair.create(2, \"2\"),\n                        Pair.create(3, \"3\")\n                ),\n                new MapUtils.Transformer<Integer, String, String, String>() {\n                    @Override\n                    public Pair<String, String> transform(Integer integer, String s) {\n                        return Pair.create(\"StringKey\" + integer, s);\n                    }\n                }\n        );\n        System.out.println(transform);\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/NumberUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport org.junit.Assert;\nimport org.junit.Test;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2020/04/13\n *     desc  : test NumberUtils\n * </pre>\n */\npublic class NumberUtilsTest {\n\n    @Test\n    public void format() {\n        double val = Math.PI * 100000;// 314159.2653589793\n\n        Assert.assertEquals(\"314159.27\", NumberUtils.format(val, 2));\n        Assert.assertEquals(\"314159.265\", NumberUtils.format(val, 3));\n\n        Assert.assertEquals(\"314159.27\", NumberUtils.format(val, 2, true));\n        Assert.assertEquals(\"314159.26\", NumberUtils.format(val, 2, false));\n\n        Assert.assertEquals(\"00314159.27\", NumberUtils.format(val, 8, 2, true));\n        Assert.assertEquals(\"0000314159.27\", NumberUtils.format(val, 10, 2, true));\n\n        Assert.assertEquals(\"314,159.27\", NumberUtils.format(val, true, 2));\n        Assert.assertEquals(\"314159.27\", NumberUtils.format(val, false, 2));\n\n        Assert.assertEquals(\"314159.27\", NumberUtils.format(val, false, 2, true));\n        Assert.assertEquals(\"314159.26\", NumberUtils.format(val, false, 2, false));\n\n        Assert.assertEquals(\"314159.27\", NumberUtils.format(val, false, 2, true));\n        Assert.assertEquals(\"314159.265\", NumberUtils.format(val, false, 3, false));\n    }\n\n    @Test\n    public void float2Double() {\n        float val = 3.14f;\n        System.out.println((double) val);\n        System.out.println(NumberUtils.float2Double(val));\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/ObjectUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport android.util.SparseArray;\nimport android.util.SparseBooleanArray;\nimport android.util.SparseIntArray;\nimport android.util.SparseLongArray;\n\nimport org.junit.Test;\n\nimport java.util.HashMap;\nimport java.util.LinkedList;\n\nimport androidx.collection.LongSparseArray;\nimport androidx.collection.SimpleArrayMap;\n\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/12/24\n *     desc  : test ObjectUtils\n * </pre>\n */\npublic class ObjectUtilsTest extends BaseTest {\n\n    @Test\n    public void isEmpty() {\n        StringBuilder sb = new StringBuilder(\"\");\n        StringBuilder sb1 = new StringBuilder(\" \");\n        String string = \"\";\n        String string1 = \" \";\n        int[][] arr = new int[][]{};\n        LinkedList<Integer> list = new LinkedList<>();\n        HashMap<String, Integer> map = new HashMap<>();\n        SimpleArrayMap<String, Integer> sam = new SimpleArrayMap<>();\n        SparseArray<String> sa = new SparseArray<>();\n        SparseBooleanArray sba = new SparseBooleanArray();\n        SparseIntArray sia = new SparseIntArray();\n        SparseLongArray sla = new SparseLongArray();\n        LongSparseArray<String> lsa = new LongSparseArray<>();\n        android.util.LongSparseArray<String> lsaV4 = new android.util.LongSparseArray<>();\n\n        assertTrue(ObjectUtils.isEmpty(sb));\n        assertFalse(ObjectUtils.isEmpty(sb1));\n        assertTrue(ObjectUtils.isEmpty(string));\n        assertFalse(ObjectUtils.isEmpty(string1));\n        assertTrue(ObjectUtils.isEmpty(arr));\n        assertTrue(ObjectUtils.isEmpty(list));\n        assertTrue(ObjectUtils.isEmpty(map));\n        assertTrue(ObjectUtils.isEmpty(sam));\n        assertTrue(ObjectUtils.isEmpty(sa));\n        assertTrue(ObjectUtils.isEmpty(sba));\n        assertTrue(ObjectUtils.isEmpty(sia));\n        assertTrue(ObjectUtils.isEmpty(sla));\n        assertTrue(ObjectUtils.isEmpty(lsa));\n        assertTrue(ObjectUtils.isEmpty(lsaV4));\n\n        assertTrue(!ObjectUtils.isNotEmpty(sb));\n        assertFalse(!ObjectUtils.isNotEmpty(sb1));\n        assertTrue(!ObjectUtils.isNotEmpty(string));\n        assertFalse(!ObjectUtils.isNotEmpty(string1));\n        assertTrue(!ObjectUtils.isNotEmpty(arr));\n        assertTrue(!ObjectUtils.isNotEmpty(list));\n        assertTrue(!ObjectUtils.isNotEmpty(map));\n        assertTrue(!ObjectUtils.isNotEmpty(sam));\n        assertTrue(!ObjectUtils.isNotEmpty(sa));\n        assertTrue(!ObjectUtils.isNotEmpty(sba));\n        assertTrue(!ObjectUtils.isNotEmpty(sia));\n        assertTrue(!ObjectUtils.isNotEmpty(sla));\n        assertTrue(!ObjectUtils.isNotEmpty(lsa));\n        assertTrue(!ObjectUtils.isNotEmpty(lsaV4));\n    }\n\n    @Test\n    public void equals() {\n        assertTrue(ObjectUtils.equals(1, 1));\n        assertTrue(ObjectUtils.equals(\"str\", \"str\"));\n        assertTrue(ObjectUtils.equals(null, null));\n\n        assertFalse(ObjectUtils.equals(null, 1));\n        assertFalse(ObjectUtils.equals(null, \"\"));\n    }\n\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/PathUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.assertEquals;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2020/04/09\n *     desc  :\n * </pre>\n */\npublic class PathUtilsTest extends BaseTest {\n\n    @Test\n    public void join() {\n        assertEquals(PathUtils.join(\"\", \"\"), \"\");\n        assertEquals(PathUtils.join(\"\", \"data\"), \"/data\");\n\n        assertEquals(PathUtils.join(\"\", \"//data\"), \"/data\");\n        assertEquals(PathUtils.join(\"\", \"data//\"), \"/data\");\n        assertEquals(PathUtils.join(\"\", \"//data//\"), \"/data\");\n\n        assertEquals(PathUtils.join(\"/sdcard\", \"data\"), \"/sdcard/data\");\n        assertEquals(PathUtils.join(\"/sdcard/\", \"data\"), \"/sdcard/data\");\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/RegexUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\r\n\r\nimport com.blankj.utilcode.constant.RegexConstants;\r\n\r\nimport org.junit.Test;\r\n\r\nimport java.util.Arrays;\r\n\r\nimport static org.junit.Assert.assertFalse;\r\nimport static org.junit.Assert.assertTrue;\r\n\r\n/**\r\n * <pre>\r\n *     author: Blankj\r\n *     blog  : http://blankj.com\r\n *     time  : 2016/08/16\r\n *     desc  : test RegexUtils\r\n * </pre>\r\n */\r\npublic class RegexUtilsTest extends BaseTest {\r\n\r\n    @Test\r\n    public void isMobileSimple() {\r\n        assertTrue(RegexUtils.isMobileSimple(\"11111111111\"));\r\n    }\r\n\r\n    @Test\r\n    public void isMobileExact() {\r\n        assertFalse(RegexUtils.isMobileExact(\"11111111111\"));\r\n        assertTrue(RegexUtils.isMobileExact(\"13888880000\"));\r\n        assertTrue(RegexUtils.isMobileExact(\"12088880000\", CollectionUtils.newArrayList(\"120\")));\r\n    }\r\n\r\n    @Test\r\n    public void isTel() {\r\n        assertTrue(RegexUtils.isTel(\"033-88888888\"));\r\n        assertTrue(RegexUtils.isTel(\"033-7777777\"));\r\n        assertTrue(RegexUtils.isTel(\"0444-88888888\"));\r\n        assertTrue(RegexUtils.isTel(\"0444-7777777\"));\r\n        assertTrue(RegexUtils.isTel(\"033 88888888\"));\r\n        assertTrue(RegexUtils.isTel(\"033 7777777\"));\r\n        assertTrue(RegexUtils.isTel(\"0444 88888888\"));\r\n        assertTrue(RegexUtils.isTel(\"0444 7777777\"));\r\n        assertTrue(RegexUtils.isTel(\"03388888888\"));\r\n        assertTrue(RegexUtils.isTel(\"0337777777\"));\r\n        assertTrue(RegexUtils.isTel(\"044488888888\"));\r\n        assertTrue(RegexUtils.isTel(\"04447777777\"));\r\n\r\n        assertFalse(RegexUtils.isTel(\"133-88888888\"));\r\n        assertFalse(RegexUtils.isTel(\"033-666666\"));\r\n        assertFalse(RegexUtils.isTel(\"0444-999999999\"));\r\n    }\r\n\r\n    @Test\r\n    public void isIDCard18() {\r\n        assertTrue(RegexUtils.isIDCard18(\"33698418400112523x\"));\r\n        assertTrue(RegexUtils.isIDCard18(\"336984184001125233\"));\r\n        assertFalse(RegexUtils.isIDCard18(\"336984184021125233\"));\r\n    }\r\n\r\n    @Test\r\n    public void isIDCard18Exact() {\r\n        assertFalse(RegexUtils.isIDCard18Exact(\"33698418400112523x\"));\r\n        assertTrue(RegexUtils.isIDCard18Exact(\"336984184001125233\"));\r\n        assertFalse(RegexUtils.isIDCard18Exact(\"336984184021125233\"));\r\n    }\r\n\r\n    @Test\r\n    public void isEmail() {\r\n        assertTrue(RegexUtils.isEmail(\"blankj@qq.com\"));\r\n        assertFalse(RegexUtils.isEmail(\"blankj@qq\"));\r\n    }\r\n\r\n    @Test\r\n    public void isURL() {\r\n        assertTrue(RegexUtils.isURL(\"http://blankj.com\"));\r\n        assertFalse(RegexUtils.isURL(\"https:blank\"));\r\n    }\r\n\r\n    @Test\r\n    public void isZh() {\r\n        assertTrue(RegexUtils.isZh(\"我\"));\r\n        assertFalse(RegexUtils.isZh(\"wo\"));\r\n    }\r\n\r\n    @Test\r\n    public void isUsername() {\r\n        assertTrue(RegexUtils.isUsername(\"小明233333\"));\r\n        assertFalse(RegexUtils.isUsername(\"小明\"));\r\n        assertFalse(RegexUtils.isUsername(\"小明233333_\"));\r\n    }\r\n\r\n    @Test\r\n    public void isDate() {\r\n        assertTrue(RegexUtils.isDate(\"2016-08-16\"));\r\n        assertTrue(RegexUtils.isDate(\"2016-02-29\"));\r\n        assertFalse(RegexUtils.isDate(\"2015-02-29\"));\r\n        assertFalse(RegexUtils.isDate(\"2016-8-16\"));\r\n    }\r\n\r\n    @Test\r\n    public void isIP() {\r\n        assertTrue(RegexUtils.isIP(\"255.255.255.0\"));\r\n        assertFalse(RegexUtils.isIP(\"256.255.255.0\"));\r\n    }\r\n\r\n    @Test\r\n    public void isMatch() {\r\n        assertTrue(RegexUtils.isMatch(\"\\\\d?\", \"1\"));\r\n        assertFalse(RegexUtils.isMatch(\"\\\\d?\", \"a\"));\r\n    }\r\n\r\n    @Test\r\n    public void getMatches() {\r\n        // 贪婪\r\n        System.out.println(RegexUtils.getMatches(\"b.*j\", \"blankj blankj\"));\r\n        // 懒惰\r\n        System.out.println(RegexUtils.getMatches(\"b.*?j\", \"blankj blankj\"));\r\n    }\r\n\r\n    @Test\r\n    public void getSplits() {\r\n        System.out.println(Arrays.asList(RegexUtils.getSplits(\"1 2 3\", \" \")));\r\n    }\r\n\r\n    @Test\r\n    public void getReplaceFirst() {\r\n        System.out.println(RegexUtils.getReplaceFirst(\"1 2 3\", \" \", \", \"));\r\n    }\r\n\r\n    @Test\r\n    public void getReplaceAll() {\r\n        System.out.println(RegexUtils.getReplaceAll(\"1 2 3\", \" \", \", \"));\r\n    }\r\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/StringUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\r\n\r\nimport org.junit.Test;\r\n\r\nimport static org.junit.Assert.assertEquals;\r\nimport static org.junit.Assert.assertFalse;\r\nimport static org.junit.Assert.assertTrue;\r\n\r\n/**\r\n * <pre>\r\n *     author: Blankj\r\n *     blog  : http://blankj.com\r\n *     time  : 2016/08/16\r\n *     desc  : test StringUtils\r\n * </pre>\r\n */\r\npublic class StringUtilsTest extends BaseTest {\r\n\r\n    @Test\r\n    public void isEmpty() {\r\n        assertTrue(StringUtils.isEmpty(\"\"));\r\n        assertTrue(StringUtils.isEmpty(null));\r\n        assertFalse(StringUtils.isEmpty(\" \"));\r\n    }\r\n\r\n    @Test\r\n    public void isTrimEmpty() {\r\n        assertTrue(StringUtils.isTrimEmpty(\"\"));\r\n        assertTrue(StringUtils.isTrimEmpty(null));\r\n        assertTrue(StringUtils.isTrimEmpty(\" \"));\r\n    }\r\n\r\n    @Test\r\n    public void isSpace() {\r\n        assertTrue(StringUtils.isSpace(\"\"));\r\n        assertTrue(StringUtils.isSpace(null));\r\n        assertTrue(StringUtils.isSpace(\" \"));\r\n        assertTrue(StringUtils.isSpace(\"　 \\n\\t\\r\"));\r\n    }\r\n\r\n    @Test\r\n    public void equals() {\r\n        assertTrue(StringUtils.equals(null, null));\r\n        assertTrue(StringUtils.equals(\"blankj\", \"blankj\"));\r\n        assertFalse(StringUtils.equals(\"blankj\", \"Blankj\"));\r\n    }\r\n\r\n    @Test\r\n    public void equalsIgnoreCase() {\r\n        assertTrue(StringUtils.equalsIgnoreCase(null, null));\r\n        assertFalse(StringUtils.equalsIgnoreCase(null, \"blankj\"));\r\n        assertTrue(StringUtils.equalsIgnoreCase(\"blankj\", \"Blankj\"));\r\n        assertTrue(StringUtils.equalsIgnoreCase(\"blankj\", \"blankj\"));\r\n        assertFalse(StringUtils.equalsIgnoreCase(\"blankj\", \"blank\"));\r\n    }\r\n\r\n    @Test\r\n    public void null2Length0() {\r\n        assertEquals(\"\", StringUtils.null2Length0(null));\r\n    }\r\n\r\n    @Test\r\n    public void length() {\r\n        assertEquals(0, StringUtils.length(null));\r\n        assertEquals(0, StringUtils.length(\"\"));\r\n        assertEquals(6, StringUtils.length(\"blankj\"));\r\n    }\r\n\r\n    @Test\r\n    public void upperFirstLetter() {\r\n        assertEquals(\"Blankj\", StringUtils.upperFirstLetter(\"blankj\"));\r\n        assertEquals(\"Blankj\", StringUtils.upperFirstLetter(\"Blankj\"));\r\n        assertEquals(\"1Blankj\", StringUtils.upperFirstLetter(\"1Blankj\"));\r\n    }\r\n\r\n    @Test\r\n    public void lowerFirstLetter() {\r\n        assertEquals(\"blankj\", StringUtils.lowerFirstLetter(\"blankj\"));\r\n        assertEquals(\"blankj\", StringUtils.lowerFirstLetter(\"Blankj\"));\r\n        assertEquals(\"1blankj\", StringUtils.lowerFirstLetter(\"1blankj\"));\r\n    }\r\n\r\n    @Test\r\n    public void reverse() {\r\n        assertEquals(\"jknalb\", StringUtils.reverse(\"blankj\"));\r\n        assertEquals(\"knalb\", StringUtils.reverse(\"blank\"));\r\n        assertEquals(\"文中试测\", StringUtils.reverse(\"测试中文\"));\r\n        assertEquals(\"\", StringUtils.reverse(null));\r\n    }\r\n\r\n    @Test\r\n    public void toDBC() {\r\n        assertEquals(\" ,.&\", StringUtils.toDBC(\"　，．＆\"));\r\n    }\r\n\r\n    @Test\r\n    public void toSBC() {\r\n        assertEquals(\"　，．＆\", StringUtils.toSBC(\" ,.&\"));\r\n    }\r\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/TestConfig.java",
    "content": "package com.blankj.utilcode.util;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/09/10\n *     desc  : config of test\n * </pre>\n */\npublic class TestConfig {\n\n    static final String FILE_SEP = System.getProperty(\"file.separator\");\n\n    static final String LINE_SEP = System.getProperty(\"line.separator\");\n\n    static final String TEST_PATH;\n\n    static {\n        String projectPath = System.getProperty(\"user.dir\");\n        if (!projectPath.contains(\"utilcode\")) {\n            projectPath += FILE_SEP + \"utilcode\";\n        }\n        TEST_PATH = projectPath + FILE_SEP + \"src\" + FILE_SEP + \"test\" + FILE_SEP + \"res\" + FILE_SEP;\n    }\n\n    static final String PATH_TEMP = TEST_PATH + \"temp\" + FILE_SEP;\n\n    static final String PATH_CACHE = TEST_PATH + \"cache\" + FILE_SEP;\n\n    static final String PATH_ENCRYPT = TEST_PATH + \"encrypt\" + FILE_SEP;\n\n    static final String PATH_FILE = TEST_PATH + \"file\" + FILE_SEP;\n\n    static final String PATH_IMAGE = TEST_PATH + \"image\" + FILE_SEP;\n\n    static final String PATH_ZIP = TEST_PATH + \"zip\" + FILE_SEP;\n}\n"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/ThreadUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport org.junit.Test;\n\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/05/21\n *     desc  :\n * </pre>\n */\npublic class ThreadUtilsTest extends BaseTest {\n\n    @Test\n    public void executeByFixed() throws Exception {\n        asyncTest(10, new TestRunnable<String>() {\n            @Override\n            public void run(final int index, CountDownLatch latch) {\n                final TestTask<String> task = new TestTask<String>(latch) {\n                    @Override\n                    public String doInBackground() throws Throwable {\n                        new ThreadGroup(\"name\");\n                        Thread.sleep(500 + index * 10);\n                        if (index < 4) {\n                            return Thread.currentThread() + \" :\" + index;\n                        } else if (index < 7) {\n                            cancel();\n                            return null;\n                        } else {\n                            throw new NullPointerException(String.valueOf(index));\n                        }\n                    }\n\n                    @Override\n                    void onTestSuccess(String result) {\n                        System.out.println(result);\n                    }\n                };\n                ThreadUtils.executeByFixed(3, task);\n            }\n        });\n    }\n\n    @Test\n    public void executeByFixedWithDelay() throws Exception {\n        asyncTest(10, new TestRunnable<String>() {\n            @Override\n            public void run(final int index, CountDownLatch latch) {\n                final TestTask<String> task = new TestTask<String>(latch) {\n                    @Override\n                    public String doInBackground() throws Throwable {\n                        Thread.sleep(500);\n                        if (index < 4) {\n                            return Thread.currentThread() + \" :\" + index;\n                        } else if (index < 7) {\n                            cancel();\n                            return null;\n                        } else {\n                            throw new NullPointerException(String.valueOf(index));\n                        }\n                    }\n\n                    @Override\n                    void onTestSuccess(String result) {\n                        System.out.println(result);\n                    }\n                };\n                ThreadUtils.executeByFixedWithDelay(3, task, 500 + index * 10, TimeUnit.MILLISECONDS);\n            }\n        });\n    }\n\n    @Test\n    public void executeByFixedAtFixRate() throws Exception {\n        asyncTest(10, new TestRunnable<String>() {\n            @Override\n            public void run(final int index, CountDownLatch latch) {\n                final TestScheduledTask<String> task = new TestScheduledTask<String>(latch, 3) {\n                    @Override\n                    public String doInBackground() throws Throwable {\n                        Thread.sleep(500 + index * 10);\n                        if (index < 4) {\n                            return Thread.currentThread() + \" :\" + index;\n                        } else if (index < 7) {\n                            cancel();\n                            return null;\n                        } else {\n                            throw new NullPointerException(String.valueOf(index));\n                        }\n                    }\n\n                    @Override\n                    void onTestSuccess(String result) {\n                        System.out.println(result);\n                    }\n                };\n                ThreadUtils.executeByFixedAtFixRate(3, task, 3000 + index * 10, TimeUnit.MILLISECONDS);\n            }\n        });\n    }\n\n    @Test\n    public void executeBySingle() throws Exception {\n        asyncTest(10, new TestRunnable<String>() {\n            @Override\n            public void run(final int index, CountDownLatch latch) {\n                final TestTask<String> task = new TestTask<String>(latch) {\n                    @Override\n                    public String doInBackground() throws Throwable {\n                        Thread.sleep(200);\n                        if (index < 4) {\n                            return Thread.currentThread() + \" :\" + index;\n                        } else if (index < 7) {\n                            cancel();\n                            return null;\n                        } else {\n                            throw new NullPointerException(String.valueOf(index));\n                        }\n                    }\n\n                    @Override\n                    void onTestSuccess(String result) {\n                        System.out.println(result);\n                    }\n                };\n                ThreadUtils.executeBySingle(task);\n            }\n        });\n    }\n\n    @Test\n    public void executeBySingleWithDelay() throws Exception {\n        asyncTest(10, new TestRunnable<String>() {\n            @Override\n            public void run(final int index, CountDownLatch latch) {\n                final TestTask<String> task = new TestTask<String>(latch) {\n                    @Override\n                    public String doInBackground() throws Throwable {\n                        Thread.sleep(500);\n                        if (index < 4) {\n                            return Thread.currentThread() + \" :\" + index;\n                        } else if (index < 7) {\n                            cancel();\n                            return null;\n                        } else {\n                            throw new NullPointerException(String.valueOf(index));\n                        }\n                    }\n\n                    @Override\n                    void onTestSuccess(String result) {\n                        System.out.println(result);\n                    }\n                };\n                ThreadUtils.executeBySingleWithDelay(task, 500 + index * 10, TimeUnit.MILLISECONDS);\n            }\n        });\n    }\n\n    @Test\n    public void executeBySingleAtFixRate() throws Exception {\n        asyncTest(10, new TestRunnable<String>() {\n            @Override\n            public void run(final int index, CountDownLatch latch) {\n                final TestScheduledTask<String> task = new TestScheduledTask<String>(latch, 3) {\n                    @Override\n                    public String doInBackground() throws Throwable {\n                        Thread.sleep(100 + index * 10);\n                        if (index < 4) {\n                            return Thread.currentThread() + \" :\" + index;\n                        } else if (index < 7) {\n                            cancel();\n                            return null;\n                        } else {\n                            throw new NullPointerException(String.valueOf(index));\n                        }\n                    }\n\n                    @Override\n                    void onTestSuccess(String result) {\n                        System.out.println(result);\n                    }\n                };\n                ThreadUtils.executeBySingleAtFixRate(task, 2000 + index * 10, TimeUnit.MILLISECONDS);\n            }\n        });\n    }\n\n    @Test\n    public void executeByIo() throws Exception {\n        asyncTest(10, new TestRunnable<String>() {\n            @Override\n            public void run(final int index, CountDownLatch latch) {\n                final TestTask<String> task = new TestTask<String>(latch) {\n                    @Override\n                    public String doInBackground() throws Throwable {\n                        Thread.sleep(500 + index * 10);\n                        if (index < 4) {\n                            return Thread.currentThread() + \" :\" + index;\n                        } else if (index < 7) {\n                            cancel();\n                            return null;\n                        } else {\n                            throw new NullPointerException(String.valueOf(index));\n                        }\n                    }\n\n                    @Override\n                    void onTestSuccess(String result) {\n                        System.out.println(result);\n                    }\n                };\n                ThreadUtils.executeByIo(task);\n            }\n        });\n    }\n\n    @Test\n    public void executeByIoWithDelay() throws Exception {\n        asyncTest(10, new TestRunnable<String>() {\n            @Override\n            public void run(final int index, CountDownLatch latch) {\n                final TestTask<String> task = new TestTask<String>(latch) {\n                    @Override\n                    public String doInBackground() throws Throwable {\n                        Thread.sleep(500);\n                        if (index < 4) {\n                            return Thread.currentThread() + \" :\" + index;\n                        } else if (index < 7) {\n                            cancel();\n                            return null;\n                        } else {\n                            throw new NullPointerException(String.valueOf(index));\n                        }\n                    }\n\n                    @Override\n                    void onTestSuccess(String result) {\n                        System.out.println(result);\n                    }\n                };\n                ThreadUtils.executeByIoWithDelay(task, 500 + index * 10, TimeUnit.MILLISECONDS);\n            }\n        });\n    }\n\n    @Test\n    public void executeByIoAtFixRate() throws Exception {\n        asyncTest(10, new TestRunnable<String>() {\n            @Override\n            public void run(final int index, CountDownLatch latch) {\n                final TestScheduledTask<String> task = new TestScheduledTask<String>(latch, 3) {\n                    @Override\n                    public String doInBackground() throws Throwable {\n                        Thread.sleep(100 + index * 10);\n                        if (index < 4) {\n                            return Thread.currentThread() + \" :\" + index;\n                        } else if (index < 7) {\n                            cancel();\n                            return null;\n                        } else {\n                            throw new NullPointerException(String.valueOf(index));\n                        }\n                    }\n\n                    @Override\n                    void onTestSuccess(String result) {\n                        System.out.println(result);\n                    }\n                };\n                ThreadUtils.executeByIoAtFixRate(task, 1000, TimeUnit.MILLISECONDS);\n            }\n        });\n    }\n\n    @Test\n    public void executeByCpu() throws Exception {\n        asyncTest(10, new TestRunnable<String>() {\n            @Override\n            public void run(final int index, CountDownLatch latch) {\n                final TestTask<String> task = new TestTask<String>(latch) {\n                    @Override\n                    public String doInBackground() throws Throwable {\n                        Thread.sleep(500 + index * 10);\n                        if (index < 4) {\n                            return Thread.currentThread() + \" :\" + index;\n                        } else if (index < 7) {\n                            cancel();\n                            return null;\n                        } else {\n                            throw new NullPointerException(String.valueOf(index));\n                        }\n                    }\n\n                    @Override\n                    void onTestSuccess(String result) {\n                        System.out.println(result);\n                    }\n                };\n                ThreadUtils.executeByCpu(task);\n            }\n        });\n    }\n\n    @Test\n    public void executeByCpuWithDelay() throws Exception {\n        asyncTest(10, new TestRunnable<String>() {\n            @Override\n            public void run(final int index, CountDownLatch latch) {\n                final TestTask<String> task = new TestTask<String>(latch) {\n                    @Override\n                    public String doInBackground() throws Throwable {\n                        Thread.sleep(500);\n                        if (index < 4) {\n                            return Thread.currentThread() + \" :\" + index;\n                        } else if (index < 7) {\n                            cancel();\n                            return null;\n                        } else {\n                            throw new NullPointerException(String.valueOf(index));\n                        }\n                    }\n\n                    @Override\n                    void onTestSuccess(String result) {\n                        System.out.println(result);\n                    }\n                };\n                ThreadUtils.executeByCpuWithDelay(task, 500 + index * 10, TimeUnit.MILLISECONDS);\n            }\n        });\n    }\n\n    @Test\n    public void executeByCpuAtFixRate() throws Exception {\n        asyncTest(10, new TestRunnable<String>() {\n            @Override\n            public void run(final int index, CountDownLatch latch) {\n                final TestScheduledTask<String> task = new TestScheduledTask<String>(latch, 3) {\n                    @Override\n                    public String doInBackground() throws Throwable {\n                        Thread.sleep(100 + index * 10);\n                        if (index < 4) {\n                            return Thread.currentThread() + \" :\" + index;\n                        } else if (index < 7) {\n                            cancel();\n                            return null;\n                        } else {\n                            throw new NullPointerException(String.valueOf(index));\n                        }\n                    }\n\n                    @Override\n                    void onTestSuccess(String result) {\n                        System.out.println(result);\n                    }\n                };\n                ThreadUtils.executeByCpuAtFixRate(task, 1000, TimeUnit.MILLISECONDS);\n            }\n        });\n    }\n\n    @Test\n    public void cancelPoolTask() throws InterruptedException {\n        int count = 300;\n        final CountDownLatch countDownLatch = new CountDownLatch(count);\n        for (int i = 0; i < count; i++) {\n            final int finalI = i;\n            ThreadUtils.executeByCached(new ThreadUtils.Task<Integer>() {\n                @Override\n                public Integer doInBackground() throws Throwable {\n                    Thread.sleep(10 * finalI);\n                    return finalI;\n                }\n\n                @Override\n                public void onSuccess(Integer result) {\n                    System.out.println(result);\n                    if (result == 10) {\n                        ThreadUtils.cancel(ThreadUtils.getCachedPool());\n                    }\n                    countDownLatch.countDown();\n                }\n\n                @Override\n                public void onCancel() {\n                    System.out.println(\"onCancel: \" + finalI);\n                    countDownLatch.countDown();\n                }\n\n                @Override\n                public void onFail(Throwable t) {\n                    countDownLatch.countDown();\n                }\n            });\n        }\n        countDownLatch.await();\n    }\n\n    @Test\n    public void testTimeout() throws InterruptedException {\n        CountDownLatch latch = new CountDownLatch(1);\n        ThreadUtils.Task<Boolean> task = new ThreadUtils.SimpleTask<Boolean>() {\n            @Override\n            public Boolean doInBackground() throws Throwable {\n                System.out.println(\"doInBackground start\");\n                Thread.sleep(2000);\n                System.out.println(\"doInBackground end\");\n                return null;\n            }\n\n            @Override\n            public void onSuccess(Boolean result) {\n                System.out.println(\"onSuccess\");\n            }\n        }.setTimeout(1000, new ThreadUtils.Task.OnTimeoutListener() {\n            @Override\n            public void onTimeout() {\n                System.out.println(\"onTimeout\");\n            }\n        });\n        ThreadUtils.executeByCached(task);\n\n        latch.await(3, TimeUnit.SECONDS);\n    }\n\n    abstract static class TestScheduledTask<T> extends ThreadUtils.Task<T> {\n\n        private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger();\n        private              int           mTimes;\n        CountDownLatch mLatch;\n\n        TestScheduledTask(final CountDownLatch latch, final int times) {\n            mLatch = latch;\n            mTimes = times;\n        }\n\n        abstract void onTestSuccess(T result);\n\n        @Override\n        public void onSuccess(T result) {\n            onTestSuccess(result);\n            if (ATOMIC_INTEGER.addAndGet(1) % mTimes == 0) {\n                mLatch.countDown();\n            }\n        }\n\n        @Override\n        public void onCancel() {\n            System.out.println(Thread.currentThread() + \" onCancel: \");\n            mLatch.countDown();\n        }\n\n        @Override\n        public void onFail(Throwable t) {\n            System.out.println(Thread.currentThread() + \" onFail: \" + t);\n            mLatch.countDown();\n        }\n    }\n\n    abstract static class TestTask<T> extends ThreadUtils.Task<T> {\n        CountDownLatch mLatch;\n\n        TestTask(final CountDownLatch latch) {\n            mLatch = latch;\n        }\n\n        abstract void onTestSuccess(T result);\n\n        @Override\n        public void onSuccess(T result) {\n            onTestSuccess(result);\n            mLatch.countDown();\n        }\n\n        @Override\n        public void onCancel() {\n            System.out.println(Thread.currentThread() + \" onCancel: \");\n            mLatch.countDown();\n        }\n\n        @Override\n        public void onFail(Throwable t) {\n            System.out.println(Thread.currentThread() + \" onFail: \" + t);\n            mLatch.countDown();\n        }\n    }\n\n    private <T> void asyncTest(int threadCount, TestRunnable<T> runnable) throws Exception {\n        CountDownLatch latch = new CountDownLatch(threadCount);\n        for (int i = 0; i < threadCount; i++) {\n            runnable.run(i, latch);\n        }\n        latch.await();\n    }\n\n    interface TestRunnable<T> {\n        void run(final int index, CountDownLatch latch);\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/TimeUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\nimport com.blankj.utilcode.constant.TimeConstants;\n\nimport org.junit.Test;\n\nimport java.text.DateFormat;\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.Locale;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertTrue;\n\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/12\n *     desc  : test TimeUtils\n * </pre>\n */\npublic class TimeUtilsTest extends BaseTest {\n\n    private final DateFormat defaultFormat = new SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\", Locale.getDefault());\n    private final DateFormat mFormat       = new SimpleDateFormat(\"yyyy MM dd HH:mm:ss\", Locale.getDefault());\n\n    private final long   timeMillis               = 1493887049000L;// 2017-05-04 16:37:29\n    private final Date   timeDate                 = new Date(timeMillis);\n    private final String timeString               = defaultFormat.format(timeDate);\n    private final String timeStringFormat         = mFormat.format(timeDate);\n    private final long   tomorrowTimeMillis       = 1493973449000L;\n    private final Date   tomorrowTimeDate         = new Date(tomorrowTimeMillis);\n    private final String tomorrowTimeString       = defaultFormat.format(tomorrowTimeDate);\n    private final String tomorrowTimeStringFormat = mFormat.format(tomorrowTimeDate);\n    private final long   delta                    = 20;// 允许误差 10ms\n\n    @Test\n    public void millis2String() {\n        assertEquals(timeString, TimeUtils.millis2String(timeMillis));\n        assertEquals(timeStringFormat, TimeUtils.millis2String(timeMillis, mFormat));\n        assertEquals(timeStringFormat, TimeUtils.millis2String(timeMillis, \"yyyy MM dd HH:mm:ss\"));\n    }\n\n    @Test\n    public void string2Millis() {\n        assertEquals(timeMillis, TimeUtils.string2Millis(timeString));\n        assertEquals(timeMillis, TimeUtils.string2Millis(timeStringFormat, mFormat));\n        assertEquals(timeMillis, TimeUtils.string2Millis(timeStringFormat, \"yyyy MM dd HH:mm:ss\"));\n    }\n\n    @Test\n    public void string2Date() {\n        assertEquals(timeDate, TimeUtils.string2Date(timeString));\n        assertEquals(timeDate, TimeUtils.string2Date(timeStringFormat, mFormat));\n        assertEquals(timeDate, TimeUtils.string2Date(timeStringFormat, \"yyyy MM dd HH:mm:ss\"));\n    }\n\n    @Test\n    public void date2String() {\n        assertEquals(timeString, TimeUtils.date2String(timeDate));\n        assertEquals(timeStringFormat, TimeUtils.date2String(timeDate, mFormat));\n        assertEquals(timeStringFormat, TimeUtils.date2String(timeDate, \"yyyy MM dd HH:mm:ss\"));\n    }\n\n    @Test\n    public void date2Millis() {\n        assertEquals(timeMillis, TimeUtils.date2Millis(timeDate));\n    }\n\n    @Test\n    public void millis2Date() {\n        assertEquals(timeDate, TimeUtils.millis2Date(timeMillis));\n    }\n\n    @Test\n    public void getTimeSpan() {\n        long testTimeMillis = timeMillis + 120 * TimeConstants.SEC;\n        String testTimeString = TimeUtils.millis2String(testTimeMillis);\n        String testTimeStringFormat = TimeUtils.millis2String(testTimeMillis, mFormat);\n        Date testTimeDate = TimeUtils.millis2Date(testTimeMillis);\n        assertEquals(-120, TimeUtils.getTimeSpan(timeString, testTimeString, TimeConstants.SEC));\n        assertEquals(2, TimeUtils.getTimeSpan(testTimeStringFormat, timeStringFormat, mFormat, TimeConstants.MIN));\n        assertEquals(-2, TimeUtils.getTimeSpan(timeDate, testTimeDate, TimeConstants.MIN));\n        assertEquals(120, TimeUtils.getTimeSpan(testTimeMillis, timeMillis, TimeConstants.SEC));\n    }\n\n    @Test\n    public void getFitTimeSpan() {\n        long testTimeMillis = timeMillis + 10 * TimeConstants.DAY + 10 * TimeConstants.MIN + 10 * TimeConstants.SEC;\n        String testTimeString = TimeUtils.millis2String(testTimeMillis);\n        String testTimeStringFormat = TimeUtils.millis2String(testTimeMillis, mFormat);\n        Date testTimeDate = TimeUtils.millis2Date(testTimeMillis);\n        assertEquals(\"-10天10分钟10秒\", TimeUtils.getFitTimeSpan(timeString, testTimeString, 5));\n        assertEquals(\"10天10分钟10秒\", TimeUtils.getFitTimeSpan(testTimeStringFormat, timeStringFormat, mFormat, 5));\n        assertEquals(\"-10天10分钟10秒\", TimeUtils.getFitTimeSpan(timeDate, testTimeDate, 5));\n        assertEquals(\"10天10分钟10秒\", TimeUtils.getFitTimeSpan(testTimeMillis, timeMillis, 5));\n    }\n\n    @Test\n    public void getNowMills() {\n        assertEquals(System.currentTimeMillis(), TimeUtils.getNowMills(), delta);\n    }\n\n    @Test\n    public void getNowString() {\n        assertEquals(System.currentTimeMillis(), TimeUtils.string2Millis(TimeUtils.getNowString()), delta);\n        assertEquals(System.currentTimeMillis(), TimeUtils.string2Millis(TimeUtils.getNowString(mFormat), mFormat), delta);\n    }\n\n    @Test\n    public void getNowDate() {\n        assertEquals(System.currentTimeMillis(), TimeUtils.date2Millis(TimeUtils.getNowDate()), delta);\n    }\n\n    @Test\n    public void getTimeSpanByNow() {\n        assertEquals(0, TimeUtils.getTimeSpanByNow(TimeUtils.getNowString(), TimeConstants.MSEC), delta);\n        assertEquals(0, TimeUtils.getTimeSpanByNow(TimeUtils.getNowString(mFormat), mFormat, TimeConstants.MSEC), delta);\n        assertEquals(0, TimeUtils.getTimeSpanByNow(TimeUtils.getNowDate(), TimeConstants.MSEC), delta);\n        assertEquals(0, TimeUtils.getTimeSpanByNow(TimeUtils.getNowMills(), TimeConstants.MSEC), delta);\n    }\n\n    @Test\n    public void getFitTimeSpanByNow() {\n//        long spanMillis = 6 * TimeConstants.DAY + 6 * TimeConstants.HOUR + 6 * TimeConstants.MIN + 6 * TimeConstants.SEC;\n//        assertEquals(\"6天6小时6分钟6秒\", TimeUtils.getFitTimeSpanByNow(TimeUtils.millis2String(System.currentTimeMillis() + spanMillis), 4));\n//        assertEquals(\"6天6小时6分钟6秒\", TimeUtils.getFitTimeSpanByNow(TimeUtils.millis2String(System.currentTimeMillis() + spanMillis, mFormat), mFormat, 4));\n//        assertEquals(\"6天6小时6分钟6秒\", TimeUtils.getFitTimeSpanByNow(TimeUtils.millis2Date(System.currentTimeMillis() + spanMillis), 4));\n//        assertEquals(\"6天6小时6分钟6秒\", TimeUtils.getFitTimeSpanByNow(System.currentTimeMillis() + spanMillis, 4));\n    }\n\n    @Test\n    public void getFriendlyTimeSpanByNow() {\n        assertEquals(\"刚刚\", TimeUtils.getFriendlyTimeSpanByNow(TimeUtils.getNowString()));\n        assertEquals(\"刚刚\", TimeUtils.getFriendlyTimeSpanByNow(TimeUtils.getNowString(mFormat), mFormat));\n        assertEquals(\"刚刚\", TimeUtils.getFriendlyTimeSpanByNow(TimeUtils.getNowDate()));\n        assertEquals(\"刚刚\", TimeUtils.getFriendlyTimeSpanByNow(TimeUtils.getNowMills()));\n        assertEquals(\"1秒前\", TimeUtils.getFriendlyTimeSpanByNow(TimeUtils.getNowMills() - TimeConstants.SEC));\n        assertEquals(\"1分钟前\", TimeUtils.getFriendlyTimeSpanByNow(TimeUtils.getNowMills() - TimeConstants.MIN));\n    }\n\n    @Test\n    public void getMillis() {\n        assertEquals(tomorrowTimeMillis, TimeUtils.getMillis(timeMillis, 1, TimeConstants.DAY));\n        assertEquals(tomorrowTimeMillis, TimeUtils.getMillis(timeString, 1, TimeConstants.DAY));\n        assertEquals(tomorrowTimeMillis, TimeUtils.getMillis(timeStringFormat, mFormat, 1, TimeConstants.DAY));\n        assertEquals(tomorrowTimeMillis, TimeUtils.getMillis(timeDate, 1, TimeConstants.DAY));\n    }\n\n    @Test\n    public void getString() {\n        assertEquals(tomorrowTimeString, TimeUtils.getString(timeMillis, 1, TimeConstants.DAY));\n        assertEquals(tomorrowTimeStringFormat, TimeUtils.getString(timeMillis, mFormat, 1, TimeConstants.DAY));\n        assertEquals(tomorrowTimeString, TimeUtils.getString(timeString, 1, TimeConstants.DAY));\n        assertEquals(tomorrowTimeStringFormat, TimeUtils.getString(timeStringFormat, mFormat, 1, TimeConstants.DAY));\n        assertEquals(tomorrowTimeString, TimeUtils.getString(timeDate, 1, TimeConstants.DAY));\n        assertEquals(tomorrowTimeStringFormat, TimeUtils.getString(timeDate, mFormat, 1, TimeConstants.DAY));\n    }\n\n    @Test\n    public void getDate() {\n        assertEquals(tomorrowTimeDate, TimeUtils.getDate(timeMillis, 1, TimeConstants.DAY));\n        assertEquals(tomorrowTimeDate, TimeUtils.getDate(timeString, 1, TimeConstants.DAY));\n        assertEquals(tomorrowTimeDate, TimeUtils.getDate(timeStringFormat, mFormat, 1, TimeConstants.DAY));\n        assertEquals(tomorrowTimeDate, TimeUtils.getDate(timeDate, 1, TimeConstants.DAY));\n    }\n\n    @Test\n    public void getMillisByNow() {\n        assertEquals(System.currentTimeMillis() + TimeConstants.DAY, TimeUtils.getMillisByNow(1, TimeConstants.DAY), delta);\n    }\n\n    @Test\n    public void getStringByNow() {\n        long tomorrowMillis = TimeUtils.string2Millis(TimeUtils.getStringByNow(1, TimeConstants.DAY));\n        assertEquals(System.currentTimeMillis() + TimeConstants.DAY, tomorrowMillis, delta);\n        tomorrowMillis = TimeUtils.string2Millis(TimeUtils.getStringByNow(1, mFormat, TimeConstants.DAY), mFormat);\n        assertEquals(System.currentTimeMillis() + TimeConstants.DAY, tomorrowMillis, delta);\n    }\n\n    @Test\n    public void getDateByNow() {\n        long tomorrowMillis = TimeUtils.date2Millis(TimeUtils.getDateByNow(1, TimeConstants.DAY));\n        assertEquals(System.currentTimeMillis() + TimeConstants.DAY, TimeUtils.getMillisByNow(1, TimeConstants.DAY), delta);\n    }\n\n    @Test\n    public void isToday() {\n        long todayTimeMillis = System.currentTimeMillis();\n        String todayTimeString = TimeUtils.millis2String(todayTimeMillis);\n        String todayTimeStringFormat = TimeUtils.millis2String(todayTimeMillis, mFormat);\n        Date todayTimeDate = TimeUtils.millis2Date(todayTimeMillis);\n        long tomorrowTimeMillis = todayTimeMillis + TimeConstants.DAY;\n        String tomorrowTimeString = TimeUtils.millis2String(tomorrowTimeMillis);\n        Date tomorrowTimeDate = TimeUtils.millis2Date(tomorrowTimeMillis);\n        assertTrue(TimeUtils.isToday(todayTimeString));\n        assertTrue(TimeUtils.isToday(todayTimeStringFormat, mFormat));\n        assertTrue(TimeUtils.isToday(todayTimeDate));\n        assertTrue(TimeUtils.isToday(todayTimeMillis));\n        assertFalse(TimeUtils.isToday(tomorrowTimeString));\n        assertFalse(TimeUtils.isToday(tomorrowTimeStringFormat, mFormat));\n        assertFalse(TimeUtils.isToday(tomorrowTimeDate));\n        assertFalse(TimeUtils.isToday(tomorrowTimeMillis));\n    }\n\n    @Test\n    public void isLeapYear() {\n        assertFalse(TimeUtils.isLeapYear(timeString));\n        assertFalse(TimeUtils.isLeapYear(timeStringFormat, mFormat));\n        assertFalse(TimeUtils.isLeapYear(timeDate));\n        assertFalse(TimeUtils.isLeapYear(timeMillis));\n        assertTrue(TimeUtils.isLeapYear(2016));\n        assertFalse(TimeUtils.isLeapYear(2017));\n    }\n\n    @Test\n    public void getChineseWeek() {\n        assertEquals(\"星期四\", TimeUtils.getChineseWeek(timeString));\n        assertEquals(\"星期四\", TimeUtils.getChineseWeek(timeStringFormat, mFormat));\n        assertEquals(\"星期四\", TimeUtils.getChineseWeek(timeDate));\n        assertEquals(\"星期四\", TimeUtils.getChineseWeek(timeMillis));\n    }\n\n    @Test\n    public void getUSWeek() {\n        assertEquals(\"Thursday\", TimeUtils.getUSWeek(timeString));\n        assertEquals(\"Thursday\", TimeUtils.getUSWeek(timeStringFormat, mFormat));\n        assertEquals(\"Thursday\", TimeUtils.getUSWeek(timeDate));\n        assertEquals(\"Thursday\", TimeUtils.getUSWeek(timeMillis));\n    }\n\n    //@Test\n    //public void isAm() {\n    //    assertFalse(TimeUtils.isAm(timeMillis));\n    //}\n    //\n    //@Test\n    //public void isPm() {\n    //    assertTrue(TimeUtils.isPm(timeMillis));\n    //}\n\n    @Test\n    public void getWeekIndex() {\n        assertEquals(5, TimeUtils.getValueByCalendarField(timeString, Calendar.DAY_OF_WEEK));\n        assertEquals(5, TimeUtils.getValueByCalendarField(timeString, Calendar.DAY_OF_WEEK));\n        assertEquals(5, TimeUtils.getValueByCalendarField(timeStringFormat, mFormat, Calendar.DAY_OF_WEEK));\n        assertEquals(5, TimeUtils.getValueByCalendarField(timeDate, Calendar.DAY_OF_WEEK));\n        assertEquals(5, TimeUtils.getValueByCalendarField(timeMillis, Calendar.DAY_OF_WEEK));\n    }\n\n    @Test\n    public void getWeekOfMonth() {\n        assertEquals(1, TimeUtils.getValueByCalendarField(timeString, Calendar.WEEK_OF_MONTH));\n        assertEquals(1, TimeUtils.getValueByCalendarField(timeStringFormat, mFormat, Calendar.WEEK_OF_MONTH));\n        assertEquals(1, TimeUtils.getValueByCalendarField(timeDate, Calendar.WEEK_OF_MONTH));\n        assertEquals(1, TimeUtils.getValueByCalendarField(timeMillis, Calendar.WEEK_OF_MONTH));\n    }\n\n    @Test\n    public void getWeekOfYear() {\n        assertEquals(18, TimeUtils.getValueByCalendarField(timeString, Calendar.WEEK_OF_YEAR));\n        assertEquals(18, TimeUtils.getValueByCalendarField(timeStringFormat, mFormat, Calendar.WEEK_OF_YEAR));\n        assertEquals(18, TimeUtils.getValueByCalendarField(timeDate, Calendar.WEEK_OF_YEAR));\n        assertEquals(18, TimeUtils.getValueByCalendarField(timeMillis, Calendar.WEEK_OF_YEAR));\n    }\n\n    @Test\n    public void getChineseZodiac() {\n        assertEquals(\"鸡\", TimeUtils.getChineseZodiac(timeString));\n        assertEquals(\"鸡\", TimeUtils.getChineseZodiac(timeStringFormat, mFormat));\n        assertEquals(\"鸡\", TimeUtils.getChineseZodiac(timeDate));\n        assertEquals(\"鸡\", TimeUtils.getChineseZodiac(timeMillis));\n        assertEquals(\"鸡\", TimeUtils.getChineseZodiac(2017));\n    }\n\n    @Test\n    public void getZodiac() {\n        assertEquals(\"金牛座\", TimeUtils.getZodiac(timeString));\n        assertEquals(\"金牛座\", TimeUtils.getZodiac(timeStringFormat, mFormat));\n        assertEquals(\"金牛座\", TimeUtils.getZodiac(timeDate));\n        assertEquals(\"金牛座\", TimeUtils.getZodiac(timeMillis));\n        assertEquals(\"狮子座\", TimeUtils.getZodiac(8, 16));\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/UiMessageUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\n\n\nimport org.junit.Test;\n\nimport androidx.annotation.NonNull;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/10/21\n *     desc  : test UiMessageUtils\n * </pre>\n */\npublic class UiMessageUtilsTest extends BaseTest {\n\n    @Test\n    public void singleMessageTest() {\n        UiMessageUtils.UiMessageCallback listener = new UiMessageUtils.UiMessageCallback() {\n            @Override\n            public void handleMessage(@NonNull UiMessageUtils.UiMessage localMessage) {\n                System.out.println(\"receive -> \" + localMessage.getId() + \": \" + localMessage.getObject());\n            }\n        };\n        UiMessageUtils.getInstance().addListener(listener);\n\n        UiMessageUtils.getInstance().send(1, \"msg\");\n\n        UiMessageUtils.getInstance().removeListener(listener);\n\n        UiMessageUtils.getInstance().send(1, \"msg\");\n    }\n\n    @Test\n    public void multiMessageTest() {\n        UiMessageUtils.UiMessageCallback listener = new UiMessageUtils.UiMessageCallback() {\n            @Override\n            public void handleMessage(@NonNull UiMessageUtils.UiMessage localMessage) {\n                switch (localMessage.getId()) {\n                    case 1:\n                        System.out.println(\"receive -> 1: \" + localMessage.getObject());\n                        break;\n                    case 2:\n                        System.out.println(\"receive -> 2: \" + localMessage.getObject());\n                        break;\n                    case 4:\n                        System.out.println(\"receive -> 4: \" + localMessage.getObject());\n                        break;\n                }\n            }\n        };\n        UiMessageUtils.getInstance().addListener(listener);\n\n        UiMessageUtils.getInstance().send(1, \"msg1\");\n        UiMessageUtils.getInstance().send(2, \"msg2\");\n        UiMessageUtils.getInstance().send(4, \"msg4\");\n\n        UiMessageUtils.getInstance().removeListener(listener);\n\n        UiMessageUtils.getInstance().send(1, \"msg1\");\n        UiMessageUtils.getInstance().send(2, \"msg2\");\n        UiMessageUtils.getInstance().send(4, \"msg4\");\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/ZipUtilsTest.java",
    "content": "package com.blankj.utilcode.util;\r\n\r\nimport org.junit.After;\r\nimport org.junit.Before;\r\nimport org.junit.Test;\r\n\r\nimport java.util.ArrayList;\r\nimport java.util.List;\r\n\r\nimport static com.blankj.utilcode.util.TestConfig.PATH_TEMP;\r\nimport static com.blankj.utilcode.util.TestConfig.PATH_ZIP;\r\nimport static junit.framework.TestCase.assertTrue;\r\n\r\n/**\r\n * <pre>\r\n *     author: Blankj\r\n *     blog  : http://blankj.com\r\n *     time  : 2016/09/10\r\n *     desc  : test ZipUtils\r\n * </pre>\r\n */\r\npublic class ZipUtilsTest extends BaseTest {\r\n\r\n    private String zipFile  = PATH_TEMP + \"zipFile.zip\";\r\n    private String zipFiles = PATH_TEMP + \"zipFiles.zip\";\r\n\r\n    @Before\r\n    public void setUp() throws Exception {\r\n        FileUtils.createOrExistsDir(PATH_TEMP);\r\n        assertTrue(ZipUtils.zipFile(PATH_ZIP, zipFile, \"测试zip\"));\r\n    }\r\n\r\n    @Test\r\n    public void zipFiles() throws Exception {\r\n        List<String> files = new ArrayList<>();\r\n        files.add(PATH_ZIP + \"test.txt\");\r\n        files.add(PATH_ZIP);\r\n        files.add(PATH_ZIP + \"testDir\");\r\n        assertTrue(ZipUtils.zipFiles(files, zipFiles));\r\n    }\r\n\r\n    @Test\r\n    public void unzipFile() throws Exception {\r\n        System.out.println(ZipUtils.unzipFile(zipFile, PATH_TEMP));\r\n    }\r\n\r\n    @Test\r\n    public void unzipFileByKeyword() throws Exception {\r\n        System.out.println((ZipUtils.unzipFileByKeyword(zipFile, PATH_TEMP, null)).toString());\r\n    }\r\n\r\n    @Test\r\n    public void getFilesPath() throws Exception {\r\n        System.out.println(ZipUtils.getFilesPath(zipFile));\r\n    }\r\n\r\n    @Test\r\n    public void getComments() throws Exception {\r\n        System.out.println(ZipUtils.getComments(zipFile));\r\n    }\r\n\r\n    @After\r\n    public void tearDown() {\r\n        FileUtils.deleteAllInDir(PATH_TEMP);\r\n    }\r\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/reflect/PrivateConstructors.java",
    "content": "package com.blankj.utilcode.util.reflect;\r\n\r\n/**\r\n * <pre>\r\n *     author: Blankj\r\n *     blog  : http://blankj.com\r\n *     time  : 2018/01/12\r\n *     desc  :\r\n * </pre>\r\n */\r\npublic class PrivateConstructors {\r\n\r\n    public final String string;\r\n\r\n    private PrivateConstructors() {\r\n        this(null);\r\n    }\r\n\r\n    private PrivateConstructors(String string) {\r\n        this.string = string;\r\n    }\r\n}\r\n"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/reflect/ReflectUtilsTest.java",
    "content": "package com.blankj.utilcode.util.reflect;\n\nimport com.blankj.utilcode.util.ReflectUtils;\n\nimport org.hamcrest.Matchers;\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertFalse;\nimport static org.junit.Assert.assertNull;\nimport static org.junit.Assert.assertThat;\nimport static org.junit.Assert.assertTrue;\nimport static org.junit.Assert.fail;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/12/15\n *     desc  : ReflectUtils 单元测试\n * </pre>\n */\npublic class ReflectUtilsTest {\n\n    @Rule\n    public final ExpectedException expectedException = ExpectedException.none();\n\n    @Test\n    public void reflect() {\n        Assert.assertEquals(\n                ReflectUtils.reflect(Object.class),\n                ReflectUtils.reflect(\"java.lang.Object\", ClassLoader.getSystemClassLoader())\n        );\n        assertEquals(\n                ReflectUtils.reflect(Object.class),\n                ReflectUtils.reflect(\"java.lang.Object\")\n        );\n        assertEquals(\n                ReflectUtils.reflect(String.class).get(),\n                ReflectUtils.reflect(\"java.lang.String\").get()\n        );\n        assertEquals(\n                Object.class,\n                ReflectUtils.reflect(Object.class).get()\n        );\n        assertEquals(\n                \"abc\",\n                ReflectUtils.reflect((Object) \"abc\").get()\n        );\n        assertEquals(\n                1,\n                ReflectUtils.reflect(1).get()\n        );\n    }\n\n    @Test\n    public void newInstance() {\n        assertEquals(\n                \"\",\n                ReflectUtils.reflect(String.class).newInstance().get()\n        );\n        assertEquals(\n                \"abc\",\n                ReflectUtils.reflect(String.class).newInstance(\"abc\").get()\n        );\n        assertEquals(\n                \"abc\",\n                ReflectUtils.reflect(String.class).newInstance(\"abc\".getBytes()).get()\n        );\n        assertEquals(\n                \"abc\",\n                ReflectUtils.reflect(String.class).newInstance(\"abc\".toCharArray()).get()\n        );\n        assertEquals(\n                \"b\",\n                ReflectUtils.reflect(String.class).newInstance(\"abc\".toCharArray(), 1, 1).get()\n        );\n    }\n\n    @Test\n    public void newInstancePrivate() {\n        assertNull(ReflectUtils.reflect(PrivateConstructors.class).newInstance().field(\"string\").get());\n\n        assertEquals(\n                \"abc\",\n                ReflectUtils.reflect(PrivateConstructors.class).newInstance(\"abc\").field(\"string\").get()\n        );\n    }\n\n    @Test\n    public void newInstanceNull() {\n        Test2 test2 = ReflectUtils.reflect(Test2.class).newInstance((Object) null).get();\n        assertNull(test2.n);\n    }\n\n    @Test\n    public void newInstanceWithPrivate() {\n        Test7 t1 = ReflectUtils.reflect(Test7.class).newInstance(1).get();\n        assertEquals(1, (int) t1.i);\n        assertNull(t1.s);\n\n        Test7 t2 = ReflectUtils.reflect(Test7.class).newInstance(\"a\").get();\n        assertNull(t2.i);\n        assertEquals(\"a\", t2.s);\n\n        Test7 t3 = ReflectUtils.reflect(Test7.class).newInstance(\"a\", 1).get();\n        assertEquals(1, (int) t3.i);\n        assertEquals(\"a\", t3.s);\n    }\n\n    @Test\n    public void newInstanceAmbiguity() {\n        Test2 test;\n\n        test = ReflectUtils.reflect(Test2.class).newInstance().get();\n        assertEquals(null, test.n);\n        assertEquals(Test2.ConstructorType.NO_ARGS, test.constructorType);\n\n        test = ReflectUtils.reflect(Test2.class).newInstance(\"abc\").get();\n        assertEquals(\"abc\", test.n);\n        assertEquals(Test2.ConstructorType.OBJECT, test.constructorType);\n\n        test = ReflectUtils.reflect(Test2.class).newInstance(new Long(\"1\")).get();\n        assertEquals(1L, test.n);\n        assertEquals(Test2.ConstructorType.NUMBER, test.constructorType);\n\n        test = ReflectUtils.reflect(Test2.class).newInstance(1).get();\n        assertEquals(1, test.n);\n        assertEquals(Test2.ConstructorType.INTEGER, test.constructorType);\n\n        test = ReflectUtils.reflect(Test2.class).newInstance('a').get();\n        assertEquals('a', test.n);\n        assertEquals(Test2.ConstructorType.OBJECT, test.constructorType);\n    }\n\n    @Test\n    public void method() {\n        // instance methods\n        assertEquals(\n                \"\",\n                ReflectUtils.reflect((Object) \" \").method(\"trim\").get()\n        );\n        assertEquals(\n                \"12\",\n                ReflectUtils.reflect((Object) \" 12 \").method(\"trim\").get()\n        );\n        assertEquals(\n                \"34\",\n                ReflectUtils.reflect((Object) \"1234\").method(\"substring\", 2).get()\n        );\n        assertEquals(\n                \"12\",\n                ReflectUtils.reflect((Object) \"1234\").method(\"substring\", 0, 2).get()\n        );\n        assertEquals(\n                \"1234\",\n                ReflectUtils.reflect((Object) \"12\").method(\"concat\", \"34\").get()\n        );\n        assertEquals(\n                \"123456\",\n                ReflectUtils.reflect((Object) \"12\").method(\"concat\", \"34\").method(\"concat\", \"56\").get()\n        );\n        assertEquals(\n                2,\n                ReflectUtils.reflect((Object) \"1234\").method(\"indexOf\", \"3\").get()\n        );\n        assertEquals(\n                2.0f,\n                (float) ReflectUtils.reflect((Object) \"1234\").method(\"indexOf\", \"3\").method(\"floatValue\").get(),\n                0.0f\n        );\n        assertEquals(\n                \"2\",\n                ReflectUtils.reflect((Object) \"1234\").method(\"indexOf\", \"3\").method(\"toString\").get()\n        );\n\n        // static methods\n        assertEquals(\n                \"true\",\n                ReflectUtils.reflect(String.class).method(\"valueOf\", true).get()\n        );\n        assertEquals(\n                \"1\",\n                ReflectUtils.reflect(String.class).method(\"valueOf\", 1).get()\n        );\n        assertEquals(\n                \"abc\",\n                ReflectUtils.reflect(String.class).method(\"valueOf\", \"abc\".toCharArray()).get()\n        );\n        assertEquals(\n                \"abc\",\n                ReflectUtils.reflect(String.class).method(\"copyValueOf\", \"abc\".toCharArray()).get()\n        );\n        assertEquals(\n                \"b\",\n                ReflectUtils.reflect(String.class).method(\"copyValueOf\", \"abc\".toCharArray(), 1, 1).get()\n        );\n    }\n\n    @Test\n    public void methodVoid() {\n        // instance methods\n        Test4 test4 = new Test4();\n        assertEquals(\n                test4,\n                ReflectUtils.reflect(test4).method(\"i_method\").get()\n        );\n\n        // static methods\n        assertEquals(\n                Test4.class,\n                ReflectUtils.reflect(Test4.class).method(\"s_method\").get()\n        );\n    }\n\n    @Test\n    public void methodPrivate() {\n        // instance methods\n        Test5 test8 = new Test5();\n        assertEquals(\n                test8,\n                ReflectUtils.reflect(test8).method(\"i_method\").get()\n        );\n\n        // static methods\n        assertEquals(\n                Test5.class,\n                ReflectUtils.reflect(Test5.class).method(\"s_method\").get()\n        );\n    }\n\n    @Test\n    public void methodNullArguments() {\n        Test6 test9 = new Test6();\n        ReflectUtils.reflect(test9).method(\"put\", \"key\", \"value\");\n        assertTrue(test9.map.containsKey(\"key\"));\n        assertEquals(\"value\", test9.map.get(\"key\"));\n\n        ReflectUtils.reflect(test9).method(\"put\", \"key\", null);\n        assertTrue(test9.map.containsKey(\"key\"));\n        assertNull(test9.map.get(\"key\"));\n    }\n\n    @Test\n    public void methodSuper() {\n        TestHierarchicalMethodsSubclass subclass = new TestHierarchicalMethodsSubclass();\n        assertEquals(\n                TestHierarchicalMethodsBase.PUBLIC_RESULT,\n                ReflectUtils.reflect(subclass).method(\"pub_base_method\", 1).get()\n        );\n\n        assertEquals(\n                TestHierarchicalMethodsBase.PRIVATE_RESULT,\n                ReflectUtils.reflect(subclass).method(\"very_priv_method\").get()\n        );\n    }\n\n    @Test\n    public void methodDeclaring() {\n        TestHierarchicalMethodsSubclass subclass = new TestHierarchicalMethodsSubclass();\n        assertEquals(\n                TestHierarchicalMethodsSubclass.PRIVATE_RESULT,\n                ReflectUtils.reflect(subclass).method(\"priv_method\", 1).get()\n        );\n\n        TestHierarchicalMethodsBase baseClass = new TestHierarchicalMethodsBase();\n        assertEquals(\n                TestHierarchicalMethodsBase.PRIVATE_RESULT,\n                ReflectUtils.reflect(baseClass).method(\"priv_method\", 1).get()\n        );\n    }\n\n    @Test\n    public void methodAmbiguity() {\n        Test3 test;\n\n        test = ReflectUtils.reflect(Test3.class).newInstance().method(\"method\").get();\n        assertEquals(null, test.n);\n        assertEquals(Test3.MethodType.NO_ARGS, test.methodType);\n\n        test = ReflectUtils.reflect(Test3.class).newInstance().method(\"method\", \"abc\").get();\n        assertEquals(\"abc\", test.n);\n        assertEquals(Test3.MethodType.OBJECT, test.methodType);\n\n        test = ReflectUtils.reflect(Test3.class).newInstance().method(\"method\", new Long(\"1\")).get();\n        assertEquals(1L, test.n);\n        assertEquals(Test3.MethodType.NUMBER, test.methodType);\n\n        test = ReflectUtils.reflect(Test3.class).newInstance().method(\"method\", 1).get();\n        assertEquals(1, test.n);\n        assertEquals(Test3.MethodType.INTEGER, test.methodType);\n\n        test = ReflectUtils.reflect(Test3.class).newInstance().method(\"method\", 'a').get();\n        assertEquals('a', test.n);\n        assertEquals(Test3.MethodType.OBJECT, test.methodType);\n    }\n\n    @Test\n    public void field() {\n        // instance field\n        Test1 test1 = new Test1();\n        ReflectUtils.reflect(test1).field(\"I_INT1\", 1);\n        assertEquals(1, ReflectUtils.reflect(test1).field(\"I_INT1\").get());\n\n        ReflectUtils.reflect(test1).field(\"I_INT2\", 1);\n        assertEquals(1, ReflectUtils.reflect(test1).field(\"I_INT2\").get());\n\n        ReflectUtils.reflect(test1).field(\"I_INT2\", null);\n        assertNull(ReflectUtils.reflect(test1).field(\"I_INT2\").get());\n\n        // static field\n        ReflectUtils.reflect(Test1.class).field(\"S_INT1\", 1);\n        assertEquals(1, ReflectUtils.reflect(Test1.class).field(\"S_INT1\").get());\n\n        ReflectUtils.reflect(Test1.class).field(\"S_INT2\", 1);\n        assertEquals(1, ReflectUtils.reflect(Test1.class).field(\"S_INT2\").get());\n\n        ReflectUtils.reflect(Test1.class).field(\"S_INT2\", null);\n        assertNull(ReflectUtils.reflect(Test1.class).field(\"S_INT2\").get());\n\n        // hierarchies field\n        TestHierarchicalMethodsSubclass test2 = new TestHierarchicalMethodsSubclass();\n\n        ReflectUtils.reflect(test2).field(\"invisibleField1\", 1);\n        assertEquals(1, ReflectUtils.reflect(test2).field(\"invisibleField1\").get());\n\n        ReflectUtils.reflect(test2).field(\"invisibleField2\", 1);\n        assertEquals(1, ReflectUtils.reflect(test2).field(\"invisibleField2\").get());\n\n        ReflectUtils.reflect(test2).field(\"invisibleField3\", 1);\n        assertEquals(1, ReflectUtils.reflect(test2).field(\"invisibleField3\").get());\n\n        ReflectUtils.reflect(test2).field(\"visibleField1\", 1);\n        assertEquals(1, ReflectUtils.reflect(test2).field(\"visibleField1\").get());\n\n        ReflectUtils.reflect(test2).field(\"visibleField2\", 1);\n        assertEquals(1, ReflectUtils.reflect(test2).field(\"visibleField2\").get());\n\n        ReflectUtils.reflect(test2).field(\"visibleField3\", 1);\n        assertEquals(1, ReflectUtils.reflect(test2).field(\"visibleField3\").get());\n    }\n\n    @Test\n    public void fieldPrivate() {\n        class Foo {\n            private String bar;\n        }\n        Foo foo = new Foo();\n        ReflectUtils.reflect(foo).field(\"bar\", \"FooBar\");\n        assertThat(foo.bar, Matchers.is(\"FooBar\"));\n        assertEquals(\"FooBar\", ReflectUtils.reflect(foo).field(\"bar\").get());\n\n        ReflectUtils.reflect(foo).field(\"bar\", null);\n        assertNull(foo.bar);\n        assertNull(ReflectUtils.reflect(foo).field(\"bar\").get());\n    }\n\n    @Test\n    public void fieldFinal() {\n        // instance field\n        Test8 test11 = new Test8();\n        ReflectUtils.reflect(test11).field(\"F_INT1\", 1);\n        assertEquals(1, ReflectUtils.reflect(test11).field(\"F_INT1\").get());\n\n        ReflectUtils.reflect(test11).field(\"F_INT2\", 1);\n        assertEquals(1, ReflectUtils.reflect(test11).field(\"F_INT2\").get());\n\n        ReflectUtils.reflect(test11).field(\"F_INT2\", null);\n        assertNull(ReflectUtils.reflect(test11).field(\"F_INT2\").get());\n\n        // static field\n        ReflectUtils.reflect(Test8.class).field(\"SF_INT1\", 1);\n        assertEquals(1, ReflectUtils.reflect(Test8.class).field(\"SF_INT1\").get());\n\n        ReflectUtils.reflect(Test8.class).field(\"SF_INT2\", 1);\n        assertEquals(1, ReflectUtils.reflect(Test8.class).field(\"SF_INT2\").get());\n\n        ReflectUtils.reflect(Test8.class).field(\"SF_INT2\", null);\n        assertNull(ReflectUtils.reflect(Test8.class).field(\"SF_INT2\").get());\n    }\n\n    @Test\n    public void fieldPrivateStaticFinal() {\n        assertEquals(1, ReflectUtils.reflect(TestPrivateStaticFinal.class).field(\"I1\").get());\n        assertEquals(1, ReflectUtils.reflect(TestPrivateStaticFinal.class).field(\"I2\").get());\n\n        ReflectUtils.reflect(TestPrivateStaticFinal.class).field(\"I1\", 2);\n        ReflectUtils.reflect(TestPrivateStaticFinal.class).field(\"I2\", 2);\n\n        assertEquals(2, ReflectUtils.reflect(TestPrivateStaticFinal.class).field(\"I1\").get());\n        assertEquals(2, ReflectUtils.reflect(TestPrivateStaticFinal.class).field(\"I2\").get());\n    }\n\n    @Test\n    public void fieldAdvanced() {\n        ReflectUtils.reflect(Test1.class)\n                .field(\"S_DATA\", ReflectUtils.reflect(Test1.class).newInstance())\n                .field(\"S_DATA\")\n                .field(\"I_DATA\", ReflectUtils.reflect(Test1.class).newInstance())\n                .field(\"I_DATA\")\n                .field(\"I_INT1\", 1)\n                .field(\"S_INT1\", 2);\n        assertEquals(2, Test1.S_INT1);\n        assertEquals(null, Test1.S_INT2);\n        assertEquals(0, Test1.S_DATA.I_INT1);\n        assertEquals(null, Test1.S_DATA.I_INT2);\n        assertEquals(1, Test1.S_DATA.I_DATA.I_INT1);\n        assertEquals(null, Test1.S_DATA.I_DATA.I_INT2);\n    }\n\n    @Test\n    public void fieldFinalAdvanced() {\n        ReflectUtils.reflect(Test8.class)\n                .field(\"S_DATA\", ReflectUtils.reflect(Test8.class).newInstance())\n                .field(\"S_DATA\")\n                .field(\"I_DATA\", ReflectUtils.reflect(Test8.class).newInstance())\n                .field(\"I_DATA\")\n                .field(\"F_INT1\", 1)\n                .field(\"F_INT2\", 1)\n                .field(\"SF_INT1\", 2)\n                .field(\"SF_INT2\", 2);\n        assertEquals(2, Test8.SF_INT1);\n        assertEquals(new Integer(2), Test8.SF_INT2);\n        assertEquals(0, Test8.S_DATA.F_INT1);\n        assertEquals(new Integer(0), Test8.S_DATA.F_INT2);\n        assertEquals(1, Test8.S_DATA.I_DATA.F_INT1);\n        assertEquals(new Integer(1), Test8.S_DATA.I_DATA.F_INT2);\n    }\n\n    @Test\n    public void _hashCode() {\n        Object object = new Object();\n        assertEquals(ReflectUtils.reflect(object).hashCode(), object.hashCode());\n    }\n\n    @Test\n    public void _toString() {\n        Object object = new Object() {\n            @Override\n            public String toString() {\n                return \"test\";\n            }\n        };\n        assertEquals(ReflectUtils.reflect(object).toString(), object.toString());\n    }\n\n    @Test\n    public void _equals() {\n        Object object = new Object();\n        ReflectUtils a = ReflectUtils.reflect(object);\n        ReflectUtils b = ReflectUtils.reflect(object);\n        ReflectUtils c = ReflectUtils.reflect(object);\n\n        assertTrue(b.equals(a));\n        assertTrue(a.equals(b));\n        assertTrue(b.equals(c));\n        assertTrue(a.equals(c));\n        //noinspection ObjectEqualsNull\n        assertFalse(a.equals(null));\n    }\n\n    @Test\n    public void testProxy() {\n        assertEquals(\"abc\", ReflectUtils.reflect((Object) \"abc\").proxy(Test9.class).substring(0));\n        assertEquals(\"bc\", ReflectUtils.reflect((Object) \"abc\").proxy(Test9.class).substring(1));\n        assertEquals(\"c\", ReflectUtils.reflect((Object) \"abc\").proxy(Test9.class).substring(2));\n\n        assertEquals(\"a\", ReflectUtils.reflect((Object) \"abc\").proxy(Test9.class).substring(0, 1));\n        assertEquals(\"b\", ReflectUtils.reflect((Object) \"abc\").proxy(Test9.class).substring(1, 2));\n        assertEquals(\"c\", ReflectUtils.reflect((Object) \"abc\").proxy(Test9.class).substring(2, 3));\n\n        assertEquals(\"abc\", ReflectUtils.reflect((Object) \"abc\").proxy(Test9.class).substring(0));\n        assertEquals(\"bc\", ReflectUtils.reflect((Object) \"abc\").proxy(Test9.class).substring(1));\n        assertEquals(\"c\", ReflectUtils.reflect((Object) \"abc\").proxy(Test9.class).substring(2));\n\n        assertEquals(\"a\", ReflectUtils.reflect((Object) \"abc\").proxy(Test9.class).substring(0, 1));\n        assertEquals(\"b\", ReflectUtils.reflect((Object) \"abc\").proxy(Test9.class).substring(1, 2));\n        assertEquals(\"c\", ReflectUtils.reflect((Object) \"abc\").proxy(Test9.class).substring(2, 3));\n    }\n\n    @Test\n    public void testMapProxy() {\n        class MyMap extends HashMap<String, Object> {\n            private String baz;\n\n            public void setBaz(String baz) {\n                this.baz = \"MyMap: \" + baz;\n            }\n\n            public String getBaz() {\n                return baz;\n            }\n        }\n\n        Map<String, Object> map = new MyMap();\n\n        ReflectUtils.reflect(map).proxy(Test10.class).setFoo(\"abc\");\n        assertEquals(1, map.size());\n        assertEquals(\"abc\", map.get(\"foo\"));\n        assertEquals(\"abc\", ReflectUtils.reflect(map).proxy(Test10.class).getFoo());\n\n        ReflectUtils.reflect(map).proxy(Test10.class).setBar(true);\n        assertEquals(2, map.size());\n        assertEquals(true, map.get(\"bar\"));\n        assertEquals(true, ReflectUtils.reflect(map).proxy(Test10.class).isBar());\n\n        ReflectUtils.reflect(map).proxy(Test10.class).setBaz(\"baz\");\n        assertEquals(2, map.size());\n        assertEquals(null, map.get(\"baz\"));\n        assertEquals(\"MyMap: baz\", ReflectUtils.reflect(map).proxy(Test10.class).getBaz());\n\n        try {\n            ReflectUtils.reflect(map).proxy(Test10.class).testIgnore();\n            fail();\n        } catch (ReflectUtils.ReflectException ignored) {\n        }\n    }\n}"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/reflect/Test1.java",
    "content": "package com.blankj.utilcode.util.reflect;\r\n\r\n/**\r\n * <pre>\r\n *     author: Blankj\r\n *     blog  : http://blankj.com\r\n *     time  : 2018/01/12\r\n *     desc  :\r\n * </pre>\r\n */\r\npublic class Test1 {\r\n    public static int     S_INT1;\r\n    public static Integer S_INT2;\r\n    public        int     I_INT1;\r\n    public        Integer I_INT2;\r\n\r\n    public static Test1 S_DATA;\r\n    public        Test1 I_DATA;\r\n}\r\n"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/reflect/Test10.java",
    "content": "package com.blankj.utilcode.util.reflect;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/05/03\n *     desc  :\n * </pre>\n */\npublic interface Test10 {\n\n    void setFoo(String s);\n\n    void setBar(boolean b);\n\n    void setBaz(String baz);\n\n    void testIgnore();\n\n    String getFoo();\n\n    boolean isBar();\n\n    String getBaz();\n}\n"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/reflect/Test2.java",
    "content": "package com.blankj.utilcode.util.reflect;\r\n\r\n/**\r\n * <pre>\r\n *     author: Blankj\r\n *     blog  : http://blankj.com\r\n *     time  : 2018/01/12\r\n *     desc  :\r\n * </pre>\r\n */\r\npublic class Test2 {\r\n\r\n    public final Object          n;\r\n    public final ConstructorType constructorType;\r\n\r\n    public Test2() {\r\n        this.n = null;\r\n        this.constructorType = ConstructorType.NO_ARGS;\r\n    }\r\n\r\n    public Test2(Integer n) {\r\n        this.n = n;\r\n        this.constructorType = ConstructorType.INTEGER;\r\n    }\r\n\r\n    public Test2(Number n) {\r\n        this.n = n;\r\n        this.constructorType = ConstructorType.NUMBER;\r\n    }\r\n\r\n    public Test2(Object n) {\r\n        this.n = n;\r\n        this.constructorType = ConstructorType.OBJECT;\r\n    }\r\n\r\n    public static enum ConstructorType {\r\n        NO_ARGS,\r\n        INTEGER,\r\n        NUMBER,\r\n        OBJECT\r\n    }\r\n}\r\n"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/reflect/Test3.java",
    "content": "package com.blankj.utilcode.util.reflect;\r\n\r\n/**\r\n * <pre>\r\n *     author: Blankj\r\n *     blog  : http://blankj.com\r\n *     time  : 2018/01/12\r\n *     desc  :\r\n * </pre>\r\n */\r\npublic class Test3 {\r\n\r\n    public Object     n;\r\n    public MethodType methodType;\r\n\r\n    public void method() {\r\n        this.n = null;\r\n        this.methodType = MethodType.NO_ARGS;\r\n    }\r\n\r\n    public void method(Integer n1) {\r\n        this.n = n1;\r\n        this.methodType = MethodType.INTEGER;\r\n    }\r\n\r\n    public void method(Number n1) {\r\n        this.n = n1;\r\n        this.methodType = MethodType.NUMBER;\r\n    }\r\n\r\n    public void method(Object n1) {\r\n        this.n = n1;\r\n        this.methodType = MethodType.OBJECT;\r\n    }\r\n\r\n    public static enum MethodType {\r\n        NO_ARGS,\r\n        INTEGER,\r\n        NUMBER,\r\n        OBJECT\r\n    }\r\n}\r\n"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/reflect/Test4.java",
    "content": "package com.blankj.utilcode.util.reflect;\r\n\r\n/**\r\n * <pre>\r\n *     author: Blankj\r\n *     blog  : http://blankj.com\r\n *     time  : 2018/01/12\r\n *     desc  :\r\n * </pre>\r\n */\r\npublic class Test4 {\r\n\r\n    public static void s_method() {\r\n    }\r\n\r\n    public void i_method() {\r\n    }\r\n}\r\n"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/reflect/Test5.java",
    "content": "package com.blankj.utilcode.util.reflect;\r\n\r\n/**\r\n * <pre>\r\n *     author: Blankj\r\n *     blog  : http://blankj.com\r\n *     time  : 2018/01/12\r\n *     desc  :\r\n * </pre>\r\n */\r\npublic class Test5 {\r\n\r\n    private static void s_method() {\r\n    }\r\n\r\n    private void i_method() {\r\n    }\r\n}\r\n"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/reflect/Test6.java",
    "content": "package com.blankj.utilcode.util.reflect;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/01/12\n *     desc  :\n * </pre>\n */\npublic class Test6 {\n    public Map<String, String> map = new HashMap<String, String>();\n\n    public void put(String name, String value) {\n        map.put(name, value);\n    }\n}\n"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/reflect/Test7.java",
    "content": "package com.blankj.utilcode.util.reflect;\r\n\r\n/**\r\n * <pre>\r\n *     author: Blankj\r\n *     blog  : http://blankj.com\r\n *     time  : 2018/01/12\r\n *     desc  :\r\n * </pre>\r\n */\r\npublic class Test7 {\r\n\r\n    public final String  s;\r\n    public final Integer i;\r\n\r\n    private Test7(int i) {\r\n        this(null, i);\r\n    }\r\n\r\n    private Test7(String s) {\r\n        this(s, null);\r\n    }\r\n\r\n    private Test7(String s, int i) {\r\n        this(s, (Integer) i);\r\n    }\r\n\r\n    private Test7(String s, Integer i) {\r\n        this.s = s;\r\n        this.i = i;\r\n    }\r\n}\r\n"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/reflect/Test8.java",
    "content": "package com.blankj.utilcode.util.reflect;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/01/12\n *     desc  :\n * </pre>\n */\npublic class Test8 {\n\n\n    public static final int     SF_INT1 = new Integer(0);\n    public static final Integer SF_INT2 = new Integer(0);\n    public final        int     F_INT1  = new Integer(0);\n    public final        Integer F_INT2  = new Integer(0);\n\n    public static Test8 S_DATA;\n    public        Test8 I_DATA;\n}\n"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/reflect/Test9.java",
    "content": "package com.blankj.utilcode.util.reflect;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/05/03\n *     desc  :\n * </pre>\n */\npublic interface Test9 {\n    String substring(int beginIndex);\n\n    String substring(int beginIndex, int endIndex);\n\n    String substring(Integer beginIndex);\n\n    String substring(Integer beginIndex, Integer endIndex);\n}\n"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/reflect/TestHierarchicalMethodsBase.java",
    "content": "package com.blankj.utilcode.util.reflect;\r\n\r\n/**\r\n * <pre>\r\n *     author: Blankj\r\n *     blog  : http://blankj.com\r\n *     time  : 2018/01/12\r\n *     desc  :\r\n * </pre>\r\n */\r\npublic class TestHierarchicalMethodsBase {\r\n\r\n    public static String PUBLIC_RESULT  = \"PUBLIC_BASE\";\r\n    public static String PRIVATE_RESULT = \"PRIVATE_BASE\";\r\n\r\n    private int invisibleField1;\r\n    private int invisibleField2;\r\n    public  int visibleField1;\r\n    public  int visibleField2;\r\n\r\n    public String pub_base_method(int number) {\r\n        return PUBLIC_RESULT;\r\n    }\r\n\r\n    public String pub_method(int number) {\r\n        return PUBLIC_RESULT;\r\n    }\r\n\r\n    private String priv_method(int number) {\r\n        return PRIVATE_RESULT;\r\n    }\r\n\r\n    private String very_priv_method() {\r\n        return PRIVATE_RESULT;\r\n    }\r\n}\r\n"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/reflect/TestHierarchicalMethodsSubclass.java",
    "content": "package com.blankj.utilcode.util.reflect;\r\n\r\n/**\r\n * <pre>\r\n *     author: Blankj\r\n *     blog  : http://blankj.com\r\n *     time  : 2018/01/12\r\n *     desc  :\r\n * </pre>\r\n */\r\npublic class TestHierarchicalMethodsSubclass extends TestHierarchicalMethodsBase {\r\n\r\n    public static String PUBLIC_RESULT  = \"PUBLIC_SUB\";\r\n    public static String PRIVATE_RESULT = \"PRIVATE_SUB\";\r\n\r\n    // Both of these are hiding fields in the super type\r\n    private int invisibleField2;\r\n    public  int visibleField2;\r\n\r\n    private int invisibleField3;\r\n    public  int visibleField3;\r\n\r\n    private String priv_method(int number) {\r\n        return PRIVATE_RESULT;\r\n    }\r\n\r\n    private String pub_method(Integer number) {\r\n        return PRIVATE_RESULT;\r\n    }\r\n}\r\n"
  },
  {
    "path": "lib/utilcode/src/test/java/com/blankj/utilcode/util/reflect/TestPrivateStaticFinal.java",
    "content": "package com.blankj.utilcode.util.reflect;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/01/12\n *     desc  :\n * </pre>\n */\npublic class TestPrivateStaticFinal {\n    private static final int     I1 = new Integer(1);\n    private static final Integer I2 = new Integer(1);\n}\n"
  },
  {
    "path": "lib/utilcode/src/test/res/encrypt/MD5.txt",
    "content": "MD5"
  },
  {
    "path": "lib/utilcode/src/test/res/file/GBK.txt",
    "content": "GBK\nҰй"
  },
  {
    "path": "lib/utilcode/src/test/res/file/UTF8.txt",
    "content": "﻿UTF8\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7"
  },
  {
    "path": "lib/utilcode/src/test/res/file/recuresive/UTF8.txt",
    "content": "﻿UTF8\r\n2\r\n3\r\n4\r\n5\r\n6\r\n7"
  },
  {
    "path": "lib/utilcode/src/test/res/zip/test.txt",
    "content": "test"
  },
  {
    "path": "lib/utilcode/src/test/res/zip/testDir/test.txt",
    "content": "test"
  },
  {
    "path": "lib/utilcode/src/test/res/zip/测试.txt",
    "content": "测试"
  },
  {
    "path": "lib/utilcode/src/test/res/zip/测试文件夹/测试.txt",
    "content": "测试"
  },
  {
    "path": "lib/utildebug/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "lib/utildebug/build.gradle",
    "content": "dependencies {\n    compileOnly Config.libs.androidx_appcompat.path\n    compileOnly Config.libs.androidx_material.path\n\n    implementation Config.modules.lib_utilcode.dep\n    implementation Config.libs.swipe_panel.path\n    implementation Config.libs.photo_view.path\n\n    testImplementation Config.libs.test_junit.path\n    testImplementation Config.libs.test_robolectric.path\n    testImplementation Config.libs.androidx_appcompat.path\n}"
  },
  {
    "path": "lib/utildebug/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "lib/utildebug/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.blankj.utildebug\" >\n\n    <uses-permission android:name=\"android.permission.SYSTEM_ALERT_WINDOW\" />\n\n</manifest>\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/DebugUtils.java",
    "content": "package com.blankj.utildebug;\n\nimport android.app.Activity;\nimport android.app.Application;\nimport android.os.Bundle;\nimport android.view.ViewGroup;\nimport android.view.ViewParent;\n\nimport com.blankj.utilcode.util.Utils;\nimport com.blankj.utildebug.debug.IDebug;\nimport com.blankj.utildebug.debug.tool.AbsToolDebug;\nimport com.blankj.utildebug.icon.DebugIcon;\nimport com.blankj.utildebug.menu.DebugMenu;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport androidx.annotation.DrawableRes;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/28\n *     desc  : utils about debug\n * </pre>\n */\npublic class DebugUtils {\n\n    private DebugUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    static {\n        List<IDebug> debugList = new ArrayList<>();\n        AbsToolDebug.addToolDebugs(debugList);\n        DebugMenu.getInstance().setDebugs(debugList);\n        getApp().registerActivityLifecycleCallbacks(ActivityLifecycleImpl.instance);\n    }\n\n    public static void setIconId(final @DrawableRes int icon) {\n        DebugIcon.getInstance().setIconId(icon);\n    }\n\n    public static void addDebugs(final List<IDebug> debugs) {\n        DebugMenu.getInstance().addDebugs(debugs);\n    }\n\n    public static Application getApp() {\n        return Utils.getApp();\n    }\n\n    static class ActivityLifecycleImpl implements Application.ActivityLifecycleCallbacks {\n\n        private static ActivityLifecycleImpl instance = new ActivityLifecycleImpl();\n\n        private int                    mConfigCount = 0;\n        private ViewGroup.LayoutParams mParams      = new ViewGroup.LayoutParams(\n                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT\n        );\n\n        @Override\n        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {\n\n        }\n\n        @Override\n        public void onActivityStarted(Activity activity) {\n            if (mConfigCount < 0) {\n                ++mConfigCount;\n            }\n        }\n\n        @Override\n        public void onActivityResumed(Activity activity) {\n            ViewParent parent = DebugIcon.getInstance().getParent();\n            if (parent != null) {\n                ((ViewGroup) parent).removeView(DebugIcon.getInstance());\n            }\n            ((ViewGroup) activity.findViewById(android.R.id.content)).addView(DebugIcon.getInstance(), mParams);\n        }\n\n        @Override\n        public void onActivityPaused(Activity activity) {\n            ((ViewGroup) activity.findViewById(android.R.id.content)).removeView(DebugIcon.getInstance());\n        }\n\n        @Override\n        public void onActivityStopped(Activity activity) {\n            if (activity.isChangingConfigurations()) {\n                --mConfigCount;\n            }\n        }\n\n        @Override\n        public void onActivitySaveInstanceState(Activity activity, Bundle outState) {\n\n        }\n\n        @Override\n        public void onActivityDestroyed(Activity activity) {\n\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/base/drawable/PolygonDrawable.java",
    "content": "package com.blankj.utildebug.base.drawable;\n\nimport android.graphics.Canvas;\nimport android.graphics.ColorFilter;\nimport android.graphics.Paint;\nimport android.graphics.Path;\nimport android.graphics.PixelFormat;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.Nullable;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/04\n *     desc  :\n * </pre>\n */\npublic class PolygonDrawable extends Drawable {\n\n    private Paint mPaint;\n    private int   mNum;\n\n    public PolygonDrawable(int num, int color) {\n        mNum = num;\n        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);\n        mPaint.setStyle(Paint.Style.FILL);\n        mPaint.setColor(color);\n    }\n\n    @Override\n    public void draw(@NonNull Canvas canvas) {\n        if (mNum < 3) return;\n        final Rect rect = getBounds();\n        float r = rect.right / 2;\n        float x = r;\n        float y = r;\n        Path path = new Path();\n        for (int i = 0; i <= mNum; i++) {\n            float alpha = Double.valueOf(((2f / mNum) * i - 0.5) * Math.PI).floatValue();\n            float nextX = x + Double.valueOf(r * Math.cos(alpha)).floatValue();\n            float nextY = y + Double.valueOf(r * Math.sin(alpha)).floatValue();\n            if (i == 0) {\n                path.moveTo(nextX, nextY);\n            } else {\n                path.lineTo(nextX, nextY);\n            }\n        }\n        canvas.drawPath(path, mPaint);\n    }\n\n    @Override\n    public void setAlpha(int alpha) {\n\n    }\n\n    @Override\n    public void setColorFilter(@Nullable ColorFilter colorFilter) {\n\n    }\n\n    @Override\n    public int getOpacity() {\n        return PixelFormat.OPAQUE;\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/base/rv/BaseItem.java",
    "content": "package com.blankj.utildebug.base.rv;\n\nimport android.util.SparseArray;\nimport android.util.SparseIntArray;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\n\nimport androidx.annotation.LayoutRes;\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/03/16\n *     desc  :\n * </pre>\n */\npublic abstract class BaseItem<T extends BaseItem> {\n\n    private static final SparseIntArray    LAYOUT_SPARSE_ARRAY = new SparseIntArray();\n    private static final SparseArray<View> VIEW_SPARSE_ARRAY   = new SparseArray<>();\n\n    static ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n        int layoutByType = LAYOUT_SPARSE_ARRAY.get(viewType, -1);\n        if (layoutByType != -1) {\n            return new ItemViewHolder(LayoutInflater.from(parent.getContext()).inflate(layoutByType, parent, false));\n        }\n        View viewByType = VIEW_SPARSE_ARRAY.get(viewType);\n        if (viewByType != null) {\n            return new ItemViewHolder(viewByType);\n        }\n        throw new RuntimeException(\"onCreateViewHolder: get holder from view type failed.\");\n    }\n\n    public abstract void bind(@NonNull final ItemViewHolder holder, final int position);\n\n    public void onViewRecycled(@NonNull final ItemViewHolder holder, final int position) {/**/}\n\n    public long getItemId() {\n        return RecyclerView.NO_ID;\n    }\n\n    private int viewType;\n    BaseItemAdapter<T> mAdapter;\n\n    public BaseItem(@LayoutRes int layoutId) {\n        viewType = getViewTypeByLayoutId(layoutId);\n        LAYOUT_SPARSE_ARRAY.put(viewType, layoutId);\n    }\n\n    public BaseItem(@NonNull View view) {\n        viewType = getViewTypeByView(view);\n        VIEW_SPARSE_ARRAY.put(viewType, view);\n    }\n\n    public int getViewType() {\n        return viewType;\n    }\n\n    public BaseItemAdapter<T> getAdapter() {\n        return mAdapter;\n    }\n\n    public boolean isViewType(@LayoutRes int layoutId) {\n        return viewType == getViewTypeByLayoutId(layoutId);\n    }\n\n    public boolean isViewType(@NonNull View view) {\n        return viewType == getViewTypeByView(view);\n    }\n\n    private int getViewTypeByLayoutId(@LayoutRes int layoutId) {\n        return layoutId + getClass().hashCode();\n    }\n\n    private int getViewTypeByView(@NonNull View view) {\n        return view.hashCode() + getClass().hashCode();\n    }\n\n    public void update() {\n        //noinspection unchecked\n        getAdapter().updateItem((T) this);\n    }\n\n    public int getIndex() {\n        //noinspection SuspiciousMethodCalls\n        return getAdapter().getItems().indexOf(this);\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/base/rv/BaseItemAdapter.java",
    "content": "package com.blankj.utildebug.base.rv;\n\nimport android.view.ViewGroup;\n\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\n\nimport androidx.annotation.IntRange;\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/08/22\n *     desc  :\n * </pre>\n */\npublic class BaseItemAdapter<Item extends BaseItem> extends RecyclerView.Adapter<ItemViewHolder> {\n\n    public  List<Item>   mItems;\n    private RecyclerView mRecyclerView;\n\n    public BaseItemAdapter() {\n        this(false);\n    }\n\n    public BaseItemAdapter(boolean hasStableIds) {\n        setHasStableIds(hasStableIds);\n    }\n\n    @Override\n    public final int getItemViewType(int position) {\n        Item item = mItems.get(position);\n        item.mAdapter = this;\n        return item.getViewType();\n    }\n\n    @Override\n    public long getItemId(int position) {\n        return mItems.get(position).getItemId();\n    }\n\n    @NonNull\n    @Override\n    public ItemViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {\n        return Item.onCreateViewHolder(parent, viewType);\n    }\n\n    @Override\n    public final void onBindViewHolder(@NonNull ItemViewHolder holder, int position) {\n        mItems.get(position).bind(holder, position);\n    }\n\n    @Override\n    public int getItemCount() {\n        return mItems.size();\n    }\n\n    @Override\n    public void onViewRecycled(@NonNull ItemViewHolder holder) {\n        super.onViewRecycled(holder);\n        int position = holder.getAdapterPosition();\n        if (position < 0 || position >= mItems.size()) {\n            return;\n        }\n        mItems.get(position).onViewRecycled(holder, position);\n    }\n\n    @Override\n    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {\n        super.onAttachedToRecyclerView(recyclerView);\n        mRecyclerView = recyclerView;\n    }\n\n    public RecyclerView getRecyclerView() {\n        return mRecyclerView;\n    }\n\n    public void setItems(@NonNull final List<Item> items) {\n        mItems = items;\n    }\n\n    public List<Item> getItems() {\n        return Collections.unmodifiableList(mItems);\n    }\n\n    public Item getItem(@IntRange(from = 0) final int position) {\n        return mItems.get(position);\n    }\n\n    public boolean isEmpty() {\n        return mItems.isEmpty();\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // id\n    ///////////////////////////////////////////////////////////////////////////\n\n    public Item getItemById(final long id) {\n        int itemIndex = getItemIndexById(id);\n        if (itemIndex != -1) {\n            return mItems.get(itemIndex);\n        } else {\n            return null;\n        }\n    }\n\n    public int getItemIndexById(final long id) {\n        for (int i = 0; i < mItems.size(); i++) {\n            if (getItemId(i) == id) {\n                return i;\n            }\n        }\n        return -1;\n    }\n\n    public boolean hasItemWithId(final long id) {\n        return getItemIndexById(id) != -1;\n    }\n\n    public int replaceItemById(final long id, @NonNull final Item item) {\n        return replaceItemById(id, item, false);\n    }\n\n    public int replaceItemById(final long id, @NonNull final Item item, boolean notifyChanged) {\n        int itemIndex = getItemIndexById(id);\n        if (itemIndex != -1) {\n            replaceItem(itemIndex, item, notifyChanged);\n        }\n        return itemIndex;\n    }\n\n    public int removeItemById(final long id) {\n        return removeItemById(id, false);\n    }\n\n    public int removeItemById(final long id, boolean notifyRemoved) {\n        for (int i = 0; i < mItems.size(); i++) {\n            if (getItemId(i) == id) {\n                removeItem(i, notifyRemoved);\n                return i;\n            }\n        }\n        return -1;\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // operate\n    ///////////////////////////////////////////////////////////////////////////\n\n    public void updateItem(@NonNull final Item item) {\n        int itemIndex = mItems.indexOf(item);\n        if (itemIndex != -1) {\n            notifyItemChanged(itemIndex);\n        }\n    }\n\n    public void updateItem(@IntRange(from = 0) final int index) {\n        notifyItemChanged(index);\n    }\n\n    public void addItem(@NonNull final Item item) {\n        addItem(item, false);\n    }\n\n    public void addItem(@NonNull final Item item, boolean notifyInserted) {\n        mItems.add(item);\n        if (notifyInserted) notifyItemInserted(mItems.size() - 1);\n    }\n\n    public void addItem(@IntRange(from = 0) final int index, @NonNull final Item item) {\n        addItem(index, item, false);\n    }\n\n    public void addItem(@IntRange(from = 0) final int index, @NonNull final Item item, boolean notifyInserted) {\n        mItems.add(index, item);\n        if (notifyInserted) notifyItemInserted(index);\n    }\n\n    public void addItems(@NonNull final List<Item> items) {\n        addItems(items, false);\n    }\n\n    public void addItems(@NonNull final List<Item> items, boolean notifyInserted) {\n        mItems.addAll(items);\n        if (notifyInserted) notifyItemRangeInserted(mItems.size() - items.size() - 1, items.size());\n    }\n\n    public void addItems(@IntRange(from = 0) final int index, @NonNull final List<Item> items) {\n        addItems(index, items, false);\n    }\n\n    public void addItems(@IntRange(from = 0) final int index, @NonNull final List<Item> items, boolean notifyInserted) {\n        mItems.addAll(index, items);\n        if (notifyInserted) notifyItemRangeInserted(index, items.size());\n    }\n\n    public void swapItem(@IntRange(from = 0) final int firstIndex, @IntRange(from = 0) final int secondIndex) {\n        swapItem(firstIndex, secondIndex, false);\n    }\n\n    public void swapItem(@IntRange(from = 0) final int firstIndex,\n                         @IntRange(from = 0) final int secondIndex, boolean notifyMoved) {\n        Collections.swap(mItems, firstIndex, secondIndex);\n        if (notifyMoved) notifyItemMoved(firstIndex, secondIndex);\n    }\n\n    public Item replaceItem(@IntRange(from = 0) final int index, @NonNull final Item item) {\n        return replaceItem(index, item, false);\n    }\n\n    public Item replaceItem(@IntRange(from = 0) final int index, @NonNull final Item item, boolean notifyChanged) {\n        Item prevItem = mItems.set(index, item);\n        if (notifyChanged) notifyItemChanged(index);\n        return prevItem;\n    }\n\n    public boolean replaceItems(@NonNull final List<Item> items) {\n        return replaceItems(items, false);\n    }\n\n    public boolean replaceItems(@NonNull final List<Item> items, boolean notifyChanged) {\n        mItems.clear();\n        boolean added = mItems.addAll(items);\n        if (notifyChanged) notifyDataSetChanged();\n        return added;\n    }\n\n    public Item removeItem(@IntRange(from = 0) final int index) {\n        return removeItem(index, false);\n    }\n\n    public Item removeItem(@IntRange(from = 0) final int index, boolean notifyRemoved) {\n        Item removedItem = mItems.remove(index);\n        if (notifyRemoved) notifyItemRemoved(index);\n        return removedItem;\n    }\n\n    public int removeItem(@NonNull final Item item) {\n        return removeItem(item, false);\n    }\n\n    public int removeItem(@NonNull final Item item, boolean notifyRemoved) {\n        int itemIndex = mItems.indexOf(item);\n        if (itemIndex != -1) {\n            mItems.remove(itemIndex);\n            if (notifyRemoved) notifyItemRemoved(itemIndex);\n        }\n        return itemIndex;\n    }\n\n    public void clear() {\n        clear(false);\n    }\n\n    public void clear(boolean notifyDataSetChanged) {\n        mItems.clear();\n        if (notifyDataSetChanged) notifyDataSetChanged();\n    }\n\n    public void sortItems(@NonNull final Comparator<Item> comparator) {\n        sortItems(comparator, false);\n    }\n\n    public void sortItems(@NonNull final Comparator<Item> comparator, boolean notifyDataSetChanged) {\n        Collections.sort(mItems, comparator);\n        if (notifyDataSetChanged) notifyDataSetChanged();\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/base/rv/ItemViewHolder.java",
    "content": "package com.blankj.utildebug.base.rv;\n\nimport android.util.SparseArray;\nimport android.view.View;\n\nimport androidx.annotation.IdRes;\nimport androidx.recyclerview.widget.RecyclerView;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/08/22\n *     desc  :\n * </pre>\n */\npublic class ItemViewHolder extends RecyclerView.ViewHolder {\n\n    private SparseArray<View> viewArray = new SparseArray<>();\n\n    public ItemViewHolder(View itemView) {\n        super(itemView);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public <T extends View> T findViewById(@IdRes final int viewId) {\n        View view = viewArray.get(viewId);\n        if (view == null) {\n            view = itemView.findViewById(viewId);\n            viewArray.put(viewId, view);\n        }\n        return (T) view;\n    }\n\n    public void setOnClickListener(@IdRes final int viewId, View.OnClickListener listener) {\n        findViewById(viewId).setOnClickListener(listener);\n    }\n\n    public void setOnLongClickListener(@IdRes final int viewId, View.OnLongClickListener listener) {\n        findViewById(viewId).setOnLongClickListener(listener);\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/base/rv/RecycleViewDivider.java",
    "content": "package com.blankj.utildebug.base.rv;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.graphics.Canvas;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\nimport android.view.View;\nimport android.widget.LinearLayout;\n\nimport androidx.annotation.DrawableRes;\nimport androidx.annotation.NonNull;\nimport androidx.core.content.ContextCompat;\nimport androidx.core.view.ViewCompat;\nimport androidx.recyclerview.widget.RecyclerView;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2017/08/17\n *     desc  :\n * </pre>\n */\npublic class RecycleViewDivider extends RecyclerView.ItemDecoration {\n\n    public static final int HORIZONTAL = LinearLayout.HORIZONTAL;\n    public static final int VERTICAL   = LinearLayout.VERTICAL;\n\n    protected Drawable mDivider;\n\n    protected int     mOrientation;\n    protected boolean mShowFooterDivider;\n\n    protected final Rect mBounds = new Rect();\n\n    public RecycleViewDivider(Context context, int orientation, @DrawableRes int resId) {\n        this(context, orientation, resId, false);\n    }\n\n    public RecycleViewDivider(Context context, int orientation, @NonNull Drawable divider) {\n        this(context, orientation, divider, false);\n    }\n\n    public RecycleViewDivider(Context context, int orientation, @DrawableRes int resId, boolean showFooterDivider) {\n        this(context, orientation, ContextCompat.getDrawable(context, resId), showFooterDivider);\n    }\n\n    public RecycleViewDivider(Context context, int orientation, @NonNull Drawable divider, boolean showFooterDivider) {\n        setOrientation(orientation);\n        mDivider = divider;\n        mShowFooterDivider = showFooterDivider;\n    }\n\n    private void setOrientation(int orientation) {\n        if (orientation != HORIZONTAL && orientation != VERTICAL) {\n            throw new IllegalArgumentException(\n                    \"Invalid orientation. It should be either HORIZONTAL or VERTICAL\");\n        }\n        mOrientation = orientation;\n    }\n\n    @Override\n    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {\n        if (parent.getLayoutManager() == null) {\n            return;\n        }\n        if (mOrientation == VERTICAL) {\n            drawVertical(c, parent);\n        } else {\n            drawHorizontal(c, parent);\n        }\n    }\n\n    @SuppressLint(\"NewApi\")\n    protected void drawVertical(Canvas canvas, RecyclerView parent) {\n        canvas.save();\n        final int left;\n        final int right;\n        if (parent.getClipToPadding()) {\n            left = parent.getPaddingLeft();\n            right = parent.getWidth() - parent.getPaddingRight();\n            canvas.clipRect(left, parent.getPaddingTop(), right,\n                    parent.getHeight() - parent.getPaddingBottom());\n        } else {\n            left = 0;\n            right = parent.getWidth();\n        }\n\n        final int childCount = parent.getChildCount();\n        for (int i = 0; i < childCount; i++) {\n            if (i == childCount - 1 && !mShowFooterDivider) continue;\n            final View child = parent.getChildAt(i);\n            parent.getDecoratedBoundsWithMargins(child, mBounds);\n            final int bottom = mBounds.bottom + Math.round(ViewCompat.getTranslationY(child));\n            final int top = bottom - mDivider.getIntrinsicHeight();\n            mDivider.setBounds(left, top, right, bottom);\n            mDivider.draw(canvas);\n        }\n        canvas.restore();\n    }\n\n    @SuppressLint(\"NewApi\")\n    protected void drawHorizontal(Canvas canvas, RecyclerView parent) {\n        canvas.save();\n        final int top;\n        final int bottom;\n        if (parent.getClipToPadding()) {\n            top = parent.getPaddingTop();\n            bottom = parent.getHeight() - parent.getPaddingBottom();\n            canvas.clipRect(parent.getPaddingLeft(), top,\n                    parent.getWidth() - parent.getPaddingRight(), bottom);\n        } else {\n            top = 0;\n            bottom = parent.getHeight();\n        }\n\n        final int childCount = parent.getChildCount();\n        for (int i = 0; i < childCount; i++) {\n            if (i == childCount - 1 && !mShowFooterDivider) continue;\n            final View child = parent.getChildAt(i);\n            parent.getLayoutManager().getDecoratedBoundsWithMargins(child, mBounds);\n            final int right = mBounds.right + Math.round(ViewCompat.getTranslationX(child));\n            final int left = right - mDivider.getIntrinsicWidth();\n            mDivider.setBounds(left, top, right, bottom);\n            mDivider.draw(canvas);\n        }\n        canvas.restore();\n    }\n\n    @Override\n    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {\n        if (mOrientation == VERTICAL) {\n            outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());\n        } else {\n            outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/base/view/BaseContentFloatView.java",
    "content": "package com.blankj.utildebug.base.view;\n\nimport android.content.res.Configuration;\nimport android.graphics.drawable.Drawable;\nimport android.view.Gravity;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.WindowManager;\nimport android.widget.ImageView;\nimport android.widget.LinearLayout;\nimport android.widget.RelativeLayout;\nimport android.widget.TextView;\n\nimport com.blankj.swipepanel.SwipePanel;\nimport com.blankj.utilcode.util.ClickUtils;\nimport com.blankj.utilcode.util.SizeUtils;\nimport com.blankj.utilcode.util.StringUtils;\nimport com.blankj.utilcode.util.TouchUtils;\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.base.view.listener.OnRefreshListener;\nimport com.blankj.utildebug.config.DebugConfig;\nimport com.blankj.utildebug.helper.ShadowHelper;\nimport com.blankj.utildebug.helper.WindowHelper;\n\nimport java.util.Stack;\n\nimport androidx.annotation.LayoutRes;\nimport androidx.annotation.StringRes;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/30\n *     desc  :\n * </pre>\n */\npublic abstract class BaseContentFloatView<T extends BaseContentFloatView<T>> extends BaseFloatView {\n\n    private static final int ROTATE_DELAY = 30;\n\n    private LinearLayout       bcfRootLayout;\n    private RelativeLayout     bcfTitleRl;\n    private ImageView          bcfCloseIv;\n    private TextView           bcfTitleTv;\n    private ImageView          bcfAdjustIv;\n    private SwipePanel         swipePanel;\n    private BaseContentView<T> mContentView;\n\n    private OnRefreshListener mRefreshListener;\n    private Runnable          mRotateRunnable = new Runnable() {\n        @Override\n        public void run() {\n            Drawable topDrawable = swipePanel.getTopDrawable();\n            topDrawable.setLevel(topDrawable.getLevel() + 500);\n            swipePanel.invalidate();\n            startRotate();\n        }\n    };\n\n    private static final ViewGroup.LayoutParams PARAMS = new ViewGroup.LayoutParams(\n            ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT\n    );\n\n    private Stack<BaseContentView<T>> mViewStack = new Stack<>();\n\n    @StringRes\n    public abstract int bindTitle();\n\n    @LayoutRes\n    public abstract int bindContentLayout();\n\n    @Override\n    public int bindLayout() {\n        return R.layout.du_base_content_float;\n    }\n\n    public BaseContentFloatView() {\n        bcfRootLayout = findViewById(R.id.bcfRootLayout);\n        ShadowHelper.applyFloatView(bcfRootLayout);\n\n        initTitleBar();\n        initSwipePanel();\n\n        if (bindContentLayout() != NO_ID) {\n            //noinspection unchecked\n            new BaseContentView<T>() {\n                @Override\n                public int bindLayout() {\n                    return BaseContentFloatView.this.bindContentLayout();\n                }\n\n                @Override\n                public void onAttach() {\n                }\n            }.attach((T) this, true);\n        }\n    }\n\n    public void setTitle(CharSequence title) {\n        bcfTitleTv.setText(title);\n    }\n\n    void setOnRefreshListener(OnRefreshListener listener) {\n        mRefreshListener = listener;\n    }\n\n    void setRefreshEnabled(boolean enabled) {\n        swipePanel.setTopEnabled(enabled);\n    }\n\n    public void setSwipeBackEnabled(boolean enabled) {\n        swipePanel.setLeftEnabled(enabled);\n    }\n\n    public void back() {\n        swipePanel.close(SwipePanel.TOP, false);// 返回先关闭刷新\n        swipePanel.removeAllViews();\n        if (mContentView == null) {\n            dismiss();\n            return;\n        }\n        mContentView.onBack();\n        if (mViewStack.isEmpty()) {\n            dismiss();\n            return;\n        }\n        if (mContentView.isAddStack()) {\n            mViewStack.pop();\n        }\n        if (mViewStack.isEmpty()) {\n            dismiss();\n            return;\n        }\n        BaseContentView<T> peek = mViewStack.peek();\n        swipePanel.addView(peek, PARAMS);\n        setOnRefreshListener(peek.getOnRefreshListener());\n        setRefreshEnabled(peek.isRefreshEnabled());\n        mContentView = peek;\n    }\n\n    public void closeRefresh() {\n        postDelayed(new Runnable() {\n            @Override\n            public void run() {\n                swipePanel.close(SwipePanel.TOP);\n            }\n        }, 500);\n    }\n\n    public BaseContentView<T> getContentView() {\n        return mContentView;\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        DebugConfig.saveViewY(this, mLayoutParams.y);\n        DebugConfig.saveViewHeight(this, mLayoutParams.height);\n        DebugConfig.saveViewAlpha(this, mLayoutParams.alpha);\n        super.onDetachedFromWindow();\n    }\n\n    void replace(BaseContentView<T> view, boolean isAddStack) {\n        if (view == null) return;\n        if (isAddStack) {\n            mViewStack.add(view);\n        }\n        swipePanel.removeAllViews();\n        mContentView = view;\n        mContentView.onAttach();\n        swipePanel.addView(mContentView, PARAMS);\n    }\n\n    private void initTitleBar() {\n        bcfTitleRl = findViewById(R.id.bcfTitleRl);\n        bcfCloseIv = findViewById(R.id.bcfCloseIv);\n        bcfTitleTv = findViewById(R.id.bcfTitleTv);\n        bcfAdjustIv = findViewById(R.id.bcfAdjustIv);\n\n        bcfTitleTv.setText(bindTitle());\n\n        ClickUtils.applyPressedBgDark(bcfTitleRl);\n        bcfTitleRl.setOnClickListener(new ClickUtils.OnMultiClickListener(2) {\n            @Override\n            public void onTriggerClick(View v) {\n                mLayoutParams.alpha = mLayoutParams.alpha == 0.5f ? 1f : 0.5f;\n                WindowHelper.updateViewLayout(BaseContentFloatView.this, mLayoutParams);\n                DebugConfig.saveViewAlpha(BaseContentFloatView.this, mLayoutParams.alpha);\n            }\n\n            @Override\n            public void onBeforeTriggerClick(View v, int count) {\n                if (count == 1) {\n\n                }\n            }\n        });\n        TouchUtils.setOnTouchListener(bcfTitleRl, new TouchUtils.OnTouchUtilsListener() {\n            @Override\n            public boolean onDown(View view, int x, int y, MotionEvent event) {\n                return true;\n            }\n\n            @Override\n            public boolean onMove(View view, int direction, int x, int y, int dx, int dy, int totalX, int totalY, MotionEvent event) {\n                mLayoutParams.y = Math.min(Math.max(mLayoutParams.y + dy, 0), WindowHelper.getAppWindowHeight() - bcfRootLayout.getHeight());\n                WindowHelper.updateViewLayout(BaseContentFloatView.this, mLayoutParams);\n                return true;\n            }\n\n            @Override\n            public boolean onStop(View view, int direction, int x, int y, int totalX, int totalY, int vx, int vy, MotionEvent event) {\n                DebugConfig.saveViewY(BaseContentFloatView.this, mLayoutParams.y);\n                return true;\n            }\n        });\n\n        ClickUtils.applyPressedBgDark(bcfCloseIv, 0.8f);\n        bcfCloseIv.setOnClickListener(new OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                dismiss();\n            }\n        });\n\n        ClickUtils.applyPressedBgDark(bcfAdjustIv, 0.8f);\n        bcfAdjustIv.setOnClickListener(new OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                FloatToast.showLong(FloatToast.WARNING, StringUtils.getString(R.string.du_adjust_tips));\n            }\n        });\n        TouchUtils.setOnTouchListener(bcfAdjustIv, new TouchUtils.OnTouchUtilsListener() {\n\n            private int minHeight;\n\n            @Override\n            public boolean onDown(View view, int x, int y, MotionEvent event) {\n                int[] locations = new int[2];\n                getLocationOnScreen(locations);\n                mLayoutParams.height = WindowHelper.getAppWindowHeight() - locations[1];\n                WindowHelper.updateViewLayout(BaseContentFloatView.this, mLayoutParams);\n\n                minHeight = bcfTitleRl.getHeight() + SizeUtils.dp2px(30);\n                return true;\n            }\n\n            @Override\n            public boolean onMove(View view, int direction, int x, int y, int dx, final int dy, int totalX, int totalY, MotionEvent event) {\n                ViewGroup.LayoutParams layoutParams = bcfRootLayout.getLayoutParams();\n                layoutParams.height = Math.min(Math.max(bcfRootLayout.getHeight() + dy, minHeight), mLayoutParams.height);\n                bcfRootLayout.setLayoutParams(layoutParams);\n                return true;\n            }\n\n            @Override\n            public boolean onStop(View view, int direction, int x, int y, int totalX, int totalY, int vx, int vy, MotionEvent event) {\n                mLayoutParams.height = bcfRootLayout.getHeight();\n                WindowHelper.updateViewLayout(BaseContentFloatView.this, mLayoutParams);\n                DebugConfig.saveViewHeight(BaseContentFloatView.this, mLayoutParams.height);\n                return true;\n            }\n        });\n    }\n\n    private void initSwipePanel() {\n        swipePanel = findViewById(R.id.bcfSwipePanel);\n        swipePanel.setOnFullSwipeListener(new SwipePanel.OnFullSwipeListener() {\n            @Override\n            public void onFullSwipe(int direction) {\n                if (direction == SwipePanel.LEFT) {\n                    swipePanel.close(direction);\n                    back();\n                } else if (direction == SwipePanel.TOP) {\n                    if (mRefreshListener != null) {\n                        startRotate();\n                        mRefreshListener.onRefresh(BaseContentFloatView.this);\n                    }\n                }\n            }\n        });\n\n        swipePanel.setOnProgressChangedListener(new SwipePanel.OnProgressChangedListener() {\n            @Override\n            public void onProgressChanged(int direction, float progress, boolean isTouch) {\n                if (direction == SwipePanel.TOP) {\n                    Drawable topDrawable = swipePanel.getTopDrawable();\n                    if (isTouch) {\n                        topDrawable.setLevel((int) (progress * 20000));\n                    } else {\n                        if (progress < 0.5) {\n                            stopRotate();\n                        }\n                    }\n                }\n            }\n        });\n    }\n\n    private void startRotate() {\n        swipePanel.postDelayed(mRotateRunnable, ROTATE_DELAY);\n    }\n\n    private void stopRotate() {\n        swipePanel.removeCallbacks(mRotateRunnable);\n    }\n\n    @Override\n    protected void onCreateLayoutParams() {\n        super.onCreateLayoutParams();\n        mLayoutParams.gravity = Gravity.TOP;\n        mLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;\n        mLayoutParams.height = DebugConfig.getViewHeight(BaseContentFloatView.this, WindowManager.LayoutParams.WRAP_CONTENT);\n        mLayoutParams.windowAnimations = R.style.FloatAnimation;\n        mLayoutParams.alpha = DebugConfig.getViewAlpha(this);\n        mLayoutParams.y = DebugConfig.getViewY(this);\n        post(new Runnable() {\n            @Override\n            public void run() {\n                if (getParent() != null) {\n                    wrapWindow();\n                }\n            }\n        });\n    }\n\n    @Override\n    protected void onConfigurationChanged(Configuration newConfig) {\n        super.onConfigurationChanged(newConfig);\n        wrapWindow();\n    }\n\n    private void wrapWindow() {\n        int[] locations = new int[2];\n        getLocationOnScreen(locations);\n        int floatViewHeight = DebugConfig.getViewHeight(BaseContentFloatView.this, bcfRootLayout.getHeight());\n        if (locations[1] + floatViewHeight > WindowHelper.getAppWindowHeight()) {\n            floatViewHeight = WindowHelper.getAppWindowHeight() - locations[1];\n        }\n        mLayoutParams.height = floatViewHeight;\n\n        WindowHelper.updateViewLayout(BaseContentFloatView.this, mLayoutParams);\n\n        ViewGroup.LayoutParams layoutParams = bcfRootLayout.getLayoutParams();\n        layoutParams.height = mLayoutParams.height;\n        bcfRootLayout.setLayoutParams(layoutParams);\n    }\n}"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/base/view/BaseContentView.java",
    "content": "package com.blankj.utildebug.base.view;\n\nimport android.widget.FrameLayout;\n\nimport com.blankj.utildebug.DebugUtils;\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.base.view.listener.OnBackListener;\nimport com.blankj.utildebug.base.view.listener.OnRefreshListener;\n\nimport androidx.annotation.LayoutRes;\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.RecyclerView;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/01\n *     desc  :\n * </pre>\n */\npublic abstract class BaseContentView<T extends BaseContentFloatView<T>> extends FrameLayout\n        implements OnBackListener {\n\n    private T       mFloatView;\n    private boolean isAddStack;\n\n    private OnRefreshListener mRefreshRunnable;\n    private boolean           mRefreshEnabled;\n\n    public BaseContentView() {\n        super(DebugUtils.getApp());\n        setId(R.id.baseContentView);\n        inflate(getContext(), bindLayout(), this);\n    }\n\n    public void attach(T floatView, boolean isAddStack) {\n        this.mFloatView = floatView;\n        this.isAddStack = isAddStack;\n        floatView.replace(this, isAddStack);\n    }\n\n    @LayoutRes\n    public abstract int bindLayout();\n\n    public abstract void onAttach();\n\n    public T getFloatView() {\n        return mFloatView;\n    }\n\n    public boolean isAddStack() {\n        return isAddStack;\n    }\n\n    public void setOnRefreshListener(RecyclerView rv, OnRefreshListener listener) {\n        mRefreshRunnable = listener;\n        mFloatView.setOnRefreshListener(listener);\n        attachRefresh(rv);\n    }\n\n    @Override\n    public void onBack() {\n    }\n\n    OnRefreshListener getOnRefreshListener() {\n        return mRefreshRunnable;\n    }\n\n    boolean isRefreshEnabled() {\n        return mRefreshEnabled;\n    }\n\n    private void attachRefresh(RecyclerView rv) {\n        rv.addOnScrollListener(new RecyclerView.OnScrollListener() {\n            @Override\n            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {\n                super.onScrollStateChanged(recyclerView, newState);\n                mRefreshEnabled = newState == RecyclerView.SCROLL_STATE_IDLE && !recyclerView.canScrollVertically(-1);\n                mFloatView.setRefreshEnabled(mRefreshEnabled);\n            }\n        });\n        mRefreshEnabled = !rv.canScrollVertically(-1);\n        mFloatView.setRefreshEnabled(mRefreshEnabled);\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/base/view/BaseFloatView.java",
    "content": "package com.blankj.utildebug.base.view;\n\nimport android.app.Activity;\nimport android.graphics.PixelFormat;\nimport android.os.Build;\nimport android.view.KeyEvent;\nimport android.view.WindowManager;\nimport android.widget.RelativeLayout;\n\nimport com.blankj.utilcode.util.ActivityUtils;\nimport com.blankj.utilcode.util.AppUtils;\nimport com.blankj.utilcode.util.Utils;\nimport com.blankj.utildebug.DebugUtils;\nimport com.blankj.utildebug.R;\n\nimport androidx.annotation.CallSuper;\nimport androidx.annotation.LayoutRes;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/10\n *     desc  :\n * </pre>\n */\npublic abstract class BaseFloatView extends RelativeLayout\n        implements Utils.OnAppStatusChangedListener {\n\n    private boolean isCreated;\n\n    protected WindowManager.LayoutParams mLayoutParams = new WindowManager.LayoutParams();\n\n    @LayoutRes\n    public abstract int bindLayout();\n\n    public abstract void initContentView();\n\n    public BaseFloatView() {\n        super(DebugUtils.getApp());\n        setId(R.id.baseFloatView);\n        if (bindLayout() != NO_ID) {\n            inflate(getContext(), bindLayout(), this);\n        }\n        onCreateLayoutParams();\n    }\n\n    void createFloatView() {\n        if (isCreated) return;\n        isCreated = true;\n        initContentView();\n    }\n\n    @CallSuper\n    protected void onCreateLayoutParams() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {\n            mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;\n        } else {\n            mLayoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;\n        }\n        mLayoutParams.format = PixelFormat.TRANSPARENT;\n        mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;\n        try {\n            int currentFlags = (Integer) mLayoutParams.getClass().getField(\"privateFlags\").get(mLayoutParams);\n            mLayoutParams.getClass().getField(\"privateFlags\").set(mLayoutParams, currentFlags | 0x00000040);\n        } catch (Exception ignore) {\n        }\n    }\n\n    public void show() {\n        FloatViewManager.getInstance().show(this);\n    }\n\n    public void dismiss() {\n        FloatViewManager.getInstance().dismiss(this);\n    }\n\n    @Override\n    public WindowManager.LayoutParams getLayoutParams() {\n        return mLayoutParams;\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        AppUtils.registerAppStatusChangedListener(this);\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        AppUtils.unregisterAppStatusChangedListener(this);\n        super.onDetachedFromWindow();\n    }\n\n    @Override\n    public void onForeground(Activity activity) {\n        setVisibility(VISIBLE);\n    }\n\n    @Override\n    public void onBackground(Activity activity) {\n        setVisibility(GONE);\n    }\n\n    ///////////////////////////////////////////////////////////////////////////\n    // When flag with FLAG_NOT_TOUCH_MODAL, should process the key event.\n    ///////////////////////////////////////////////////////////////////////////\n\n    @Override\n    public boolean dispatchKeyEvent(KeyEvent event) {\n        Activity topActivity = ActivityUtils.getTopActivity();\n        if (topActivity != null) {\n            if (topActivity.getWindow().getDecorView().dispatchKeyEvent(event)) {\n                return true;\n            }\n        }\n        return super.dispatchKeyEvent(event);\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/base/view/EmptyGoneTextView.java",
    "content": "package com.blankj.utildebug.base.view;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.widget.TextView;\n\nimport com.blankj.utilcode.util.StringUtils;\n\nimport androidx.annotation.Nullable;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/03\n *     desc  :\n * </pre>\n */\n@SuppressLint(\"AppCompatCustomView\")\npublic class EmptyGoneTextView extends TextView {\n\n    public EmptyGoneTextView(Context context) {\n        this(context, null);\n    }\n\n    public EmptyGoneTextView(Context context, @Nullable AttributeSet attrs) {\n        super(context, attrs);\n        setVisibility(GONE);\n    }\n\n    @Override\n    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {\n        super.onTextChanged(text, start, lengthBefore, lengthAfter);\n        if (StringUtils.isEmpty(text)) {\n            setVisibility(GONE);\n        } else {\n            setVisibility(VISIBLE);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/base/view/FloatEditText.java",
    "content": "package com.blankj.utildebug.base.view;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport android.util.AttributeSet;\nimport android.view.View;\nimport android.view.WindowManager;\nimport android.widget.EditText;\n\nimport com.blankj.utilcode.util.KeyboardUtils;\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.helper.WindowHelper;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/10\n *     desc  :\n * </pre>\n */\n@SuppressLint(\"AppCompatCustomView\")\npublic class FloatEditText extends EditText {\n\n    public FloatEditText(Context context) {\n        super(context);\n        init();\n    }\n\n    public FloatEditText(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init();\n    }\n\n    public FloatEditText(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init();\n    }\n\n    private void init() {\n        setBackgroundResource(R.drawable.du_sel_et_bg);\n        post(new Runnable() {\n            @Override\n            public void run() {\n                View rootView = getRootView();\n                if (rootView instanceof BaseContentFloatView) {\n                    bindFloatView((BaseContentFloatView) rootView);\n                }\n            }\n        });\n    }\n\n    public void bindFloatView(final BaseContentFloatView floatView) {\n        setOnFocusChangeListener(new OnFocusChangeListener() {\n            @Override\n            public void onFocusChange(View v, boolean hasFocus) {\n                WindowManager.LayoutParams params = floatView.getLayoutParams();\n                if ((params.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) {\n                    params.flags = params.flags & ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;\n                    params.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;\n                    WindowHelper.updateViewLayout(floatView, params);\n                    KeyboardUtils.showSoftInput(v);\n                }\n            }\n        });\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        KeyboardUtils.hideSoftInput(this);\n        super.onDetachedFromWindow();\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/base/view/FloatToast.java",
    "content": "package com.blankj.utildebug.base.view;\n\nimport android.annotation.SuppressLint;\nimport android.view.Gravity;\nimport android.view.WindowManager;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport com.blankj.utilcode.util.SizeUtils;\nimport com.blankj.utildebug.R;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport androidx.annotation.IntDef;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/10\n *     desc  :\n * </pre>\n */\npublic class FloatToast extends BaseFloatView {\n\n    public static final int SUCCESS = 0;\n    public static final int WARNING = 1;\n    public static final int ERROR   = 2;\n\n    @IntDef({SUCCESS, WARNING, ERROR})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface Type {\n    }\n\n    private static final FloatToast INSTANCE = new FloatToast();\n\n    private static final Runnable DISMISS_RUNNABLE = new Runnable() {\n        @Override\n        public void run() {\n            INSTANCE.dismiss();\n        }\n    };\n\n    @Type\n    private int    mType;\n    private String mMsg;\n\n    private ImageView toastIconIv;\n    private TextView  toastMsgTv;\n\n    private FloatToast() {\n    }\n\n    @Override\n    public int bindLayout() {\n        return R.layout.du_float_toast;\n    }\n\n    @Override\n    public void initContentView() {\n        toastIconIv = findViewById(R.id.toastIconIv);\n        toastMsgTv = findViewById(R.id.toastMsgTv);\n    }\n\n    @Override\n    protected void onCreateLayoutParams() {\n        super.onCreateLayoutParams();\n\n        mLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;\n        mLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;\n        mLayoutParams.windowAnimations = android.R.style.Animation_Toast;\n        mLayoutParams.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON\n                | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE\n                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;\n        mLayoutParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;\n        mLayoutParams.y = SizeUtils.dp2px(64);\n    }\n\n    @Override\n    public void show() {\n        super.show();\n        if (mType < 0 || mType > 2) {\n            toastIconIv.setVisibility(GONE);\n        } else {\n            toastIconIv.setVisibility(VISIBLE);\n            if (mType == SUCCESS) {\n                toastIconIv.setImageResource(R.drawable.du_ic_toast_success);\n            } else if (mType == WARNING) {\n                toastIconIv.setImageResource(R.drawable.du_ic_toast_warn);\n            } else {\n                toastIconIv.setImageResource(R.drawable.du_ic_toast_error);\n            }\n        }\n        toastMsgTv.setText(mMsg);\n    }\n\n    private void setType(int type) {\n        mType = type;\n    }\n\n    private void setMsg(String msg) {\n        mMsg = msg == null ? \"\" : msg;\n    }\n\n    public static void showShort(String msg) {\n        show(msg, 2000);\n    }\n\n    public static void showShort(@Type int type, String msg) {\n        show(type, msg, 2000);\n    }\n\n    public static void showLong(String msg) {\n        show(msg, 3500);\n    }\n\n    public static void showLong(@Type int type, String msg) {\n        show(type, msg, 3500);\n    }\n\n    @SuppressLint(\"WrongConstant\")\n    public static void show(String msg, long millis) {\n        show(-1, msg, millis);\n    }\n\n    public static void show(@Type int type, String msg, long millis) {\n        INSTANCE.removeCallbacks(DISMISS_RUNNABLE);\n        INSTANCE.dismiss();\n        INSTANCE.setType(type);\n        INSTANCE.setMsg(msg);\n        INSTANCE.show();\n        INSTANCE.postDelayed(DISMISS_RUNNABLE, millis);\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/base/view/FloatViewManager.java",
    "content": "package com.blankj.utildebug.base.view;\n\nimport android.view.WindowManager;\n\nimport com.blankj.utilcode.util.ThreadUtils;\nimport com.blankj.utildebug.helper.WindowHelper;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/30\n *     desc  :\n * </pre>\n */\npublic class FloatViewManager {\n\n    private final WindowManager       mWM         = WindowHelper.getWindowManager();\n    private final List<BaseFloatView> mFloatViews = new ArrayList<>();\n\n    private FloatViewManager() {\n    }\n\n    public static FloatViewManager getInstance() {\n        return LazyHolder.INSTANCE;\n    }\n\n    private static final class LazyHolder {\n        private static final FloatViewManager INSTANCE = new FloatViewManager();\n    }\n\n    public void show(final BaseFloatView view) {\n        ThreadUtils.runOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                if (mFloatViews.contains(view)) return;\n                view.createFloatView();\n                mWM.addView(view, view.getLayoutParams());\n                mFloatViews.add(view);\n            }\n        });\n    }\n\n    public void dismiss(final BaseFloatView view) {\n        ThreadUtils.runOnUiThread(new Runnable() {\n            @Override\n            public void run() {\n                if (!mFloatViews.contains(view)) return;\n                mWM.removeView(view);\n                mFloatViews.remove(view);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/base/view/SearchEditText.java",
    "content": "package com.blankj.utildebug.base.view;\n\nimport android.content.Context;\nimport android.util.AttributeSet;\n\nimport com.blankj.utilcode.util.KeyboardUtils;\nimport com.blankj.utilcode.util.StringUtils;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/06\n *     desc  :\n * </pre>\n */\npublic class SearchEditText extends FloatEditText {\n\n    private static final long LIMIT = 200;\n\n    private OnTextChangedListener mListener;\n    private String                mStartSearchText = \"\";// 记录开始输入前的文本内容\n    private Runnable              mAction          = new Runnable() {\n        @Override\n        public void run() {\n            if (mListener != null) {\n                // 判断最终和开始前是否一致\n                if (!StringUtils.equals(mStartSearchText, getText().toString())) {\n                    mStartSearchText = getText().toString();// 更新 mStartSearchText\n                    mListener.onTextChanged(mStartSearchText);\n                }\n            }\n        }\n    };\n\n    public SearchEditText(Context context) {\n        this(context, null);\n    }\n\n    public SearchEditText(Context context, AttributeSet attrs) {\n        super(context, attrs);\n    }\n\n    /**\n     * 在 LIMIT 时间内连续输入不触发文本变化\n     */\n    public void setOnTextChangedListener(OnTextChangedListener listener) {\n        mListener = listener;\n    }\n\n    public void reset() {\n        mStartSearchText = \"\";\n        setText(\"\");\n        KeyboardUtils.hideSoftInput(this);\n    }\n\n    @Override\n    protected void onTextChanged(final CharSequence text, int start, int lengthBefore, int lengthAfter) {\n        super.onTextChanged(text, start, lengthBefore, lengthAfter);\n        // 移除上一次的回调\n        removeCallbacks(mAction);\n        postDelayed(mAction, LIMIT);\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        removeCallbacks(mAction);\n        KeyboardUtils.hideSoftInput(this);\n        super.onDetachedFromWindow();\n    }\n\n    public interface OnTextChangedListener {\n        void onTextChanged(String text);\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/base/view/SwipeRightMenu.java",
    "content": "package com.blankj.utildebug.base.view;\n\nimport android.animation.ValueAnimator;\nimport android.content.Context;\nimport android.os.SystemClock;\nimport android.util.AttributeSet;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.animation.AccelerateInterpolator;\nimport android.view.animation.DecelerateInterpolator;\nimport android.widget.LinearLayout;\n\nimport com.blankj.utilcode.util.SizeUtils;\n\nimport java.lang.ref.WeakReference;\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport androidx.annotation.Nullable;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/10\n *     desc  :\n * </pre>\n */\npublic class SwipeRightMenu extends LinearLayout {\n\n    private static final AccelerateInterpolator OPEN_INTERPOLATOR  = new AccelerateInterpolator(0.5f);\n    private static final DecelerateInterpolator CLOSE_INTERPOLATOR = new DecelerateInterpolator(0.5f);\n\n    private static boolean                       isTouching;\n    private static WeakReference<SwipeRightMenu> swipeMenuOpened;\n\n    private View mContentView;\n\n    private List<MenuBean> mMenus = new ArrayList<>();\n\n    private int mMenusWidth = 0;\n\n    public SwipeRightMenu(Context context) {\n        this(context, null);\n    }\n\n    public SwipeRightMenu(Context context, @Nullable AttributeSet attrs) {\n        super(context, attrs);\n        setOrientation(HORIZONTAL);\n        post(new Runnable() {\n            @Override\n            public void run() {\n                initView();\n            }\n        });\n    }\n\n    private void initView() {\n        int childCount = getChildCount();\n        if (childCount <= 1) {\n            throw new IllegalArgumentException(\"no menus\");\n        }\n        mContentView = getChildAt(0);\n\n        for (int i = 1; i < childCount; i++) {\n            MenuBean bean = new MenuBean(getChildAt(i));\n            mMenus.add(bean);\n            if (i == 1) {\n                bean.setCloseMargin(0);\n            } else {\n                bean.setCloseMargin(-mMenus.get(i - 2).getWidth());\n            }\n            mMenusWidth += bean.getWidth();\n        }\n        for (int i = 0; i < mMenus.size(); i++) {\n            mMenus.get(i).setOpenMargin(0);\n        }\n    }\n\n    private static final int STATE_DOWN = 0;\n    private static final int STATE_MOVE = 1;\n    private static final int STATE_STOP = 2;\n\n    private static final int SLIDE_INIT       = 0;\n    private static final int SLIDE_HORIZONTAL = 1;\n    private static final int SLIDE_VERTICAL   = 2;\n\n    private static final int MIN_DISTANCE_MOVE  = SizeUtils.dp2px(4);\n    private static final int THRESHOLD_DISTANCE = SizeUtils.dp2px(20);\n    private static final int ANIM_TIMING        = 350;\n\n    private int mState;\n    private int mDownX;\n    private int mDownY;\n    private int mLastX;\n    private int mLastY;\n    private int slideDirection;\n\n    private boolean isTouchPointInView(View view, int x, int y) {\n        if (view == null) {\n            return false;\n        }\n        int[] location = new int[2];\n        view.getLocationOnScreen(location);\n        int left = location[0];\n        int top = location[1];\n        int right = left + view.getMeasuredWidth();\n        int bottom = top + view.getMeasuredHeight();\n        return y >= top && y <= bottom && x >= left && x <= right;\n    }\n\n    public boolean isOpen() {\n        return swipeMenuOpened != null;\n    }\n\n    @Override\n    public boolean dispatchTouchEvent(MotionEvent event) {\n        int x = (int) event.getRawX();\n        int y = (int) event.getRawY();\n        switch (event.getAction()) {\n            case MotionEvent.ACTION_DOWN:\n                if (isTouching) {\n                    return false;\n                }\n                isTouching = true;\n                close(this);\n                mDownX = x;\n                mDownY = y;\n                mLastX = x;\n                mLastY = y;\n                mState = STATE_DOWN;\n                slideDirection = SLIDE_INIT;\n                super.dispatchTouchEvent(event);\n                break;\n            case MotionEvent.ACTION_MOVE:\n                if (mState == STATE_DOWN\n                        && Math.abs(x - mDownX) < MIN_DISTANCE_MOVE\n                        && Math.abs(y - mDownY) < MIN_DISTANCE_MOVE) {\n                    break;\n                }\n                if (slideDirection == SLIDE_INIT) {\n                    if (Math.abs(x - mDownX) > Math.abs(y - mDownY)) {\n                        slideDirection = SLIDE_HORIZONTAL;\n                        cancelChildViewTouch();\n                        requestDisallowInterceptTouchEvent(true);// 让父 view 不要拦截\n                    } else {\n                        slideDirection = SLIDE_VERTICAL;\n                    }\n                }\n                if (slideDirection == SLIDE_VERTICAL) {\n                    return super.dispatchTouchEvent(event);\n                }\n\n                scrollTo(Math.max(Math.min(getScrollX() - x + mLastX, mMenusWidth), 0), 0);\n\n                float percent = getScrollX() / (float) mMenusWidth;\n                updateLeftMarginByPercent(percent);\n\n                mLastX = x;\n                mLastY = y;\n                mState = STATE_MOVE;\n                break;\n            case MotionEvent.ACTION_UP:\n            case MotionEvent.ACTION_CANCEL:\n                try {\n                    if (event.getAction() == MotionEvent.ACTION_UP) {\n                        if (mState == STATE_DOWN) {\n                            if (isOpen()) {\n                                if (isTouchPointInView(mContentView, x, y)) {\n                                    close(true);\n                                    final long now = SystemClock.elapsedRealtime();\n                                    final MotionEvent cancelEvent = MotionEvent.obtain(now, now,\n                                            MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);\n                                    super.dispatchTouchEvent(cancelEvent);\n                                    return true;\n                                }\n                            }\n                            super.dispatchTouchEvent(event);\n                            close(true);\n                            return true;\n                        }\n                    } else {\n                        super.dispatchTouchEvent(event);\n                    }\n                    if (swipeMenuOpened != null && swipeMenuOpened.get() == this) {// 如果之前是展开状态\n                        if (getScrollX() < mMenusWidth - THRESHOLD_DISTANCE) {// 超过阈值则关闭\n                            close(true);\n                        } else {// 否则还是打开\n                            open(true);\n                        }\n                    } else {\n                        if (getScrollX() > THRESHOLD_DISTANCE) {// 如果是关闭\n                            open(true);// 超过阈值则打开\n                        } else {\n                            close(true);// 否则还是关闭\n                        }\n                    }\n                } finally {\n                    isTouching = false;\n                    mState = STATE_STOP;\n                }\n                break;\n            default:\n                break;\n        }\n        return true;\n    }\n\n    private void updateLeftMarginByPercent(float percent) {\n        for (MenuBean menu : mMenus) {\n            menu.getParams().leftMargin = (int) (menu.getCloseMargin() + percent * (menu.getOpenMargin() - menu.getCloseMargin()));\n            menu.getView().requestLayout();\n        }\n    }\n\n    private void close(boolean isAnim) {\n        swipeMenuOpened = null;\n        if (isAnim) {\n            ValueAnimator anim = ValueAnimator.ofInt(getScrollX(), 0);\n            anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n                @Override\n                public void onAnimationUpdate(ValueAnimator animation) {\n                    scrollTo((int) animation.getAnimatedValue(), 0);\n                }\n            });\n            anim.setInterpolator(CLOSE_INTERPOLATOR);\n            anim.setDuration(ANIM_TIMING).start();\n\n            for (final MenuBean menu : mMenus) {\n                ValueAnimator menuAnim = ValueAnimator.ofInt(menu.getParams().leftMargin, menu.getCloseMargin());\n                menuAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n                    @Override\n                    public void onAnimationUpdate(ValueAnimator animation) {\n                        menu.getParams().leftMargin = (int) animation.getAnimatedValue();\n                        menu.getView().requestLayout();\n                    }\n                });\n                menuAnim.setInterpolator(CLOSE_INTERPOLATOR);\n                menuAnim.setDuration(ANIM_TIMING).start();\n            }\n        } else {\n            scrollTo(0, 0);\n\n            for (final MenuBean menu : mMenus) {\n                menu.getParams().leftMargin = menu.getCloseMargin();\n                menu.getView().requestLayout();\n            }\n        }\n    }\n\n    private void open(boolean isAnim) {\n        swipeMenuOpened = new WeakReference<>(this);\n        if (isAnim) {\n            ValueAnimator anim = ValueAnimator.ofInt(getScrollX(), mMenusWidth);\n            anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n                @Override\n                public void onAnimationUpdate(ValueAnimator animation) {\n                    scrollTo((int) animation.getAnimatedValue(), 0);\n                }\n            });\n            anim.setInterpolator(OPEN_INTERPOLATOR);\n            anim.setDuration(ANIM_TIMING).start();\n\n            for (final MenuBean menu : mMenus) {\n                ValueAnimator menuAnim = ValueAnimator.ofInt(menu.getParams().leftMargin, menu.getOpenMargin());\n                menuAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {\n                    @Override\n                    public void onAnimationUpdate(ValueAnimator animation) {\n                        menu.getParams().leftMargin = (int) animation.getAnimatedValue();\n                        menu.getView().requestLayout();\n                    }\n                });\n                menuAnim.setInterpolator(OPEN_INTERPOLATOR);\n                menuAnim.setDuration(ANIM_TIMING).start();\n            }\n        } else {\n            scrollTo(mMenusWidth, 0);\n\n            for (final MenuBean menu : mMenus) {\n                menu.getParams().leftMargin = menu.getOpenMargin();\n                menu.getView().requestLayout();\n            }\n        }\n    }\n\n    public void close(SwipeRightMenu exclude) {\n        if (swipeMenuOpened != null) {\n            final SwipeRightMenu swipeMenu = swipeMenuOpened.get();\n            if (swipeMenu != exclude) {\n                swipeMenu.close(true);\n            }\n        }\n    }\n\n    private void cancelChildViewTouch() {\n        final long now = SystemClock.elapsedRealtime();\n        final MotionEvent cancelEvent = MotionEvent.obtain(now, now,\n                MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);\n        final int childCount = getChildCount();\n        for (int i = 0; i < childCount; i++) {\n            getChildAt(i).dispatchTouchEvent(cancelEvent);\n        }\n        cancelEvent.recycle();\n    }\n\n    private static class MenuBean {\n        private View                      mView;\n        private LinearLayout.LayoutParams mParams;\n        private int                       mWidth;\n        private int                       mCloseMargin;\n        private int                       mOpenMargin;\n\n        public MenuBean(View view) {\n            mView = view;\n            mParams = (LayoutParams) view.getLayoutParams();\n            mWidth = view.getWidth();\n        }\n\n        public View getView() {\n            return mView;\n        }\n\n        public LayoutParams getParams() {\n            return mParams;\n        }\n\n        public int getWidth() {\n            return mWidth;\n        }\n\n        public int getCloseMargin() {\n            return mCloseMargin;\n        }\n\n        public int getOpenMargin() {\n            return mOpenMargin;\n        }\n\n        public void setCloseMargin(int closeMargin) {\n            mCloseMargin = closeMargin;\n        }\n\n        public void setOpenMargin(int openMargin) {\n            mOpenMargin = openMargin;\n        }\n    }\n}"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/base/view/listener/OnBackListener.java",
    "content": "package com.blankj.utildebug.base.view.listener;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/20\n *     desc  :\n * </pre>\n */\npublic interface OnBackListener {\n    void onBack();\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/base/view/listener/OnRefreshListener.java",
    "content": "package com.blankj.utildebug.base.view.listener;\n\nimport com.blankj.utildebug.base.view.BaseContentFloatView;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/20\n *     desc  :\n * </pre>\n */\npublic interface OnRefreshListener {\n    void onRefresh(BaseContentFloatView floatView);\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/config/DebugConfig.java",
    "content": "package com.blankj.utildebug.config;\n\nimport android.view.View;\n\nimport com.blankj.utilcode.util.SPUtils;\nimport com.blankj.utilcode.util.ScreenUtils;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/02\n *     desc  :\n * </pre>\n */\npublic class DebugConfig {\n\n    private static final String DEBUG_ICON_X     = \"DEBUG_ICON_X\";\n    private static final String DEBUG_ICON_Y     = \"DEBUG_ICON_Y\";\n    private static final String NO_MORE_REMINDER = \"NO_MORE_REMINDER\";\n\n    public static void saveDebugIconX(float x) {\n        getSp().put(DEBUG_ICON_X, x);\n    }\n\n    public static float getDebugIconX() {\n        return getSp().getFloat(DEBUG_ICON_X);\n    }\n\n    public static void saveDebugIconY(float y) {\n        getSp().put(DEBUG_ICON_Y, y);\n    }\n\n    public static float getDebugIconY() {\n        return getSp().getFloat(DEBUG_ICON_Y, ScreenUtils.getAppScreenHeight() / 3);\n    }\n\n    public static void saveNoMoreReminder() {\n        getSp().put(NO_MORE_REMINDER, true);\n    }\n\n    public static boolean isNoMoreReminder() {\n        return getSp().getBoolean(NO_MORE_REMINDER, false);\n    }\n\n    public static void saveViewY(View view, int y) {\n        if (ScreenUtils.isPortrait()) {\n            getSp().put(view.getClass().getSimpleName() + \".yP\", y);\n        } else {\n            getSp().put(view.getClass().getSimpleName() + \".yL\", y);\n        }\n    }\n\n    public static int getViewY(View view) {\n        return getViewY(view, 0);\n    }\n\n    public static int getViewY(View view, int defaultVal) {\n        if (ScreenUtils.isPortrait()) {\n            return getSp().getInt(view.getClass().getSimpleName() + \".yP\", defaultVal);\n        } else {\n            return getSp().getInt(view.getClass().getSimpleName() + \".yL\", defaultVal);\n        }\n    }\n\n    public static void saveViewX(View view, int x) {\n        if (ScreenUtils.isPortrait()) {\n            getSp().put(view.getClass().getSimpleName() + \".xP\", x);\n        } else {\n            getSp().put(view.getClass().getSimpleName() + \".xL\", x);\n        }\n    }\n\n    public static int getViewX(View view) {\n        if (ScreenUtils.isPortrait()) {\n            return getSp().getInt(view.getClass().getSimpleName() + \".xP\");\n        } else {\n            return getSp().getInt(view.getClass().getSimpleName() + \".xL\");\n        }\n    }\n\n    public static void saveViewHeight(View view, int height) {\n        if (ScreenUtils.isPortrait()) {\n            getSp().put(view.getClass().getSimpleName() + \".heightP\", height);\n        } else {\n            getSp().put(view.getClass().getSimpleName() + \".heightL\", height);\n        }\n    }\n\n    public static int getViewHeight(View view, int height) {\n        if (ScreenUtils.isPortrait()) {\n            return getSp().getInt(view.getClass().getSimpleName() + \".heightP\", height);\n        } else {\n            return getSp().getInt(view.getClass().getSimpleName() + \".heightL\", height);\n        }\n    }\n\n    public static void saveViewAlpha(View view, float alpha) {\n        getSp().put(view.getClass().getSimpleName() + \".alpha\", alpha);\n    }\n\n    public static float getViewAlpha(View view) {\n        return getSp().getFloat(view.getClass().getSimpleName() + \".alpha\", 1f);\n    }\n\n    private static SPUtils getSp() {\n        return SPUtils.getInstance(\"DebugUtils\");\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/IDebug.java",
    "content": "package com.blankj.utildebug.debug;\n\nimport android.content.Context;\nimport android.view.View;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport androidx.annotation.DrawableRes;\nimport androidx.annotation.IntDef;\nimport androidx.annotation.StringRes;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/28\n *     desc  :\n * </pre>\n */\npublic interface IDebug {\n\n    void onAppCreate(Context context);\n\n    @Category\n    int getCategory();\n\n    @DrawableRes\n    int getIcon();\n\n    @StringRes\n    int getName();\n\n    void onClick(View view);\n\n    int TOOLS       = 0;\n    int PERFORMANCE = 1;\n    int UI          = 2;\n    int BIZ         = 3;\n\n    @IntDef({TOOLS, PERFORMANCE, UI, BIZ})\n    @Retention(RetentionPolicy.SOURCE)\n    @interface Category {\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/tool/AbsToolDebug.java",
    "content": "package com.blankj.utildebug.debug.tool;\n\nimport com.blankj.utildebug.debug.IDebug;\nimport com.blankj.utildebug.debug.tool.appInfo.AppInfoDebug;\nimport com.blankj.utildebug.debug.tool.clearCache.ClearCacheDebug;\nimport com.blankj.utildebug.debug.tool.clearStorage.ClearStorageDebug;\nimport com.blankj.utildebug.debug.tool.deviceInfo.DeviceInfoDebug;\nimport com.blankj.utildebug.debug.tool.fileExplorer.FileExplorerDebug;\nimport com.blankj.utildebug.debug.tool.logcat.LogcatDebug;\nimport com.blankj.utildebug.debug.tool.restartApp.RestartAppDebug;\n\nimport java.util.List;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/04\n *     desc  :\n * </pre>\n */\npublic abstract class AbsToolDebug implements IDebug {\n\n    @Override\n    public int getCategory() {\n        return TOOLS;\n    }\n\n    public static void addToolDebugs(List<IDebug> debugList) {\n        debugList.add(new AppInfoDebug());\n        debugList.add(new DeviceInfoDebug());\n        debugList.add(new FileExplorerDebug());\n        debugList.add(new LogcatDebug());\n        debugList.add(new RestartAppDebug());\n        debugList.add(new ClearStorageDebug());\n        debugList.add(new ClearCacheDebug());\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/tool/appInfo/AppInfoDebug.java",
    "content": "package com.blankj.utildebug.debug.tool.appInfo;\n\nimport android.content.Context;\nimport android.view.View;\n\nimport com.blankj.utilcode.util.AppUtils;\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.debug.tool.AbsToolDebug;\nimport com.blankj.utildebug.menu.DebugMenu;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/02\n *     desc  :\n * </pre>\n */\npublic class AppInfoDebug extends AbsToolDebug {\n\n    @Override\n    public void onAppCreate(Context context) {\n\n    }\n\n    @Override\n    public int getIcon() {\n        int appIconId = AppUtils.getAppIconId();\n        if (appIconId != 0) return appIconId;\n        return R.drawable.du_ic_debug_app_info_default;\n    }\n\n    @Override\n    public int getName() {\n        return R.string.du_app_info;\n    }\n\n    @Override\n    public void onClick(View view) {\n        DebugMenu.getInstance().dismiss();\n        new AppInfoFloatView().show();\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/tool/appInfo/AppInfoFloatView.java",
    "content": "package com.blankj.utildebug.debug.tool.appInfo;\n\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.base.rv.BaseItemAdapter;\nimport com.blankj.utildebug.base.rv.RecycleViewDivider;\nimport com.blankj.utildebug.base.view.BaseContentFloatView;\n\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/02\n *     desc  :\n * </pre>\n */\npublic class AppInfoFloatView extends BaseContentFloatView<AppInfoFloatView> {\n\n    private RecyclerView appInfoRv;\n\n    @Override\n    public int bindTitle() {\n        return R.string.du_app_info;\n    }\n\n    @Override\n    public int bindContentLayout() {\n        return R.layout.du_debug_app_info;\n    }\n\n    @Override\n    public void initContentView() {\n        appInfoRv = findViewById(R.id.appInfoRv);\n        BaseItemAdapter<AppInfoItem> adapter = new BaseItemAdapter<>();\n        adapter.setItems(AppInfoItem.getAppInfoItems());\n        appInfoRv.setAdapter(adapter);\n        appInfoRv.setLayoutManager(new LinearLayoutManager(getContext()));\n        appInfoRv.addItemDecoration(new RecycleViewDivider(getContext(), RecycleViewDivider.VERTICAL, R.drawable.du_shape_divider));\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/tool/appInfo/AppInfoItem.java",
    "content": "package com.blankj.utildebug.debug.tool.appInfo;\n\nimport android.os.Build;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.TextView;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.StringRes;\n\nimport com.blankj.utilcode.util.AppUtils;\nimport com.blankj.utilcode.util.ClickUtils;\nimport com.blankj.utilcode.util.StringUtils;\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.base.rv.BaseItem;\nimport com.blankj.utildebug.base.rv.ItemViewHolder;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/02\n *     desc  :\n * </pre>\n */\npublic class AppInfoItem extends BaseItem<AppInfoItem> {\n\n    private String mTitle;\n    private String mContent;\n    private OnClickListener mListener;\n\n    private TextView titleTv;\n    private TextView contentTv;\n\n    public AppInfoItem(@StringRes int name, String info) {\n        this(name, info, null);\n    }\n\n    public AppInfoItem(@StringRes int name, String info, OnClickListener listener) {\n        super(R.layout.du_item_base_info);\n        mTitle = StringUtils.getString(name);\n        mContent = info;\n        mListener = listener;\n    }\n\n    @Override\n    public void bind(@NonNull ItemViewHolder holder, int position) {\n        titleTv = holder.findViewById(R.id.baseInfoTitleTv);\n        contentTv = holder.findViewById(R.id.baseInfoContentTv);\n\n        titleTv.setText(mTitle);\n        contentTv.setText(mContent);\n        if (mListener != null) {\n            ClickUtils.applyPressedBgDark(holder.itemView);\n            ClickUtils.applyGlobalDebouncing(holder.itemView, mListener);\n            holder.findViewById(R.id.baseInfoGoIv).setVisibility(View.VISIBLE);\n        } else {\n            holder.itemView.setOnTouchListener(null);\n            holder.itemView.setOnClickListener(null);\n            holder.findViewById(R.id.baseInfoGoIv).setVisibility(View.GONE);\n        }\n    }\n\n    public static List<AppInfoItem> getAppInfoItems() {\n        final List<AppInfoItem> appInfoItems = new ArrayList<>();\n        appInfoItems.add(new AppInfoItem(R.string.du_app_info_pkg_name, AppUtils.getAppPackageName()));\n        appInfoItems.add(new AppInfoItem(R.string.du_app_info_version_name, AppUtils.getAppVersionName()));\n        appInfoItems.add(new AppInfoItem(R.string.du_app_info_version_code, String.valueOf(AppUtils.getAppVersionCode())));\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {\n            appInfoItems.add(new AppInfoItem(R.string.du_app_info_min_sdk_version, String.valueOf(AppUtils.getAppMinSdkVersion())));\n        }\n        appInfoItems.add(new AppInfoItem(R.string.du_app_info_target_sdk_version, String.valueOf(AppUtils.getAppTargetSdkVersion())));\n        appInfoItems.add(new AppInfoItem(R.string.du_app_info_open_app_info_page, \"\", new OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                AppUtils.launchAppDetailsSettings();\n            }\n        }));\n        return appInfoItems;\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/tool/clearCache/ClearCacheDebug.java",
    "content": "package com.blankj.utildebug.debug.tool.clearCache;\n\nimport android.content.Context;\nimport android.view.View;\n\nimport com.blankj.utilcode.util.ConvertUtils;\nimport com.blankj.utilcode.util.FileUtils;\nimport com.blankj.utilcode.util.LogUtils;\nimport com.blankj.utilcode.util.PathUtils;\nimport com.blankj.utilcode.util.ThreadUtils;\nimport com.blankj.utilcode.util.ToastUtils;\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.debug.tool.AbsToolDebug;\nimport com.blankj.utildebug.menu.DebugMenu;\n\nimport java.io.File;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/04\n *     desc  :\n * </pre>\n */\npublic class ClearCacheDebug extends AbsToolDebug {\n\n    private ThreadUtils.SimpleTask<Long> clearCacheTask;\n\n    @Override\n    public void onAppCreate(Context context) {\n\n    }\n\n    @Override\n    public int getIcon() {\n        return R.drawable.du_ic_debug_clear_cache;\n    }\n\n    @Override\n    public int getName() {\n        return R.string.du_clear_cache;\n    }\n\n    @Override\n    public void onClick(View view) {\n        clearCache();\n    }\n\n    private void clearCache() {\n        DebugMenu.getInstance().dismiss();\n        if (clearCacheTask != null && !clearCacheTask.isDone()) {\n            ToastUtils.showShort(\"Cleaning...\");\n            return;\n        }\n        clearCacheTask = createClearCacheTask();\n        ThreadUtils.executeByIo(clearCacheTask);\n    }\n\n    private ThreadUtils.SimpleTask<Long> createClearCacheTask() {\n        return new ThreadUtils.SimpleTask<Long>() {\n            @Override\n            public Long doInBackground() throws Throwable {\n                try {\n                    long len = 0;\n                    File appDataDir = new File(PathUtils.getInternalAppDataPath());\n                    if (appDataDir.exists()) {\n                        String[] names = appDataDir.list();\n                        for (String name : names) {\n                            if (!name.equals(\"lib\")) {\n                                File file = new File(appDataDir, name);\n                                len += FileUtils.getLength(file);\n                                FileUtils.delete(file);\n                                LogUtils.i(\"「\" + file + \"」 was deleted.\");\n                            }\n                        }\n                    }\n                    String externalAppCachePath = PathUtils.getExternalAppCachePath();\n                    len += FileUtils.getLength(externalAppCachePath);\n                    FileUtils.delete(externalAppCachePath);\n                    LogUtils.i(\"「\" + externalAppCachePath + \"」 was deleted.\");\n                    return len;\n                } catch (Exception e) {\n                    ToastUtils.showLong(e.toString());\n                    return -1L;\n                }\n            }\n\n            @Override\n            public void onSuccess(Long result) {\n                if (result != -1) {\n                    ToastUtils.showLong(\"Clear Cache: \" + ConvertUtils.byte2FitMemorySize(result));\n                }\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/tool/clearStorage/ClearStorageDebug.java",
    "content": "package com.blankj.utildebug.debug.tool.clearStorage;\n\nimport android.content.Context;\nimport android.view.View;\n\nimport com.blankj.utilcode.util.AppUtils;\nimport com.blankj.utilcode.util.ShellUtils;\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.debug.tool.AbsToolDebug;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/04\n *     desc  :\n * </pre>\n */\npublic class ClearStorageDebug extends AbsToolDebug {\n\n    @Override\n    public void onAppCreate(Context context) {\n\n    }\n\n    @Override\n    public int getIcon() {\n        return R.drawable.du_ic_debug_clear_storage;\n    }\n\n    @Override\n    public int getName() {\n        return R.string.du_clear_storage;\n    }\n\n    @Override\n    public void onClick(View view) {\n        ShellUtils.execCmd(\"pm clear \" + AppUtils.getAppPackageName(), false);\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/tool/deviceInfo/DeviceInfoDebug.java",
    "content": "package com.blankj.utildebug.debug.tool.deviceInfo;\n\nimport android.content.Context;\nimport android.view.View;\n\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.debug.tool.AbsToolDebug;\nimport com.blankj.utildebug.menu.DebugMenu;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/02\n *     desc  :\n * </pre>\n */\npublic class DeviceInfoDebug extends AbsToolDebug {\n\n    @Override\n    public void onAppCreate(Context context) {\n\n    }\n\n    @Override\n    public int getIcon() {\n        return R.drawable.du_ic_debug_device_info;\n    }\n\n    @Override\n    public int getName() {\n        return R.string.du_device_info;\n    }\n\n    @Override\n    public void onClick(View view) {\n        DebugMenu.getInstance().dismiss();\n        new DeviceInfoFloatView().show();\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/tool/deviceInfo/DeviceInfoFloatView.java",
    "content": "package com.blankj.utildebug.debug.tool.deviceInfo;\n\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.base.rv.BaseItemAdapter;\nimport com.blankj.utildebug.base.rv.RecycleViewDivider;\nimport com.blankj.utildebug.base.view.BaseContentFloatView;\nimport com.blankj.utildebug.base.view.listener.OnRefreshListener;\n\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/02\n *     desc  :\n * </pre>\n */\npublic class DeviceInfoFloatView extends BaseContentFloatView<DeviceInfoFloatView> {\n\n    private RecyclerView deviceInfoRv;\n\n    @Override\n    public int bindTitle() {\n        return R.string.du_device_info;\n    }\n\n    @Override\n    public int bindContentLayout() {\n        return R.layout.du_debug_device_info;\n    }\n\n    @Override\n    public void initContentView() {\n        deviceInfoRv = findViewById(R.id.deviceInfoRv);\n        final BaseItemAdapter<DeviceInfoItem> adapter = new BaseItemAdapter<>();\n        adapter.setItems(DeviceInfoItem.getAppInfoItems());\n        deviceInfoRv.setAdapter(adapter);\n        deviceInfoRv.setLayoutManager(new LinearLayoutManager(getContext()));\n        deviceInfoRv.addItemDecoration(new RecycleViewDivider(getContext(), RecycleViewDivider.VERTICAL, R.drawable.du_shape_divider));\n\n        getContentView().setOnRefreshListener(deviceInfoRv, new OnRefreshListener() {\n            @Override\n            public void onRefresh(final BaseContentFloatView floatView) {\n                adapter.setItems(DeviceInfoItem.getAppInfoItems());\n                adapter.notifyDataSetChanged();\n                floatView.closeRefresh();\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/tool/deviceInfo/DeviceInfoItem.java",
    "content": "package com.blankj.utildebug.debug.tool.deviceInfo;\n\nimport android.content.Intent;\nimport android.os.Build;\nimport android.provider.Settings;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.TextView;\n\nimport com.blankj.utilcode.util.ActivityUtils;\nimport com.blankj.utilcode.util.ArrayUtils;\nimport com.blankj.utilcode.util.ClickUtils;\nimport com.blankj.utilcode.util.DeviceUtils;\nimport com.blankj.utilcode.util.ScreenUtils;\nimport com.blankj.utilcode.util.StringUtils;\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.base.rv.BaseItem;\nimport com.blankj.utildebug.base.rv.ItemViewHolder;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport androidx.annotation.NonNull;\nimport androidx.annotation.StringRes;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/02\n *     desc  :\n * </pre>\n */\npublic class DeviceInfoItem extends BaseItem<DeviceInfoItem> {\n\n    private String          mTitle;\n    private String          mContent;\n    private OnClickListener mListener;\n\n    private TextView titleTv;\n    private TextView contentTv;\n\n    public DeviceInfoItem(@StringRes int name, String info) {\n        this(name, info, null);\n    }\n\n    public DeviceInfoItem(@StringRes int name, String info, OnClickListener listener) {\n        super(R.layout.du_item_base_info);\n        mTitle = StringUtils.getString(name);\n        mContent = info;\n        mListener = listener;\n    }\n\n    @Override\n    public void bind(@NonNull ItemViewHolder holder, int position) {\n        titleTv = holder.findViewById(R.id.baseInfoTitleTv);\n        contentTv = holder.findViewById(R.id.baseInfoContentTv);\n\n        titleTv.setText(mTitle);\n        contentTv.setText(mContent);\n        if (mListener != null) {\n            ClickUtils.applyPressedBgDark(holder.itemView);\n            ClickUtils.applyGlobalDebouncing(holder.itemView, mListener);\n            holder.findViewById(R.id.baseInfoGoIv).setVisibility(View.VISIBLE);\n        } else {\n            holder.itemView.setOnTouchListener(null);\n            holder.itemView.setOnClickListener(null);\n            holder.findViewById(R.id.baseInfoGoIv).setVisibility(View.GONE);\n        }\n    }\n\n    public static List<DeviceInfoItem> getAppInfoItems() {\n        List<DeviceInfoItem> appInfoItems = new ArrayList<>();\n        appInfoItems.add(new DeviceInfoItem(R.string.du_device_info_name, Build.MANUFACTURER + \" \" + Build.MODEL));\n        appInfoItems.add(new DeviceInfoItem(R.string.du_device_info_android_version, DeviceUtils.getSDKVersionName() + \" (\" + DeviceUtils.getSDKVersionCode() + \")\"));\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {\n            appInfoItems.add(new DeviceInfoItem(R.string.du_device_info_adb_enabled, String.valueOf(DeviceUtils.isAdbEnabled())));\n        }\n        appInfoItems.add(new DeviceInfoItem(R.string.du_device_info_support_abis, ArrayUtils.toString(DeviceUtils.getABIs())));\n        appInfoItems.add(new DeviceInfoItem(R.string.du_device_info_screen_info, getScreenInfo()));\n        appInfoItems.add(new DeviceInfoItem(R.string.du_device_info_open_development_settings_page, \"\", new OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                ActivityUtils.startActivity(new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS));\n            }\n        }));\n        return appInfoItems;\n    }\n\n    private static String getScreenInfo() {\n        return \"width=\" + ScreenUtils.getScreenWidth() +\n                \", height=\" + ScreenUtils.getScreenHeight() +\n                \", density=\" + ScreenUtils.getScreenDensity();\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/tool/fileExplorer/FileContentView.java",
    "content": "package com.blankj.utildebug.debug.tool.fileExplorer;\n\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.base.rv.BaseItemAdapter;\nimport com.blankj.utildebug.base.rv.RecycleViewDivider;\nimport com.blankj.utildebug.base.view.BaseContentFloatView;\nimport com.blankj.utildebug.base.view.BaseContentView;\nimport com.blankj.utildebug.base.view.SearchEditText;\nimport com.blankj.utildebug.base.view.listener.OnRefreshListener;\n\nimport java.util.List;\n\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/06\n *     desc  :\n * </pre>\n */\npublic class FileContentView extends BaseContentView<FileExplorerFloatView> {\n\n    private FileItem                  mParent;\n    private BaseItemAdapter<FileItem> mAdapter;\n    private List<FileItem>            mSrcItems;\n\n    private SearchEditText fileExplorerSearchEt;\n    private RecyclerView   fileExplorerRv;\n\n    public static void show(FileExplorerFloatView floatView) {\n        new FileContentView(null).attach(floatView, true);\n    }\n\n    public static void show(FileExplorerFloatView floatView, FileItem fileItem) {\n        new FileContentView(fileItem).attach(floatView, true);\n    }\n\n    public FileContentView(FileItem parent) {\n        mParent = parent;\n        mSrcItems = FileItem.getFileItems(mParent);\n    }\n\n    @Override\n    public int bindLayout() {\n        return R.layout.du_debug_file_explorer;\n    }\n\n    @Override\n    public void onAttach() {\n        fileExplorerSearchEt = findViewById(R.id.fileExplorerSearchEt);\n        fileExplorerRv = findViewById(R.id.fileExplorerRv);\n\n        if (FileItem.isEmptyItems(mSrcItems)) {\n            fileExplorerSearchEt.setVisibility(GONE);\n        }\n\n        mAdapter = new BaseItemAdapter<>();\n        mAdapter.setItems(mSrcItems);\n        fileExplorerRv.setAdapter(mAdapter);\n        fileExplorerRv.setLayoutManager(new LinearLayoutManager(getContext()));\n        fileExplorerRv.addItemDecoration(new RecycleViewDivider(getContext(), RecycleViewDivider.VERTICAL, R.drawable.du_shape_file_divider));\n\n        fileExplorerSearchEt.setOnTextChangedListener(new SearchEditText.OnTextChangedListener() {\n            @Override\n            public void onTextChanged(String text) {\n                mAdapter.setItems(FileItem.filterItems(mSrcItems, text));\n                mAdapter.notifyDataSetChanged();\n            }\n        });\n\n        setOnRefreshListener(fileExplorerRv, new OnRefreshListener() {\n            @Override\n            public void onRefresh(BaseContentFloatView floatView) {\n                mSrcItems = FileItem.getFileItems(mParent);\n                mAdapter.setItems(mSrcItems);\n                mAdapter.notifyDataSetChanged();\n                fileExplorerSearchEt.reset();\n                floatView.closeRefresh();\n            }\n        });\n    }\n\n    @Override\n    public void onBack() {\n        super.onBack();\n        if (mParent != null) {\n            mParent.update();\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/tool/fileExplorer/FileExplorerDebug.java",
    "content": "package com.blankj.utildebug.debug.tool.fileExplorer;\n\nimport android.content.Context;\nimport android.view.View;\n\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.debug.tool.AbsToolDebug;\nimport com.blankj.utildebug.menu.DebugMenu;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/04\n *     desc  :\n * </pre>\n */\npublic class FileExplorerDebug extends AbsToolDebug {\n\n    @Override\n    public void onAppCreate(Context context) {\n\n    }\n\n    @Override\n    public int getIcon() {\n        return R.drawable.du_ic_debug_file_explorer;\n    }\n\n    @Override\n    public int getName() {\n        return R.string.du_file_explorer;\n    }\n\n    @Override\n    public void onClick(View view) {\n        DebugMenu.getInstance().dismiss();\n        new FileExplorerFloatView().show();\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/tool/fileExplorer/FileExplorerFloatView.java",
    "content": "package com.blankj.utildebug.debug.tool.fileExplorer;\n\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.base.view.BaseContentFloatView;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/04\n *     desc  :\n * </pre>\n */\npublic class FileExplorerFloatView extends BaseContentFloatView<FileExplorerFloatView> {\n\n    @Override\n    public int bindTitle() {\n        return R.string.du_file_explorer;\n    }\n\n    @Override\n    public int bindContentLayout() {\n        return NO_ID;\n    }\n\n    @Override\n    public void initContentView() {\n        FileContentView.show(this);\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/tool/fileExplorer/FileItem.java",
    "content": "package com.blankj.utildebug.debug.tool.fileExplorer;\n\nimport android.content.Intent;\nimport android.view.View;\nimport android.widget.ImageView;\nimport android.widget.RelativeLayout;\nimport android.widget.TextView;\n\nimport com.blankj.utilcode.constant.PermissionConstants;\nimport com.blankj.utilcode.util.ActivityUtils;\nimport com.blankj.utilcode.util.ClickUtils;\nimport com.blankj.utilcode.util.CollectionUtils;\nimport com.blankj.utilcode.util.FileUtils;\nimport com.blankj.utilcode.util.PathUtils;\nimport com.blankj.utilcode.util.PermissionUtils;\nimport com.blankj.utilcode.util.SDCardUtils;\nimport com.blankj.utilcode.util.StringUtils;\nimport com.blankj.utilcode.util.TimeUtils;\nimport com.blankj.utilcode.util.UriUtils;\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.base.rv.BaseItem;\nimport com.blankj.utildebug.base.rv.ItemViewHolder;\nimport com.blankj.utildebug.base.view.FloatToast;\nimport com.blankj.utildebug.debug.tool.fileExplorer.image.ImageViewer;\nimport com.blankj.utildebug.debug.tool.fileExplorer.sp.SpViewerContentView;\nimport com.blankj.utildebug.helper.FileHelper;\nimport com.blankj.utildebug.helper.ImageLoader;\n\nimport java.io.File;\nimport java.util.ArrayList;\nimport java.util.Comparator;\nimport java.util.List;\n\nimport androidx.annotation.NonNull;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/05\n *     desc  :\n * </pre>\n */\npublic class FileItem extends BaseItem<FileItem> {\n\n    private static final ArrayList<FileItem> EMPTY = CollectionUtils.newArrayList(new FileItem());\n\n    private FileItem mParent;\n    private File     mFile;\n    private String   mName;\n    private boolean  isSdcard;\n\n    private RelativeLayout fileContentRl;\n    private ImageView      fileTypeIv;\n    private TextView       fileNameTv;\n    private TextView       fileInfoTv;\n    private TextView       fileMenuDeleteTv;\n\n    public FileItem(File file, String name) {\n        this(file, name, false);\n    }\n\n    public FileItem(File file, String name, boolean isSdcard) {\n        super(R.layout.du_item_file);\n        mFile = file;\n        mName = name;\n        this.isSdcard = isSdcard;\n    }\n\n    public FileItem(FileItem parent, File file) {\n        super(R.layout.du_item_file);\n        mParent = parent;\n        mFile = file;\n        mName = file.getName();\n    }\n\n    public FileItem() {\n        super(R.layout.du_item_empty);\n    }\n\n    @Override\n    public void bind(@NonNull ItemViewHolder holder, int position) {\n        if (isViewType(R.layout.du_item_empty)) return;\n        fileContentRl = holder.findViewById(R.id.fileContentRl);\n        fileTypeIv = holder.findViewById(R.id.fileTypeIv);\n        fileNameTv = holder.findViewById(R.id.fileNameTv);\n        fileInfoTv = holder.findViewById(R.id.fileInfoTv);\n        fileMenuDeleteTv = holder.findViewById(R.id.fileMenuDeleteTv);\n\n        ClickUtils.applyPressedBgDark(fileContentRl);\n        ClickUtils.applyPressedBgDark(fileMenuDeleteTv);\n\n        fileNameTv.setText(mName);\n\n        fileMenuDeleteTv.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                boolean delete = FileUtils.delete(mFile);\n                if (delete) {\n                    getAdapter().removeItem(FileItem.this, true);\n                    if (getAdapter().getItems().isEmpty()) {\n                        getAdapter().addItem(new FileItem());\n                        getAdapter().notifyDataSetChanged();\n                        v.getRootView().findViewById(R.id.fileExplorerSearchEt).setVisibility(View.GONE);\n                    }\n                } else {\n                    FloatToast.showLong(FloatToast.WARNING, \"Delete failed!\");\n                }\n            }\n        });\n        fileMenuDeleteTv.setVisibility(mParent == null ? View.GONE : View.VISIBLE);\n\n        if (mFile.isDirectory()) {\n            fileTypeIv.setImageResource(R.drawable.du_ic_debug_file_explorer);\n            fileInfoTv.setText(String.format(\"%s  %s\", StringUtils.getString(R.string.du_file_item_num, CollectionUtils.size(mFile.list())), TimeUtils.millis2String(mFile.lastModified(), \"yyyy.MM.dd\")));\n            fileContentRl.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(final View v) {\n                    if (isSdcard) {\n                        PermissionUtils.permission(PermissionConstants.STORAGE)\n                                .callback(new PermissionUtils.SimpleCallback() {\n                                    @Override\n                                    public void onGranted() {\n                                        FileExplorerFloatView floatView = (FileExplorerFloatView) v.getRootView();\n                                        FileContentView.show(floatView, FileItem.this);\n                                    }\n\n                                    @Override\n                                    public void onDenied() {\n                                        FloatToast.showShort(\"Permission of storage denied!\");\n                                    }\n                                })\n                                .request();\n                    } else {\n                        FileExplorerFloatView floatView = (FileExplorerFloatView) v.getRootView();\n                        FileContentView.show(floatView, FileItem.this);\n                    }\n                }\n            });\n        } else {\n            fileInfoTv.setText(String.format(\"%s  %s\", FileUtils.getSize(mFile), TimeUtils.millis2String(mFile.lastModified(), \"yyyy.MM.dd\")));\n\n            @FileHelper.FileType int fileType = FileHelper.getFileType(mFile);\n            if (fileType == FileHelper.IMAGE) {\n                ImageLoader.load(mFile, fileTypeIv);\n                fileContentRl.setOnClickListener(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        FileExplorerFloatView floatView = (FileExplorerFloatView) v.getRootView();\n                        ImageViewer.show(floatView, mFile);\n                    }\n                });\n            } else if (fileType == FileHelper.UTF8) {\n                fileTypeIv.setImageResource(R.drawable.du_ic_item_file_utf8);\n            } else if (fileType == FileHelper.SP) {\n                fileTypeIv.setImageResource(R.drawable.du_ic_item_file_sp);\n                fileContentRl.setOnClickListener(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        FileExplorerFloatView floatView = (FileExplorerFloatView) v.getRootView();\n                        SpViewerContentView.show(floatView, mFile);\n                    }\n                });\n            } else {\n                fileContentRl.setOnClickListener(new View.OnClickListener() {\n                    @Override\n                    public void onClick(View v) {\n                        Intent intent = new Intent(Intent.ACTION_VIEW);\n                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n                        intent.setData(UriUtils.file2Uri(mFile));\n                        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);\n                        intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);\n                        ActivityUtils.startActivity(intent);\n                    }\n                });\n                fileTypeIv.setImageResource(R.drawable.du_ic_item_file_default);\n            }\n        }\n    }\n\n    public File getFile() {\n        return mFile;\n    }\n\n    public static List<FileItem> getFileItems(final FileItem parent) {\n        if (parent == null) return getFileItems();\n        List<File> files = FileUtils.listFilesInDir(parent.getFile(), new Comparator<File>() {\n            @Override\n            public int compare(File o1, File o2) {\n                if (o1.isDirectory() && o2.isFile()) {\n                    return -1;\n                } else if (o1.isFile() && o2.isDirectory()) {\n                    return 1;\n                } else {\n                    return o1.getName().toLowerCase().compareTo(o2.getName().toLowerCase());\n                }\n            }\n        });\n        return (List<FileItem>) CollectionUtils.collect(files, new CollectionUtils.Transformer<File, FileItem>() {\n            @Override\n            public FileItem transform(File input) {\n                return new FileItem(parent, input);\n            }\n        });\n    }\n\n    private static List<FileItem> getFileItems() {\n        List<FileItem> fileItems = new ArrayList<>();\n        String internalAppDataPath = PathUtils.getInternalAppDataPath();\n        if (!StringUtils.isEmpty(internalAppDataPath)) {\n            File internalDataFile = new File(internalAppDataPath);\n            if (internalDataFile.exists()) {\n                fileItems.add(new FileItem(internalDataFile, \"internal\"));\n            }\n        }\n        String externalAppDataPath = PathUtils.getExternalAppDataPath();\n        if (!StringUtils.isEmpty(externalAppDataPath)) {\n            File externalDataFile = new File(externalAppDataPath);\n            if (externalDataFile.exists()) {\n                fileItems.add(new FileItem(externalDataFile, \"external\"));\n            }\n        }\n        List<String> mountedSDCardPath = SDCardUtils.getMountedSDCardPath();\n        if (!mountedSDCardPath.isEmpty()) {\n            for (int i = 0; i < mountedSDCardPath.size(); i++) {\n                String path = mountedSDCardPath.get(i);\n                File sdPath = new File(path);\n                if (sdPath.exists()) {\n                    fileItems.add(new FileItem(sdPath, \"sdcard\" + i + \"_\" + sdPath.getName(), true));\n                }\n            }\n        }\n        return fileItems;\n    }\n\n    public static List<FileItem> filterItems(List<FileItem> items, final String key) {\n        return (List<FileItem>) CollectionUtils.select(items, new CollectionUtils.Predicate<FileItem>() {\n            @Override\n            public boolean evaluate(FileItem item) {\n                return item.mName.toLowerCase().contains(key.toLowerCase());\n            }\n        });\n    }\n\n    public static boolean isEmptyItems(List<FileItem> items) {\n        return EMPTY == items;\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/tool/fileExplorer/image/ImageViewer.java",
    "content": "package com.blankj.utildebug.debug.tool.fileExplorer.image;\n\nimport com.blankj.utilcode.util.ImageUtils;\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.base.view.BaseContentView;\nimport com.blankj.utildebug.debug.tool.fileExplorer.FileExplorerFloatView;\nimport com.github.chrisbanes.photoview.PhotoView;\n\nimport java.io.File;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/08\n *     desc  :\n * </pre>\n */\npublic class ImageViewer extends BaseContentView<FileExplorerFloatView> {\n\n    private File mFile;\n\n    private PhotoView photoView;\n\n    public static void show(FileExplorerFloatView floatView, File file) {\n        new ImageViewer(file).attach(floatView, true);\n    }\n\n    public ImageViewer(File file) {\n        mFile = file;\n    }\n\n    @Override\n    public int bindLayout() {\n        return R.layout.du_debug_file_explorer_image;\n    }\n\n    @Override\n    public void onAttach() {\n        photoView = findViewById(R.id.imageViewerPv);\n        photoView.setImageBitmap(ImageUtils.getBitmap(mFile));\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/tool/fileExplorer/sp/SpItem.java",
    "content": "package com.blankj.utildebug.debug.tool.fileExplorer.sp;\n\nimport android.view.View;\nimport android.widget.ImageView;\nimport android.widget.RelativeLayout;\nimport android.widget.Switch;\nimport android.widget.TextView;\n\nimport com.blankj.utilcode.util.ClickUtils;\nimport com.blankj.utilcode.util.CollectionUtils;\nimport com.blankj.utilcode.util.ColorUtils;\nimport com.blankj.utilcode.util.MapUtils;\nimport com.blankj.utilcode.util.SPUtils;\nimport com.blankj.utilcode.util.SpanUtils;\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.base.rv.BaseItem;\nimport com.blankj.utildebug.base.rv.ItemViewHolder;\nimport com.blankj.utildebug.base.view.FloatToast;\nimport com.blankj.utildebug.debug.tool.fileExplorer.FileExplorerFloatView;\nimport com.blankj.utildebug.helper.SpHelper;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\n\nimport androidx.annotation.NonNull;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/09\n *     desc  :\n * </pre>\n */\npublic class SpItem extends BaseItem<SpItem> {\n\n    private SPUtils mSPUtils;\n    private String  mKey;\n    private Object  mValue;\n    private Class   mClass;\n\n    private RelativeLayout contentRl;\n    private TextView       titleTv;\n    private TextView       contentTv;\n    private ImageView      goIv;\n    private Switch         aSwitch;\n    private TextView       deleteTv;\n\n    public SpItem() {\n        super(R.layout.du_item_empty);\n    }\n\n    public SpItem(SPUtils spUtils, String key, Object value) {\n        super(R.layout.du_item_sp);\n        mSPUtils = spUtils;\n        mKey = key;\n        mValue = value;\n        mClass = mValue.getClass();\n    }\n\n    @Override\n    public void bind(@NonNull final ItemViewHolder holder, int position) {\n        if (isViewType(R.layout.du_item_empty)) return;\n        contentRl = holder.findViewById(R.id.itemSpContentRl);\n        titleTv = holder.findViewById(R.id.itemSpTitleTv);\n        contentTv = holder.findViewById(R.id.itemSpContentTv);\n        goIv = holder.findViewById(R.id.itemSpGoIv);\n        aSwitch = holder.findViewById(R.id.itemSpSwitch);\n        deleteTv = holder.findViewById(R.id.itemSpDeleteTv);\n\n        SpanUtils.with(titleTv)\n                .append(mKey)\n                .append(\"(\" + SpHelper.getSpClassName(mClass) + \")\").setForegroundColor(ColorUtils.getColor(R.color.loveGreen))\n                .create();\n        contentTv.setText(mValue.toString());\n\n        deleteTv.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                FloatToast.showShort(\"haha\");\n            }\n        });\n\n        if (Boolean.class.equals(mClass)) {\n            holder.itemView.setOnTouchListener(null);\n            aSwitch.setVisibility(View.VISIBLE);\n            goIv.setVisibility(View.GONE);\n            aSwitch.setChecked((Boolean) mValue);\n            View.OnClickListener toggle = new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    final boolean state = !(Boolean) mValue;\n                    mValue = state;\n                    mSPUtils.put(mKey, state);\n                    aSwitch.setChecked(state);\n                    contentTv.setText(mValue.toString());\n                }\n            };\n            aSwitch.setOnClickListener(toggle);\n            contentRl.setOnClickListener(toggle);\n        } else if (HashSet.class.equals(mClass)) {\n            holder.itemView.setOnTouchListener(null);\n            aSwitch.setVisibility(View.GONE);\n            goIv.setVisibility(View.GONE);\n            contentRl.setOnClickListener(null);\n        } else {\n            ClickUtils.applyPressedBgDark(holder.itemView);\n            aSwitch.setVisibility(View.GONE);\n            goIv.setVisibility(View.VISIBLE);\n            contentRl.setOnClickListener(new View.OnClickListener() {\n                @Override\n                public void onClick(View v) {\n                    FileExplorerFloatView floatView = (FileExplorerFloatView) v.getRootView();\n                    SpModifyContentView.show(floatView, mSPUtils, mKey, mValue);\n                }\n            });\n        }\n    }\n\n    public static List<SpItem> getSpItems(SPUtils spUtils) {\n        Map<String, ?> spMap = spUtils.getAll();\n        if (MapUtils.isEmpty(spMap)) {\n            return CollectionUtils.newArrayList(new SpItem());\n        }\n        List<SpItem> items = new ArrayList<>();\n        for (Map.Entry<String, ?> entry : spMap.entrySet()) {\n            items.add(new SpItem(spUtils, entry.getKey(), entry.getValue()));\n        }\n        return items;\n    }\n\n    public static List<SpItem> filterItems(List<SpItem> items, final String key) {\n        return (List<SpItem>) CollectionUtils.select(items, new CollectionUtils.Predicate<SpItem>() {\n            @Override\n            public boolean evaluate(SpItem item) {\n                return item.mKey.toLowerCase().contains(key.toLowerCase());\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/tool/fileExplorer/sp/SpModifyContentView.java",
    "content": "package com.blankj.utildebug.debug.tool.fileExplorer.sp;\n\nimport android.view.View;\nimport android.widget.EditText;\nimport android.widget.TextView;\n\nimport com.blankj.utilcode.util.ClickUtils;\nimport com.blankj.utilcode.util.ColorUtils;\nimport com.blankj.utilcode.util.SPUtils;\nimport com.blankj.utilcode.util.SpanUtils;\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.base.view.BaseContentView;\nimport com.blankj.utildebug.base.view.FloatToast;\nimport com.blankj.utildebug.debug.tool.fileExplorer.FileExplorerFloatView;\nimport com.blankj.utildebug.helper.SpHelper;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/09\n *     desc  :\n * </pre>\n */\npublic class SpModifyContentView extends BaseContentView<FileExplorerFloatView> {\n\n    private SPUtils mSPUtils;\n    private String  mKey;\n    private Object  mValue;\n    private Class   mClass;\n\n    private TextView spModifyTitleTv;\n    private EditText spModifyEt;\n    private TextView spModifyCancelTv;\n    private TextView spModifyYesTv;\n\n    public static void show(FileExplorerFloatView floatView, SPUtils spUtils, String key, Object value) {\n        new SpModifyContentView(spUtils, key, value).attach(floatView, true);\n    }\n\n    public SpModifyContentView(SPUtils spUtils, String key, Object value) {\n        mSPUtils = spUtils;\n        mKey = key;\n        mValue = value;\n        mClass = value.getClass();\n    }\n\n    @Override\n    public int bindLayout() {\n        return R.layout.du_debug_file_explorer_sp_modify;\n    }\n\n    @Override\n    public void onAttach() {\n        spModifyTitleTv = findViewById(R.id.spModifyTitleTv);\n        spModifyEt = findViewById(R.id.spModifyEt);\n        spModifyCancelTv = findViewById(R.id.spModifyCancelTv);\n        spModifyYesTv = findViewById(R.id.spModifyYesTv);\n\n        SpanUtils.with(spModifyTitleTv)\n                .append(mKey)\n                .append(\"(\" + SpHelper.getSpClassName(mClass) + \")\").setForegroundColor(ColorUtils.getColor(R.color.loveGreen))\n                .create();\n        spModifyEt.setText(mValue.toString());\n\n        ClickUtils.applyPressedViewScale(spModifyCancelTv, spModifyYesTv);\n\n        spModifyCancelTv.setOnClickListener(new OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                getFloatView().back();\n            }\n        });\n\n        spModifyYesTv.setOnClickListener(new OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                String val = spModifyEt.getText().toString();\n                boolean isSuccess = SpHelper.putValue(mSPUtils, mKey, val, mClass);\n                if (isSuccess) {\n                    getFloatView().back();\n                } else {\n                    FloatToast.showShort(\"Type is wrong.\");\n                }\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/tool/fileExplorer/sp/SpViewerContentView.java",
    "content": "package com.blankj.utildebug.debug.tool.fileExplorer.sp;\n\nimport android.widget.TextView;\n\nimport com.blankj.utilcode.util.FileUtils;\nimport com.blankj.utilcode.util.SPUtils;\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.base.rv.BaseItemAdapter;\nimport com.blankj.utildebug.base.rv.RecycleViewDivider;\nimport com.blankj.utildebug.base.view.BaseContentFloatView;\nimport com.blankj.utildebug.base.view.BaseContentView;\nimport com.blankj.utildebug.base.view.SearchEditText;\nimport com.blankj.utildebug.base.view.listener.OnRefreshListener;\nimport com.blankj.utildebug.debug.tool.fileExplorer.FileExplorerFloatView;\n\nimport java.io.File;\nimport java.util.List;\n\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/09\n *     desc  :\n * </pre>\n */\npublic class SpViewerContentView extends BaseContentView<FileExplorerFloatView> {\n\n    private File                    mFile;\n    private BaseItemAdapter<SpItem> mAdapter;\n    private List<SpItem>            mSrcItems;\n    private String                  mSpName;\n    private SPUtils                 mSPUtils;\n\n    private TextView       spViewTitle;\n    private SearchEditText spViewSearchEt;\n    private RecyclerView   spViewRv;\n\n    public static void show(FileExplorerFloatView floatView, File file) {\n        new SpViewerContentView(file).attach(floatView, true);\n    }\n\n    public SpViewerContentView(File file) {\n        mFile = file;\n        mSpName = FileUtils.getFileNameNoExtension(mFile);\n        mSPUtils = SPUtils.getInstance(mSpName);\n    }\n\n    @Override\n    public int bindLayout() {\n        return R.layout.du_debug_file_explorer_sp;\n    }\n\n    @Override\n    public void onAttach() {\n        spViewTitle = findViewById(R.id.spViewTitle);\n        spViewSearchEt = findViewById(R.id.spViewSearchEt);\n        spViewRv = findViewById(R.id.spViewRv);\n\n        spViewTitle.setText(mSpName);\n\n        mAdapter = new BaseItemAdapter<>();\n        mSrcItems = SpItem.getSpItems(mSPUtils);\n        mAdapter.setItems(mSrcItems);\n        spViewRv.setAdapter(mAdapter);\n        spViewRv.setLayoutManager(new LinearLayoutManager(getContext()));\n        spViewRv.addItemDecoration(new RecycleViewDivider(getContext(), RecycleViewDivider.VERTICAL, R.drawable.du_shape_divider));\n\n        spViewSearchEt.setOnTextChangedListener(new SearchEditText.OnTextChangedListener() {\n            @Override\n            public void onTextChanged(String text) {\n                mAdapter.setItems(SpItem.filterItems(mSrcItems, text));\n                mAdapter.notifyDataSetChanged();\n            }\n        });\n\n        setOnRefreshListener(spViewRv, new OnRefreshListener() {\n            @Override\n            public void onRefresh(BaseContentFloatView floatView) {\n                mSrcItems = SpItem.getSpItems(mSPUtils);\n                mAdapter.setItems(mSrcItems);\n                mAdapter.notifyDataSetChanged();\n                spViewSearchEt.reset();\n                floatView.closeRefresh();\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/tool/fileExplorer/text/TextViewer.java",
    "content": "package com.blankj.utildebug.debug.tool.fileExplorer.text;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/08\n *     desc  :\n * </pre>\n */\npublic class TextViewer {\n\n\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/tool/logcat/LogcatDebug.java",
    "content": "package com.blankj.utildebug.debug.tool.logcat;\n\nimport android.content.Context;\nimport android.view.View;\n\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.base.view.FloatToast;\nimport com.blankj.utildebug.debug.tool.AbsToolDebug;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/05\n *     desc  :\n * </pre>\n */\npublic class LogcatDebug extends AbsToolDebug {\n    @Override\n    public void onAppCreate(Context context) {\n\n    }\n\n    @Override\n    public int getIcon() {\n        return R.drawable.du_ic_debug_logcat;\n    }\n\n    @Override\n    public int getName() {\n        return R.string.du_logcat;\n    }\n\n    @Override\n    public void onClick(View view) {\n        FloatToast.showShort(FloatToast.WARNING, \"Developing...\");\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/debug/tool/restartApp/RestartAppDebug.java",
    "content": "package com.blankj.utildebug.debug.tool.restartApp;\n\nimport android.content.Context;\nimport android.view.View;\n\nimport com.blankj.utilcode.util.AppUtils;\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.debug.tool.AbsToolDebug;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/04\n *     desc  :\n * </pre>\n */\npublic class RestartAppDebug extends AbsToolDebug {\n\n    @Override\n    public void onAppCreate(Context context) {\n\n    }\n\n    @Override\n    public int getIcon() {\n        return R.drawable.du_ic_debug_restart_app;\n    }\n\n    @Override\n    public int getName() {\n        return R.string.du_restart_app;\n    }\n\n    @Override\n    public void onClick(View view) {\n        AppUtils.relaunchApp(true);\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/helper/FileHelper.java",
    "content": "package com.blankj.utildebug.helper;\n\nimport com.blankj.utilcode.util.FileUtils;\nimport com.blankj.utilcode.util.ImageUtils;\nimport com.blankj.utilcode.util.StringUtils;\n\nimport java.io.File;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\n\nimport androidx.annotation.IntDef;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/08\n *     desc  :\n * </pre>\n */\npublic class FileHelper {\n\n    public static final int IMAGE   = 0;\n    public static final int SP      = 1;\n    public static final int UTF8    = 2;\n    public static final int UNKNOWN = -1;\n\n    @IntDef({IMAGE, SP, UTF8, UNKNOWN})\n    @Retention(RetentionPolicy.SOURCE)\n    public @interface FileType {\n    }\n\n    @FileType\n    public static int getFileType(String path) {\n        return getFileType(FileUtils.getFileByPath(path));\n    }\n\n    @FileType\n    public static int getFileType(File file) {\n        if (!FileUtils.isFileExists(file)) return UNKNOWN;\n        if (ImageUtils.isImage(file)) {\n            return IMAGE;\n        }\n        if (FileUtils.getFileExtension(file).equals(\"xml\")) {\n            File parentFile = file.getParentFile();\n            if (parentFile != null) {\n                if (StringUtils.equals(parentFile.getName(), \"shared_prefs\")) {\n                    return SP;\n                }\n            }\n        }\n        if (FileUtils.isUtf8(file)) {\n            return UTF8;\n        }\n        return UNKNOWN;\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/helper/ImageLoader.java",
    "content": "package com.blankj.utildebug.helper;\n\nimport android.graphics.Bitmap;\nimport android.widget.ImageView;\n\nimport com.blankj.utilcode.util.FileUtils;\nimport com.blankj.utilcode.util.ImageUtils;\nimport com.blankj.utilcode.util.ThreadUtils;\n\nimport java.io.File;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/06\n *     desc  :\n * </pre>\n */\npublic class ImageLoader {\n\n    public static void load(final String path, final ImageView imageView) {\n        load(FileUtils.getFileByPath(path), imageView);\n    }\n\n    public static void load(final File file, final ImageView imageView) {\n        if (!FileUtils.isFileExists(file)) return;\n        imageView.post(new Runnable() {\n            @Override\n            public void run() {\n                ThreadUtils.executeByCached(new ThreadUtils.SimpleTask<Bitmap>() {\n                    @Override\n                    public Bitmap doInBackground() throws Throwable {\n                        return ImageUtils.getBitmap(file, imageView.getWidth(), imageView.getHeight());\n                    }\n\n                    @Override\n                    public void onSuccess(final Bitmap result) {\n                        imageView.setImageBitmap(result);\n                    }\n                });\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/helper/ShadowHelper.java",
    "content": "package com.blankj.utildebug.helper;\n\nimport android.view.View;\n\nimport com.blankj.utilcode.util.ShadowUtils;\nimport com.blankj.utilcode.util.SizeUtils;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/16\n *     desc  :\n * </pre>\n */\npublic class ShadowHelper {\n\n    public static void applyDebugIcon(View view) {\n        ShadowUtils.apply(view, new ShadowUtils.Config()\n                .setCircle()\n                .setShadowColor(0xc0_ffffff, 0x60_ffffff)\n        );\n    }\n\n    public static void applyFloatView(View view) {\n        ShadowUtils.apply(view, new ShadowUtils.Config().setShadowRadius(SizeUtils.dp2px(8)));\n    }\n\n    public static void applyMenu(View view) {\n        ShadowUtils.apply(view, new ShadowUtils.Config()\n                .setShadowRadius(SizeUtils.dp2px(4))\n        );\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/helper/SpHelper.java",
    "content": "package com.blankj.utildebug.helper;\n\nimport com.blankj.utilcode.util.SPUtils;\n\nimport java.util.HashSet;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/09\n *     desc  :\n * </pre>\n */\npublic class SpHelper {\n\n    public static String getSpClassName(Class cls) {\n        if (cls == null) return \"null\";\n        if (Boolean.class.equals(cls)) {\n            return \"boolean\";\n        }\n        if (String.class.equals(cls)) {\n            return \"String\";\n        }\n        if (Integer.class.equals(cls)) {\n            return \"int\";\n        }\n        if (Float.class.equals(cls)) {\n            return \"float\";\n        }\n        if (Long.class.equals(cls)) {\n            return \"long\";\n        }\n        if (HashSet.class.equals(cls)) {\n            return \"set\";\n        }\n        return \"unknown\";\n    }\n\n    public static boolean putValue(SPUtils spUtils, String key, String value, Class cls) {\n        if (cls == null) return false;\n        if (String.class.equals(cls)) {\n            spUtils.put(key, value);\n            return true;\n        }\n        if (Integer.class.equals(cls)) {\n            try {\n                int val = Integer.parseInt(value);\n                spUtils.put(key, val);\n                return true;\n            } catch (NumberFormatException e) {\n                return false;\n            }\n        }\n        if (Float.class.equals(cls)) {\n            try {\n                float val = Float.parseFloat(value);\n                spUtils.put(key, val);\n                return true;\n            } catch (NumberFormatException e) {\n                return false;\n            }\n        }\n        if (Long.class.equals(cls)) {\n            try {\n                long val = Long.parseLong(value);\n                spUtils.put(key, val);\n                return true;\n            } catch (NumberFormatException e) {\n                return false;\n            }\n        }\n        return false;\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/helper/WindowHelper.java",
    "content": "package com.blankj.utildebug.helper;\n\nimport android.content.Context;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.WindowManager;\n\nimport com.blankj.utilcode.util.ScreenUtils;\nimport com.blankj.utilcode.util.Utils;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/29\n *     desc  :\n * </pre>\n */\npublic class WindowHelper {\n\n    private static WindowManager sWM;\n\n    private WindowHelper() {\n    }\n\n    public static void updateViewLayout(final View view, ViewGroup.LayoutParams params) {\n        getWindowManager().updateViewLayout(view, params);\n    }\n\n    public static int getAppWindowHeight() {\n        return ScreenUtils.getAppScreenHeight();\n    }\n\n    public static WindowManager getWindowManager() {\n        if (sWM == null) {\n            sWM = (WindowManager) Utils.getApp().getSystemService(Context.WINDOW_SERVICE);\n        }\n        return sWM;\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/icon/DebugIcon.java",
    "content": "package com.blankj.utildebug.icon;\n\nimport android.content.res.Configuration;\nimport android.os.Build;\nimport android.view.MotionEvent;\nimport android.view.View;\nimport android.view.animation.DecelerateInterpolator;\nimport android.widget.ImageView;\nimport android.widget.RelativeLayout;\n\nimport com.blankj.utilcode.util.BarUtils;\nimport com.blankj.utilcode.util.PermissionUtils;\nimport com.blankj.utilcode.util.ToastUtils;\nimport com.blankj.utilcode.util.TouchUtils;\nimport com.blankj.utildebug.DebugUtils;\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.config.DebugConfig;\nimport com.blankj.utildebug.helper.ShadowHelper;\nimport com.blankj.utildebug.menu.DebugMenu;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/26\n *     desc  :\n * </pre>\n */\npublic class DebugIcon extends RelativeLayout {\n\n    private static final DebugIcon INSTANCE = new DebugIcon();\n\n    private int mIconId;\n\n    public static DebugIcon getInstance() {\n        return INSTANCE;\n    }\n\n    public static void setVisibility(boolean isShow) {\n        if (INSTANCE == null) return;\n        INSTANCE.setVisibility(isShow ? VISIBLE : GONE);\n    }\n\n    public DebugIcon() {\n        super(DebugUtils.getApp());\n        inflate(getContext(), R.layout.du_debug_icon, this);\n        ShadowHelper.applyDebugIcon(this);\n        TouchUtils.setOnTouchListener(this, new TouchUtils.OnTouchUtilsListener() {\n\n            private int rootViewWidth;\n            private int rootViewHeight;\n            private int viewWidth;\n            private int viewHeight;\n            private int statusBarHeight;\n\n            @Override\n            public boolean onDown(View view, int x, int y, MotionEvent event) {\n                viewWidth = view.getWidth();\n                viewHeight = view.getHeight();\n                View contentView = view.getRootView().findViewById(android.R.id.content);\n                rootViewWidth = contentView.getWidth();\n                rootViewHeight = contentView.getHeight();\n                statusBarHeight = BarUtils.getStatusBarHeight();\n\n                processScale(view, true);\n                return true;\n            }\n\n            @Override\n            public boolean onMove(View view, int direction, int x, int y, int dx, int dy, int totalX, int totalY, MotionEvent event) {\n                view.setX(Math.min(Math.max(0, view.getX() + dx), rootViewWidth - viewWidth));\n                view.setY(Math.min(Math.max(statusBarHeight, view.getY() + dy), rootViewHeight - viewHeight));\n                return true;\n            }\n\n            @Override\n            public boolean onStop(View view, int direction, int x, int y, int totalX, int totalY, int vx, int vy, MotionEvent event) {\n                stick2HorizontalSide(view);\n                processScale(view, false);\n                return true;\n            }\n\n            private void stick2HorizontalSide(View view) {\n                view.animate()\n                        .setInterpolator(new DecelerateInterpolator())\n                        .translationX(view.getX() + viewWidth / 2f > rootViewWidth / 2f ? rootViewWidth - viewWidth : 0)\n                        .setDuration(100)\n                        .withEndAction(new Runnable() {\n                            @Override\n                            public void run() {\n                                savePosition();\n                            }\n                        })\n                        .start();\n            }\n\n            private void processScale(final View view, boolean isDown) {\n                float value = isDown ? 1 - 0.1f : 1;\n                view.animate()\n                        .scaleX(value)\n                        .scaleY(value)\n                        .setDuration(100)\n                        .start();\n            }\n        });\n\n        setOnClickListener(new OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n                    PermissionUtils.requestDrawOverlays(new PermissionUtils.SimpleCallback() {\n                        @Override\n                        public void onGranted() {\n                            DebugMenu.getInstance().show();\n                        }\n\n                        @Override\n                        public void onDenied() {\n                            ToastUtils.showLong(R.string.de_permission_tips);\n                        }\n                    });\n                } else {\n                    DebugMenu.getInstance().show();\n                }\n            }\n        });\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        wrapPosition();\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        super.onDetachedFromWindow();\n        savePosition();\n    }\n\n    private void savePosition() {\n        DebugConfig.saveViewX(this, (int) getX());\n        DebugConfig.saveViewY(this, (int) getY());\n    }\n\n    @Override\n    protected void onConfigurationChanged(Configuration newConfig) {\n        super.onConfigurationChanged(newConfig);\n        wrapPosition();\n    }\n\n    private void wrapPosition() {\n        post(new Runnable() {\n            @Override\n            public void run() {\n                View contentView = getRootView().findViewById(android.R.id.content);\n                if (contentView == null) return;\n                setX(DebugConfig.getViewX(DebugIcon.this));\n                setY(DebugConfig.getViewY(DebugIcon.this, contentView.getHeight() / 3));\n                setX(getX() + getWidth() / 2f > contentView.getWidth() / 2f ? contentView.getWidth() - getWidth() : 0);\n            }\n        });\n    }\n\n    public void setIconId(final int iconId) {\n        ImageView debugPanelIconIv = findViewById(R.id.debugIconIv);\n        debugPanelIconIv.setImageResource(mIconId);\n    }\n\n    public int getIconId() {\n        return mIconId;\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/menu/DebugItem.java",
    "content": "package com.blankj.utildebug.menu;\n\nimport android.view.View;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport com.blankj.utilcode.util.ClickUtils;\nimport com.blankj.utilcode.util.CollectionUtils;\nimport com.blankj.utilcode.util.ColorUtils;\nimport com.blankj.utilcode.util.StringUtils;\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.base.drawable.PolygonDrawable;\nimport com.blankj.utildebug.base.rv.BaseItem;\nimport com.blankj.utildebug.base.rv.ItemViewHolder;\nimport com.blankj.utildebug.debug.IDebug;\n\nimport java.util.List;\nimport java.util.Random;\n\nimport androidx.annotation.NonNull;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/29\n *     desc  :\n * </pre>\n */\npublic class DebugItem extends BaseItem<DebugItem> {\n\n    private IDebug mDebug;\n    private int    mColor = getRandomColor();\n\n    private DebugItem(IDebug debug) {\n        super(R.layout.du_item_menu_item);\n        mDebug = debug;\n    }\n\n    @Override\n    public void bind(@NonNull ItemViewHolder holder, int position) {\n        ImageView menuItemIconIv = holder.findViewById(R.id.menuItemIconIv);\n        TextView menuItemNameTv = holder.findViewById(R.id.menuItemNameTv);\n\n        ClickUtils.applyPressedBgDark(holder.itemView);\n        ClickUtils.applyPressedViewScale(holder.itemView);\n\n        menuItemIconIv.setBackgroundDrawable(new PolygonDrawable(5, mColor));\n        menuItemIconIv.setImageResource(mDebug.getIcon());\n        menuItemNameTv.setText(StringUtils.getString(mDebug.getName()));\n        holder.itemView.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                mDebug.onClick(v);\n            }\n        });\n    }\n\n    public static List<DebugItem> getDebugItems(List<IDebug> debugs) {\n        return (List<DebugItem>) CollectionUtils.collect(debugs, new CollectionUtils.Transformer<IDebug, DebugItem>() {\n            @Override\n            public DebugItem transform(IDebug input) {\n                return new DebugItem(input);\n            }\n        });\n    }\n\n    private static final Random RANDOM = new Random();\n\n    private static int getRandomColor() {\n        return ColorUtils.getColor(COLORS[RANDOM.nextInt(6)]);\n    }\n\n    private static final int[] COLORS = new int[]{\n            R.color.bittersweet, R.color.sunflower, R.color.grass,\n            R.color.blueJeans, R.color.lavander, R.color.pinkRose\n    };\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/menu/DebugMenu.java",
    "content": "package com.blankj.utildebug.menu;\n\n\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.base.rv.BaseItemAdapter;\nimport com.blankj.utildebug.base.view.BaseContentFloatView;\nimport com.blankj.utildebug.config.DebugConfig;\nimport com.blankj.utildebug.debug.IDebug;\nimport com.blankj.utildebug.icon.DebugIcon;\n\nimport java.util.List;\n\nimport androidx.recyclerview.widget.LinearLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/29\n *     desc  :\n * </pre>\n */\npublic class DebugMenu extends BaseContentFloatView<DebugMenu> {\n\n    private static final DebugMenu INSTANCE = new DebugMenu();\n\n    private List<IDebug> mDebugs;\n\n    private BaseItemAdapter<DebugMenuItem> mAdapter;\n\n    private RecyclerView debugMenuRv;\n\n    public static DebugMenu getInstance() {\n        return INSTANCE;\n    }\n\n    @Override\n    public int bindTitle() {\n        return R.string.du_menus;\n    }\n\n    @Override\n    public int bindContentLayout() {\n        return R.layout.du_debug_menu;\n    }\n\n    @Override\n    protected void onAttachedToWindow() {\n        super.onAttachedToWindow();\n        DebugIcon.setVisibility(false);\n        if (!DebugConfig.isNoMoreReminder()) {\n            new ReminderView().show();\n        }\n    }\n\n    @Override\n    protected void onDetachedFromWindow() {\n        int a = 0xe1;\n        DebugIcon.setVisibility(true);\n        super.onDetachedFromWindow();\n    }\n\n    @Override\n    public void initContentView() {\n        setSwipeBackEnabled(false);\n\n        debugMenuRv = findViewById(R.id.debugMenuRv);\n        mAdapter = new BaseItemAdapter<>();\n        mAdapter.setItems(DebugMenuItem.getDebugMenuItems(mDebugs));\n        debugMenuRv.setAdapter(mAdapter);\n        debugMenuRv.setLayoutManager(new LinearLayoutManager(getContext()));\n    }\n\n    public void setDebugs(List<IDebug> debugs) {\n        mDebugs = debugs;\n    }\n\n    public void addDebugs(List<IDebug> debugs) {\n        if (debugs == null || debugs.size() == 0) return;\n        mDebugs.addAll(debugs);\n        if (mAdapter == null) return;\n        mAdapter.notifyDataSetChanged();\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/menu/DebugMenuItem.java",
    "content": "package com.blankj.utildebug.menu;\n\nimport android.widget.TextView;\n\nimport com.blankj.utilcode.util.StringUtils;\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.base.rv.BaseItem;\nimport com.blankj.utildebug.base.rv.BaseItemAdapter;\nimport com.blankj.utildebug.base.rv.ItemViewHolder;\nimport com.blankj.utildebug.debug.IDebug;\nimport com.blankj.utildebug.helper.ShadowHelper;\n\nimport java.util.ArrayList;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\nimport androidx.annotation.NonNull;\nimport androidx.recyclerview.widget.GridLayoutManager;\nimport androidx.recyclerview.widget.RecyclerView;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/29\n *     desc  :\n * </pre>\n */\npublic class DebugMenuItem extends BaseItem<DebugMenuItem> {\n\n    private String       mTitle;\n    private List<IDebug> mDebugs;\n\n    private DebugMenuItem(String title, List<IDebug> debugs) {\n        super(R.layout.du_item_menu);\n        mTitle = title;\n        mDebugs = debugs;\n    }\n\n    @Override\n    public void bind(@NonNull ItemViewHolder holder, int position) {\n        TextView menuTitle = holder.findViewById(R.id.menuCategory);\n        RecyclerView menuRv = holder.findViewById(R.id.menuRv);\n\n        ShadowHelper.applyMenu(holder.itemView);\n\n        menuTitle.setText(mTitle);\n\n        BaseItemAdapter<DebugItem> adapter = new BaseItemAdapter<>();\n        adapter.setItems(DebugItem.getDebugItems(mDebugs));\n        menuRv.setAdapter(adapter);\n        menuRv.setLayoutManager(new GridLayoutManager(menuRv.getContext(), 4));\n    }\n\n    public static List<DebugMenuItem> getDebugMenuItems(List<IDebug> debugs) {\n        Map<Integer, List<IDebug>> debugMap = new LinkedHashMap<>();\n        for (IDebug debug : debugs) {\n            List<IDebug> debugList = debugMap.get(debug.getCategory());\n            if (debugList == null) {\n                debugList = new ArrayList<>();\n                debugMap.put(debug.getCategory(), debugList);\n            }\n            debugList.add(debug);\n        }\n        List<DebugMenuItem> itemList = new ArrayList<>();\n        for (Map.Entry<Integer, List<IDebug>> entry : debugMap.entrySet()) {\n            itemList.add(new DebugMenuItem(getCategoryString(entry.getKey()), entry.getValue()));\n        }\n        return itemList;\n    }\n\n    private static String getCategoryString(int category) {\n        switch (category) {\n            case IDebug.TOOLS:\n                return StringUtils.getString(R.string.du_tools);\n            case IDebug.PERFORMANCE:\n                return StringUtils.getString(R.string.du_performance);\n            case IDebug.UI:\n                return StringUtils.getString(R.string.du_ui);\n            case IDebug.BIZ:\n                return StringUtils.getString(R.string.du_biz);\n            default:\n                return StringUtils.getString(R.string.du_uncategorized);\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/java/com/blankj/utildebug/menu/ReminderView.java",
    "content": "package com.blankj.utildebug.menu;\n\nimport android.widget.Switch;\n\nimport com.blankj.utildebug.R;\nimport com.blankj.utildebug.base.view.BaseContentFloatView;\nimport com.blankj.utildebug.config.DebugConfig;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/09/10\n *     desc  :\n * </pre>\n */\npublic class ReminderView extends BaseContentFloatView<ReminderView> {\n\n    private Switch reminderNoMoreSwitch;\n\n    @Override\n    public int bindTitle() {\n        return R.string.du_reminder;\n    }\n\n    @Override\n    public int bindContentLayout() {\n        return R.layout.du_reminder_view;\n    }\n\n    @Override\n    public void initContentView() {\n        reminderNoMoreSwitch = findViewById(R.id.reminderNoMoreSwitch);\n    }\n\n    @Override\n    public void dismiss() {\n        super.dismiss();\n        if (reminderNoMoreSwitch.isChecked()) {\n            DebugConfig.saveNoMoreReminder();\n        }\n    }\n}\n"
  },
  {
    "path": "lib/utildebug/src/main/res/anim/float_in.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shareInterpolator=\"false\">\n    <scale\n        android:duration=\"220\"\n        android:fromXScale=\"0.9\"\n        android:fromYScale=\"0.9\"\n        android:interpolator=\"@android:interpolator/decelerate_quint\"\n        android:pivotX=\"50%\"\n        android:pivotY=\"100%\"\n        android:toXScale=\"1.0\"\n        android:toYScale=\"1.0\" />\n    <alpha\n        android:duration=\"150\"\n        android:fromAlpha=\"0.0\"\n        android:interpolator=\"@android:interpolator/decelerate_cubic\"\n        android:toAlpha=\"1.0\" />\n</set>"
  },
  {
    "path": "lib/utildebug/src/main/res/anim/float_out.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shareInterpolator=\"false\">\n    <scale\n        android:duration=\"220\"\n        android:fromXScale=\"1.0\"\n        android:fromYScale=\"1.0\"\n        android:interpolator=\"@android:interpolator/decelerate_quint\"\n        android:pivotX=\"50%\"\n        android:pivotY=\"100%\"\n        android:toXScale=\"0.9\"\n        android:toYScale=\"0.9\" />\n    <alpha\n        android:duration=\"150\"\n        android:fromAlpha=\"1.0\"\n        android:interpolator=\"@android:interpolator/decelerate_cubic\"\n        android:toAlpha=\"0.0\" />\n</set>"
  },
  {
    "path": "lib/utildebug/src/main/res/drawable/du_rotate_refresh.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<rotate xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:drawable=\"@drawable/du_ic_swipe_refresh\"\n    android:fromDegrees=\"0\"\n    android:pivotX=\"50%\"\n    android:pivotY=\"50%\"\n    android:toDegrees=\"360\"\n    android:visible=\"true\" />"
  },
  {
    "path": "lib/utildebug/src/main/res/drawable/du_sel_et_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:state_focused=\"true\">\n        <shape android:shape=\"rectangle\">\n            <corners android:radius=\"8dp\" />\n            <stroke android:width=\"1dp\" android:color=\"@color/darkGray\" />\n        </shape>\n    </item>\n\n    <item>\n        <shape android:shape=\"rectangle\">\n            <corners android:radius=\"8dp\" />\n            <stroke android:width=\"1dp\" android:color=\"@color/mediumGrayDark\" />\n        </shape>\n    </item>\n</selector>"
  },
  {
    "path": "lib/utildebug/src/main/res/drawable/du_shape_base_float_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <corners android:radius=\"8dp\" />\n    <solid android:color=\"@color/mediumGray\" />\n</shape>"
  },
  {
    "path": "lib/utildebug/src/main/res/drawable/du_shape_base_float_title_adjust_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <corners android:radius=\"16dp\" />\n    <solid android:color=\"@color/loveGreen\" />\n</shape>"
  },
  {
    "path": "lib/utildebug/src/main/res/drawable/du_shape_base_float_title_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <corners \n        android:bottomLeftRadius=\"8dp\"\n        android:bottomRightRadius=\"8dp\"/>\n    <solid android:color=\"@color/mediumGray\" />\n</shape>"
  },
  {
    "path": "lib/utildebug/src/main/res/drawable/du_shape_base_float_title_close_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <corners android:radius=\"16dp\" />\n    <solid android:color=\"@color/grapefruit\" />\n</shape>"
  },
  {
    "path": "lib/utildebug/src/main/res/drawable/du_shape_btn.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <corners android:radius=\"4dp\" />\n    <solid android:color=\"@color/lightGrayDark\" />\n</shape>"
  },
  {
    "path": "lib/utildebug/src/main/res/drawable/du_shape_divider.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <size android:height=\"1px\" />\n    <solid android:color=\"@color/mediumGrayDark\" />\n</shape>"
  },
  {
    "path": "lib/utildebug/src/main/res/drawable/du_shape_file_divider.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:left=\"52dp\">\n        <shape android:shape=\"rectangle\">\n            <size android:height=\"1px\" />\n            <solid android:color=\"@color/mediumGrayDark\" />\n        </shape>\n    </item>\n</layer-list>"
  },
  {
    "path": "lib/utildebug/src/main/res/drawable/du_shape_input_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <corners android:radius=\"4dp\" />\n    <solid android:color=\"@color/lightGrayDark\" />\n</shape>"
  },
  {
    "path": "lib/utildebug/src/main/res/drawable/du_shape_item_menu_bg.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <corners android:radius=\"4dp\" />\n    <solid android:color=\"@color/lightGray\" />\n</shape>"
  },
  {
    "path": "lib/utildebug/src/main/res/drawable/du_shape_positive_btn.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <corners android:radius=\"4dp\" />\n    <solid android:color=\"@color/loveGreen\" />\n</shape>"
  },
  {
    "path": "lib/utildebug/src/main/res/drawable/du_shape_shadow.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <gradient\n        android:angle=\"270\"\n        android:endColor=\"#37000000\"\n        android:startColor=\"#03000000\" />\n</shape>"
  },
  {
    "path": "lib/utildebug/src/main/res/drawable/du_shape_toast.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"rectangle\">\n    <corners android:radius=\"16dp\" />\n    <solid android:color=\"@color/lightGrayDark\" />\n</shape>"
  },
  {
    "path": "lib/utildebug/src/main/res/drawable/du_switch_thumb.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shape=\"oval\">\n    <solid android:color=\"@color/lightGrayDark\" />\n    <stroke\n        android:width=\"2dp\"\n        android:color=\"@android:color/transparent\" />\n    <size\n        android:width=\"20dp\"\n        android:height=\"20dp\" />\n</shape>"
  },
  {
    "path": "lib/utildebug/src/main/res/drawable/du_switch_track.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item android:state_checked=\"true\">\n        <shape android:shape=\"rectangle\">\n            <solid android:color=\"@color/loveGreen\" />\n            <corners android:radius=\"20dp\" />\n        </shape>\n    </item>\n\n    <item android:state_checked=\"false\">\n        <shape android:shape=\"rectangle\">\n            <solid android:color=\"@color/mediumGrayDark\" />\n            <corners android:radius=\"20dp\" />\n        </shape>\n    </item>\n</selector>"
  },
  {
    "path": "lib/utildebug/src/main/res/layout/du_base_content_float.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?><!--RelativeLayout-->\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <LinearLayout\n        android:id=\"@+id/bcfRootLayout\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:background=\"@drawable/du_shape_base_float_bg\"\n        android:orientation=\"vertical\">\n\n        <com.blankj.swipepanel.SwipePanel\n            android:id=\"@+id/bcfSwipePanel\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"0dp\"\n            android:layout_weight=\"1\"\n            app:isTopCenter=\"true\"\n            app:isTopEnabled=\"false\"\n            app:leftDrawable=\"@drawable/du_ic_swipe_back\"\n            app:leftEdgeSize=\"20dp\"\n            app:leftSwipeColor=\"@color/darkGray\"\n            app:topDrawable=\"@drawable/du_rotate_refresh\"\n            app:topEdgeSize=\"500dp\"\n            app:topSwipeColor=\"@color/darkGray\">\n\n        </com.blankj.swipepanel.SwipePanel>\n\n        <View\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"4dp\"\n            android:background=\"@drawable/du_shape_shadow\" />\n\n        <RelativeLayout\n            android:id=\"@+id/bcfTitleRl\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"32dp\"\n            android:background=\"@drawable/du_shape_base_float_title_bg\"\n            android:paddingLeft=\"8dp\"\n            android:paddingTop=\"4dp\"\n            android:paddingRight=\"8dp\"\n            android:paddingBottom=\"4dp\">\n\n            <ImageView\n                android:id=\"@+id/bcfCloseIv\"\n                android:layout_width=\"40dp\"\n                android:layout_height=\"24dp\"\n                android:layout_centerVertical=\"true\"\n                android:background=\"@drawable/du_shape_base_float_title_close_bg\"\n                android:src=\"@drawable/du_ic_title_bar_close\" />\n\n            <TextView\n                android:id=\"@+id/bcfTitleTv\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:layout_marginLeft=\"8dp\"\n                android:layout_marginRight=\"8dp\"\n                android:layout_toLeftOf=\"@id/bcfAdjustIv\"\n                android:layout_toRightOf=\"@id/bcfCloseIv\"\n                android:ellipsize=\"end\"\n                android:gravity=\"center\"\n                android:maxLines=\"1\"\n                android:textColor=\"@color/darkGrayDark\"\n                android:textSize=\"@dimen/font_big\"\n                android:textStyle=\"bold\"\n                tools:text=\"title\" />\n\n            <ImageView\n                android:id=\"@+id/bcfAdjustIv\"\n                android:layout_width=\"40dp\"\n                android:layout_height=\"24dp\"\n                android:layout_alignParentRight=\"true\"\n                android:layout_centerVertical=\"true\"\n                android:background=\"@drawable/du_shape_base_float_title_adjust_bg\"\n                android:src=\"@drawable/du_ic_title_bar_adjustable\" />\n\n        </RelativeLayout>\n\n    </LinearLayout>\n</merge>\n"
  },
  {
    "path": "lib/utildebug/src/main/res/layout/du_debug_app_info.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.recyclerview.widget.RecyclerView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/appInfoRv\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:overScrollMode=\"never\" />"
  },
  {
    "path": "lib/utildebug/src/main/res/layout/du_debug_device_info.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.recyclerview.widget.RecyclerView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/deviceInfoRv\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:overScrollMode=\"never\" />"
  },
  {
    "path": "lib/utildebug/src/main/res/layout/du_debug_file_explorer.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\">\n\n    <com.blankj.utildebug.base.view.SearchEditText\n        android:id=\"@+id/fileExplorerSearchEt\"\n        style=\"@style/InputStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"12dp\"\n        android:layout_marginTop=\"8dp\"\n        android:layout_marginRight=\"12dp\"\n        android:hint=\"@string/du_search_tips\"\n        tools:text=\"hello\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/fileExplorerRv\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@+id/fileExplorerSearchEt\"\n        android:layout_marginTop=\"8dp\"\n        android:overScrollMode=\"never\" />\n\n</RelativeLayout>"
  },
  {
    "path": "lib/utildebug/src/main/res/layout/du_debug_file_explorer_image.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:layout_margin=\"8dp\">\n\n    <com.github.chrisbanes.photoview.PhotoView\n        android:id=\"@+id/imageViewerPv\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\" />\n\n</RelativeLayout>"
  },
  {
    "path": "lib/utildebug/src/main/res/layout/du_debug_file_explorer_sp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_margin=\"8dp\">\n\n    <TextView\n        android:id=\"@+id/spViewTitle\"\n        style=\"@style/InfoTitleStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"16dp\"\n        android:layout_marginRight=\"16dp\"\n        tools:text=\"title\" />\n\n    <com.blankj.utildebug.base.view.SearchEditText\n        android:id=\"@+id/spViewSearchEt\"\n        style=\"@style/InputStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/spViewTitle\"\n        android:layout_marginLeft=\"8dp\"\n        android:layout_marginTop=\"8dp\"\n        android:layout_marginRight=\"8dp\"\n        android:hint=\"@string/du_search_tips\"\n        tools:text=\"hello\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/spViewRv\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/spViewSearchEt\"\n        android:layout_marginLeft=\"8dp\"\n        android:layout_marginTop=\"8dp\"\n        android:layout_marginRight=\"8dp\"\n        android:overScrollMode=\"never\" />\n\n</RelativeLayout>"
  },
  {
    "path": "lib/utildebug/src/main/res/layout/du_debug_file_explorer_sp_modify.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_margin=\"8dp\">\n\n    <TextView\n        android:id=\"@+id/spModifyTitleTv\"\n        style=\"@style/InfoTitleStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"16dp\"\n        android:layout_marginRight=\"16dp\"\n        tools:text=\"title\" />\n\n    <com.blankj.utildebug.base.view.FloatEditText\n        android:id=\"@+id/spModifyEt\"\n        style=\"@style/InputStyle\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/spModifyTitleTv\"\n        android:layout_marginLeft=\"8dp\"\n        android:layout_marginTop=\"8dp\"\n        android:layout_marginRight=\"8dp\"\n        android:hint=\"@string/du_search_tips\"\n        tools:text=\"hello\" />\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/spModifyEt\"\n        android:layout_margin=\"8dp\"\n        android:orientation=\"horizontal\">\n\n        <TextView\n            android:id=\"@+id/spModifyCancelTv\"\n            style=\"@style/InfoContentStyle\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginRight=\"4dp\"\n            android:layout_weight=\"1\"\n            android:background=\"@drawable/du_shape_btn\"\n            android:gravity=\"center\"\n            android:padding=\"4dp\"\n            android:text=\"@string/du_cancel\"\n            android:textColor=\"@color/darkGrayDark\"\n            android:textSize=\"@dimen/font_normal\" />\n\n        <TextView\n            android:id=\"@+id/spModifyYesTv\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginLeft=\"4dp\"\n            android:layout_weight=\"1\"\n            android:background=\"@drawable/du_shape_btn\"\n            android:gravity=\"center\"\n            android:padding=\"4dp\"\n            android:text=\"@string/du_confirm\"\n            android:textColor=\"@color/loveGreenDark\"\n            android:textSize=\"@dimen/font_normal\" />\n\n    </LinearLayout>\n\n\n</RelativeLayout>"
  },
  {
    "path": "lib/utildebug/src/main/res/layout/du_debug_icon.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <!--parent type is RelativeLayout-->\n\n    <ImageView\n        android:id=\"@+id/debugIconIv\"\n        android:layout_width=\"40dp\"\n        android:layout_height=\"40dp\"\n        android:src=\"@drawable/du_ic_icon_default\" />\n\n</merge>\n"
  },
  {
    "path": "lib/utildebug/src/main/res/layout/du_debug_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.recyclerview.widget.RecyclerView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:id=\"@+id/debugMenuRv\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:overScrollMode=\"never\" />"
  },
  {
    "path": "lib/utildebug/src/main/res/layout/du_float_toast.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:background=\"@drawable/du_shape_toast\"\n    android:gravity=\"center\"\n    android:paddingLeft=\"16dp\"\n    android:paddingTop=\"8dp\"\n    android:paddingRight=\"16dp\"\n    android:paddingBottom=\"8dp\">\n\n    <ImageView\n        android:id=\"@+id/toastIconIv\"\n        android:layout_width=\"32dp\"\n        android:layout_height=\"32dp\"\n        android:layout_marginRight=\"8dp\"\n        android:padding=\"4dp\"\n        tools:src=\"@drawable/du_ic_toast_warn\" />\n\n    <TextView\n        android:id=\"@+id/toastMsgTv\"\n        style=\"@style/InfoTitleStyle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        tools:text=\"Toast Msg\" />\n\n\n</LinearLayout>"
  },
  {
    "path": "lib/utildebug/src/main/res/layout/du_item_base_info.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingLeft=\"12dp\"\n    android:paddingTop=\"8dp\"\n    android:paddingRight=\"12dp\"\n    android:paddingBottom=\"8dp\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerVertical=\"true\"\n        android:layout_toLeftOf=\"@id/baseInfoGoIv\"\n        android:orientation=\"vertical\">\n\n        <TextView\n            android:id=\"@+id/baseInfoTitleTv\"\n            style=\"@style/InfoTitleStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            tools:text=\"title\" />\n\n        <com.blankj.utildebug.base.view.EmptyGoneTextView\n            android:id=\"@+id/baseInfoContentTv\"\n            style=\"@style/InfoContentStyle\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"2dp\"\n            tools:text=\"content\" />\n\n    </LinearLayout>\n\n    <ImageView\n        android:id=\"@+id/baseInfoGoIv\"\n        android:layout_width=\"32dp\"\n        android:layout_height=\"32dp\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:padding=\"8dp\"\n        android:src=\"@drawable/du_ic_item_go\"\n        android:visibility=\"gone\" />\n\n    <Switch\n        android:id=\"@+id/baseInfoSwitch\"\n        style=\"@style/SwitchStyle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_centerVertical=\"true\"\n        android:visibility=\"gone\" />\n\n</RelativeLayout>"
  },
  {
    "path": "lib/utildebug/src/main/res/layout/du_item_empty.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <TextView\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:gravity=\"center\"\n        android:text=\"Empty\"\n        android:textColor=\"@color/darkGrayDark\"\n        android:textSize=\"@dimen/font_large\" />\n\n</RelativeLayout>"
  },
  {
    "path": "lib/utildebug/src/main/res/layout/du_item_file.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.blankj.utildebug.base.view.SwipeRightMenu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:gravity=\"center_vertical\">\n\n    <RelativeLayout\n        android:id=\"@+id/fileContentRl\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:paddingLeft=\"12dp\"\n        android:paddingTop=\"8dp\"\n        android:paddingRight=\"12dp\"\n        android:paddingBottom=\"8dp\">\n\n        <ImageView\n            android:id=\"@+id/fileTypeIv\"\n            android:layout_width=\"32dp\"\n            android:layout_height=\"32dp\"\n            android:layout_centerVertical=\"true\"\n            android:layout_marginRight=\"8dp\"\n            tools:src=\"@drawable/du_ic_debug_file_explorer\" />\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toRightOf=\"@id/fileTypeIv\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                android:id=\"@+id/fileNameTv\"\n                style=\"@style/InfoTitleStyle\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:ellipsize=\"end\"\n                android:maxLines=\"1\"\n                tools:text=\"title\" />\n\n            <TextView\n                android:id=\"@+id/fileInfoTv\"\n                style=\"@style/InfoContentStyle\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"2dp\"\n                tools:text=\"content\" />\n\n        </LinearLayout>\n\n    </RelativeLayout>\n\n    <TextView\n        android:id=\"@+id/fileMenuDeleteTv\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginRight=\"-200dp\"\n        android:background=\"@color/grapefruit\"\n        android:gravity=\"center\"\n        android:paddingLeft=\"8dp\"\n        android:paddingRight=\"8dp\"\n        android:text=\"@string/du_menu_delete\"\n        android:textColor=\"@color/white\"\n        android:textSize=\"@dimen/font_normal\" />\n\n</com.blankj.utildebug.base.view.SwipeRightMenu>"
  },
  {
    "path": "lib/utildebug/src/main/res/layout/du_item_menu.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_marginLeft=\"12dp\"\n    android:layout_marginTop=\"8dp\"\n    android:layout_marginRight=\"12dp\"\n    android:layout_marginBottom=\"16dp\"\n    android:background=\"@drawable/du_shape_item_menu_bg\"\n    android:elevation=\"18dp\"\n    android:orientation=\"vertical\">\n\n    <TextView\n        android:id=\"@+id/menuCategory\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"8dp\"\n        android:layout_marginTop=\"8dp\"\n        android:singleLine=\"true\"\n        android:textColor=\"@color/darkGrayDark\"\n        android:textSize=\"@dimen/font_normal\"\n        android:textStyle=\"bold\"\n        tools:text=\"title\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/menuRv\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"8dp\"\n        android:layout_marginBottom=\"8dp\"\n        android:overScrollMode=\"never\" />\n</LinearLayout>"
  },
  {
    "path": "lib/utildebug/src/main/res/layout/du_item_menu_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:gravity=\"center_horizontal\"\n    android:orientation=\"vertical\"\n    android:paddingTop=\"4dp\"\n    android:paddingBottom=\"4dp\">\n\n    <ImageView\n        android:id=\"@+id/menuItemIconIv\"\n        android:layout_width=\"44dp\"\n        android:layout_height=\"44dp\"\n        android:padding=\"8dp\"\n        tools:src=\"@drawable/du_ic_icon_default\" />\n\n    <TextView\n        android:id=\"@+id/menuItemNameTv\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"2dp\"\n        android:gravity=\"center\"\n        android:singleLine=\"true\"\n        android:textColor=\"@color/darkGrayDark\"\n        android:textSize=\"@dimen/font_small\"\n        tools:text=\"name\" />\n</LinearLayout>"
  },
  {
    "path": "lib/utildebug/src/main/res/layout/du_item_sp.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.blankj.utildebug.base.view.SwipeRightMenu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:gravity=\"center_vertical\">\n\n    <RelativeLayout\n        android:id=\"@+id/itemSpContentRl\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:layout_margin=\"8dp\">\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_centerVertical=\"true\"\n            android:layout_toLeftOf=\"@id/itemSpGoIv\"\n            android:orientation=\"vertical\">\n\n            <TextView\n                android:id=\"@+id/itemSpTitleTv\"\n                style=\"@style/InfoTitleStyle\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                tools:text=\"title\" />\n\n            <com.blankj.utildebug.base.view.EmptyGoneTextView\n                android:id=\"@+id/itemSpContentTv\"\n                style=\"@style/InfoContentStyle\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:layout_marginTop=\"2dp\"\n                tools:text=\"content\" />\n\n        </LinearLayout>\n\n        <ImageView\n            android:id=\"@+id/itemSpGoIv\"\n            android:layout_width=\"32dp\"\n            android:layout_height=\"32dp\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:padding=\"8dp\"\n            android:src=\"@drawable/du_ic_item_go\"\n            android:visibility=\"gone\" />\n\n        <Switch\n            android:id=\"@+id/itemSpSwitch\"\n            style=\"@style/SwitchStyle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_centerVertical=\"true\"\n            android:visibility=\"gone\" />\n\n    </RelativeLayout>\n\n    <TextView\n        android:id=\"@+id/itemSpDeleteTv\"\n        android:layout_width=\"100dp\"\n        android:layout_height=\"match_parent\"\n        android:background=\"@color/grapefruit\"\n        android:gravity=\"center\"\n        android:text=\"@string/du_menu_delete\"\n        android:textColor=\"@color/white\"\n        android:textSize=\"@dimen/font_normal\" />\n\n</com.blankj.utildebug.base.view.SwipeRightMenu>"
  },
  {
    "path": "lib/utildebug/src/main/res/layout/du_reminder_view.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:gravity=\"center\"\n    android:orientation=\"vertical\"\n    android:padding=\"16dp\">\n\n    <ImageView\n        android:layout_width=\"32dp\"\n        android:layout_height=\"32dp\"\n        android:src=\"@drawable/du_ic_reminder\" />\n\n    <TextView\n        style=\"@style/InfoTitleStyle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"8dp\"\n        android:gravity=\"center\"\n        android:text=\"@string/du_reminder_content\" />\n\n    <Switch\n        android:id=\"@+id/reminderNoMoreSwitch\"\n        style=\"@style/SwitchStyle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"4dp\"\n        android:checked=\"true\"\n        android:switchPadding=\"8dp\"\n        android:text=\"@string/du_reminder_no_more\"\n        android:textColor=\"@color/darkGray\"\n        android:textSize=\"@dimen/font_small\" />\n\n</LinearLayout>"
  },
  {
    "path": "lib/utildebug/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimaryOrigin\">#3F51B5</color>\n    <color name=\"colorPrimaryDarkOrigin\">#303F9F</color>\n    <color name=\"colorAccentOrigin\">#FF4081</color>\n\n    <color name=\"colorPrimary\">#34A48E</color>\n    <color name=\"colorPrimaryHalfTrans\">#8034A48E</color>\n    <color name=\"colorPrimaryDark\">#245C50</color>\n    <color name=\"colorAccent\">#FF73A3</color>\n    <color name=\"colorAccentHalfTrans\">#80245C50</color>\n\n    <color name=\"white\">#FFFFFF</color>\n    <color name=\"light_black\">#191919</color>\n\n    <color name=\"rainbow_red\">#bf0c43</color>\n    <color name=\"rainbow_yellow\">#f9ba15</color>\n    <color name=\"rainbow_green\">#8eac00</color>\n    <color name=\"rainbow_blue\">#127a97</color>\n    <color name=\"rainbow_purple\">#452b72</color>\n\n    <!--beautiful color-->\n    <color name=\"grapefruit\">#ED5565</color>\n    <color name=\"grapefruitDark\">#DA4453</color>\n\n    <color name=\"bittersweet\">#FC6E51</color>\n    <color name=\"bittersweetDark\">#E9573F</color>\n\n    <color name=\"sunflower\">#FFCE54</color>\n    <color name=\"sunflowerDark\">#F6BB42</color>\n\n    <color name=\"grass\">#A0D468</color>\n    <color name=\"grassDark\">#8CC152</color>\n\n    <color name=\"mint\">#48CFAD</color>\n    <color name=\"mintDark\">#37BC9B</color>\n\n    <color name=\"loveGreen\">#62C554</color>\n    <color name=\"loveGreenDark\">#479841</color>\n\n    <color name=\"aqua\">#4FC1E9</color>\n    <color name=\"aquaDark\">#3BAFDA</color>\n\n    <color name=\"blueJeans\">#5D9CEC</color>\n    <color name=\"blueJeansDark\">#4A89DC</color>\n\n    <color name=\"lavander\">#AC92EC</color>\n    <color name=\"lavanderDark\">#967ADC</color>\n\n    <color name=\"pinkRose\">#EC87C0</color>\n    <color name=\"pinkRoseDark\">#D770AD</color>\n\n    <color name=\"lightGray\">#F5F7FA</color>\n    <color name=\"lightGrayDark\">#E6E9ED</color>\n\n    <color name=\"mediumGray\">#CCD1D9</color>\n    <color name=\"mediumGrayDark\">#AAB2BD</color>\n\n    <color name=\"darkGray\">#656D78</color>\n    <color name=\"darkGrayDark\">#434A54</color>\n\n</resources>\n"
  },
  {
    "path": "lib/utildebug/src/main/res/values/dimens.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <dimen name=\"font_huge\">22dp</dimen>\n    <dimen name=\"font_large\">20dp</dimen>\n    <dimen name=\"font_big\">18dp</dimen>\n    <dimen name=\"font_normal\">16dp</dimen>\n    <dimen name=\"font_small\">14dp</dimen>\n    <dimen name=\"font_mini\">12dp</dimen>\n</resources>"
  },
  {
    "path": "lib/utildebug/src/main/res/values/ids.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <item name=\"baseFloatView\" type=\"id\" />\n    <item name=\"baseContentView\" type=\"id\" />\n</resources>"
  },
  {
    "path": "lib/utildebug/src/main/res/values/strings.xml",
    "content": "<resources>\n\n    <string name=\"du_menus\">Menus</string>\n    <string name=\"du_tools\">Tools</string>\n    <string name=\"du_performance\">Performance</string>\n    <string name=\"du_ui\">UI</string>\n    <string name=\"du_biz\">Business</string>\n    <string name=\"du_uncategorized\">Uncategorized</string>\n\n    <string name=\"de_permission_tips\">DebugUtils need draw over other app permission.</string>\n    <string name=\"du_adjust_tips\">Drag the button to adjust the height of the window.</string>\n    <string name=\"du_search_tips\">Enter the keywords you want to filter.</string>\n    <string name=\"du_reminder\">Reminder</string>\n    <string name=\"du_reminder_content\">Drag the title bar to move the window.\\nDouble click the title bar to toggle the transparent of window.</string>\n    <string name=\"du_reminder_no_more\">No more prompts next time.</string>\n    <string name=\"du_cancel\">Cancel</string>\n    <string name=\"du_confirm\">Confirm</string>\n\n    <string name=\"du_menu_delete\">Delete</string>\n    <string name=\"du_menu_rename\">Rename</string>\n\n    <!--App Info-->\n    <string name=\"du_app_info\">App Info</string>\n    <string name=\"du_app_info_pkg_name\">Package Name</string>\n    <string name=\"du_app_info_version_name\">Version Name</string>\n    <string name=\"du_app_info_version_code\">Version Code</string>\n    <string name=\"du_app_info_min_sdk_version\">Min SDK Version</string>\n    <string name=\"du_app_info_target_sdk_version\">Target SDK Version</string>\n    <string name=\"du_app_info_open_app_info_page\">Open App Info Page</string>\n\n    <!--Device Info-->\n    <string name=\"du_device_info\">Device Info</string>\n    <string name=\"du_device_info_name\">Name</string>\n    <string name=\"du_device_info_android_version\">Android Version</string>\n    <string name=\"du_device_info_adb_enabled\">ADB Enabled</string>\n    <string name=\"du_device_info_support_abis\">Support ABIs</string>\n    <string name=\"du_device_info_screen_info\">Screen Info</string>\n    <string name=\"du_device_info_open_development_settings_page\">Open Development Settings Page</string>\n\n    <!--File Explorer-->\n    <string name=\"du_file_explorer\">File Explorer</string>\n    <string name=\"du_file_item_num\">%1$d items</string>\n\n    <!--Logcat-->\n    <string name=\"du_logcat\">Logcat</string>\n\n    <!--Restart App-->\n    <string name=\"du_restart_app\">Restart App</string>\n\n    <!--Clear Storage-->\n    <string name=\"du_clear_storage\">Clear Storage</string>\n\n    <!--Clear Cache-->\n    <string name=\"du_clear_cache\">Clear Cache</string>\n\n\n</resources>\n"
  },
  {
    "path": "lib/utildebug/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n\n    <style name=\"FloatAnimation\">\n        <item name=\"android:windowEnterAnimation\">@anim/float_in</item>\n        <item name=\"android:windowExitAnimation\">@anim/float_out</item>\n    </style>\n\n    <style name=\"InfoTitleStyle\">\n        <item name=\"android:textColor\">@color/darkGrayDark</item>\n        <item name=\"android:textSize\">@dimen/font_normal</item>\n    </style>\n\n    <style name=\"InfoContentStyle\">\n        <item name=\"android:textColor\">@color/darkGray</item>\n        <item name=\"android:textSize\">@dimen/font_small</item>\n    </style>\n\n    <style name=\"InputStyle\" parent=\"InfoContentStyle\">\n        <item name=\"android:background\">@drawable/du_shape_input_bg</item>\n        <item name=\"android:elevation\">2dp</item>\n        <item name=\"android:inputType\">text</item>\n        <item name=\"android:maxLines\">1</item>\n        <item name=\"android:paddingLeft\">12dp</item>\n        <item name=\"android:paddingTop\">8dp</item>\n        <item name=\"android:paddingRight\">12dp</item>\n        <item name=\"android:paddingBottom\">8dp</item>\n        <item name=\"android:textColor\">@color/darkGrayDark</item>\n        <item name=\"android:textCursorDrawable\">@null</item>\n        <item name=\"android:textSize\">@dimen/font_normal</item>\n    </style>\n\n    <style name=\"SwitchStyle\">\n        <item name=\"android:thumb\">@drawable/du_switch_thumb</item>\n        <item name=\"android:track\">@drawable/du_switch_track</item>\n        <item name=\"android:background\">@android:color/transparent</item>\n    </style>\n</resources>"
  },
  {
    "path": "lib/utildebug/src/main/res/values-zh-rCN/strings.xml",
    "content": "<resources>\n\n    <string name=\"du_menus\">菜单</string>\n    <string name=\"du_tools\">常用工具</string>\n    <string name=\"du_performance\">性能监控</string>\n    <string name=\"du_ui\">视觉工具</string>\n    <string name=\"du_biz\">业务工具</string>\n    <string name=\"du_uncategorized\">未归类</string>\n\n    <string name=\"de_permission_tips\">请授权「显示在其他应用上层」的权限。</string>\n    <string name=\"du_adjust_tips\">拖动按钮以调整窗口的高度。</string>\n    <string name=\"du_search_tips\">输入想要过滤的关键字</string>\n    <string name=\"du_reminder\">温馨提示</string>\n    <string name=\"du_reminder_content\">拖动标题栏以移动窗口。\\n双击标题栏以切换窗口的透明度。</string>\n    <string name=\"du_reminder_no_more\">下次不再提示</string>\n    <string name=\"du_cancel\">取消</string>\n    <string name=\"du_confirm\">确认</string>\n\n    <string name=\"du_menu_delete\">删除</string>\n    <string name=\"du_menu_rename\">重命名</string>\n\n    <!--App Info-->\n    <string name=\"du_app_info\">应用信息</string>\n    <string name=\"du_app_info_pkg_name\">包名</string>\n    <string name=\"du_app_info_version_name\">版本名</string>\n    <string name=\"du_app_info_version_code\">版本号</string>\n    <string name=\"du_app_info_min_sdk_version\">支持最低系统版本号</string>\n    <string name=\"du_app_info_target_sdk_version\">目标系统版本号</string>\n    <string name=\"du_app_info_open_app_info_page\">跳转应用信息页</string>\n\n    <!--Device Info-->\n    <string name=\"du_device_info\">设备信息</string>\n    <string name=\"du_device_info_name\">名称</string>\n    <string name=\"du_device_info_android_version\">系统版本</string>\n    <string name=\"du_device_info_adb_enabled\">ADB 是否开启</string>\n    <string name=\"du_device_info_support_abis\">支持的 ABIs</string>\n    <string name=\"du_device_info_screen_info\">屏幕信息</string>\n    <string name=\"du_device_info_open_development_settings_page\">跳转开发者选项页</string>\n\n    <!--File Explorer-->\n    <string name=\"du_file_explorer\">沙盒浏览</string>\n    <string name=\"du_file_item_num\">%1$d 项</string>\n\n    <!--Logcat-->\n    <string name=\"du_logcat\">日志抓取</string>\n\n    <!--Restart-->\n    <string name=\"du_restart_app\">重启应用</string>\n\n    <!--Clear Storage-->\n    <string name=\"du_clear_storage\">清除数据</string>\n\n    <!--Clear Cache-->\n    <string name=\"du_clear_cache\">清除缓存</string>\n\n\n</resources>\n"
  },
  {
    "path": "lib/utildebug-no-op/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "lib/utildebug-no-op/build.gradle",
    "content": "apply {\n}\n\ndependencies {\n\n}"
  },
  {
    "path": "lib/utildebug-no-op/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "lib/utildebug-no-op/src/main/AndroidManifest.xml",
    "content": "<manifest package=\"com.blankj.utildebug\">\n\n</manifest>\n"
  },
  {
    "path": "lib/utildebug-no-op/src/main/java/com/blankj/utildebug/DebugUtils.java",
    "content": "package com.blankj.utildebug;\n\nimport com.blankj.utildebug.debug.IDebug;\n\nimport java.util.List;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/28\n *     desc  : utils about debug\n * </pre>\n */\npublic class DebugUtils {\n\n    private DebugUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    public static void setIconId(final int icon) {\n    }\n\n    public static void addDebugs(final List<IDebug> debugs) {\n    }\n}\n"
  },
  {
    "path": "lib/utildebug-no-op/src/main/java/com/blankj/utildebug/debug/IDebug.java",
    "content": "package com.blankj.utildebug.debug;\n\nimport android.content.Context;\nimport android.view.View;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/08/28\n *     desc  :\n * </pre>\n */\npublic interface IDebug {\n\n    void onAppCreate(Context context);\n\n    int getCategory();\n\n    int getIcon();\n\n    int getName();\n\n    void onClick(View view);\n\n    int TOOLS       = 0;\n    int PERFORMANCE = 1;\n    int UI          = 2;\n    int BIZ         = 3;\n}\n"
  },
  {
    "path": "module_config.gradle",
    "content": "import groovy.json.JsonSlurper\n\n/**\n * 在 module_config.json 中 根据 appConfig 和 pkgConfig 来 include 本地模块\n * 可以考虑写成插件来更方便 apply\n */\n\ndef json = new JsonSlurper().parse(file(\"./module_config.json\"))\n\nfor (def module in json.moduleConfig) {\n    String moduleName = module.name\n\n    if (moduleName == \"feature_mock\") {\n        if (json.pkgConfig.isEmpty()) {\n            module.isApply = false\n        }\n    } else if (moduleName.endsWith(\"_app\")) {\n        if (!json.appConfig.contains(moduleName)) {\n            module.isApply = false\n        }\n    } else if (moduleName.endsWith(\"_pkg\")) {\n        if (!json.pkgConfig.isEmpty()) {\n            if (!json.pkgConfig.contains(moduleName)) {\n                module.isApply = false\n            }\n        }\n    }\n\n    if (module.useLocal && module.isApply) {\n        include moduleName\n        project(\":$moduleName\").projectDir = file(module.localPath)\n    }\n}\n\ndef ls = System.getProperty(\"line.separator\")\n\nList<String> modules = []\nfor (def module in json.moduleConfig) {\n    String name = module.name\n    boolean isApply = module.isApply\n    boolean useLocal = module.useLocal\n    String localPath = module.localPath\n    String remotePath = module.remotePath\n    if (localPath != null) localPath = \"\\\"$localPath\\\"\"\n    if (remotePath != null) remotePath = \"\\\"$remotePath\\\"\"\n    modules.add(String.format(\"%-12s%-27s: new ModuleConfig(isApply: %-5s, useLocal: %-5s, localPath: $localPath%s),\",\n            \"\", name, isApply, useLocal, remotePath == null ? \"\" : \", remotePath: $remotePath\"))\n}\n\ndef configFile = file('./buildSrc/src/main/groovy/Config.groovy')\ndef lines = configFile.readLines(\"utf8\")\ndef configContent = new StringBuilder()\n\nboolean enterNeverFlag = false\nfor (def line : lines) {\n    if (enterNeverFlag) {\n        if (line.contains(\"/*Don't delete this line*/\")) {\n            configContent.append(ls).append(line)\n            enterNeverFlag = false\n        }\n        continue\n    }\n    configContent.append(ls).append(line)\n    if (line.contains(\"/*Don't delete this line*/\")) {\n        configContent.append(ls).append(String.format(\"%-12s/*Generated by \\\"module_config.json\\\"*/\", \"\"))\n        enterNeverFlag = true\n        for (String m : modules) {\n            configContent.append(ls).append(m)\n        }\n    }\n}\nconfigFile.write(configContent.substring(ls.length()).toString())"
  },
  {
    "path": "module_config.json",
    "content": "{\n  \"desc\": \"提交 git 时需要检查下配置是否正确！！！\",\n  \"appConfigDesc\": \"appConfig 配置的是可以跑 app 的模块\",\n  \"appConfig\": [\"feature_launcher_app\"],\n  \"pkgConfigDesc\": \"pkgConfig 配置的是要依赖的功能包，为空则依赖全部\",\n  \"pkgConfig\": [],\n  \"moduleConfigDesc\": \"moduleConfig 配置的是使用本地还是仓库，优先级低于 appConfig 和 pkgConfig\",\n  \"moduleConfig\": [\n    {\"name\": \"plugin_api_gradle_plugin\",  \"isApply\": true, \"useLocal\": true, \"localPath\": \"./plugin/api-gradle-plugin\"},\n    {\"name\": \"plugin_bus_gradle_plugin\",  \"isApply\": true, \"useLocal\": true, \"localPath\": \"./plugin/bus-gradle-plugin\"},\n    {\"name\": \"plugin_lib_base_transform\", \"isApply\": true, \"useLocal\": true, \"localPath\": \"./plugin/lib/base-transform\", \"remotePath\": \"com.blankj:base-transform:1.0\"},\n    {\"name\": \"plugin_buildSrc_plugin\",    \"isApply\": true, \"useLocal\": true, \"localPath\": \"./plugin/buildSrc-plugin\"},\n    {\"name\": \"feature_mock\",              \"isApply\": true, \"useLocal\": true, \"localPath\": \"./feature/mock\"},\n    {\"name\": \"feature_launcher_app\",      \"isApply\": true, \"useLocal\": true, \"localPath\": \"./feature/launcher/app\"},\n    {\"name\": \"feature_main_app\",          \"isApply\": true, \"useLocal\": true, \"localPath\": \"./feature/main/app\"},\n    {\"name\": \"feature_main_pkg\",          \"isApply\": true, \"useLocal\": true, \"localPath\": \"./feature/main/pkg\"},\n    {\"name\": \"feature_subutil_app\",       \"isApply\": true, \"useLocal\": true, \"localPath\": \"./feature/subutil/app\"},\n    {\"name\": \"feature_subutil_pkg\",       \"isApply\": true, \"useLocal\": true, \"localPath\": \"./feature/subutil/pkg\"},\n    {\"name\": \"feature_subutil_export\",    \"isApply\": true, \"useLocal\": true, \"localPath\": \"./feature/subutil/export\"},\n    {\"name\": \"feature_utilcode_app\",      \"isApply\": true, \"useLocal\": true, \"localPath\": \"./feature/utilcode/app\"},\n    {\"name\": \"feature_utilcode_pkg\",      \"isApply\": true, \"useLocal\": true, \"localPath\": \"./feature/utilcode/pkg\"},\n    {\"name\": \"feature_utilcode_export\",   \"isApply\": true, \"useLocal\": true, \"localPath\": \"./feature/utilcode/export\", \"remotePath\": \"com.blankj:utilcode-export:1.1\"},\n    {\"name\": \"lib_base\",                  \"isApply\": true, \"useLocal\": true, \"localPath\": \"./lib/base\"},\n    {\"name\": \"lib_common\",                \"isApply\": true, \"useLocal\": true, \"localPath\": \"./lib/common\"},\n    {\"name\": \"lib_subutil\",               \"isApply\": true, \"useLocal\": true, \"localPath\": \"./lib/subutil\"},\n    {\"name\": \"lib_utilcode\",              \"isApply\": true, \"useLocal\": true, \"localPath\": \"./lib/utilcode\", \"remotePath\": \"com.blankj:utilcodex:$Config.versionName\"},\n    {\"name\": \"lib_utildebug\",             \"isApply\": true, \"useLocal\": true, \"localPath\": \"./lib/utildebug\"},\n    {\"name\": \"lib_utildebug_no_op\",       \"isApply\": true, \"useLocal\": true, \"localPath\": \"./lib/utildebug-no-op\"}\n  ]\n}\n"
  },
  {
    "path": "plugin/api-gradle-plugin/.gitignore",
    "content": "/build\nApiUtils2333.class"
  },
  {
    "path": "plugin/api-gradle-plugin/CHANGELOG.md",
    "content": "# Change Log\n\n## v1.5(2020/11/06)\n对非 mock 的 visitor 的修复\n\n## v1.4(2020/05/19)\n重新发布，因为上个版本从阿里云仓库拉取是有问题的\n\n## v1.3(2020/04/29)\n重构使用 base-transform\n\n## v1.2(2019/11/30)\n去除 gradle 版本依赖的问题\n\n## v1.1(2019/10/30)\n新增 onlyScanLibRegex, jumpScanLibRegex 的 DSL\n\n## v1.0(2019/07/20)\n发布初版本"
  },
  {
    "path": "plugin/api-gradle-plugin/README.md",
    "content": "# 一学就会的模块间通讯（ApiUtils）\n\n## 背景\n\n随着项目业务越来越多，开发出一套好的组件化方案势在必行，如果还在探寻一套好的组件化架构，那么 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 想必会是你的菜。\n\n组件化方案中各业务是相互隔离的，所以两个业务模块要通信的话，就需要通过路由或者接口下沉来完成，业界的方案都无法与 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 完美融合，所以我就只好自己动手来完成一个更方便、精简、完美的 `ApiUtils`。\n\n它功能类似 SPI，但比 SPI 更适合于 Android，而且功能更强大，这里也吐槽下 Android 中使用 ServiceLoader 会引起的 ANR 的问题，虽然 kotlinx 中 R8 加入了 FastServiceLoader，但如果不用 kotlinx 的项目还是无法解决 ANR 的问题；解决方案肯定是有的，这里就不展开讨论这个问题了。\n\n在 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 架构中，我们可以通过 `ApiUtils` 来自由调用各模块的 `apis`，各业务通过对外提供的 `export` 模块来供其他业务方使用，自身只需要实现自身的 `export` 中的 `apis` 即可。其 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 的架构图如下所示：\n\n\n![AucFrame](https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/art/auc_frame.png)\n\n`ApiUtils` 扮演的角色如下所示：\n\n![ApiUtilsRole](https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/art/communication.png)\n\n图中还有提到 **[BusUtils](https://github.com/Blankj/AndroidUtilCode/blob/master/plugin/bus-gradle-plugin)**，这是一个比 EventBus 更高效的模块内通讯工具，想了解的可以点进去看看哈。当然，`ApiUtils` 不仅在 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 中可以使用，在正常项目中你也可以使用它来做业务隔离，下面来介绍其具体使用方式。\n\n\n## 使用\n\n### 配置\n\n在项目根目录的 `build.gradle` 中添加 `api` 插件：\n\n```groovy\nbuildscript {\n    dependencies {\n        ...\n        classpath 'com.blankj:api-gradle-plugin:latest.release'\n    }\n}\n```\n\n然后在 `application` 模块中使用该插件：\n\n```groovy\napply plugin: \"com.blankj.api\"\n```\n\n给你的项目添加 **[AndroidUtilCode](https://github.com/Blankj/AndroidUtilCode)** 依赖：\n\n```groovy\napi \"com.blankj:utilcode:latest.release\"\n```\n\n如果你单纯只想引入 `ApiUtils` 也是可以的，需要你自己拷贝一份这个类放到你工程里，然后在 app 下的 `build.gradle` 中 配置 api 的 DSL 域如下所示：\n\n```groovy\napi {\n    apiUtilsClass \"com.xxx.xxx.ApiUtils\"\n}\n\nandroid {\n    ...\n}\n```\n\n可以猜测到默认的 apiUtilsClass 为 `com.blankj.utilcode.util.ApiUtils` 哈。\n\n当然，如果你项目是开启混淆的话，全量引入 **[AndroidUtilCode](https://github.com/Blankj/AndroidUtilCode)** 也是可以的，混淆会帮你去除未使用到的类和方法。\n\napi 完整的 DSL 如下所示：\n\n```groovy\napi {\n    abortOnError boolean   // api 扫描有问题是否终止编译，默认 true\n    apiUtilsClass String   // ApiUtils 类的路径，默认 'com.blankj.utilcode.util.ApiUtils'\n    onlyScanLibRegex String// 设置 transform 只扫描库的正则，比如 auc 配置的 '^([:]|(com\\\\.blankj)).+$'，\n                           // [:] 表示扫描本地 module，因为本地 module 在 transform 中的 jar 包名是以 : 开头的，比如 :feature:utilcode:feature_utilcode_pkg，\n                           // com.blankj 表示扫描远端仓库以 com.blankj 开头的，比如 com.blankj:utilcode:xx\n    jumpScanLibRegex String// 和 onlyScanLibRegex 类似，不过是指要跳过哪些扫描的，和 onlyScanLibRegex 不能共存，onlyScanLibRegex 优先级更高\n}\n```\n\n### 例子\n\n插件和依赖都配置完毕，下面就让我们在项目中使用吧，举一个实际的例子，比如 `login` 模块中存在 `LoginActivity`，`main` 模块存在 `MainActivity`，这两个模块是平行的关系，两者互不依赖，现在我们通过 `ApiUtils` 让 `LoginActivity` 来启动 `MainActivity`，在 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 中每个业务模块下都有 `export` 模块，类似于你们自己项目中的底层公共模块，因为是 `login` 来调用 `main` 模块，所以是 `mian` 模块需要提供 `api` 来供 `login` 来调，所以我们在 `main` 的 `export` 中加入一个继承自 `ApiUtils.BaseApi` 的抽象类 `MainApi`，并添加启动 `MainActivity` 的抽象方法，我们把方法搞得更复杂点，带上自定义的参数和返回值，具体如下所示：\n\n```java\npublic abstract class MainApi extends ApiUtils.BaseApi {\n    public abstract MainResult startMainActivity(Context context, MainParam param);\n}\n\npublic class MainParam {\n\n    private String name;\n\n    public MainParam(String name) {\n        this.name = name;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n}\n\npublic class MainResult {\n\n    private String name;\n\n    public MainResult(String name) {\n        this.name = name;\n    }\n\n    public String getName() {\n        return name;\n    }\n\n    public void setName(String name) {\n        this.name = name;\n    }\n}\n```\n\n接下来我们在 `main` 模块新建 `MainApiImpl` 去实现这个抽象类，但需要额外做一步，**对该实现类加一个 `@ApiUtils.Api` 注解**，该注解是提供给 `api` 插件注入使用的，后面原理分析会提到，而且需要保证只能有一个空参的构造函数，因为后续调用的时候会用无参构造来生成实例，具体如下所示：\n\n```java\n@ApiUtils.Api\npublic class MainApiImpl extends MainApi {\n    @Override\n    public MainResult startMainActivity(Context context, MainParam param) {\n        MainActivity.start(context, param);\n        return new MainResult(\"result\");\n    }\n}\n```\n\n关于各模块的 `impl` 我建议最好放在各模块包名的最外层，这样方便打开这个模块源码就能找到这个模块向外暴露的 `apis`。\n\n注解 `@ApiUtils.Api` 中还提供了一个 `isMock` 的值，该值默认是 `false`，所以如上注解相当于 `@ApiUtils.Api(isMock = false)`，该值代表该接口是否用于 mock，一个 `api` 在项目中只有一个实现，所以在单模块调试的时候，该模块在通过 `ApiUtils` 调用其他模块 `api` 的时候，因为其他模块没有依赖进来，所以此时就需要 mock 其他模块的 `api`，防止因为找不到其他模块的 `api` 而 crash，在 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 中的 `mock` 层中我们就是这么做的，比如 `login` 模块在单独调试的时候，此时 `main` 模块是不存在的，我们就要写一个 mock 的 `api` 如下所示：\n\n```java\n@ApiUtils.Api(isMock = true)\npublic class MainApiMockImpl extends MainApi {\n    @Override\n    public MainResult startMainActivity(Context context, MainParam param) {\n        ToastUtils.showLong(\"Start MainActivity succeed.\");\n        return new MainResult(\"mock result\");\n    }\n}\n```\n\n当 `MainApiImpl` 和 `MainApiMockImpl` 同时存在的时候，`api` 插件会让 `isMock = false` 为最终的 `api` 实现类，也就是 `MainApiImpl`，在 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 中，mock 层是在非全量 pkg 的情况下才会参与编译，所以不用担心最终全量的 app 中存有 `mock` 的冗余操作。\n\n下面就让我们在 `login` 中通过 `ApiUtils` 来调用 `mian` 提供的 `api` 吧，如下所示：\n\n```java\nMainResult result = ApiUtils.getApi(MainApi.class)\n        .startMainActivity(LoginActivity.this, new MainParam(\"MainParam\"));\n```\n\n这里说明下，`ApiUtils.getApi(MainApi.class)` 这一步是懒加载的，也就是只会在第一次调用的时候才初始化，所以不用担心初始化的时候把所有业务 `api` 都一股脑实现降低效率的风险。\n\n现在，我们便可以运行一下查看是否可以跳转成功，`api` 插件还会在你的 application 目录下生成一份 `__api__.json` 的 api 列表文件，具体如下所示：\n\n```json\n{\n  \"ApiUtilsClass\": \"com.blankj.utilcode.util.ApiUtils\",\n  \"implApis\": {\n    \"com/blankj/main/export/api/MainApi\": \"{ implApiClass: com/blankj/main/pkg/MainApiImpl, isMock: false }\"\n  },\n  \"noImplApis\": []\n}\n```\n\n如果项目中并没有实现 `MainApi` 的话，为了确保项目不会因为 `ApiUtils` 在运行时崩溃，`api` 插件会使其在编译时就不通过，此时 `__api__.json` 文件如下所示，提示你需要实现 `MainApi`：\n\n```json\n{\n  \"ApiUtilsClass\": \"com.blankj.utilcode.util.ApiUtils\",\n  \"implApis\": {},\n  \"noImplApis\": [\n    \"com/blankj/main/export/api/MainApi\"\n  ]\n}\n```\n\n好了，使用已经介绍完毕了，看完是不是觉得还不错，6 着干嘛，赶紧扣愣吧，结合 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 使用简直美哉哈。\n\n\n## 规范\n\n要想工具用得舒服，规范肯定要遵守的，所谓无规矩不成方圆，不然五花八门的问题肯定一堆堆，这里推荐如下规范：\n\n* `impl` 和 `api` 应该都是 `public` 的，而且 `impl` 中应该只存在一个无参的 `public` 构造函数（默认不写即可）。\n* `api` 中接口的修改我们遵循类似于官方 Android SDK 的升级，大部分情况是新接口的出现需要兼容老接口，如果老接口并不影响功能的正常使用，也就无需通知业务方更新为新接口，新的接口一般都是新的业务方来调用；除非老的接口存有问题或漏洞，我们明确需要删除它，那在删除它的同时，我们还需要把业务中调用老接口的地方统一替换为新的接口，类似于我们升级 Android SDK 的时候，某些 `api` 明确被官方删除了，那我们就需要强行替换为新的接口。还是上面的跳转 `main` 的例子，由于新的业务在跳转的时候需新增一个 `UserInfo` 的参数，具体如下所示：\n```java\n// old\npublic abstract class MainApi extends ApiUtils.BaseApi {\n    public abstract MainResult startMainActivity(Context context, MainParam param);\n}\n\n// good\npublic abstract class MainApi extends ApiUtils.BaseApi {\n    public abstract MainResult startMainActivity(Context context, MainParam param);\n\n    public abstract MainResult startMainActivity(Context context, MainParam param, UserInfo info);\n}\n\n// don't\npublic abstract class MainApi extends ApiUtils.BaseApi {\n    public abstract MainResult startMainActivity(Context context, MainParam param, UserInfo info);\n}\n```\n如果删除或修改老的接口的话，会导致其他模块还没来得及更新你的接口，从而调用你老的接口直接编译不过的问题。\n* 把 `impl` 放在业务包的最外层，开门见山，方便寻找。\n* 如果能结合 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 来使用，那就更规范不过了。\n\n\n## 原理\n\n### api 插件原理分析\n\napi 插件的源码在这里：[api 插件源码传送门](https://github.com/Blankj/AndroidUtilCode/tree/master/plugin/api-gradle-plugin)，该插件通过 Gradle 的 transform 来完成对 `ApiUtils.init()` 做注入，下面来一步步分析：\n\n不明白 transform 的可以先去了解下，简单来说 transform 就是专门用来做字节码插入操作的，最常见的就是 AOP（面向切面编程），这部分我就不科普了，有兴趣的可以自己搜索了解。\n\n说到字节码操作，那就又有知识点了，想要上手快速简单的可以使用 `javassist`，不过，我选择了更强大快速的 `ASM`，这里我就不详细介绍了，有兴趣的可以自己去学习，`ASM` 其实也很简单的，在 [ASM Bytecode Outline](https://plugins.jetbrains.com/plugin/5918-asm-bytecode-outline) 这个插件帮助下写得还是很快的。\n\n通过 ASM 扫描出所有继承自 `ApiUtils.BaseApi` 的类，以及所有带有 `@ApiUtils.Api` 注解的类，然后保存起来，相关代码如下所示：\n\n```java\n@Override\npublic void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n    className = name;\n    superClassName = superName;\n    if ((mApiUtilsClass + \"$BaseApi\").equals(superName)) {\n        mApiClasses.add(name);\n    }\n    super.visit(version, access, name, signature, superName, interfaces);\n}\n\n@Override\npublic AnnotationVisitor visitAnnotation(String desc, boolean visible) {\n    if ((\"L\" + mApiUtilsClass + \"$Api;\").equals(desc)) {\n        hasAnnotation = true;\n        return new AnnotationVisitor(Opcodes.ASM5, super.visitAnnotation(desc, visible)) {\n            @Override\n            public void visit(String name, Object value) {// 可获取注解的值\n                isMock = (boolean) value;\n                super.visit(name, value);\n            }\n        };\n    }\n    return super.visitAnnotation(desc, visible);\n}\n\n@Override\npublic void visitEnd() {\n    super.visitEnd();\n    if (hasAnnotation) {\n        if (!isMock) {// 如果不是 mock 的话\n            ApiInfo apiInfo = mApiImplMap.get(superClassName);\n            if (apiInfo == null) {\n                mApiImplMap.put(superClassName, new ApiInfo(className, false));\n            } else {// 存在一个 api 多个实现就报错\n                errorStr = \"<\" + className + \"> and <\" + apiInfo.implApiClass + \"> impl same api of <\" + superClassName + \">\";\n            }\n        } else {// mock 的话，如果 map 中已存在就不覆盖了\n            if (!mApiImplMap.containsKey(superClassName)) {\n                mApiImplMap.put(superClassName, new ApiInfo(className, true));\n            }\n        }\n    }\n}\n```\n\n然后往 `ApiUtils.init()` 插入扫描出来的内容，比如上面举例的 `MainApi`，那么其最终插入的代码如下所示：\n\n```java\nprivate void init() {\n    this.registerImpl(MainApiImpl.class);\n}\n```\n\n其 ASM 插入的代码如下所示：\n\n```java\n@Override\npublic MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n    if (!\"init\".equals(name)) {\n        return super.visitMethod(access, name, descriptor, signature, exceptions);\n    }\n    // 往 init() 函数中写入\n    if (cv == null) return null;\n    MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions);\n    mv = new AdviceAdapter(Opcodes.ASM5, mv, access, name, descriptor) {\n        @Override\n        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {\n            return super.visitAnnotation(desc, visible);\n        }\n        @Override\n        protected void onMethodEnter() {\n            super.onMethodEnter();\n        }\n        @Override\n        protected void onMethodExit(int opcode) {\n            super.onMethodExit(opcode);\n            for (Map.Entry<String, ApiInfo> apiImplEntry : mApiImplMap.entrySet()) {\n                mv.visitVarInsn(Opcodes.ALOAD, 0);\n                mv.visitLdcInsn(Type.getType(\"L\" + apiImplEntry.getValue().implApiClass + \";\"));\n                mv.visitMethodInsn(Opcodes.INVOKESPECIAL, mApiUtilsClass, \"registerImpl\", \"(Ljava/lang/Class;)V\", false);\n            }\n        }\n    };\n    return mv;\n}\n```\n\n### ApiUtils 原理分析\n\n接下来看下 `ApiUtils.registerImpl` 的实现：\n\n```java\nprivate void registerImpl(Class implClass) {\n    mInjectApiImplMap.put(implClass.getSuperclass(), implClass);\n}\n```\n\n很简单，就是往 `mInjectApiImplMap` 中插入了 key 为 `MainApiImpl` 的父类：`MainApi` 的 class，value 为 `MainApiImpl` 的 class。\n\n后面就让我们来看具体调用 `getApi` 的操作吧：\n\n```java\npublic static <T extends BaseApi> T getApi(@NonNull final Class<T> apiClass) {\n    return getInstance().getApiInner(apiClass);\n}\n\nprivate <Result> Result getApiInner(Class apiClass) {\n    BaseApi api = mApiMap.get(apiClass);\n    if (api == null) {\n        synchronized (this) {\n            api = mApiMap.get(apiClass);\n            if (api == null) {\n                Class implClass = mInjectApiImplMap.get(apiClass);\n                if (implClass != null) {\n                    try {\n                        api = (BaseApi) implClass.newInstance();\n                        mApiMap.put(apiClass, api);\n                    } catch (Exception ignore) {\n                        Log.e(TAG, \"The <\" + implClass + \"> has no parameterless constructor.\");\n                        return null;\n                    }\n                } else {\n                    Log.e(TAG, \"The <\" + apiClass + \"> doesn't implement.\");\n                    return null;\n                }\n            }\n        }\n    }\n    //noinspection unchecked\n    return (Result) api;\n}\n\npublic abstract static class BaseApi {\n}\n```\n\n这段代码很好理解，而且加了同步锁操作，防止多线程生成多个 `impl`，然后，根据传进来的 `api` 的 class，我们通过注入的 `map` 中找到具体的 `impl` 的 class，如果缓存中有就取缓存中的，没有的话就通过 `newInstance` 来实例化一个 `impl`，并放入缓存中，最终返回其 `impl`。因为是通过 `newInstance` 来实例化 `impl`，这也解释了为什么 `impl` 中需保留无参构造函数，而且只有在使用时才会初始化，而不是一股脑把所有的 `api` 都初始化。\n\n简易实用，不到 100 行代码实现模块间跳转的 `ApiUtils` 已介绍完毕，接下来你就可以小试牛刀了。\n\n\n## [Change Log](https://github.com/Blankj/AndroidUtilCode/blob/master/plugin/api-gradle-plugin/CHANGELOG.md)"
  },
  {
    "path": "plugin/api-gradle-plugin/build.gradle",
    "content": "apply {\n    plugin \"groovy\"\n    plugin \"java-gradle-plugin\"\n}\n\ngradlePlugin {\n    plugins {\n        apiPlugin {\n            id = Config.plugins.plugin_api.id\n            implementationClass = 'com.blankj.api.ApiPlugin'\n        }\n    }\n}\n\ndependencies {\n    compileOnly Config.plugins.plugin_gradle.path\n    implementation Config.libs.commons_io.path\n    implementation Config.modules.plugin_lib_base_transform.remotePath\n    implementation gradleApi()\n    implementation localGroovy()\n\n    testImplementation Config.libs.test_junit.path\n    testImplementation Config.plugins.plugin_gradle.path\n}\n\nsourceSets {\n    main {\n        groovy {\n            srcDirs += 'src/main/java'\n        }\n    }\n}\n\next {\n    groupId = Config.plugins.plugin_api.groupId\n    artifactId = Config.plugins.plugin_api.artifactId\n    version = Config.plugins.plugin_api.version\n    website = \"https://github.com/Blankj/AndroidUtilCode\"\n}\napply from: \"${rootDir.path}/config/publish.gradle\"\n"
  },
  {
    "path": "plugin/api-gradle-plugin/src/main/java/com/blankj/api/ApiClassVisitor.java",
    "content": "package com.blankj.api;\n\n\nimport org.objectweb.asm.AnnotationVisitor;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.Opcodes;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/09\n *     desc  :\n * </pre>\n */\npublic class ApiClassVisitor extends ClassVisitor {\n\n    private Map<String, ApiInfo> mApiImplMap;\n    private List<String>         mApiClasses;\n    private String               className;\n    private String               superClassName;\n    private boolean              hasAnnotation;\n    private boolean              isMock;\n    private String               mApiUtilsClass;\n\n    public ApiClassVisitor(ClassVisitor classVisitor, Map<String, ApiInfo> apiImplMap, List<String> apiClasses, String apiUtilsClass) {\n        super(Opcodes.ASM5, classVisitor);\n        mApiImplMap = apiImplMap;\n        mApiClasses = apiClasses;\n        mApiUtilsClass = apiUtilsClass.replace(\".\", \"/\");\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        className = name;\n        superClassName = superName;\n        if ((mApiUtilsClass + \"$BaseApi\").equals(superName)) {\n            mApiClasses.add(name);\n        }\n        super.visit(version, access, name, signature, superName, interfaces);\n    }\n\n    @Override\n    public AnnotationVisitor visitAnnotation(String desc, boolean visible) {\n        if ((\"L\" + mApiUtilsClass + \"$Api;\").equals(desc)) {\n            hasAnnotation = true;\n            return new AnnotationVisitor(Opcodes.ASM5, super.visitAnnotation(desc, visible)) {\n                @Override\n                public void visit(String name, Object value) {// 可获取注解的值\n                    isMock = (boolean) value;\n                    super.visit(name, value);\n                }\n            };\n        }\n        return super.visitAnnotation(desc, visible);\n    }\n\n    @Override\n    public void visitEnd() {\n        super.visitEnd();\n        if (hasAnnotation) {\n            if (!isMock) {// 如果不是 mock 的话\n                ApiInfo apiInfo = mApiImplMap.get(superClassName);\n                if (apiInfo == null || apiInfo.isMock) {// 不存在或者之前存在的是 mock\n                    mApiImplMap.put(superClassName, new ApiInfo(className, false));\n                } else {// 存在一个 api 多个非 mock 实现就报错\n                    throw new IllegalArgumentException(\"<\" + className + \"> and <\" + apiInfo.implApiClass + \"> impl same api of <\" + superClassName + \">\");\n                }\n            } else {// mock 的话，如果 map 中已存在就不覆盖了\n                if (!mApiImplMap.containsKey(superClassName)) {\n                    mApiImplMap.put(superClassName, new ApiInfo(className, true));\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "plugin/api-gradle-plugin/src/main/java/com/blankj/api/ApiExtension.groovy",
    "content": "package com.blankj.api\n\nclass ApiExtension {\n\n    boolean abortOnError = true\n    String apiUtilsClass = \"com.blankj.utilcode.util.ApiUtils\";\n    String onlyScanLibRegex = \"\"\n    String jumpScanLibRegex = \"\"\n\n    @Override\n    String toString() {\n        return \"ApiExtension { \" +\n                \"abortOnError: \" + abortOnError +\n                \", apiUtilsClass: \" + apiUtilsClass +\n                (onlyScanLibRegex == \"\" ? \"\" : \", onlyScanLibRegex: \" + onlyScanLibRegex) +\n                (jumpScanLibRegex == \"\" ? \"\" : \", jumpScanLibRegex: \" + jumpScanLibRegex) +\n                \" }\";\n    }\n}\n"
  },
  {
    "path": "plugin/api-gradle-plugin/src/main/java/com/blankj/api/ApiInfo.java",
    "content": "package com.blankj.api;\n\n/**\n * <pre>\n *     author: blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/14\n *     desc  :\n * </pre>\n */\npublic class ApiInfo {\n\n    public String  implApiClass;\n    public boolean isMock;\n\n    public ApiInfo(String implApiClass, boolean isMock) {\n        this.implApiClass = implApiClass;\n        this.isMock = isMock;\n    }\n\n    @Override\n    public String toString() {\n        return \"{ implApiClass: \" + implApiClass +\n                \", isMock: \" + isMock +\n                \" }\";\n    }\n}\n"
  },
  {
    "path": "plugin/api-gradle-plugin/src/main/java/com/blankj/api/ApiInject.groovy",
    "content": "package com.blankj.api\n\nimport com.blankj.base_transform.util.ZipUtils\nimport org.apache.commons.io.FileUtils\nimport org.objectweb.asm.ClassReader\nimport org.objectweb.asm.ClassVisitor\nimport org.objectweb.asm.ClassWriter\n\nclass ApiInject {\n\n    static void start(Map<String, String> apiImplMap, File apiUtilsTransformFile, String busUtilsClass) {\n        if (apiUtilsTransformFile.getPath().endsWith(\".jar\")) {\n            String jarPath = apiUtilsTransformFile.getAbsolutePath()\n            String decompressedJarPath = jarPath.substring(0, jarPath.length() - 4);\n            File decompressedJar = new File(decompressedJarPath)\n            ZipUtils.unzipFile(apiUtilsTransformFile, decompressedJar)\n\n            File apiUtilsFile = new File(\n                    decompressedJarPath + Config.FILE_SEP +\n                            busUtilsClass.replace('.', Config.FILE_SEP) + '.class'\n            )\n\n            inject2ApiUtils(apiUtilsFile, apiImplMap, busUtilsClass)\n\n            FileUtils.forceDelete(apiUtilsTransformFile)\n            ZipUtils.zipFiles(Arrays.asList(decompressedJar.listFiles()), apiUtilsTransformFile)\n            FileUtils.forceDelete(decompressedJar)\n        } else {\n            File apiUtilsFile = new File(\n                    apiUtilsTransformFile.getAbsolutePath() + Config.FILE_SEP +\n                            busUtilsClass.replace('.', Config.FILE_SEP) + '.class'\n            )\n\n            inject2ApiUtils(apiUtilsFile, apiImplMap, busUtilsClass)\n        }\n    }\n\n    private static void inject2ApiUtils(File apiUtilsFile, Map<String, String> apiImplMap, String apiUtilsClass) {\n        ClassReader cr = new ClassReader(apiUtilsFile.bytes);\n        ClassWriter cw = new ClassWriter(cr, 0);\n        ClassVisitor cv = new ApiUtilsClassVisitor(cw, apiImplMap, apiUtilsClass);\n        cr.accept(cv, ClassReader.SKIP_FRAMES);\n        FileUtils.writeByteArrayToFile(apiUtilsFile, cw.toByteArray())\n    }\n}"
  },
  {
    "path": "plugin/api-gradle-plugin/src/main/java/com/blankj/api/ApiPlugin.groovy",
    "content": "package com.blankj.api\n\nimport com.android.build.api.transform.JarInput\nimport com.blankj.base_transform.BaseTransformPlugin\nimport com.blankj.base_transform.util.JsonUtils\nimport org.apache.commons.io.FileUtils\nimport org.objectweb.asm.ClassReader\nimport org.objectweb.asm.ClassVisitor\nimport org.objectweb.asm.ClassWriter\n\nimport java.util.regex.Pattern\n\nclass ApiPlugin extends BaseTransformPlugin<ApiExtension> {\n\n    String apiUtilsClass\n    File jsonFile\n    File apiUtilsTransformFile\n    Map<String, ApiInfo> apiImplMap = [:]\n    List<String> apiClasses = []\n\n    @Override\n    String getPluginName() {\n        return Config.EXT_NAME\n    }\n\n    @Override\n    void onScanStarted() {\n        apiUtilsClass = ext.apiUtilsClass\n        if (apiUtilsClass.trim().equals(\"\")) {\n            throw new Exception(\"ApiExtension's apiUtilsClass is empty.\")\n        }\n\n        jsonFile = new File(mProject.projectDir.getAbsolutePath(), \"__api__.json\")\n        FileUtils.write(jsonFile, \"{}\")\n    }\n\n    @Override\n    boolean isIgnoreScan(JarInput input) {\n        def jarName = input.name\n        if (jarName.contains(\"utilcode\")) {\n            return false\n        }\n\n        if (ext.onlyScanLibRegex != null && ext.onlyScanLibRegex.trim().length() > 0) {\n            return !Pattern.matches(ext.onlyScanLibRegex, jarName)\n        }\n\n        if (ext.jumpScanLibRegex != null && ext.jumpScanLibRegex.trim().length() > 0) {\n            if (Pattern.matches(ext.jumpScanLibRegex, jarName)) {\n                return true\n            }\n        }\n\n        for (exclude in Config.EXCLUDE_LIBS_START_WITH) {\n            if (jarName.startsWith(exclude)) {\n                return true\n            }\n        }\n        return false\n    }\n\n    @Override\n    void scanClassFile(File classFile, String className, File originScannedJarOrDir) {\n        if (apiUtilsClass == className) {\n            apiUtilsTransformFile = originScannedJarOrDir\n            log(\"<ApiUtils transform file>: $originScannedJarOrDir\")\n        }\n\n        ClassReader cr = new ClassReader(classFile.bytes);\n        ClassWriter cw = new ClassWriter(cr, 0);\n        ClassVisitor cv = new ApiClassVisitor(cw, apiImplMap, apiClasses, apiUtilsClass);\n        try {\n            cr.accept(cv, ClassReader.SKIP_FRAMES);\n        } catch (Exception ignore) {\n            ignore.printStackTrace()\n        }\n    }\n\n    @Override\n    void onScanFinished() {\n        if (apiUtilsTransformFile != null) {\n            if (apiClasses.isEmpty()) {\n                log(\"no api.\")\n            } else {\n                Map implApis = [:]\n                List<String> noImplApis = []\n                apiImplMap.each { key, value ->\n                    implApis.put(key, value.toString())\n                }\n                apiClasses.each {\n                    if (!apiImplMap.containsKey(it)) {\n                        noImplApis.add(it)\n                    }\n                }\n                Map apiDetails = [:]\n                apiDetails.put(\"ApiUtilsClass\", apiUtilsClass)\n                apiDetails.put(\"implApis\", implApis)\n                apiDetails.put(\"noImplApis\", noImplApis)\n                String apiJson = JsonUtils.getFormatJson(apiDetails)\n                log(jsonFile.toString() + \": \" + apiJson)\n                FileUtils.write(jsonFile, apiJson)\n\n                if (noImplApis.size() > 0) {\n                    if (ext.abortOnError) {\n                        throw new Exception(\"u should impl these apis: \" + noImplApis +\n                                \"\\n u can check it in file: \" + jsonFile.toString())\n                    }\n                }\n                ApiInject.start(apiImplMap, apiUtilsTransformFile, apiUtilsClass)\n            }\n        } else {\n            throw new Exception(\"No ApiUtils of ${apiUtilsClass} in $mProject.\")\n        }\n    }\n}"
  },
  {
    "path": "plugin/api-gradle-plugin/src/main/java/com/blankj/api/ApiUtilsClassVisitor.java",
    "content": "package com.blankj.api;\n\nimport org.objectweb.asm.AnnotationVisitor;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.commons.AdviceAdapter;\n\nimport java.util.Map;\n\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/09\n *     desc  :\n * </pre>\n */\npublic class ApiUtilsClassVisitor extends ClassVisitor {\n\n    private Map<String, ApiInfo> mApiImplMap;\n    private String               mApiUtilsClass;\n\n    public ApiUtilsClassVisitor(ClassVisitor classVisitor, Map<String, ApiInfo> apiImplMap, String apiUtilsClass) {\n        super(Opcodes.ASM5, classVisitor);\n        mApiImplMap = apiImplMap;\n        mApiUtilsClass = apiUtilsClass.replace(\".\", \"/\");\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        if (!\"init\".equals(name)) {\n            return super.visitMethod(access, name, descriptor, signature, exceptions);\n        }\n        // 往 init() 函数中写入\n        if (cv == null) return null;\n        MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions);\n        mv = new AdviceAdapter(Opcodes.ASM5, mv, access, name, descriptor) {\n\n            @Override\n            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {\n                return super.visitAnnotation(desc, visible);\n            }\n\n            @Override\n            protected void onMethodEnter() {\n                super.onMethodEnter();\n            }\n\n            @Override\n            protected void onMethodExit(int opcode) {\n                super.onMethodExit(opcode);\n                for (Map.Entry<String, ApiInfo> apiImplEntry : mApiImplMap.entrySet()) {\n                    mv.visitVarInsn(Opcodes.ALOAD, 0);\n                    mv.visitLdcInsn(Type.getType(\"L\" + apiImplEntry.getValue().implApiClass + \";\"));\n                    mv.visitMethodInsn(Opcodes.INVOKESPECIAL, mApiUtilsClass, \"registerImpl\", \"(Ljava/lang/Class;)V\", false);\n                }\n            }\n        };\n        return mv;\n    }\n}\n"
  },
  {
    "path": "plugin/api-gradle-plugin/src/main/java/com/blankj/api/Config.groovy",
    "content": "package com.blankj.api\n\nimport com.blankj.base_transform.BaseTransformConfig\n\nclass Config {\n\n    public static final String EXT_NAME = 'api'\n\n    public static final List<String> EXCLUDE_LIBS_START_WITH = [\n            'com.android.support',\n            'androidx',\n            'com.google',\n            'android.arch',\n            'org.jetbrains',\n            'com.squareup',\n            'org.greenrobot',\n            'com.github.bumptech.glide'\n    ]\n\n    public static final String FILE_SEP = BaseTransformConfig.FILE_SEP\n}\n"
  },
  {
    "path": "plugin/api-gradle-plugin/src/test/java/com/blankj/api/ApiTest.java",
    "content": "package com.blankj.api;\n\nimport org.apache.commons.io.FileUtils;\nimport org.junit.Test;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.ClassWriter;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/09\n *     desc  :\n * </pre>\n */\npublic class ApiTest {\n\n    @Test\n    public void testInject() throws IOException {\n        inject2ApiUtils(getApiImplMap());\n    }\n\n    private static Map<String, ApiInfo> getApiImplMap() throws IOException {\n        Map<String, ApiInfo> apiImplMap = new HashMap<>();\n        List<String> apiClasses = new ArrayList<>();\n\n        ClassReader cr = new ClassReader(TestApiImpl.class.getName());\n        ClassWriter cw = new ClassWriter(cr, 0);\n        ClassVisitor cv = new ApiClassVisitor(cw, apiImplMap, apiClasses, ApiUtils.class.getCanonicalName());\n        cr.accept(cv, ClassReader.SKIP_FRAMES);\n\n        System.out.println(\"apiImplMap = \" + apiImplMap);\n\n        apiClasses = new ArrayList<>();\n        cr = new ClassReader(TestApi.class.getName());\n        cw = new ClassWriter(cr, 0);\n        cv = new ApiClassVisitor(cw, apiImplMap, apiClasses, ApiUtils.class.getCanonicalName());\n        cr.accept(cv, ClassReader.SKIP_FRAMES);\n\n        System.out.println(\"apiClasses = \" + apiClasses);\n        return apiImplMap;\n    }\n\n    private static void inject2ApiUtils(Map<String, ApiInfo> apiImpls) throws IOException {\n        ClassReader cr = new ClassReader(ApiUtils.class.getName());\n        ClassWriter cw = new ClassWriter(cr, 0);\n        ClassVisitor cv = new ApiUtilsClassVisitor(cw, apiImpls, ApiUtils.class.getCanonicalName());\n        cr.accept(cv, ClassReader.SKIP_FRAMES);\n\n        FileUtils.writeByteArrayToFile(new File(\"ApiUtils2333.class\"), cw.toByteArray());\n    }\n\n    @ApiUtils.Api(isMock = true)\n    public static class TestApiImpl extends TestApi {\n\n        @Override\n        public String test(String param) {\n            System.out.println(\"param = \" + param);\n            return \"haha\";\n        }\n\n    }\n\n    public static abstract class TestApi extends ApiUtils.BaseApi {\n\n        public abstract String test(String name);\n\n    }\n}"
  },
  {
    "path": "plugin/api-gradle-plugin/src/test/java/com/blankj/api/ApiUtils.java",
    "content": "package com.blankj.api;\n\nimport com.android.annotations.NonNull;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/09\n *     desc  :\n * </pre>\n */\npublic class ApiUtils {\n\n    private static final String TAG = \"ApiUtils\";\n\n    private Map<Class, BaseApi> mApiMap           = new ConcurrentHashMap<>();\n    private Map<Class, Class>   mInjectApiImplMap = new HashMap<>();\n\n    private ApiUtils() {\n        init();\n    }\n\n    /**\n     * It'll be injected the implClasses who have {@link ApiUtils.Api} annotation\n     * by function of {@link ApiUtils#registerImpl} when execute transform task.\n     */\n    private void init() {/*inject*/}\n\n    private void registerImpl(Class implClass) {\n        mInjectApiImplMap.put(implClass.getSuperclass(), implClass);\n    }\n\n    /**\n     * Get api.\n     *\n     * @param apiClass The class of api.\n     * @param <T>      The type.\n     * @return the api\n     */\n    public static <T extends BaseApi> T getApi(@NonNull final Class<T> apiClass) {\n        return getInstance().getApiInner(apiClass);\n    }\n\n    public static String toString_() {\n        return getInstance().toString();\n    }\n\n    @Override\n    public String toString() {\n        return \"ApiUtils: \" + mInjectApiImplMap;\n    }\n\n    private static ApiUtils getInstance() {\n        return LazyHolder.INSTANCE;\n    }\n\n    private <Result> Result getApiInner(Class apiClass) {\n        BaseApi api = mApiMap.get(apiClass);\n        if (api == null) {\n            synchronized (this) {\n                api = mApiMap.get(apiClass);\n                if (api == null) {\n                    Class implClass = mInjectApiImplMap.get(apiClass);\n                    if (implClass != null) {\n                        try {\n                            api = (BaseApi) implClass.newInstance();\n                            mApiMap.put(apiClass, api);\n                        } catch (Exception ignore) {\n                            System.out.println(\"The <\" + implClass + \"> has no parameterless constructor.\");\n                            return null;\n                        }\n                    } else {\n                        System.out.println(\"The <\" + apiClass + \"> doesn't implement.\");\n                        return null;\n                    }\n                }\n            }\n        }\n        //noinspection unchecked\n        return (Result) api;\n    }\n\n    private static class LazyHolder {\n        private static final ApiUtils INSTANCE = new ApiUtils();\n    }\n\n    @Target({ElementType.TYPE})\n    @Retention(RetentionPolicy.CLASS)\n    public @interface Api {\n        boolean isMock() default false;\n    }\n\n    public abstract static class BaseApi {\n    }\n}\n"
  },
  {
    "path": "plugin/buildSrc-plugin/.gitignore",
    "content": "/build"
  },
  {
    "path": "plugin/buildSrc-plugin/build.gradle",
    "content": "apply {\n    plugin \"groovy\"\n    plugin \"java-gradle-plugin\"\n}\n\ngradlePlugin {\n    plugins {\n        apiPlugin {\n            id = 'com.blankj.buildSrc'\n            implementationClass = 'com.blankj.buildSrc.BuildSrcPlugin'\n        }\n    }\n}\n\ndependencies {\n    compileOnly Config.plugins.plugin_gradle.path\n    implementation Config.libs.commons_io.path\n    implementation gradleApi()\n    implementation localGroovy()\n}\n\nsourceSets {\n    main {\n        groovy {\n            srcDirs += 'src/main/java'\n        }\n    }\n}\n\next {\n    groupId = Config.plugins.plugin_buildSrc.groupId\n    artifactId = Config.plugins.plugin_buildSrc.artifactId\n    version = Config.plugins.plugin_buildSrc.version\n    website = \"https://github.com/Blankj/AndroidUtilCode\"\n}\napply from: \"${rootDir.path}/config/publish.gradle\"\n//./gradlew clean plugin:plugin_buildSrc-plugin:publish2Local     // 上传到本地 mavenLocal\n"
  },
  {
    "path": "plugin/buildSrc-plugin/src/main/java/com/blankj/buildSrc/BuildSrcPlugin.groovy",
    "content": "package com.blankj.buildSrc;\n\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\n\nclass BuildSrcPlugin implements Plugin<Project> {\n\n    @Override\n    void apply(Project project) {\n        println 'apply BuildSrcPlugin'\n        ModuleCfg.main()\n    }\n}"
  },
  {
    "path": "plugin/buildSrc-plugin/src/main/java/com/blankj/buildSrc/ModuleCfg.groovy",
    "content": "package com.blankj.buildSrc\n\nimport groovy.json.JsonSlurper\n\n\n/**\n * 在 config.json 中 根据 appConfig 和 pkgConfig 来 include 本地模块\n * 可以考虑写成插件来更方便 apply\n */\n\nprintln 'exe ModuleCfg.groovy'\n\ndef config = new JsonSlurper().parse(file(\"./config.json\"))\n\nfor (def pro in config.proConfig) {\n    String localPath = pro.localPath\n\n    if (localPath == \":feature:mock\") {\n        if (config.pkgConfig.isEmpty()) {\n            pro.isApply = false\n        }\n    } else if (localPath.endsWith(\":app\")) {\n        def appName = localPath.substring(\":feature:\".length(), localPath.length() - 4)\n        if (!config.appConfig.contains(appName)) {\n            pro.isApply = false\n        }\n    } else if (localPath.endsWith(\":pkg\")) {\n        if (!config.pkgConfig.isEmpty()) {\n            def pkgName = localPath.substring(\":feature:\".length(), localPath.length() - 4)\n            if (!config.pkgConfig.contains(pkgName)) {\n                pro.isApply = false\n            }\n        }\n    }\n\n    if (pro.useLocal && pro.isApply) {\n        def projectPath = \":\" + localPath.substring(1).replace(\":\", \"_\")\n        include projectPath\n        project(projectPath).projectDir = file(localPath.substring(1).replace(\":\", \"/\"))\n    }\n}\n\ndef ls = System.getProperty(\"line.separator\")\n\nList<String> proDeps = []\nfor (def pro in config.proConfig) {\n    boolean useLocal = pro.useLocal\n    String localPath = pro.localPath\n    String remotePath = pro.remotePath\n    String name = localPath.replace(\":\", \"_\").replace(\"-\", \"_\").substring(1)\n    if (localPath != null) localPath = \"\\\"$localPath\\\"\"\n    if (remotePath != null) remotePath = \"\\\"$remotePath\\\"\"\n    boolean isApply = pro.isApply\n    proDeps.add(String.format(\"%-12s%-27s: new DepConfig(%-5s, %-5s, $localPath%s),\",\n            \"\", name, isApply, useLocal, remotePath == null ? \"\" : \", $remotePath\"))\n}\n\ndef configFile = file('./buildSrc/src/main/groovy/ModuleConfig.groovy')\ndef lines = configFile.readLines(\"utf8\")\ndef configContent = new StringBuilder()\n\nboolean enterNeverFlag = false\nfor (def line : lines) {\n    if (enterNeverFlag) {\n        if (line.contains(\"/*Never\")) {\n            configContent.append(ls).append(line)\n            enterNeverFlag = false\n        }\n        continue\n    }\n    configContent.append(ls).append(line)\n    if (line.contains(\"/*Never\")) {\n        configContent.append(ls).append(String.format(\"%-12s/*Generated by \\\"config.json\\\"*/\", \"\"))\n        enterNeverFlag = true\n        for (String dep : proDeps) {\n            configContent.append(ls).append(dep)\n        }\n    }\n}\nconfigFile.write(configContent.substring(ls.length()).toString())"
  },
  {
    "path": "plugin/bus-gradle-plugin/.gitignore",
    "content": "/build\nBusUtils2333.class"
  },
  {
    "path": "plugin/bus-gradle-plugin/CHANGELOG.md",
    "content": "# Change Log\n\n## v2.6(2020/05/19)\n重新发布，因为上个版本从阿里云仓库拉取是有问题的\n\n## v2.5(2020/04/29)\n重构使用 base-transform\n\n## v2.4\n去除 gradle 版本依赖的问题\n\n## v2.3\n新增 onlyScanLibRegex, jumpScanLibRegex 的 DSL\n\n## v2.2\n修复含有 for 循环的字段导致 out of index 的 bug\n\n## v2.1\n支持 Tag 一对多，同 Tag 可设置事件优先级\n\n## v2.0\n更改为 EventBus 模式\n\n## v1.8\n兼容最新工程组件化\n\n## v1.7\n兼容 utilcodex\n\n## v1.6\n修复 inject 时候 zip 操作不对导致混淆出错的问题\n\n## v1.5\n升级 javassist 来修复 Kotlin 匿名类 NotFoundException\n\n## v1.4\n优化 inject 兼容 Kotlin 的 bus\n\n## v1.3\n去除 static bus，只注入 utilcode\n\n## v1.2\n修复 bug\n\n## v1.1\n升级 javassist 版本，兼容 Java8\n\n## v1.0\n发布初版本\n"
  },
  {
    "path": "plugin/bus-gradle-plugin/README.md",
    "content": "# 比 EventBus 更高效的事件总线（BusUtils）\n\n## 背景\n\n设计这个 `BusUtils` 其实是在做 [ApiUtils](https://github.com/Blankj/AndroidUtilCode/tree/master/plugin/api-gradle-plugin) 时顺手做的，因为两者实现方式基本一致，设计前我也没想着要和 greenrobot 的 `EventBus` 一较高低，但设计完总需要一个对比，所以就拿业界最优秀的事件总线 `EventBus` 比较一下吧，然后就发现我这区区 300 行不到的 `BusUtils` 性能比 `EventBus` 要高出好多，当然，这一切的前提都是在 `BusUtils` 是切实可用并且有效的，它也是一款线程安全的事件总线，这些我都在单测中有做过实际测试的，不吹不擂，后面我们拿数据说话，有小伙伴不相信的话也可以通过下载我的源码来比较即可，单测地址：[BusUtilsVsEventBusTest](https://github.com/Blankj/AndroidUtilCode/blob/master/lib/utilcode/src/test/java/com/blankj/utilcode/util/BusUtilsVsEventBusTest.java)，Android 测试地址：[BusCompareActivity](https://github.com/Blankj/AndroidUtilCode/blob/master/feature/utilcode/pkg/src/main/java/com/blankj/utilcode/pkg/feature/bus/BusCompareActivity.kt)，`BusUtils` 在 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 中的作用就是模块内传值，其扮演的角色如下所示：\n\n![BusUtilsRole](https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/art/communication.png)\n\n下面介绍其使用：\n\n\n## 使用\n\n### 配置\n\n在项目根目录的 `build.gradle` 中添加 `bus` 插件：\n\n```groovy\nbuildscript {\n    dependencies {\n        ...\n        classpath 'com.blankj:bus-gradle-plugin:latest.release'\n    }\n}\n```\n\n然后在 application 模块中使用该插件：\n\n```groovy\napply plugin: \"com.blankj.bus\"\n```\n\n给你的项目添加 [AndroidUtilCode](https://github.com/Blankj/AndroidUtilCode) 依赖：\n\n```groovy\napi \"com.blankj:utilcode:latest.release\n```\n\n如果你单纯只想引入 `BusUtils` 也是可以的，需要你自己拷贝一份这个类放到你工程里，记得还要拷贝 `ThreadUtils` 哦，然后在 app 下的 `build.gradle` 中 配置 bus 的 DSL 域如下所示：\n\n```groovy\nbus {\n    busUtilsClass \"com.xxx.xxx.BusUtils\"\n}\n\nandroid {\n    ...\n}\n```\n\n可以猜测到默认的 busUtilsClass 为 `com.blankj.utilcode.util.BusUtils` 哈。\n\n如果开启混淆的话还需要配置你的 `BusUtils` 中注解方法的防混淆，如果直接用 **[AndroidUtilCode](https://github.com/Blankj/AndroidUtilCode)** 的话是不需要你配置的，我已经帮你做完了，配置你自己的 `BusUtils` 防混淆应该如下所示：\n\n```java\n-keepattributes *Annotation*\n-keepclassmembers class * {\n    @com.xxx.xxx.BusUtils$Bus <methods>;\n}\n```\n\n当然，如果你项目是开启混淆的话，全量引入 **[AndroidUtilCode](https://github.com/Blankj/AndroidUtilCode)** 也是可以的，混淆会帮你去除未使用到的类和方法。\n\nbus 完整的 DSL 如下所示：\n\n```groovy\nbus {\n    abortOnError boolean   // bus 扫描有问题是否终止编译，默认 true\n    busUtilsClass String   // BusUtils 类的路径，默认 'com.blankj.utilcode.util.BusUtils'\n    onlyScanLibRegex String// 设置 transform 只扫描库的正则，比如 auc 配置的 '^([:]|(com\\\\.blankj)).+$'，\n                           // [:] 表示扫描本地 module，因为本地 module 在 transform 中的 jar 包名是以 : 开头的，比如 :feature:utilcode:feature_utilcode_pkg，\n                           // com.blankj 表示扫描远端仓库以 com.blankj 开头的，比如 com.blankj:utilcode:xx\n    jumpScanLibRegex String// 和 onlyScanLibRegex 类似，不过是指要跳过哪些扫描的，和 onlyScanLibRegex 不能共存，onlyScanLibRegex 优先级更高\n}\n```\n\n好了，插件和依赖都配置完毕，下面介绍基本使用。\n\n### 基本使用\n\n```java\npublic static final String TAG_NO_PARAM  = \"TagNoParam\";\npublic static final String TAG_ONE_PARAM = \"TagOneParam\";\n\n@BusUtils.Bus(tag = TAG_NO_PARAM)\npublic void noParamFun() {/* Do something */}\n\n@BusUtils.Bus(tag = TAG_ONE_PARAM)\npublic void oneParamFun(String param) {/* Do something */}\n\n@Override\npublic void onStart() {\n    super.onStart();\n    BusUtils.register(this);\n}\n\n@Override\npublic void onStop() {\n    super.onStop();\n    BusUtils.unregister(this);\n}\n\nBusUtils.post(TAG_NO_PARAM);// noParamFun() will receive\nBusUtils.post(TAG_ONE_PARAM, \"param\");// oneParamFun() will receive\n```\n\n使用过 `EventBus` 的肯定一下子就能看懂。\n\n### 高级使用\n\n#### 粘性事件\n\n支持粘性事件，也就是先发送，然后在订阅的时候接收到之前发送的粘性事件，把其消费掉，使用方式和 `EventBus` 一致，就是在 `@BusUtils.Bus` 注解中设置 `sticky = true`，具体例子如下所示：\n```java\npublic static final String TAG_NO_PARAM_STICKY  = \"TagNoParamSticky\";\n\n@BusUtils.Bus(tag = TAG_NO_PARAM_STICKY, sticky = true)\npublic void noParamStickyFun() {/* Do something */}\n\nBusUtils.postSticky(TAG_NO_PARAM_STICKY);\n\nBusUtils.register(xxx);// will invoke noParamStickyFun\n\nBusUtils.removeSticky(TAG_NO_PARAM_STICKY);// When u needn't use the sticky, remove it\n\nBusUtils.unregister(xxx);\n```\n\n#### 线程切换\n\n线程切换使用的是 ThreadUtils 中的线程池，它具有安全的 Cached 线程池，以及 MAIN, IO, CPU, CACHED, SINGLE 线程池，默认不设置的话就是在提交的线程 POSTING，使用的话就是在 `@BusUtils.Bus` 注解中设置 `threadMode = BusUtils.ThreadMode.xx` 即可。\n\n\n## 规范\n\n要想工具用得舒服，规范肯定要遵守的，所谓无规矩不成方圆，不然五花八门的问题肯定一堆堆，这里推荐如下规范：\n\n* 持有事件的类和函数确保确保都是 `public` 的。\n* 由于 `BusUtils` 是用于模块内调用，所以可以写一个 `BusConfig` 的类来保存一个模块内所有 bus 的 `Tag`，方便查找到使用方及调用方。\n* `Tag` 中最好还能带有业务模块后缀名防止重复，是 sticky 类型的话也带上 sticky，指定具体线程的话也带上线程名，例如：`update_avatar_sticky_main_info` 这个 `Tag`，让人直接望文生义。\n* 如果能结合 **[AucFrame](https://github.com/Blankj/AucFrameTemplate)** 来使用，那就更规范不过了。\n* 对 `BusUtils` 中事件传输的的 `bean` 都需要 `keep` 下来，否则开启混淆后会找不到该实体对象而报错。\n\n\n使用已经介绍完毕，下面我们来和 `EventBus` 对比下性能。\n\n\n## 性能测试\n\n首先，把两者的事件定义好，因为比较的是事件达到的快慢，所以内部都是空实现即可，具体代码如下所示：\n\n```java\n@Subscribe\npublic void eventBusFun(String param) {\n}\n\n@BusUtils.Bus(tag = \"busUtilsFun\")\npublic void busUtilsFun(String param) {\n}\n```\n\n`BusUtils` 在编译时会根据 `@BusUtils.Bus` 注解生成一份记录 tag 和 方法签名的映射表，因为是在编译时完成的，这里我们通过反射来完成。\n\n```java\n@Before\npublic void setUp() throws Exception {\n    // 这一步是在 AOP 的时候注入的，这里通过反射来注入 busUtilsFun 事件，效果是一样的。\n    ReflectUtils getInstance = ReflectUtils.reflect(BusUtils.class).method(\"getInstance\");\n    getInstance.method(\"registerBus\", \"busUtilsFun\", BusUtilsVsEventBusTest.class.getName(), \"busUtilsFun\", String.class.getName(), \"param\", false, \"POSTING\");\n}\n```\n\n通过比较如下几点的测试来完成对比：\n\n* 注册 10000 个订阅者，共执行 10 次取平均值\n* 向 1 个订阅者发送 * 1000000 次，共执行 10 次取平均值\n* 向 100 个订阅者发送 * 100000 次，共执行 10 次取平均值\n* 注销 10000 个订阅者，共执行 10 次取平均值\n\n测试机器如下所示：\n\n```\nmacOS: 2.2GHz Intel Core i7 16GB\n一加6: Android 9 8GB\n```\n\n在 Android 上，我们加入 `EventBus` 的注解处理器来提升 `EventBus` 效率，让其在最优情况下和 `BusUtils` 比较。\n\n接下来，我们把测试的模板代码写好，方便后续可以直接把两者比较的代码往回调中塞入即可，具体代码如下所示：\n\n```java\n/**\n * @param name       传入的测试函数名\n * @param sampleSize 样本的数量\n * @param times      每次执行的次数\n * @param callback   比较的回调函数\n */\nprivate void compareWithEventBus(String name, int sampleSize, int times, CompareCallback callback) {\n    long[][] dur = new long[2][sampleSize];\n    for (int i = 0; i < sampleSize; i++) {\n        long cur = System.currentTimeMillis();\n        for (int j = 0; j < times; j++) {\n            callback.runEventBus();\n        }\n        dur[0][i] = System.currentTimeMillis() - cur;\n        cur = System.currentTimeMillis();\n        for (int j = 0; j < times; j++) {\n            callback.runBusUtils();\n        }\n        dur[1][i] = System.currentTimeMillis() - cur;\n        callback.restState();\n    }\n    long eventBusAverageTime = 0;\n    long busUtilsAverageTime = 0;\n    for (int i = 0; i < sampleSize; i++) {\n        eventBusAverageTime += dur[0][i];\n        busUtilsAverageTime += dur[1][i];\n    }\n    System.out.println(\n            name +\n            \"\\nEventBusCostTime: \" + eventBusAverageTime / sampleSize +\n            \"\\nBusUtilsCostTime: \" + busUtilsAverageTime / sampleSize\n    );\n}\n\npublic interface CompareCallback {\n    void runEventBus();\n    void runBusUtils();\n    void restState();\n}\n```\n\n下面就让我们来一一对比测试。\n\n### 注册 10000 个订阅者，共执行 10 次取平均值\n\n```java\n/**\n * 注册 10000 个订阅者，共执行 10 次取平均值\n */\n@Test\npublic void compareRegister10000Times() {\n    final List<BusUtilsVsEventBusTest> eventBusTests = new ArrayList<>();\n    final List<BusUtilsVsEventBusTest> busUtilsTests = new ArrayList<>();\n    compareWithEventBus(\"Register 10000 times.\", 10, 10000, new CompareCallback() {\n        @Override\n        public void runEventBus() {\n            BusUtilsVsEventBusTest test = new BusUtilsVsEventBusTest();\n            EventBus.getDefault().register(test);\n            eventBusTests.add(test);\n        }\n        @Override\n        public void runBusUtils() {\n            BusUtilsVsEventBusTest test = new BusUtilsVsEventBusTest();\n            BusUtils.register(test);\n            busUtilsTests.add(test);\n        }\n        @Override\n        public void restState() {\n            for (BusUtilsVsEventBusTest test : eventBusTests) {\n                EventBus.getDefault().unregister(test);\n            }\n            eventBusTests.clear();\n            for (BusUtilsVsEventBusTest test : busUtilsTests) {\n                BusUtils.unregister(test);\n            }\n            busUtilsTests.clear();\n        }\n    });\n}\n// MacOS Output:\n// Register 10000 times.\n// EventBusCostTime: 427\n// BusUtilsCostTime: 41\n\n// 一加6 Output:\n// Register 10000 times.\n// EventBusCostTime: 1268\n// BusUtilsCostTime: 399\n```\n\n### 向 1 个订阅者发送 * 1000000 次，共执行 10 次取平均值\n\n``` java\n/**\n * 向 1 个订阅者发送 * 1000000 次，共执行 10 次取平均值\n */\n@Test\npublic void comparePostTo1Subscriber1000000Times() {\n    comparePostTemplate(\"Post to 1 subscriber 1000000 times.\", 1, 1000000);\n}\n// MacOS Output:\n// Post to 1 subscriber 1000000 times.\n// EventBusCostTime: 145\n// BusUtilsCostTime: 33\n\n// 一加6 Output:\n// Post to 1 subscriber 1000000 times.\n// EventBusCostTime: 1247\n// BusUtilsCostTime: 696\n\nprivate void comparePostTemplate(String name, int subscribeNum, int postTimes) {\n    final List<BusUtilsVsEventBusTest> tests = new ArrayList<>();\n    for (int i = 0; i < subscribeNum; i++) {\n        BusUtilsVsEventBusTest test = new BusUtilsVsEventBusTest();\n        EventBus.getDefault().register(test);\n        BusUtils.register(test);\n        tests.add(test);\n    }\n    compareWithEventBus(name, 10, postTimes, new CompareCallback() {\n        @Override\n        public void runEventBus() {\n            EventBus.getDefault().post(\"EventBus\");\n        }\n        @Override\n        public void runBusUtils() {\n            BusUtils.post(\"busUtilsFun\", \"BusUtils\");\n        }\n        @Override\n        public void restState() {\n        }\n    });\n    for (BusUtilsVsEventBusTest test : tests) {\n        EventBus.getDefault().unregister(test);\n        BusUtils.unregister(test);\n    }\n}\n```\n\n### 向 100 个订阅者发送 * 100000 次，共执行 10 次取平均值\n\n```java\n/**\n * 向 100 个订阅者发送 * 100000 次，共执行 10 次取平均值\n */\n@Test\npublic void comparePostTo100Subscribers10000Times() {\n    comparePostTemplate(\"Post to 100 subscribers 100000 times.\", 100, 100000);\n}\n// MacOS Output:\n// Post to 100 subscribers 100000 times.\n// EventBusCostTime: 139\n// BusUtilsCostTime: 79\n\n// 一加6 Output:\n// Post to 100 subscribers 100000 times.\n// EventBusCostTime: 3092\n// BusUtilsCostTime: 2900\n```\n\n### 注销 10000 个订阅者，共执行 10 次取平均值\n```java\n/**\n * 注销 10000 个订阅者，共执行 10 次取平均值\n */\n@Test\npublic void compareUnregister10000Times() {\n    final List<BusUtilsVsEventBusTest> tests = new ArrayList<>();\n    for (int i = 0; i < 10000; i++) {\n        BusUtilsVsEventBusTest test = new BusUtilsVsEventBusTest();\n        EventBus.getDefault().register(test);\n        BusUtils.register(test);\n        tests.add(test);\n    }\n    compareWithEventBus(\"Unregister 10000 times.\", 10, 1, new CompareCallback() {\n        @Override\n        public void runEventBus() {\n            for (BusUtilsVsEventBusTest test : tests) {\n                EventBus.getDefault().unregister(test);\n            }\n        }\n        @Override\n        public void runBusUtils() {\n            for (BusUtilsVsEventBusTest test : tests) {\n                BusUtils.unregister(test);\n            }\n        }\n        @Override\n        public void restState() {\n            for (BusUtilsVsEventBusTest test : tests) {\n                EventBus.getDefault().register(test);\n                BusUtils.register(test);\n            }\n        }\n    });\n    for (BusUtilsVsEventBusTest test : tests) {\n        EventBus.getDefault().unregister(test);\n        BusUtils.unregister(test);\n    }\n}\n// MacOS Output:\n// Unregister 10000 times.\n// EventBusCostTime: 231\n// BusUtilsCostTime: 23\n\n// 一加6 Output:\n// Unregister 10000 times.\n// EventBusCostTime: 800\n// BusUtilsCostTime: 199\n```\n\n## 结论\n\n为了方便观察，我们生成一份图表来比较两者之间的性能：\n\n![BusUtilsVsEventBusChart](https://raw.githubusercontent.com/Blankj/AndroidUtilCode/master/art/busutil_vs_eventbus.png)\n\n图表中分别对四个函数在 MacOS 和 OnePlus6 中的表现进行统计，每个函数中从左向右分别是 「MacOS 的 BusUtils」、「MacOS 的 EventBus」、「OnePlus6 的 BusUtils」、「OnePlus6 的 EventBus」，可以发现，BusUtils 在注册和注销上基本比 `EventBus` 要快上好几倍，BusUtils 在向少量订阅者发送多次事件比 `EventBus` 也快上好多，在向多个订阅者发送多次事件也比 `EventBus` 快上些许。\n\n基于以上说的这么多，如果你项目中事件总线用得比较频繁，那么可以试着用我的 `BusUtils` 来替代 `EventBus` 来提升性能，或者在新的项目中，你也可以直接使用性能更好的 `BusUtils`。\n\n下面来总结下 `BusUtils` 的优点：\n\n* `BusUtils` 是通过事件 `Tag` 来确定唯一事件的，所以接收函数支持无参或者一个参数，而 `EventBus` 只能通过 MessageEvent 来确定具体的接收者，只能接收一个参数，即便仅仅是通知，也需要定义一个 MessageEvent，所以，`BusUtils` 传参更灵活。\n* `BusUtils` 在应用到项目中后，编译后便会在 application 中生成 `__bus__.json` 事件列表，如上生成的事件列表如下所示：\n\n```json\n{\n  \"BusUtilsClass\": \"com.blankj.utilcode.util.BusUtils\",\n  \"rightBus\": {\n    \"noParamFun\": \"{ desc: com.blankj.utilcode.pkg.feature.bus.BusActivity#noParamFun(), threadMode: POSTING }\",\n    \"oneParamFun\": \"{ desc: com.blankj.utilcode.pkg.feature.bus.BusActivity#oneParamFun(java.lang.String param), threadMode: POSTING }\"\n  },\n  \"wrongBus\": {}\n}\n```\n\n修改 `oneParamFun` 为两个参数的话，为了确保项目不会因为 `BusUtils` 在运行时崩溃，`bus` 插件会使其在编译时就不过，此时 `__bus__.json` 文件如下所示，提示你参数个数不对：\n\n```json\n{\n  \"BusUtilsClass\": \"com.blankj.utilcode.util.BusUtils\",\n  \"rightBus\": {\n    \"noParamFun\": \"{ desc: com.blankj.utilcode.pkg.feature.bus.BusActivity#noParamFun(), threadMode: POSTING }\",\n  },\n  \"wrongBus\": {\n    \"oneParamFun\": \"{ desc: com.blankj.utilcode.pkg.feature.bus.BusActivity#oneParamFun(java.lang.String param, java.lang.String param1), threadMode: POSTING, paramSize: 2 }\"\n  }\n```\n\n~~同理，如果两个 bus 的 `Tag` 相同了，也会编译不过，提示你项目中存在 `Tag` 相同的 bus。~~（2.1 版本已支持 Tag 一对多及事件优先级）\n\n所以，`BusUtils` 比 `EventBus` 更友好。\n\n* `BusUtils` 比 `EventBus` 代码少得太多，`BusUtils` 的源码只有区区 300 行，而 `EventBus` 3000 行肯定是不止的哈。\n* `BusUtils` 比 `EventBus` 性能更好。\n\n\n## 原理\n\n### bus 插件原理分析\n\nbus 插件的源码在这里：[bus 插件源码传送门](https://github.com/Blankj/AndroidUtilCode/tree/master/plugin/bus-gradle-plugin)，该插件通过 Gradle 的 transform 来完成对 `BusUtils.init()` 做注入，下面来一步步分析：\n\n不明白 transform 的可以先去了解下，简单来说 transform 就是专门用来做字节码插入操作的，最常见的就是 AOP（面向切面编程），这部分我就不科普了，有兴趣的可以自己搜索了解。\n\n说到字节码操作，那就又有知识点了，想要上手快速简单的可以使用 `javassist`，不过，我选择了更强大快速的 `ASM`，这里我就不详细介绍了，有兴趣的可以自己去学习，`ASM` 其实也很简单的，在 [ASM Bytecode Outline](https://plugins.jetbrains.com/plugin/5918-asm-bytecode-outline) 这个插件帮助下写得还是很快的。\n\n通过 ASM 扫描出所有带有 `@BusUtils.Bus` 注解的函数，读取并保存注解的值和函数的参数信息，相关代码如下所示：\n\n```java\n@Override\npublic void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n    className = name.replace(\"/\", \".\");\n    super.visit(version, access, name, signature, superName, interfaces);\n}\n\n@Override\npublic MethodVisitor visitMethod(int access, String funName, String desc, String signature, String[] exceptions) {\n    if (cv == null) return null;\n    MethodVisitor mv = cv.visitMethod(access, funName, desc, signature, exceptions);\n    busInfo = null;\n    mv = new AdviceAdapter(Opcodes.ASM5, mv, access, funName, desc) {\n        @Override\n        public AnnotationVisitor visitAnnotation(String desc1, boolean visible) {\n            final AnnotationVisitor av = super.visitAnnotation(desc1, visible);\n            if ((\"L\" + mBusUtilsClass + \"$Bus;\").equals(desc1)) {\n                busInfo = new BusInfo(className, funName);\n                funParamDesc = desc.substring(1, desc.indexOf(\")\"));\n                return new AnnotationVisitor(Opcodes.ASM5, av) {\n                    @Override\n                    public void visit(String name, Object value) {// 可获取注解的值\n                        super.visit(name, value);\n                        if (\"tag\".equals(name)) {\n                            tag = (String) value;\n                        } else if (\"sticky\".equals(name) && (Boolean) value) {\n                            busInfo.sticky = true;\n                        }\n                    }\n                    @Override\n                    public void visitEnum(String name, String desc, String value) {\n                        super.visitEnum(name, desc, value);\n                        if (\"threadMode\".equals(name)) {\n                            busInfo.threadMode = value;\n                        }\n                    }\n                };\n            }\n            return av;\n        }\n        @Override\n        public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {\n            super.visitLocalVariable(name, desc, signature, start, end, index);// 获取方法参数信息\n            if (busInfo != null && !funParamDesc.equals(\"\")) {\n                if (\"this\".equals(name)) {\n                    return;\n                }\n                funParamDesc = funParamDesc.substring(desc.length());// 每次去除参数直到为 \"\"，那么之后的就不是参数了\n                busInfo.paramsInfo.add(new BusInfo.ParamsInfo(Type.getType(desc).getClassName(), name));\n                if (busInfo.isParamSizeNoMoreThanOne && busInfo.paramsInfo.size() > 1) {\n                    busInfo.isParamSizeNoMoreThanOne = false;\n                }\n            }\n        }\n        @Override\n        public void visitEnd() {\n            super.visitEnd();\n            if (busInfo != null) {\n                List<BusInfo> infoList = mBusMap.get(tag);\n                if (infoList == null) {\n                    infoList = new ArrayList<>();\n                    mBusMap.put(tag, infoList);\n                } else if (infoList.size() == 0) {\n                    mBusMap.put(tag, infoList);\n                } else if (infoList.size() == 1) {\n                    BusInfo info0 = infoList.get(0);\n                    info0.isTagRepeat = true;\n                    busInfo.isTagRepeat = true;\n                } else {\n                    busInfo.isTagRepeat = true;\n                }\n                infoList.add(busInfo);\n            }\n        }\n    };\n    return mv;\n}\n```\n\n然后往 `BusUtils.init()` 插入扫描出来的内容，比如上面提到的 `oneParamFun` 这个函数，那么其最终插入的代码如下所示：\n\n```java\n\nprivate void init() {\n    this.registerBus(\"TagOneParam\", \"com.blankj.bus.BusTest\", \"oneParamFun\", \"java.lang.String\", \"param\", false, \"POSTING\");\n}\n```\n\n其 ASM 插入的代码如下所示：\n\n```java\n@Override\npublic MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n    if (!\"init\".equals(name)) {\n        return super.visitMethod(access, name, descriptor, signature, exceptions);\n    }\n    // 往 init() 函数中写入\n    if (cv == null) return null;\n    MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions);\n    mv = new AdviceAdapter(Opcodes.ASM5, mv, access, name, descriptor) {\n        @Override\n        public AnnotationVisitor visitAnnotation(String desc, boolean visible) {\n            return super.visitAnnotation(desc, visible);\n        }\n        @Override\n        protected void onMethodEnter() {\n            super.onMethodEnter();\n        }\n        @Override\n        protected void onMethodExit(int opcode) {\n            super.onMethodExit(opcode);\n            for (Map.Entry<String, List<BusInfo>> busEntry : mBusMap.entrySet()) {\n                List<BusInfo> infoList = busEntry.getValue();\n                if (infoList.size() != 1) continue;\n                BusInfo busInfo = infoList.get(0);\n                if (!busInfo.isParamSizeNoMoreThanOne) continue;\n                mv.visitVarInsn(ALOAD, 0);\n                mv.visitLdcInsn(busEntry.getKey());\n                mv.visitLdcInsn(busInfo.className);\n                mv.visitLdcInsn(busInfo.funName);\n                if (busInfo.paramsInfo.size() == 1) {\n                    mv.visitLdcInsn(busInfo.paramsInfo.get(0).className);\n                    mv.visitLdcInsn(busInfo.paramsInfo.get(0).name);\n                } else {\n                    mv.visitLdcInsn(\"\");\n                    mv.visitLdcInsn(\"\");\n                }\n                mv.visitInsn(busInfo.sticky ? ICONST_1 : ICONST_0);\n                mv.visitLdcInsn(busInfo.threadMode);\n                mv.visitMethodInsn(INVOKESPECIAL, mBusUtilsClass, \"registerBus\", \"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;)V\", false);\n            }\n        }\n    };\n    return mv;\n}\n```\n\n### BusUtils 原理分析\n\n接下来看下 `BusUtils.registerBus` 的实现：\n\n```java\nprivate void registerBus(String tag,\n                         String className, String funName, String paramType, String paramName,\n                         boolean sticky, String threadMode) {\n    mTag_BusInfoMap.put(tag, new BusInfo(className, funName, paramType, paramName, sticky, threadMode));\n}\n```\n\n很简单，就是往 `mTag_BusInfoMap` 中插入了 key 为 `tag`，value 为 `BusInfo` 的一个实例，这样便把一个事件保留了下来。\n\n接下来就是使用了，一开始我们都是先 register，源码如下所示：\n\n```java\npublic static void register(final Object bus) {\n    getInstance().registerInner(bus);\n}\n\nprivate void registerInner(final Object bus) {\n    if (bus == null) return;\n    String className = bus.getClass().getName();\n    synchronized (mClassName_BusesMap) {\n        Set<Object> buses = mClassName_BusesMap.get(className);\n        if (buses == null) {\n            buses = new CopyOnWriteArraySet<>();\n            mClassName_BusesMap.put(className, buses);\n        }\n        buses.add(bus);\n    }\n    processSticky(bus);\n}\n```\n\n我们获取 bus 的类名，然后对 `mClassName_BusesMap` 加锁来把它插入到 `mClassName_BusesMap` 的 value 的集合中，可以看到我们用了线程安全的 `CopyOnWriteArraySet` 集合，然后还需要处理下之前是否订阅过粘性事件 `processSticky`，到这里 register 便结束了。\n\n然后就是 `post` 来发送事件了，源码如下：\n\n```java\npublic static void post(final String tag) {\n    post(tag, NULL);\n}\n\npublic static void post(final String tag, final Object arg) {\n    getInstance().postInner(tag, arg);\n}\n\nprivate void postInner(final String tag, final Object arg) {\n    postInner(tag, arg, false);\n}\n\nprivate void postInner(final String tag, final Object arg, final boolean sticky) {\n    BusInfo busInfo = mTag_BusInfoMap.get(tag);\n    if (busInfo == null) {\n        Log.e(TAG, \"The bus of tag <\" + tag + \"> is not exists.\");\n        return;\n    }\n    if (busInfo.method == null) {\n        Method method = getMethodByBusInfo(busInfo);\n        if (method == null) {\n            return;\n        }\n        busInfo.method = method;\n    }\n    invokeMethod(tag, arg, busInfo, sticky);\n}\n\nprivate Method getMethodByBusInfo(BusInfo busInfo) {\n    try {\n        if (\"\".equals(busInfo.paramType)) {\n            return Class.forName(busInfo.className).getDeclaredMethod(busInfo.funName);\n        } else {\n            return Class.forName(busInfo.className).getDeclaredMethod(busInfo.funName, Class.forName(busInfo.paramType));\n        }\n    } catch (ClassNotFoundException e) {\n        e.printStackTrace();\n    } catch (NoSuchMethodException e) {\n        e.printStackTrace();\n    }\n    return null;\n}\n\nprivate void invokeMethod(final String tag, final Object arg, final BusInfo busInfo, final boolean sticky) {\n    Runnable runnable = new Runnable() {\n        @Override\n        public void run() {\n            realInvokeMethod(tag, arg, busInfo, sticky);\n        }\n    };\n    switch (busInfo.threadMode) {\n        case \"MAIN\":\n            Utils.runOnUiThread(runnable);\n            return;\n        case \"IO\":\n            ThreadUtils.getIoPool().execute(runnable);\n            return;\n        case \"CPU\":\n            ThreadUtils.getCpuPool().execute(runnable);\n            return;\n        case \"CACHED\":\n            ThreadUtils.getCachedPool().execute(runnable);\n            return;\n        case \"SINGLE\":\n            ThreadUtils.getSinglePool().execute(runnable);\n            return;\n        default:\n            runnable.run();\n    }\n}\n\nprivate void realInvokeMethod(final String tag, Object arg, BusInfo busInfo, boolean sticky) {\n    Set<Object> buses = mClassName_BusesMap.get(busInfo.className);\n    if (buses == null || buses.size() == 0) {\n        if (!sticky) {\n            Log.e(TAG, \"The bus of tag <\" + tag + \"> was not registered before.\");\n            return;\n        } else {\n            return;\n        }\n    }\n    try {\n        if (arg == NULL) {\n            for (Object bus : buses) {\n                busInfo.method.invoke(bus);\n            }\n        } else {\n            for (Object bus : buses) {\n                busInfo.method.invoke(bus, arg);\n            }\n        }\n    } catch (IllegalAccessException e) {\n        e.printStackTrace();\n    } catch (InvocationTargetException e) {\n        e.printStackTrace();\n    }\n}\n```\n\n可以看到代码还是比较多的，不过别急，我们一步步来还是很简单的，首先在我们之前注入的 `mTag_BusInfoMap` 中查找是否有该 `tag` 的 `BusInfo`，没有的话就输出错误日志直接返回。\n\n然后我们根据获取到的 `BusInfo` 来找到 `method` 实例，`BusInfo` 第一次会把 `method` 保存在实例中，之后调用的话直接从实例中取出 `method` 即可。\n\n接着我们从 `BusInfo` 中取出线程信息，最后在线程中执行 `method` 的反射，大体就是这样，具体细节的话还是需要自己分析源码。\n\n最后就是 `unregister` 了：\n\n```java\npublic static void unregister(final Object bus) {\n    getInstance().unregisterInner(bus);\n}\n\nprivate void unregisterInner(final Object bus) {\n    if (bus == null) return;\n    String className = bus.getClass().getName();\n    synchronized (mClassName_BusesMap) {\n        Set<Object> buses = mClassName_BusesMap.get(className);\n        if (buses == null || !buses.contains(bus)) {\n            Log.e(TAG, \"The bus of <\" + bus + \"> was not registered before.\");\n            return;\n        }\n        buses.remove(bus);\n    }\n}\n```\n\n`unregister` 和 `register` 相反，就是从 `mClassName_BusesMap` 的 value 集合中移除，同样需要对 `mClassName_BusesMap` 加锁哦。\n\n\n## [Change Log](https://github.com/Blankj/AndroidUtilCode/blob/master/plugin/bus-gradle-plugin/CHANGELOG.md)"
  },
  {
    "path": "plugin/bus-gradle-plugin/build.gradle",
    "content": "apply {\n    plugin \"groovy\"\n    plugin \"java-gradle-plugin\"\n}\n\ngradlePlugin {\n    plugins {\n        busPlugin {\n            id = Config.plugins.plugin_bus.id\n            implementationClass = 'com.blankj.bus.BusPlugin'\n        }\n    }\n}\n\ndependencies {\n    compileOnly Config.plugins.plugin_gradle.path\n    implementation Config.modules.plugin_lib_base_transform.remotePath\n    implementation Config.libs.commons_io.path\n    implementation gradleApi()\n    implementation localGroovy()\n\n    testImplementation Config.libs.test_junit.path\n    testImplementation Config.plugins.plugin_gradle.path\n}\n\nsourceSets {\n    main {\n        groovy {\n            srcDirs += 'src/main/java'\n        }\n    }\n}\n\next {\n    groupId = Config.plugins.plugin_bus.groupId\n    artifactId = Config.plugins.plugin_bus.artifactId\n    version = Config.plugins.plugin_bus.version\n    website = \"https://github.com/Blankj/AndroidUtilCode\"\n}\napply from: \"${rootDir.path}/config/publish.gradle\"\n//./gradlew clean :plugin_bus-gradle-plugin:mavenLocal    // 上传到本地 mavenLocal\n//./gradlew clean :plugin_bus-gradle-plugin:bintrayUpload // 上传到 jcenter"
  },
  {
    "path": "plugin/bus-gradle-plugin/src/main/java/com/blankj/bus/BusClassVisitor.java",
    "content": "package com.blankj.bus;\n\nimport org.objectweb.asm.AnnotationVisitor;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.Label;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.Type;\nimport org.objectweb.asm.commons.AdviceAdapter;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/09\n *     desc  :\n * </pre>\n */\npublic class BusClassVisitor extends ClassVisitor {\n\n    private Map<String, List<BusInfo>> mBusMap;\n\n    private String  className;\n    private BusInfo busInfo;\n    private String  tag;\n    private String  funParamDesc;\n    private String  mBusUtilsClass;\n    private boolean isStartVisitParams;\n\n    public BusClassVisitor(ClassVisitor classVisitor, Map<String, List<BusInfo>> busMap, String busUtilsClass) {\n        super(Opcodes.ASM5, classVisitor);\n        mBusMap = busMap;\n        mBusUtilsClass = busUtilsClass.replace(\".\", \"/\");\n    }\n\n    @Override\n    public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {\n        className = name.replace(\"/\", \".\");\n        super.visit(version, access, name, signature, superName, interfaces);\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String funName, String desc, String signature, String[] exceptions) {\n        if (cv == null) return null;\n        MethodVisitor mv = cv.visitMethod(access, funName, desc, signature, exceptions);\n        busInfo = null;\n        isStartVisitParams = false;\n        mv = new AdviceAdapter(Opcodes.ASM5, mv, access, funName, desc) {\n            @Override\n            public AnnotationVisitor visitAnnotation(String desc1, boolean visible) {\n                final AnnotationVisitor av = super.visitAnnotation(desc1, visible);\n                if ((\"L\" + mBusUtilsClass + \"$Bus;\").equals(desc1)) {\n                    busInfo = new BusInfo(className, funName);\n                    funParamDesc = desc.substring(1, desc.indexOf(\")\"));\n                    return new AnnotationVisitor(Opcodes.ASM5, av) {\n                        @Override\n                        public void visit(String name, Object value) {// 可获取注解的值\n                            super.visit(name, value);\n                            if (\"tag\".equals(name)) {\n                                tag = (String) value;\n                            } else if (\"sticky\".equals(name) && (Boolean) value) {\n                                busInfo.sticky = true;\n                            } else if (\"priority\".equals(name)) {\n                                busInfo.priority = (int) value;\n                            }\n                        }\n\n                        @Override\n                        public void visitEnum(String name, String desc, String value) {\n                            super.visitEnum(name, desc, value);\n                            if (\"threadMode\".equals(name)) {\n                                busInfo.threadMode = value;\n                            }\n                        }\n                    };\n                }\n                return av;\n            }\n\n            @Override\n            public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {\n                super.visitLocalVariable(name, desc, signature, start, end, index);// 获取方法参数信息\n                if (busInfo != null && !funParamDesc.equals(\"\")) {\n                    if (!isStartVisitParams && index != 0) {\n                        return;\n                    }\n                    isStartVisitParams = true;\n                    if (\"this\".equals(name)) {\n                        return;\n                    }\n                    funParamDesc = funParamDesc.substring(desc.length());// 每次去除参数直到为 \"\"，那么之后的就不是参数了\n                    busInfo.paramsInfo.add(new BusInfo.ParamsInfo(Type.getType(desc).getClassName(), name));\n                    if (busInfo.isParamSizeNoMoreThanOne && busInfo.paramsInfo.size() > 1) {\n                        busInfo.isParamSizeNoMoreThanOne = false;\n                    }\n                }\n            }\n\n            @Override\n            public void visitEnd() {\n                super.visitEnd();\n                if (busInfo != null) {\n                    List<BusInfo> infoList = mBusMap.get(tag);\n                    if (infoList == null) {\n                        infoList = new ArrayList<>();\n                        mBusMap.put(tag, infoList);\n                    }\n                    infoList.add(busInfo);\n                }\n            }\n        };\n\n        return mv;\n    }\n}\n"
  },
  {
    "path": "plugin/bus-gradle-plugin/src/main/java/com/blankj/bus/BusExtension.groovy",
    "content": "package com.blankj.bus;\n\nclass BusExtension {\n\n    boolean abortOnError = true;\n    String busUtilsClass = \"com.blankj.utilcode.util.BusUtils\";\n    String onlyScanLibRegex = \"\"\n    String jumpScanLibRegex = \"\"\n\n    @Override\n    String toString() {\n        return \"BusExtension { \" +\n                \"abortOnError: \" + abortOnError +\n                \", busUtilsClass: \" + busUtilsClass +\n                (onlyScanLibRegex == \"\" ? \"\" : \", onlyScanLibRegex: \" + onlyScanLibRegex) +\n                (jumpScanLibRegex == \"\" ? \"\" : \", jumpScanLibRegex: \" + jumpScanLibRegex) +\n                \" }\";\n    }\n}\n"
  },
  {
    "path": "plugin/bus-gradle-plugin/src/main/java/com/blankj/bus/BusInfo.java",
    "content": "package com.blankj.bus;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/10\n *     desc  :\n * </pre>\n */\npublic class BusInfo {\n\n    public String           className;                // 函数所在类名\n    public String           funName;                  // 方法名\n    public List<ParamsInfo> paramsInfo;               // 参数列表信息\n    public boolean          sticky;                   // 是否粘性\n    public String           threadMode;               // 线程模式\n    public int              priority;                 // 优先级\n    public boolean          isParamSizeNoMoreThanOne; // 参数是否不多于 1 个\n\n    public BusInfo(String className, String funName) {\n        this.className = className;\n        this.funName = funName;\n        paramsInfo = new ArrayList<>();\n        sticky = false;\n        threadMode = \"POSTING\";\n        priority = 0;\n        isParamSizeNoMoreThanOne = true;\n    }\n\n    @Override\n    public String toString() {\n        String paramsInfoString = paramsInfo.toString();\n        return \"{ desc: \" + className + \"#\" + funName +\n                \"(\" + paramsInfoString.substring(1, paramsInfoString.length() - 1) + \")\" +\n                (!sticky ? \"\" : \", sticky: true\") +\n                (threadMode.equals(\"POSTING\") ? \"\" : \", threadMode: \" + threadMode) +\n                (priority == 0 ? \"\" : \", priority: \" + priority) +\n                (isParamSizeNoMoreThanOne ? \"\" : \", paramSize: \" + paramsInfo.size()) +\n                \" }\";\n    }\n\n    public static class ParamsInfo {\n        public String className;\n        public String name;\n\n        public ParamsInfo(String className, String name) {\n            this.className = className;\n            this.name = name;\n        }\n\n        @Override\n        public String toString() {\n            return (\"\".equals(className) ? \"\" : (className + \" \" + name));\n        }\n    }\n}\n"
  },
  {
    "path": "plugin/bus-gradle-plugin/src/main/java/com/blankj/bus/BusInject.groovy",
    "content": "package com.blankj.bus\n\nimport com.blankj.base_transform.util.ZipUtils\nimport org.apache.commons.io.FileUtils\nimport org.objectweb.asm.ClassReader\nimport org.objectweb.asm.ClassVisitor\nimport org.objectweb.asm.ClassWriter\n\nclass BusInject {\n\n    static void start(Map<String, BusInfo> busMap, File busUtilsTransformFile, String busUtilsClass) {\n        if (busUtilsTransformFile.getPath().endsWith(\".jar\")) {\n            String jarPath = busUtilsTransformFile.getAbsolutePath()\n            String decompressedJarPath = jarPath.substring(0, jarPath.length() - 4);\n            File decompressedJar = new File(decompressedJarPath)\n            ZipUtils.unzipFile(busUtilsTransformFile, decompressedJar)\n\n            File busUtilsFile = new File(\n                    decompressedJarPath + Config.FILE_SEP +\n                            busUtilsClass.replace('.', Config.FILE_SEP) + '.class'\n            )\n\n            inject2BusUtils(busUtilsFile, busMap, busUtilsClass)\n\n            FileUtils.forceDelete(busUtilsTransformFile)\n            ZipUtils.zipFiles(Arrays.asList(decompressedJar.listFiles()), busUtilsTransformFile)\n            FileUtils.forceDelete(decompressedJar)\n        } else {\n            File busUtilsFile = new File(\n                    busUtilsTransformFile.getAbsolutePath() + Config.FILE_SEP +\n                            busUtilsClass.replace('.', Config.FILE_SEP) + '.class'\n            )\n\n            inject2BusUtils(busUtilsFile, busMap, busUtilsClass)\n        }\n    }\n\n    private static void inject2BusUtils(File busUtilsFile, Map<String, BusInfo> busMap, String busUtilsClass) {\n        ClassReader cr = new ClassReader(busUtilsFile.bytes);\n        ClassWriter cw = new ClassWriter(cr, 0);\n        ClassVisitor cv = new BusUtilsClassVisitor(cw, busMap, busUtilsClass);\n        cr.accept(cv, ClassReader.SKIP_FRAMES);\n        FileUtils.writeByteArrayToFile(busUtilsFile, cw.toByteArray())\n    }\n}"
  },
  {
    "path": "plugin/bus-gradle-plugin/src/main/java/com/blankj/bus/BusPlugin.groovy",
    "content": "package com.blankj.bus\n\nimport com.android.build.api.transform.JarInput\nimport com.blankj.base_transform.BaseTransformPlugin\nimport com.blankj.base_transform.util.JsonUtils\nimport org.apache.commons.io.FileUtils\nimport org.objectweb.asm.ClassReader\nimport org.objectweb.asm.ClassVisitor\nimport org.objectweb.asm.ClassWriter\n\nimport java.util.regex.Pattern\n\nclass BusPlugin extends BaseTransformPlugin<BusExtension> {\n\n    String busUtilsClass\n    File jsonFile\n    Map<String, List<BusInfo>> busMap = [:]\n    File busUtilsTransformFile\n\n    @Override\n    String getPluginName() {\n        return Config.EXT_NAME\n    }\n\n    @Override\n    void onScanStarted() {\n        busUtilsClass = ext.busUtilsClass\n        if (busUtilsClass.trim().equals(\"\")) {\n            throw new Exception(\"BusExtension's busUtilsClass is empty.\")\n        }\n\n        jsonFile = new File(mProject.projectDir.getAbsolutePath(), \"__bus__.json\")\n        FileUtils.write(jsonFile, \"{}\")\n    }\n\n    @Override\n    boolean isIgnoreScan(JarInput input) {\n        def jarName = input.name\n        if (jarName.contains(\"utilcode\")) {\n            return false\n        }\n\n        if (ext.onlyScanLibRegex != null && ext.onlyScanLibRegex.trim().length() > 0) {\n            return !Pattern.matches(ext.onlyScanLibRegex, jarName)\n        }\n\n        if (ext.jumpScanLibRegex != null && ext.jumpScanLibRegex.trim().length() > 0) {\n            if (Pattern.matches(ext.jumpScanLibRegex, jarName)) {\n                return true\n            }\n        }\n\n        for (exclude in Config.EXCLUDE_LIBS_START_WITH) {\n            if (jarName.startsWith(exclude)) {\n                return true\n            }\n        }\n        return false\n    }\n\n    @Override\n    void scanClassFile(File classFile, String className, File originScannedJarOrDir) {\n        if (busUtilsClass == className) {\n            busUtilsTransformFile = originScannedJarOrDir\n            log(\"<BusUtils transform file>: $originScannedJarOrDir\")\n        }\n\n        ClassReader cr = new ClassReader(classFile.bytes);\n        ClassWriter cw = new ClassWriter(cr, 0);\n        ClassVisitor cv = new BusClassVisitor(cw, busMap, busUtilsClass);\n        try {\n            cr.accept(cv, ClassReader.SKIP_FRAMES);\n        } catch (Exception ignore) {\n            ignore.printStackTrace()\n        }\n    }\n\n    @Override\n    void onScanFinished() {\n        if (busUtilsTransformFile != null) {\n            if (busMap.isEmpty()) {\n                log(\"no bus.\")\n            } else {\n                busMap.each { String tag, List<BusInfo> infoList ->\n                    infoList.sort(new Comparator<BusInfo>() {\n                        @Override\n                        int compare(BusInfo t0, BusInfo t1) {\n                            return t1.priority - t0.priority\n                        }\n                    })\n                }\n\n                Map<String, List<String>> rightBus = [:]\n                Map<String, List<String>> wrongBus = [:]\n                busMap.each { String tag, List<BusInfo> infoList ->\n                    List<String> rightInfoString = []\n                    List<String> wrongInfoString = []\n                    infoList.each { BusInfo info ->\n                        if (info.isParamSizeNoMoreThanOne) {\n                            rightInfoString.add(info.toString())\n                        } else {\n                            wrongInfoString.add(info.toString())\n                        }\n                    }\n                    if (!rightInfoString.isEmpty()) {\n                        rightBus.put(tag, rightInfoString)\n                    }\n                    if (!wrongInfoString.isEmpty()) {\n                        wrongBus.put(tag, wrongInfoString)\n                    }\n                }\n                Map busDetails = [:]\n                busDetails.put(\"BusUtilsClass\", ext.busUtilsClass)\n                busDetails.put(\"rightBus\", rightBus)\n                busDetails.put(\"wrongBus\", wrongBus)\n                String busJson = JsonUtils.getFormatJson(busDetails)\n                log(jsonFile.toString() + \": \" + busJson)\n                FileUtils.write(jsonFile, busJson)\n\n                if (wrongBus.size() > 0) {\n                    if (ext.abortOnError) {\n                        throw new Exception(\"These buses is not right: \" + wrongBus +\n                                \"\\n u can check it in file: \" + jsonFile.toString())\n                    }\n                }\n\n                BusInject.start(busMap, busUtilsTransformFile, ext.busUtilsClass)\n            }\n        } else {\n            throw new Exception(\"No BusUtils of ${ext.busUtilsClass} in $mProject.\")\n        }\n    }\n}"
  },
  {
    "path": "plugin/bus-gradle-plugin/src/main/java/com/blankj/bus/BusUtilsClassVisitor.java",
    "content": "package com.blankj.bus;\n\nimport org.objectweb.asm.AnnotationVisitor;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.MethodVisitor;\nimport org.objectweb.asm.Opcodes;\nimport org.objectweb.asm.commons.AdviceAdapter;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/09\n *     desc  :\n * </pre>\n */\npublic class BusUtilsClassVisitor extends ClassVisitor {\n\n    private Map<String, List<BusInfo>> mBusMap;\n    private String                     mBusUtilsClass;\n\n    public BusUtilsClassVisitor(ClassVisitor classVisitor, Map<String, List<BusInfo>> busMap, String busUtilsClass) {\n        super(Opcodes.ASM5, classVisitor);\n        mBusMap = busMap;\n        mBusUtilsClass = busUtilsClass.replace(\".\", \"/\");\n    }\n\n    @Override\n    public MethodVisitor visitMethod(int access, String name, String descriptor, String signature, String[] exceptions) {\n        if (!\"init\".equals(name)) {\n            return super.visitMethod(access, name, descriptor, signature, exceptions);\n        }\n        // 往 init() 函数中写入\n        if (cv == null) return null;\n        MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions);\n        mv = new AdviceAdapter(Opcodes.ASM5, mv, access, name, descriptor) {\n\n            @Override\n            public AnnotationVisitor visitAnnotation(String desc, boolean visible) {\n                return super.visitAnnotation(desc, visible);\n            }\n\n            @Override\n            protected void onMethodEnter() {\n                super.onMethodEnter();\n            }\n\n            @Override\n            protected void onMethodExit(int opcode) {\n                super.onMethodExit(opcode);\n                for (Map.Entry<String, List<BusInfo>> busEntry : mBusMap.entrySet()) {\n                    List<BusInfo> infoList = busEntry.getValue();\n                    for (BusInfo busInfo : infoList) {\n                        if (!busInfo.isParamSizeNoMoreThanOne) continue;\n                        mv.visitVarInsn(ALOAD, 0);\n                        mv.visitLdcInsn(busEntry.getKey());\n                        mv.visitLdcInsn(busInfo.className);\n                        mv.visitLdcInsn(busInfo.funName);\n                        if (busInfo.paramsInfo.size() == 1) {\n                            mv.visitLdcInsn(busInfo.paramsInfo.get(0).className);\n                            mv.visitLdcInsn(busInfo.paramsInfo.get(0).name);\n                        } else {\n                            mv.visitLdcInsn(\"\");\n                            mv.visitLdcInsn(\"\");\n                        }\n                        mv.visitInsn(busInfo.sticky ? ICONST_1 : ICONST_0);\n                        mv.visitLdcInsn(busInfo.threadMode);\n                        mv.visitIntInsn(SIPUSH, busInfo.priority);\n                        mv.visitMethodInsn(INVOKESPECIAL, mBusUtilsClass, \"registerBus\", \"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ZLjava/lang/String;I)V\", false);\n                    }\n                }\n            }\n        };\n        return mv;\n    }\n}\n"
  },
  {
    "path": "plugin/bus-gradle-plugin/src/main/java/com/blankj/bus/Config.groovy",
    "content": "package com.blankj.bus\n\nimport com.blankj.base_transform.BaseTransformConfig\n\nclass Config {\n\n    public static final String EXT_NAME = 'bus'\n\n    public static final List<String> EXCLUDE_LIBS_START_WITH = [\n            'com.android.support',\n            'androidx',\n            'com.google',\n            'android.arch',\n            'org.jetbrains',\n            'com.squareup',\n            'org.greenrobot',\n            'com.github.bumptech.glide'\n    ]\n\n    public static final String FILE_SEP = BaseTransformConfig.FILE_SEP\n}\n"
  },
  {
    "path": "plugin/bus-gradle-plugin/src/test/java/com/blankj/bus/BusTest.java",
    "content": "package com.blankj.bus;\n\n\nimport org.apache.commons.io.FileUtils;\nimport org.junit.Test;\nimport org.objectweb.asm.ClassReader;\nimport org.objectweb.asm.ClassVisitor;\nimport org.objectweb.asm.ClassWriter;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.util.Comparator;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2019/07/09\n *     desc  :\n * </pre>\n */\npublic class BusTest {\n\n    private static final String TAG_NO_PARAM         = \"TagNoParam\";\n    private static final String TAG_ONE_PARAM        = \"TagOneParam\";\n    private static final String TAG_NO_PARAM_STICKY  = \"TagNoParamSticky\";\n    private static final String TAG_ONE_PARAM_STICKY = \"TagOneParamSticky\";\n\n    private String[] arr = new String[]{\"0\", \"1\"};\n    private String[] arr2 = new String[]{\"0\", \"1\"};\n\n    @BusUtils.Bus(tag = TAG_NO_PARAM)\n    public void noParamFun() {\n        System.out.println(\"noParam\");\n    }\n\n    @BusUtils.Bus(tag = TAG_NO_PARAM, priority = 1)\n    public void sameTagP1Fun() {\n        System.out.println(\"noParam\");\n    }\n\n    @BusUtils.Bus(tag = TAG_NO_PARAM)\n    public void sameTagParam2Fun(int arg0, Object arg1) {\n        System.out.println(\"params2\");\n    }\n\n    @BusUtils.Bus(tag = \"params2\")\n    public void param2Fun(int arg0, Object arg1) {\n        System.out.println(\"params2\");\n    }\n\n    @BusUtils.Bus(tag = TAG_ONE_PARAM)\n    public void oneParamFun(String param) {\n        System.out.println(param);\n    }\n\n    @BusUtils.Bus(tag = TAG_NO_PARAM_STICKY, sticky = true)\n    public void noParamStickyFun() {\n        System.out.println(\"noParamSticky\");\n    }\n\n    @BusUtils.Bus(tag = TAG_ONE_PARAM_STICKY, sticky = true)\n    public void oneParamStickyFun(Callback callback) {\n        for (String str : arr) {\n            System.out.println(str);\n        }\n        for (String str1 : arr2) {\n            System.out.println(str1);\n        }\n        if (callback != null) {\n            System.out.println(callback.call());\n        }\n    }\n\n    @BusUtils.Bus(tag = \"manyparam\", threadMode = BusUtils.ThreadMode.SINGLE)\n    public void haha(int a, int b) {\n        final Thread thread = Thread.currentThread();\n        System.out.println(new Callback() {\n            @Override\n            public String call() {\n                return thread.toString();\n            }\n        });\n    }\n\n    @Test\n    public void testInject() throws IOException {\n        inject2BusUtils(getBuses());\n    }\n\n    private static Map<String, List<BusInfo>> getBuses() throws IOException {\n        Map<String, List<BusInfo>> busMap = new HashMap<>();\n\n        ClassReader cr = new ClassReader(BusTest.class.getName());\n        ClassWriter cw = new ClassWriter(cr, 0);\n        ClassVisitor cv = new BusClassVisitor(cw, busMap, BusUtils.class.getName());\n        cr.accept(cv, ClassReader.SKIP_FRAMES);\n\n        for (List<BusInfo> value : busMap.values()) {\n            value.sort(new Comparator<BusInfo>() {\n                @Override\n                public int compare(BusInfo t0, BusInfo t1) {\n                    return t1.priority - t0.priority;\n                }\n            });\n        }\n        System.out.println(\"busMap = \" + busMap);\n        return busMap;\n    }\n\n    private static void inject2BusUtils(Map<String, List<BusInfo>> busMap) throws IOException {\n        ClassReader cr = new ClassReader(BusUtils.class.getName());\n        ClassWriter cw = new ClassWriter(cr, 0);\n        ClassVisitor cv = new BusUtilsClassVisitor(cw, busMap, BusUtils.class.getName());\n        cr.accept(cv, ClassReader.SKIP_FRAMES);\n\n        FileUtils.writeByteArrayToFile(new File(\"BusUtils2333.class\"), cw.toByteArray());\n    }\n\n    public interface Callback {\n        String call();\n    }\n}"
  },
  {
    "path": "plugin/bus-gradle-plugin/src/test/java/com/blankj/bus/BusUtils.java",
    "content": "package com.blankj.bus;\n\nimport java.lang.annotation.ElementType;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.annotation.Target;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.CopyOnWriteArrayList;\nimport java.util.concurrent.CopyOnWriteArraySet;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/10/02\n *     desc  : utils about bus\n * </pre>\n */\npublic final class BusUtils {\n\n    private static final Object NULL = \"nULl\";\n    private static final String TAG  = \"BusUtils\";\n\n    private final Map<String, List<BusInfo>> mTag_BusInfoListMap = new HashMap<>();\n\n    private final Map<String, Set<Object>>         mClassName_BusesMap          = new ConcurrentHashMap<>();\n    private final Map<String, List<String>>        mClassName_TagsMap           = new HashMap<>();\n    private final Map<String, Map<String, Object>> mClassName_Tag_Arg4StickyMap = new ConcurrentHashMap<>();\n\n    private BusUtils() {\n        init();\n    }\n\n    /**\n     * It'll be injected the bus who have {@link Bus} annotation\n     * by function of {@link BusUtils#registerBus} when execute transform task.\n     */\n    private void init() {/*inject*/}\n\n    private void registerBus(String tag,\n                             String className, String funName, String paramType, String paramName,\n                             boolean sticky, String threadMode) {\n        registerBus(tag, className, funName, paramType, paramName, sticky, threadMode, 0);\n    }\n\n    private void registerBus(String tag,\n                             String className, String funName, String paramType, String paramName,\n                             boolean sticky, String threadMode, int priority) {\n        List<BusInfo> busInfoList = mTag_BusInfoListMap.get(tag);\n        if (busInfoList == null) {\n            busInfoList = new ArrayList<>();\n            mTag_BusInfoListMap.put(tag, busInfoList);\n        }\n        busInfoList.add(new BusInfo(className, funName, paramType, paramName, sticky, threadMode, priority));\n    }\n\n    public static void register(final Object bus) {\n        getInstance().registerInner(bus);\n    }\n\n    public static void unregister(final Object bus) {\n        getInstance().unregisterInner(bus);\n    }\n\n    public static void post(final String tag) {\n        post(tag, NULL);\n    }\n\n    public static void post(final String tag, final Object arg) {\n        getInstance().postInner(tag, arg);\n    }\n\n    public static void postSticky(final String tag) {\n        postSticky(tag, NULL);\n    }\n\n    public static void postSticky(final String tag, final Object arg) {\n        getInstance().postStickyInner(tag, arg);\n    }\n\n    public static void removeSticky(final String tag) {\n        getInstance().removeStickyInner(tag);\n    }\n\n    public static String toString_() {\n        return getInstance().toString();\n    }\n\n    @Override\n    public String toString() {\n        return \"BusUtils: \" + mTag_BusInfoListMap;\n    }\n\n    private static BusUtils getInstance() {\n        return LazyHolder.INSTANCE;\n    }\n\n    private void registerInner(final Object bus) {\n        if (bus == null) return;\n        Class aClass = bus.getClass();\n        String className = aClass.getName();\n        synchronized (mClassName_BusesMap) {\n            Set<Object> buses = mClassName_BusesMap.get(className);\n            if (buses == null) {\n                buses = new CopyOnWriteArraySet<>();\n                mClassName_BusesMap.put(className, buses);\n            }\n            buses.add(bus);\n        }\n        List<String> tags = mClassName_TagsMap.get(className);\n        if (tags == null) {\n            synchronized (mClassName_TagsMap) {\n                tags = mClassName_TagsMap.get(className);\n                if (tags == null) {\n                    tags = new ArrayList<>();\n                    for (Map.Entry<String, List<BusInfo>> entry : mTag_BusInfoListMap.entrySet()) {\n                        for (BusInfo busInfo : entry.getValue()) {\n                            try {\n                                if (Class.forName(busInfo.className).isAssignableFrom(aClass)) {\n                                    tags.add(entry.getKey());\n                                    busInfo.classNames.add(className);\n                                }\n                            } catch (ClassNotFoundException e) {\n                                e.printStackTrace();\n                            }\n                        }\n                    }\n                    mClassName_TagsMap.put(className, tags);\n                }\n            }\n        }\n        processSticky(bus);\n    }\n\n    private void processSticky(final Object bus) {\n        Map<String, Object> tagArgMap = mClassName_Tag_Arg4StickyMap.get(bus.getClass().getName());\n        if (tagArgMap == null) return;\n        synchronized (mClassName_Tag_Arg4StickyMap) {\n            for (Map.Entry<String, Object> tagArgEntry : tagArgMap.entrySet()) {\n                postInner(tagArgEntry.getKey(), tagArgEntry.getValue());\n            }\n        }\n    }\n\n    private void unregisterInner(final Object bus) {\n        if (bus == null) return;\n        String className = bus.getClass().getName();\n        synchronized (mClassName_BusesMap) {\n            Set<Object> buses = mClassName_BusesMap.get(className);\n            if (buses == null || !buses.contains(bus)) {\n                System.out.println(\"The bus of <\" + bus + \"> was not registered before.\");\n                return;\n            }\n            buses.remove(bus);\n        }\n    }\n\n    private void postInner(final String tag, final Object arg) {\n        postInner(tag, arg, false);\n    }\n\n    private void postInner(final String tag, final Object arg, final boolean sticky) {\n        List<BusInfo> busInfoList = mTag_BusInfoListMap.get(tag);\n        if (busInfoList == null) {\n            System.out.println(\"The bus of tag <\" + tag + \"> is not exists.\");\n            return;\n        }\n        for (BusInfo busInfo : busInfoList) {\n            if (busInfo.method == null) {\n                Method method = getMethodByBusInfo(busInfo);\n                if (method == null) {\n                    return;\n                }\n                busInfo.method = method;\n            }\n            invokeMethod(tag, arg, busInfo, sticky);\n        }\n    }\n\n    private Method getMethodByBusInfo(BusInfo busInfo) {\n        try {\n            if (\"\".equals(busInfo.paramType)) {\n                return Class.forName(busInfo.className).getDeclaredMethod(busInfo.funName);\n            } else {\n                return Class.forName(busInfo.className).getDeclaredMethod(busInfo.funName, getClassName(busInfo.paramType));\n            }\n        } catch (ClassNotFoundException e) {\n            e.printStackTrace();\n        } catch (NoSuchMethodException e) {\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    private Class getClassName(String paramType) throws ClassNotFoundException {\n        switch (paramType) {\n            case \"boolean\":\n                return boolean.class;\n            case \"int\":\n                return int.class;\n            case \"long\":\n                return long.class;\n            case \"short\":\n                return short.class;\n            case \"byte\":\n                return byte.class;\n            case \"double\":\n                return double.class;\n            case \"float\":\n                return float.class;\n            case \"char\":\n                return char.class;\n            default:\n                return Class.forName(paramType);\n        }\n    }\n\n    private void invokeMethod(final String tag, final Object arg, final BusInfo busInfo, final boolean sticky) {\n        Runnable runnable = new Runnable() {\n            @Override\n            public void run() {\n                realInvokeMethod(tag, arg, busInfo, sticky);\n            }\n        };\n        switch (busInfo.threadMode) {\n//            case \"MAIN\":\n//                Utils.runOnUiThread(runnable);\n//                return;\n//            case \"IO\":\n//                ThreadUtils.getIoPool().execute(runnable);\n//                return;\n//            case \"CPU\":\n//                ThreadUtils.getCpuPool().execute(runnable);\n//                return;\n//            case \"CACHED\":\n//                ThreadUtils.getCachedPool().execute(runnable);\n//                return;\n//            case \"SINGLE\":\n//                ThreadUtils.getSinglePool().execute(runnable);\n//                return;\n            default:\n                runnable.run();\n        }\n    }\n\n    private void realInvokeMethod(final String tag, Object arg, BusInfo busInfo, boolean sticky) {\n        Set<Object> buses = new HashSet<>();\n        for (String className : busInfo.classNames) {\n            Set<Object> subBuses = mClassName_BusesMap.get(className);\n            if (subBuses != null && !subBuses.isEmpty()) {\n                buses.addAll(subBuses);\n            }\n        }\n        if (buses.size() == 0) {\n            if (!sticky) {\n                System.out.println(\"The bus of tag <\" + tag + \"> was not registered before.\");\n                return;\n            } else {\n                return;\n            }\n        }\n        try {\n            if (arg == NULL) {\n                for (Object bus : buses) {\n                    busInfo.method.invoke(bus);\n                }\n            } else {\n                for (Object bus : buses) {\n                    busInfo.method.invoke(bus, arg);\n                }\n            }\n        } catch (IllegalAccessException e) {\n            e.printStackTrace();\n        } catch (InvocationTargetException e) {\n            e.printStackTrace();\n        }\n    }\n\n    private void postStickyInner(final String tag, final Object arg) {\n        List<BusInfo> busInfoList = mTag_BusInfoListMap.get(tag);\n        if (busInfoList == null) {\n            System.out.println(\"The bus of tag <\" + tag + \"> is not exists.\");\n            return;\n        }\n        for (BusInfo busInfo : busInfoList) {\n            if (!busInfo.sticky) { // not sticky bus will post directly.\n                postInner(tag, arg);\n                return;\n            }\n            synchronized (mClassName_Tag_Arg4StickyMap) {\n                Map<String, Object> tagArgMap = mClassName_Tag_Arg4StickyMap.get(busInfo.className);\n                if (tagArgMap == null) {\n                    tagArgMap = new HashMap<>();\n                    mClassName_Tag_Arg4StickyMap.put(busInfo.className, tagArgMap);\n                }\n                tagArgMap.put(tag, arg);\n            }\n            postInner(tag, arg, true);\n        }\n    }\n\n    private void removeStickyInner(final String tag) {\n        List<BusInfo> busInfoList = mTag_BusInfoListMap.get(tag);\n        if (busInfoList == null) {\n            System.out.println(\"The bus of tag <\" + tag + \"> is not exists.\");\n            return;\n        }\n        for (BusInfo busInfo : busInfoList) {\n            if (!busInfo.sticky) {\n                System.out.println(\"The bus of tag <\" + tag + \"> is not sticky.\");\n                return;\n            }\n            synchronized (mClassName_Tag_Arg4StickyMap) {\n                Map<String, Object> tagArgMap = mClassName_Tag_Arg4StickyMap.get(busInfo.className);\n                if (tagArgMap == null || !tagArgMap.containsKey(tag)) {\n                    System.out.println(\"The sticky bus of tag <\" + tag + \"> didn't post.\");\n                    return;\n                }\n                tagArgMap.remove(tag);\n            }\n        }\n    }\n\n    private static final class BusInfo {\n\n        String       className;\n        String       funName;\n        String       paramType;\n        String       paramName;\n        boolean      sticky;\n        String       threadMode;\n        int          priority;\n        Method       method;\n        List<String> classNames;\n\n        BusInfo(String className, String funName, String paramType, String paramName,\n                boolean sticky, String threadMode, int priority) {\n            this.className = className;\n            this.funName = funName;\n            this.paramType = paramType;\n            this.paramName = paramName;\n            this.sticky = sticky;\n            this.threadMode = threadMode;\n            this.priority = priority;\n            this.classNames = new CopyOnWriteArrayList<>();\n        }\n\n        @Override\n        public String toString() {\n            return \"BusInfo { desc: \" + className + \"#\" + funName +\n                    (\"\".equals(paramType) ? \"()\" : (\"(\" + paramType + \" \" + paramName + \")\")) +\n                    \", sticky: \" + sticky +\n                    \", threadMode: \" + threadMode +\n                    \", method: \" + method +\n                    \", priority: \" + priority +\n                    \" }\";\n        }\n    }\n\n    public enum ThreadMode {\n        MAIN, IO, CPU, CACHED, SINGLE, POSTING\n    }\n\n    @Target({ElementType.METHOD})\n    @Retention(RetentionPolicy.CLASS)\n    public @interface Bus {\n        String tag();\n\n        boolean sticky() default false;\n\n        ThreadMode threadMode() default ThreadMode.POSTING;\n\n        int priority() default 0;\n    }\n\n    private static class LazyHolder {\n        private static final BusUtils INSTANCE = new BusUtils();\n    }\n}"
  },
  {
    "path": "plugin/lib/base-transform/.gitignore",
    "content": "/build\nApiUtils2333.class"
  },
  {
    "path": "plugin/lib/base-transform/CHANGELOG.md",
    "content": "# Change Log\n\n## v1.0(2020/04/28)\n发布初版本"
  },
  {
    "path": "plugin/lib/base-transform/README.md",
    "content": "# 基础 transform 库\n\n## 背景\n\napi 和 bus 插件存在大量重复代码，所以抽出这么一个基础 transform 库。\n\n\n## 使用\n\n```groovy\nimplementation com.blankj:base-transform:1.0\n```\n\n写插件直接继承 BaseTransformPlugin 即可，比如 ApiPlugin：\n```groovy\nclass ApiPlugin extends BaseTransformPlugin<ApiExtension> {\n\n    @Override\n    String getPluginName() {\n        // 获取插件名\n    }\n\n    @Override\n    void onScanStarted() {\n        // 扫描开始的处理\n    }\n\n    @Override\n    boolean isIgnoreScan(JarInput input) {\n        // 对 jar 包进行过滤扫描，\n        // 工程中的 module 就是以 : 开头的 jar 包\n        // 远端仓库也是 jar 包\n    }\n\n    @Override\n    void scanClassFile(File classFile, String className, File originScannedJarOrDir) {\n        // 扫描到类文件的处理\n    }\n\n    @Override\n    void onScanFinished() {\n        // 扫描结束的处理\n    }\n}\n```\n\n更具体可以参考 ApiPlugin 及 BusPlugin 的源码。\n\n\n## [Change Log](https://github.com/Blankj/AndroidUtilCode/blob/master/plugin/lib/base-transform/CHANGELOG.md)"
  },
  {
    "path": "plugin/lib/base-transform/build.gradle",
    "content": "apply {\n    plugin \"groovy\"\n    plugin \"java-gradle-plugin\"\n}\n\ndependencies {\n    compileOnly Config.plugins.plugin_gradle.path\n    implementation Config.libs.commons_io.path\n    implementation gradleApi()\n    implementation localGroovy()\n}\n\nsourceSets {\n    main {\n        groovy {\n            srcDirs += 'src/main/java'\n        }\n    }\n}\n\next {\n    groupId = Config.modules.plugin_lib_base_transform.groupId\n    artifactId = Config.modules.plugin_lib_base_transform.artifactId\n    version = Config.modules.plugin_lib_base_transform.version\n    website = \"https://github.com/Blankj/AndroidUtilCode\"\n}\napply from: \"${rootDir.path}/config/publish.gradle\"\n//./gradlew clean :plugin_lib_base-transform:mavenLocal     // 上传到本地 mavenLocal\n//./gradlew clean :plugin_lib_base-transform:bintrayUpload  // 上传到 gradle 插件库中\n"
  },
  {
    "path": "plugin/lib/base-transform/src/main/java/com/blankj/base_transform/BaseTransformCallback.groovy",
    "content": "package com.blankj.base_transform\n\nimport com.android.build.api.transform.JarInput\n\ninterface BaseTransformCallback<T> {\n    String getPluginName();\n\n    void onScanStarted();\n\n    boolean isIgnoreScan(JarInput input);\n\n    void scanClassFile(File classFile, String className, File originScannedJarOrDir);\n\n    void onScanFinished();\n}"
  },
  {
    "path": "plugin/lib/base-transform/src/main/java/com/blankj/base_transform/BaseTransformConfig.groovy",
    "content": "package com.blankj.base_transform\n\nclass BaseTransformConfig {\n    public static final String FILE_SEP = System.getProperty(\"file.separator\")\n}"
  },
  {
    "path": "plugin/lib/base-transform/src/main/java/com/blankj/base_transform/BaseTransformPlugin.groovy",
    "content": "package com.blankj.base_transform\n\nimport com.android.build.api.transform.*\nimport com.android.build.gradle.AppExtension\nimport com.android.build.gradle.AppPlugin\nimport com.android.build.gradle.internal.pipeline.TransformManager\nimport com.blankj.base_transform.util.LogUtils\nimport com.blankj.base_transform.util.ZipUtils\nimport groovy.io.FileType\nimport org.apache.commons.io.FileUtils\nimport org.gradle.api.Plugin\nimport org.gradle.api.Project\n\nimport java.lang.reflect.ParameterizedType\n\nabstract class BaseTransformPlugin<T> implements Plugin<Project>, BaseTransformCallback<T> {\n\n    Project mProject\n\n    T getExt() {\n        return mProject.getExtensions().getByName(getPluginName())\n    }\n\n    @Override\n    void apply(Project project) {\n        if (project.plugins.hasPlugin(AppPlugin)) {\n            mProject = project\n            LogUtils.init(project)\n            log('project(' + project.toString() + ') apply ' + getPluginName() + ' gradle plugin!')\n            project.extensions.create(getPluginName(), getGenericClass())\n            def android = project.extensions.getByType(AppExtension)\n            android.registerTransform(new BaseTransform())\n        }\n    }\n\n    Class<T> getGenericClass() {\n        return ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0]\n    }\n\n    class BaseTransform extends Transform {\n\n        @Override\n        String getName() {\n            return \"${getPluginName()}Transform\"\n        }\n\n        @Override\n        Set<QualifiedContent.ContentType> getInputTypes() {\n            return TransformManager.CONTENT_CLASS\n        }\n\n        @Override\n        Set<? super QualifiedContent.Scope> getScopes() {\n            return TransformManager.SCOPE_FULL_PROJECT\n        }\n\n        @Override\n        boolean isIncremental() {\n            return false\n        }\n\n        @Override\n        void transform(TransformInvocation transformInvocation)\n                throws TransformException, InterruptedException, IOException {\n            super.transform(transformInvocation)\n            log(getName() + \" started\")\n            long stTime = System.currentTimeMillis()\n\n            def inputs = transformInvocation.getInputs()\n            def referencedInputs = transformInvocation.getReferencedInputs()\n            def outputProvider = transformInvocation.getOutputProvider()\n            def isIncremental = transformInvocation.isIncremental()\n            outputProvider.deleteAll()\n\n            log(\"${getPluginName()}Extension: $ext\")\n            onScanStarted()\n\n            inputs.each { TransformInput input ->\n                input.directoryInputs.each { DirectoryInput dirInput ->// 遍历文件夹\n                    File dir = dirInput.file\n                    File dest = outputProvider.getContentLocation(\n                            dirInput.name,\n                            dirInput.contentTypes,\n                            dirInput.scopes,\n                            Format.DIRECTORY\n                    )\n                    FileUtils.copyDirectory(dir, dest)\n\n                    log(\"scan dir: ${dirInput.file} -> $dest\")\n                    scanDir(dest)\n                }\n                input.jarInputs.each { JarInput jarInput ->// 遍历 jar 文件\n                    File jar = jarInput.file\n\n                    def jarName = jarInput.name\n                    def dest = outputProvider.getContentLocation(\n                            jarName,\n                            jarInput.contentTypes,\n                            jarInput.scopes,\n                            Format.JAR\n                    )\n                    FileUtils.copyFile(jar, dest)\n\n                    if (isIgnoreScan(jarInput)) {\n                        log(\"jump jar: $jarName -> $dest\")\n                        return\n                    }\n\n                    log(\"scan jar: $jarName -> $dest\")\n                    scanJar(dest)\n                }\n            }\n\n            onScanFinished()\n            log(getName() + \" finished: \" + (System.currentTimeMillis() - stTime) + \"ms\")\n        }\n\n\n        void scanJar(File jar) {\n            File tmp = new File(jar.getParent(), \"temp_\" + jar.getName())\n            List<File> unzipFile = ZipUtils.unzipFile(jar, tmp)\n            if (unzipFile != null && unzipFile.size() > 0) {\n                scanDir(tmp, jar)\n                FileUtils.forceDelete(tmp)\n            }\n        }\n\n        void scanDir(File root) {\n            scanDir(root, root)\n        }\n\n        void scanDir(File dir, File originScannedJarOrDir) {\n            if (!dir.isDirectory()) return\n            String rootPath = dir.getAbsolutePath()\n            if (!rootPath.endsWith(BaseTransformConfig.FILE_SEP)) {\n                rootPath += BaseTransformConfig.FILE_SEP\n            }\n\n            dir.eachFileRecurse(FileType.FILES) { File file ->\n                def fileName = file.name\n                if (!fileName.endsWith('.class')\n                        || fileName.startsWith('R$')\n                        || fileName == 'R.class'\n                        || fileName == 'BuildConfig.class') {\n                    return\n                }\n\n                def filePath = file.absolutePath\n                def packagePath = filePath.replace(rootPath, '')\n                def className = packagePath.replace(BaseTransformConfig.FILE_SEP, \".\")\n                // delete .class\n                className = className.substring(0, className.length() - 6)\n\n                scanClassFile(file, className, originScannedJarOrDir)\n            }\n        }\n    }\n\n    void log(Object obj) {\n        LogUtils.l(getPluginName(), obj)\n    }\n}"
  },
  {
    "path": "plugin/lib/base-transform/src/main/java/com/blankj/base_transform/util/JsonUtils.groovy",
    "content": "package com.blankj.base_transform.util\n\nimport com.google.gson.Gson\nimport com.google.gson.GsonBuilder\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2018/10/08\n *     desc  :\n * </pre>\n */\nfinal class JsonUtils {\n\n    static final Gson GSON = new GsonBuilder().setPrettyPrinting().create()\n\n    static String getFormatJson(Object object) {\n        return GSON.toJson(object)\n    }\n}\n"
  },
  {
    "path": "plugin/lib/base-transform/src/main/java/com/blankj/base_transform/util/LogUtils.groovy",
    "content": "package com.blankj.base_transform.util\n\nimport org.gradle.api.Project\nimport org.gradle.api.logging.Logger\n\nfinal class LogUtils {\n\n    private static Logger sLogger\n\n    static void init(Project project) {\n        sLogger = project.getLogger()\n    }\n\n    static void l(Object content) {\n        l(\"\", content)\n    }\n\n    static void d(Object content) {\n        d(\"\", content)\n    }\n\n    static void i(Object content) {\n        i(\"\", content)\n    }\n\n    static void w(Object content) {\n        w(\"\", content)\n    }\n\n    static void e(Object content) {\n        e(\"\", content)\n    }\n\n    static void l(String tag, Object content) {\n        sLogger.lifecycle(getTag(tag) + content)\n    }\n\n    static void d(String tag, Object content) {\n        sLogger.debug(getTag(tag) + content)\n    }\n\n    static void i(String tag, Object content) {\n        sLogger.info(getTag(tag) + content)\n    }\n\n    static void w(String tag, Object content) {\n        sLogger.warn(getTag(tag) + content)\n    }\n\n    static void e(String tag, Object content) {\n        sLogger.error(getTag(tag) + content)\n    }\n\n    private static String getTag(String tag) {\n        if (tag == null || tag.isEmpty()) {\n            return \"LogUtils >>> \"\n        }\n        return tag + \" >>> \"\n    }\n}"
  },
  {
    "path": "plugin/lib/base-transform/src/main/java/com/blankj/base_transform/util/ZipUtils.java",
    "content": "package com.blankj.base_transform.util;\n\nimport java.io.BufferedInputStream;\nimport java.io.BufferedOutputStream;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.io.FileOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Enumeration;\nimport java.util.List;\nimport java.util.zip.ZipEntry;\nimport java.util.zip.ZipFile;\nimport java.util.zip.ZipOutputStream;\n\n/**\n * <pre>\n *     author: Blankj\n *     blog  : http://blankj.com\n *     time  : 2016/08/27\n *     desc  : utils about zip or jar\n * </pre>\n */\npublic final class ZipUtils {\n\n    private static final int BUFFER_LEN = 8192;\n\n    private ZipUtils() {\n        throw new UnsupportedOperationException(\"u can't instantiate me...\");\n    }\n\n    /**\n     * Zip the files.\n     *\n     * @param srcFiles    The source of files.\n     * @param zipFilePath The path of ZIP file.\n     * @return {@code true}: success<br>{@code false}: fail\n     * @throws IOException if an I/O error has occurred\n     */\n    public static boolean zipFiles(final Collection<String> srcFiles,\n                                   final String zipFilePath)\n            throws IOException {\n        return zipFiles(srcFiles, zipFilePath, null);\n    }\n\n    /**\n     * Zip the files.\n     *\n     * @param srcFilePaths The paths of source files.\n     * @param zipFilePath  The path of ZIP file.\n     * @param comment      The comment.\n     * @return {@code true}: success<br>{@code false}: fail\n     * @throws IOException if an I/O error has occurred\n     */\n    public static boolean zipFiles(final Collection<String> srcFilePaths,\n                                   final String zipFilePath,\n                                   final String comment)\n            throws IOException {\n        if (srcFilePaths == null || zipFilePath == null) return false;\n        ZipOutputStream zos = null;\n        try {\n            zos = new ZipOutputStream(new FileOutputStream(zipFilePath));\n            for (String srcFile : srcFilePaths) {\n                if (!zipFile(getFileByPath(srcFile), \"\", zos, comment)) return false;\n            }\n            return true;\n        } finally {\n            if (zos != null) {\n                zos.finish();\n                zos.close();\n            }\n        }\n    }\n\n    /**\n     * Zip the files.\n     *\n     * @param srcFiles The source of files.\n     * @param zipFile  The ZIP file.\n     * @return {@code true}: success<br>{@code false}: fail\n     * @throws IOException if an I/O error has occurred\n     */\n    public static boolean zipFiles(final Collection<File> srcFiles, final File zipFile)\n            throws IOException {\n        return zipFiles(srcFiles, zipFile, null);\n    }\n\n    /**\n     * Zip the files.\n     *\n     * @param srcFiles The source of files.\n     * @param zipFile  The ZIP file.\n     * @param comment  The comment.\n     * @return {@code true}: success<br>{@code false}: fail\n     * @throws IOException if an I/O error has occurred\n     */\n    public static boolean zipFiles(final Collection<File> srcFiles,\n                                   final File zipFile,\n                                   final String comment)\n            throws IOException {\n        if (srcFiles == null || zipFile == null) return false;\n        ZipOutputStream zos = null;\n        try {\n            zos = new ZipOutputStream(new FileOutputStream(zipFile));\n            for (File srcFile : srcFiles) {\n                if (!zipFile(srcFile, \"\", zos, comment)) return false;\n            }\n            return true;\n        } finally {\n            if (zos != null) {\n                zos.finish();\n                zos.close();\n            }\n        }\n    }\n\n    /**\n     * Zip the file.\n     *\n     * @param srcFilePath The path of source file.\n     * @param zipFilePath The path of ZIP file.\n     * @return {@code true}: success<br>{@code false}: fail\n     * @throws IOException if an I/O error has occurred\n     */\n    public static boolean zipFile(final String srcFilePath,\n                                  final String zipFilePath)\n            throws IOException {\n        return zipFile(getFileByPath(srcFilePath), getFileByPath(zipFilePath), null);\n    }\n\n    /**\n     * Zip the file.\n     *\n     * @param srcFilePath The path of source file.\n     * @param zipFilePath The path of ZIP file.\n     * @param comment     The comment.\n     * @return {@code true}: success<br>{@code false}: fail\n     * @throws IOException if an I/O error has occurred\n     */\n    public static boolean zipFile(final String srcFilePath,\n                                  final String zipFilePath,\n                                  final String comment)\n            throws IOException {\n        return zipFile(getFileByPath(srcFilePath), getFileByPath(zipFilePath), comment);\n    }\n\n    /**\n     * Zip the file.\n     *\n     * @param srcFile The source of file.\n     * @param zipFile The ZIP file.\n     * @return {@code true}: success<br>{@code false}: fail\n     * @throws IOException if an I/O error has occurred\n     */\n    public static boolean zipFile(final File srcFile,\n                                  final File zipFile)\n            throws IOException {\n        return zipFile(srcFile, zipFile, null);\n    }\n\n    /**\n     * Zip the file.\n     *\n     * @param srcFile The source of file.\n     * @param zipFile The ZIP file.\n     * @param comment The comment.\n     * @return {@code true}: success<br>{@code false}: fail\n     * @throws IOException if an I/O error has occurred\n     */\n    public static boolean zipFile(final File srcFile,\n                                  final File zipFile,\n                                  final String comment)\n            throws IOException {\n        if (srcFile == null || zipFile == null) return false;\n        ZipOutputStream zos = null;\n        try {\n            zos = new ZipOutputStream(new FileOutputStream(zipFile));\n            return zipFile(srcFile, \"\", zos, comment);\n        } finally {\n            if (zos != null) {\n                zos.close();\n            }\n        }\n    }\n\n    private static boolean zipFile(final File srcFile,\n                                   String rootPath,\n                                   final ZipOutputStream zos,\n                                   final String comment)\n            throws IOException {\n        rootPath = rootPath + (isSpace(rootPath) ? \"\" : File.separator) + srcFile.getName();\n        if (srcFile.isDirectory()) {\n            File[] fileList = srcFile.listFiles();\n            if (fileList == null || fileList.length <= 0) {\n                ZipEntry entry = new ZipEntry(rootPath + '/');\n                entry.setComment(comment);\n                zos.putNextEntry(entry);\n                zos.closeEntry();\n            } else {\n                for (File file : fileList) {\n                    if (!zipFile(file, rootPath, zos, comment)) return false;\n                }\n            }\n        } else {\n            InputStream is = null;\n            try {\n                is = new BufferedInputStream(new FileInputStream(srcFile));\n                ZipEntry entry = new ZipEntry(rootPath);\n                entry.setComment(comment);\n                zos.putNextEntry(entry);\n                byte buffer[] = new byte[BUFFER_LEN];\n                int len;\n                while ((len = is.read(buffer, 0, BUFFER_LEN)) != -1) {\n                    zos.write(buffer, 0, len);\n                }\n                zos.closeEntry();\n            } finally {\n                if (is != null) {\n                    is.close();\n                }\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Unzip the file.\n     *\n     * @param zipFilePath The path of ZIP file.\n     * @param destDirPath The path of destination directory.\n     * @return the unzipped files\n     * @throws IOException if unzip unsuccessfully\n     */\n    public static List<File> unzipFile(final String zipFilePath,\n                                       final String destDirPath)\n            throws IOException {\n        return unzipFileByKeyword(zipFilePath, destDirPath, null);\n    }\n\n    /**\n     * Unzip the file.\n     *\n     * @param zipFile The ZIP file.\n     * @param destDir The destination directory.\n     * @return the unzipped files\n     * @throws IOException if unzip unsuccessfully\n     */\n    public static List<File> unzipFile(final File zipFile,\n                                       final File destDir)\n            throws IOException {\n        return unzipFileByKeyword(zipFile, destDir, null);\n    }\n\n    /**\n     * Unzip the file by keyword.\n     *\n     * @param zipFilePath The path of ZIP file.\n     * @param destDirPath The path of destination directory.\n     * @param keyword     The keyboard.\n     * @return the unzipped files\n     * @throws IOException if unzip unsuccessfully\n     */\n    public static List<File> unzipFileByKeyword(final String zipFilePath,\n                                                final String destDirPath,\n                                                final String keyword)\n            throws IOException {\n        return unzipFileByKeyword(getFileByPath(zipFilePath), getFileByPath(destDirPath), keyword);\n    }\n\n    /**\n     * Unzip the file by keyword.\n     *\n     * @param zipFile The ZIP file.\n     * @param destDir The destination directory.\n     * @param keyword The keyboard.\n     * @return the unzipped files\n     * @throws IOException if unzip unsuccessfully\n     */\n    public static List<File> unzipFileByKeyword(final File zipFile,\n                                                final File destDir,\n                                                final String keyword)\n            throws IOException {\n        if (zipFile == null || destDir == null) return null;\n        List<File> files = new ArrayList<>();\n        ZipFile zip = new ZipFile(zipFile);\n        Enumeration<?> entries = zip.entries();\n        try {\n            if (isSpace(keyword)) {\n                while (entries.hasMoreElements()) {\n                    ZipEntry entry = ((ZipEntry) entries.nextElement());\n                    String entryName = entry.getName();\n                    if (entryName.contains(\"../\")) {\n                        System.err.println(\"entryName: \" + entryName + \" is dangerous!\");\n                        continue;\n                    }\n                    if (!unzipChildFile(destDir, files, zip, entry, entryName)) return files;\n                }\n            } else {\n                while (entries.hasMoreElements()) {\n                    ZipEntry entry = ((ZipEntry) entries.nextElement());\n                    String entryName = entry.getName();\n                    if (entryName.contains(\"../\")) {\n                        System.out.println(\"entryName: \" + entryName + \" is dangerous!\");\n                        continue;\n                    }\n                    if (entryName.contains(keyword)) {\n                        if (!unzipChildFile(destDir, files, zip, entry, entryName)) return files;\n                    }\n                }\n            }\n        } finally {\n            zip.close();\n        }\n        return files;\n    }\n\n    private static boolean unzipChildFile(final File destDir,\n                                          final List<File> files,\n                                          final ZipFile zip,\n                                          final ZipEntry entry,\n                                          final String name) throws IOException {\n        File file = new File(destDir, name);\n        files.add(file);\n        if (entry.isDirectory()) {\n            return createOrExistsDir(file);\n        } else {\n            if (!createOrExistsFile(file)) return false;\n            InputStream in = null;\n            OutputStream out = null;\n            try {\n                in = new BufferedInputStream(zip.getInputStream(entry));\n                out = new BufferedOutputStream(new FileOutputStream(file));\n                byte buffer[] = new byte[BUFFER_LEN];\n                int len;\n                while ((len = in.read(buffer)) != -1) {\n                    out.write(buffer, 0, len);\n                }\n            } finally {\n                if (in != null) {\n                    in.close();\n                }\n                if (out != null) {\n                    out.close();\n                }\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Return the files' path in ZIP file.\n     *\n     * @param zipFilePath The path of ZIP file.\n     * @return the files' path in ZIP file\n     * @throws IOException if an I/O error has occurred\n     */\n    public static List<String> getFilesPath(final String zipFilePath)\n            throws IOException {\n        return getFilesPath(getFileByPath(zipFilePath));\n    }\n\n    /**\n     * Return the files' path in ZIP file.\n     *\n     * @param zipFile The ZIP file.\n     * @return the files' path in ZIP file\n     * @throws IOException if an I/O error has occurred\n     */\n    public static List<String> getFilesPath(final File zipFile)\n            throws IOException {\n        if (zipFile == null) return null;\n        List<String> paths = new ArrayList<>();\n        ZipFile zip = new ZipFile(zipFile);\n        Enumeration<?> entries = zip.entries();\n        while (entries.hasMoreElements()) {\n            String entryName = ((ZipEntry) entries.nextElement()).getName();\n            if (entryName.contains(\"../\")) {\n                System.out.println(\"entryName: \" + entryName + \" is dangerous!\");\n                paths.add(entryName);\n            } else {\n                paths.add(entryName);\n            }\n        }\n        zip.close();\n        return paths;\n    }\n\n    /**\n     * Return the files' comment in ZIP file.\n     *\n     * @param zipFilePath The path of ZIP file.\n     * @return the files' comment in ZIP file\n     * @throws IOException if an I/O error has occurred\n     */\n    public static List<String> getComments(final String zipFilePath)\n            throws IOException {\n        return getComments(getFileByPath(zipFilePath));\n    }\n\n    /**\n     * Return the files' comment in ZIP file.\n     *\n     * @param zipFile The ZIP file.\n     * @return the files' comment in ZIP file\n     * @throws IOException if an I/O error has occurred\n     */\n    public static List<String> getComments(final File zipFile)\n            throws IOException {\n        if (zipFile == null) return null;\n        List<String> comments = new ArrayList<>();\n        ZipFile zip = new ZipFile(zipFile);\n        Enumeration<?> entries = zip.entries();\n        while (entries.hasMoreElements()) {\n            ZipEntry entry = ((ZipEntry) entries.nextElement());\n            comments.add(entry.getComment());\n        }\n        zip.close();\n        return comments;\n    }\n\n    private static boolean createOrExistsDir(final File file) {\n        return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());\n    }\n\n    private static boolean createOrExistsFile(final File file) {\n        if (file == null) return false;\n        if (file.exists()) return file.isFile();\n        if (!createOrExistsDir(file.getParentFile())) return false;\n        try {\n            return file.createNewFile();\n        } catch (IOException e) {\n            e.printStackTrace();\n            return false;\n        }\n    }\n\n    private static File getFileByPath(final String filePath) {\n        return isSpace(filePath) ? null : new File(filePath);\n    }\n\n    private static boolean isSpace(final String s) {\n        if (s == null) return true;\n        for (int i = 0, len = s.length(); i < len; ++i) {\n            if (!Character.isWhitespace(s.charAt(i))) {\n                return false;\n            }\n        }\n        return true;\n    }\n}\n"
  },
  {
    "path": "script/gitHelp.sh",
    "content": "#!/usr/bin/env bash\nwhile true; do\n  echo \"                 ############## input command code #################\"\n  echo \"                 #          [1] Git Push                           #\"\n  echo \"                 #          [2] Git Push And Merge to Master       #\"\n  echo \"                 #          [other] exit                           #\"\n  echo \"                 ###################################################\"\n\n  read which\n\n  case $which in\n  1)\n    curBranch=$(git symbolic-ref --short -q HEAD)\n    gitPush $curBranch\n    ;;\n  2)\n    curBranch=$(git symbolic-ref --short -q HEAD)\n    gitPush $curBranch\n    echo \"git checkout master\"\n    echo $(git checkout master)\n    echo \"git merge $branchName\"\n    echo $(git merge $branchName)\n    echo \"git push origin master\"\n    echo $(git push origin master)\n    echo \"git checkout $branchName\"\n    echo $(git checkout $branchName)\n    ;;\n  *)\n    echo \"88\"\n    break\n    ;;\n  esac\ndone\n\nfunction gitPush() {\n  curBranch=$1\n  echo \"curBranch = $curBranch\"\n  echo \"git add -A\"\n  echo $(git add -A)\n  date=$(date \"+%m/%d\")\n  echo \"git commit -m \\\"see $date log\\\"\"\n  echo $(git commit -m \"see $date log\")\n  echo \"git push origin $curBranch\"\n  echo $(git push origin $curBranch)\n}\n"
  },
  {
    "path": "script/runDevDebug.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\n\n# build dev debug apk\n./gradlew --daemon installDevDebug\n\n# start main activity\nadb shell am start -n \"com.blankj.androidutilcode.dev/com.blankj.main.pkg.MainActivity\" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER"
  },
  {
    "path": "script/runProductionRelease.sh",
    "content": "#!/usr/bin/env bash\n\nset -e\n\n# build dev debug apk\n./gradlew --daemon installProductionRelease\n\n# start main activity\nadb shell am start -n \"com.blankj.androidutilcode/com.blankj.main.pkg.MainActivity\" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER"
  },
  {
    "path": "settings.gradle",
    "content": "//includeBuild 'configPlugin'\n\n//buildscript {\n////    ConfigUtils.init(gradle)\n//    repositories {\n//        mavenLocal()\n//        mavenCentral()\n//    }\n//\n//    dependencies {\n//        classpath 'com.blankj:buildSrc-plugin:1.0'\n//    }\n//}\n\n//apply plugin: \"com.blankj.buildSrc\"\n\n\n//dependencyResolutionManagement {\n//    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)\n//    repositories {\n//        google()\n//        mavenCentral()\n//        jcenter() // Warning: this repository is going to shut down soon\n//        maven {url 'https://jetpack.io'} // Warning: this repository is going to shut down soon\n//    }\n//\n////    configurations.all {\n////        resolutionStrategy.cacheChangingModulesFor 0, 'seconds'\n////\n////        resolutionStrategy.eachDependency {\n////            if (it.requested.group == 'com.android.support' && !it.requested.name.contains(\n////                    'multidex')) {\n////                it.useVersion Config.supportVersion\n////            }\n////        }\n////    }\n//}\n\napply from: 'module_config.gradle'\n"
  },
  {
    "path": "sign/keystore.properties",
    "content": "keystore=keystore.jks\nstorePassword=utilcode\nkeyAlias=utilcode\nkeyPassword=utilcode"
  }
]