DoggyZhang 5 meses atrás
commit
71b37c4b9b
100 arquivos alterados com 11083 adições e 0 exclusões
  1. 23 0
      .gitignore
  2. 528 0
      .tolgeerc
  3. 23 0
      README.md
  4. 2 0
      app/.gitignore
  5. 60 0
      app/aab_res_guard.gradle
  6. 514 0
      app/build.gradle
  7. 298 0
      app/dependencies/releaseRuntimeClasspath.txt
  8. 4 0
      app/dependencyGuard.gradle
  9. BIN
      app/libs/R_TuringSDK_v90_c0_lcD97A2DB9093760EF_release_20250220154612_pri_mini_nolog.aar
  10. BIN
      app/libs/wenext_jni-release.aar
  11. BIN
      app/libs/xcrash_lib-release.aar
  12. 0 0
      app/proguard-log-empty.pro
  13. 176 0
      app/proguard-log.gradle
  14. 9 0
      app/proguard-log.pro
  15. 103 0
      app/proguard-log.py
  16. 413 0
      app/proguard-rules.pro
  17. 4688 0
      app/s-proguard.txt
  18. 24 0
      app/src/androidTest/java/com/adealink/weparty/ExampleInstrumentedTest.kt
  19. 11 0
      app/src/debug/assets/timpush-configs.json
  20. 47 0
      app/src/debug/google-services.json
  21. 199 0
      app/src/main/AndroidManifest.xml
  22. BIN
      app/src/main/assets/cocosgame_coins_collect_effect.svga
  23. BIN
      app/src/main/assets/common_svip_ic.svga
  24. BIN
      app/src/main/assets/common_task_reward_dialog.svga
  25. BIN
      app/src/main/assets/couple_love_play.svga
  26. BIN
      app/src/main/assets/custom_refresh_loading.svga
  27. BIN
      app/src/main/assets/game_downloading.svga
  28. BIN
      app/src/main/assets/home_float_income_bg.svga
  29. BIN
      app/src/main/assets/home_float_income_fg.svga
  30. BIN
      app/src/main/assets/home_say_hi.svga
  31. BIN
      app/src/main/assets/home_say_hi_rtl.svga
  32. BIN
      app/src/main/assets/home_user_in_room.svga
  33. BIN
      app/src/main/assets/ic_greedy_personal_out_room.svga
  34. BIN
      app/src/main/assets/ic_home_entrance_new_user_guide.svga
  35. BIN
      app/src/main/assets/ic_home_entrance_new_user_guide_ar.svga
  36. BIN
      app/src/main/assets/im_user_in_room.svga
  37. BIN
      app/src/main/assets/income_message_reminder_reply_btn.svga
  38. BIN
      app/src/main/assets/level/wealth_level_0_to_29_bg.svga
  39. BIN
      app/src/main/assets/level/wealth_level_100_bg.svga
  40. BIN
      app/src/main/assets/level/wealth_level_30_to_39_bg.svga
  41. BIN
      app/src/main/assets/level/wealth_level_40_to_49_bg.svga
  42. BIN
      app/src/main/assets/level/wealth_level_50_to_59_bg.svga
  43. BIN
      app/src/main/assets/level/wealth_level_60_to_69_bg.svga
  44. BIN
      app/src/main/assets/level/wealth_level_70_to_79_bg.svga
  45. BIN
      app/src/main/assets/level/wealth_level_80_to_89_bg.svga
  46. BIN
      app/src/main/assets/level/wealth_level_90_to_99_bg.svga
  47. BIN
      app/src/main/assets/match_entrance_bg.svga
  48. BIN
      app/src/main/assets/match_entrance_bg_rtl.svga
  49. BIN
      app/src/main/assets/message_chat_cp.svga
  50. BIN
      app/src/main/assets/message_chat_guard.svga
  51. BIN
      app/src/main/assets/message_income_diamond.svga
  52. BIN
      app/src/main/assets/message_reminder_reply_btn.svga
  53. BIN
      app/src/main/assets/new_user_reward.svga
  54. BIN
      app/src/main/assets/profile_custom_id_bg.svga
  55. BIN
      app/src/main/assets/profile_sid_1_4.svga
  56. BIN
      app/src/main/assets/profile_sid_5_7.svga
  57. BIN
      app/src/main/assets/profile_sid_8_10.svga
  58. BIN
      app/src/main/assets/room_rocket_entrance_level1.svga
  59. BIN
      app/src/main/assets/room_rocket_entrance_level2.svga
  60. BIN
      app/src/main/assets/room_rocket_entrance_level3.svga
  61. BIN
      app/src/main/assets/room_rocket_entrance_level4.svga
  62. BIN
      app/src/main/assets/room_rocket_entrance_level5.svga
  63. BIN
      app/src/main/assets/skin.zip
  64. BIN
      app/src/main/assets/sound_wave_loading.svga
  65. BIN
      app/src/main/assets/svip_6_digit_good_id.svga
  66. BIN
      app/src/main/assets/svip_7_digit_good_id.svga
  67. BIN
      app/src/main/assets/svip_8_digit_good_id.svga
  68. BIN
      app/src/main/assets/today_fate_btn.svga
  69. BIN
      app/src/main/assets/today_fate_content_animation_bg.svga
  70. BIN
      app/src/main/assets/today_fate_head_animation_bg.svga
  71. 0 0
      app/src/main/assets/v_cube.license
  72. BIN
      app/src/main/assets/wallet_recharge_daily_task_gift.svga
  73. 782 0
      app/src/main/java/androidx/viewpager2/adapter/LazyFragmentStateAdapter.java
  74. 514 0
      app/src/main/java/com/adealink/weparty/App.kt
  75. 227 0
      app/src/main/java/com/adealink/weparty/MainActivity.kt
  76. 16 0
      app/src/main/java/com/adealink/weparty/Routers.kt
  77. 34 0
      app/src/main/java/com/adealink/weparty/aab/AABConfig.kt
  78. 13 0
      app/src/main/java/com/adealink/weparty/apm/APMConfig.kt
  79. 173 0
      app/src/main/java/com/adealink/weparty/apm/APMInit.kt
  80. 71 0
      app/src/main/java/com/adealink/weparty/apm/HookTest.kt
  81. 18 0
      app/src/main/java/com/adealink/weparty/apm/stat/APMStatEvent.kt
  82. 24 0
      app/src/main/java/com/adealink/weparty/base/AppBaseConfig.kt
  83. 66 0
      app/src/main/java/com/adealink/weparty/cache/CacheRepository.kt
  84. 14 0
      app/src/main/java/com/adealink/weparty/channel/AppChannel.kt
  85. 23 0
      app/src/main/java/com/adealink/weparty/channel/ChannelUtil.kt
  86. 6 0
      app/src/main/java/com/adealink/weparty/channel/Tags.kt
  87. 201 0
      app/src/main/java/com/adealink/weparty/cocosgame/BaseCocosWebGameFragment.kt
  88. 5 0
      app/src/main/java/com/adealink/weparty/cocosgame/CocosGame.kt
  89. 35 0
      app/src/main/java/com/adealink/weparty/cocosgame/chat/adapter/QuickTextViewBinder.kt
  90. 103 0
      app/src/main/java/com/adealink/weparty/cocosgame/chat/component/ChatMessageComp.kt
  91. 638 0
      app/src/main/java/com/adealink/weparty/cocosgame/chat/component/QuickChatComp.kt
  92. 20 0
      app/src/main/java/com/adealink/weparty/cocosgame/chat/data/Data.kt
  93. 16 0
      app/src/main/java/com/adealink/weparty/cocosgame/chat/datasource/remote/ChatSocketService.kt
  94. 119 0
      app/src/main/java/com/adealink/weparty/cocosgame/chat/fragment/QuickChatFragment.kt
  95. 9 0
      app/src/main/java/com/adealink/weparty/cocosgame/chat/listener/IChatOperateListener.kt
  96. 24 0
      app/src/main/java/com/adealink/weparty/cocosgame/chat/viewmodel/QuickChatViewModel.kt
  97. 181 0
      app/src/main/java/com/adealink/weparty/cocosgame/comp/CoinsCollectAnimComp.kt
  98. 39 0
      app/src/main/java/com/adealink/weparty/cocosgame/data/CocosDialogData.kt
  99. 401 0
      app/src/main/java/com/adealink/weparty/cocosgame/data/CocosGameData.kt
  100. 189 0
      app/src/main/java/com/adealink/weparty/cocosgame/data/CocosNativeData.kt

+ 23 - 0
.gitignore

@@ -0,0 +1,23 @@
+*.iml
+.gradle
+/local.properties
+/.idea/caches
+/.idea/libraries
+/.idea/modules.xml
+/.idea/workspace.xml
+/.idea/navEditor.xml
+/.idea/assetWizardSettings.xml
+.DS_Store
+/build
+/captures
+.externalNativeBuild
+.cxx
+local.properties
+.idea/
+external/retrofit/build/
+external/drawee/build/
+schemas
+schemas/
+schemas/*
+app/release/
+app/yoki/release

+ 528 - 0
.tolgeerc

@@ -0,0 +1,528 @@
+{
+  "apiUrl": "http://47.236.31.243:8090/api",
+  "apiKey": "tgpak_hbpxgyltnryguy3lonyxiyrtgruta4rroryhiolooe3q",
+  "projectId": "8",
+  "format": "ANDROID_XML",
+  "convertPlaceholdersTo": "icu",
+  "structureDelimiter": ".",
+  "push": {
+    "files": [
+      {
+        "path": "app/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "app/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "app/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/operation/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/operation/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/operation/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/music/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/music/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/music/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/visitors/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/visitors/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/visitors/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/userprotect/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/userprotect/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/userprotect/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/skin/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/skin/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/skin/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/pk/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/pk/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/pk/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/room/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/room/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/room/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/moment/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/moment/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/moment/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/party/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/party/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/party/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/follow/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/follow/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/follow/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/webview/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/webview/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/webview/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/family/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/family/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/family/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/superadmin/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/superadmin/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/superadmin/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/headline/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/headline/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/headline/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/message/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/message/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/message/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/game/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/game/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/game/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/youtube/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/youtube/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/youtube/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/image/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/image/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/image/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/gift/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/gift/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/gift/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/search/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/search/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/search/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/call/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/call/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/call/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/profile/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/profile/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/profile/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/medal/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/medal/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/medal/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/theme/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/theme/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/theme/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/micgrab/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/micgrab/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/micgrab/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/wallet/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/wallet/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/wallet/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/gamehub/domino/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/gamehub/domino/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/gamehub/domino/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/gamehub/ludo/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/gamehub/ludo/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/gamehub/ludo/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/gamehub/carrom/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/gamehub/carrom/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/gamehub/carrom/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/gamehub/uno/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/gamehub/uno/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/gamehub/uno/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/anchor/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/anchor/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/anchor/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/level/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/level/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/level/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/backpack/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/backpack/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/backpack/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/account/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/account/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/account/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/setting/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/setting/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/setting/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/rank/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/rank/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/rank/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/email/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/email/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/email/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/certification/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/certification/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/certification/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/emotion/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/emotion/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/emotion/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/store/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/store/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/store/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/couple/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/couple/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/couple/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      },
+      {
+        "path": "module/share/src/main/res/values/strings.xml",
+        "language": "en"
+      },
+      {
+        "path": "module/share/src/main/res/values-ar/strings.xml",
+        "language": "ar"
+      },
+      {
+        "path": "module/share/src/main/res/values-zh/strings.xml",
+        "language": "zh"
+      }
+    ]
+  }
+}

+ 23 - 0
README.md

@@ -0,0 +1,23 @@
+# WeParty工程规范
+
+## 资源命名规范
+
+* 独立模块专属资源文件统一到AAB模块相应res下
+* 通用的drawable、anim、color等资源文件统一App res下
+* 资源文件命名规范:<模块>\_<功能>\_<描述>
+
+```xml
+//字符串
+<!-- 通用文案模块名:common -->
+<string name="common_network_error">网络错误!</string>
+<!-- 房间礼物功能 -->
+<string name="room_gift_parse_error">礼物资源解析错误!</string>
+ 
+//color,颜色命名使用color_前缀,颜色值统一大写
+<color name="color_7EBA3E">#7EBA3E</color>
+ 
+//drawable文件:<模块>_<功能>_<颜色>_<size>_<ic/bg>.png,其中size单位dp,width等于height时写一个值,width不同于height时<width>_<height>,如果资源是图标使用ic后缀,如果资源是背景使用bg后缀
+common_close_white_24_ic.png //通用白色width=height=24dp的关闭图标
+room_create_purple_375_667_bg.png //紫色width=375dp&height=667dp的房间创建页面背景
+```
+

+ 2 - 0
app/.gitignore

@@ -0,0 +1,2 @@
+/build
+/src/main/assets/audio.zip

+ 60 - 0
app/aab_res_guard.gradle

@@ -0,0 +1,60 @@
+apply plugin: "com.bytedance.android.aabResGuard"
+aabResGuard {
+    enableObfuscate = true
+    mappingFile = file("mapping.txt").toPath() // 用于增量混淆的 mapping 文件
+    whiteList = [
+            // keep resource file
+            "*/res/raw*",
+
+            // Google Service
+            "*.R.string.google_app_id",
+            "*.R.string.gcm_defaultSenderId",
+            "*.R.string.default_web_client_id",
+            "*.R.string.ga_trackingId",
+            "*.R.string.firebase_database_url",
+            "*.R.string.google_storage_bucket",
+            "*.R.string.google_api_key",
+            "*.R.string.google_crash_reporting_api_key",
+            "*.R.string.fcm_fallback_notification_channel",
+
+            // firebase
+            "*.R.string.project_id",
+
+            // app relative with getIdentifier
+            "*.R.dimen.navigation_bar_height",
+            "*.R.raw.youtube_auth_config",
+            "*.R.dimen.status_bar_height_large",
+            "*.R.dimen.status_bar_height",
+            "*.R.style.BaseSkinTheme.*",
+            "*.R.style.DefaultSkinTheme.*",
+            "*.R.attr.*",
+
+            "*.R.bool.config_showNavigationBar",
+            "*.R.string.config_mainBuiltInDisplayCutout",
+            "*.R.drawable.config_mainBuiltInDisplayCutout",
+            "*.R.string.com.twitter.sdk.android.CONSUMER_KEY",
+            "*.R.string.com.twitter.sdk.android.CONSUMER_SECRET",
+            "*.R.integer.com_vk_sdk_AppId",
+            "*.R.string.com_vk_sdk_ApiVersion",
+
+            "*.R.bool.*",
+            //国家
+            "*.R.string.[A-Z][A-Z]",
+
+            "*.R.raw.*",
+
+            //避免获取View的id的名称不正确
+            "*.R.id.*",
+    ]
+    obfuscatedBundleFileName = "duplicated-app.aab" // 混淆后的文件名称,必须以 `.aab` 结尾
+    mergeDuplicatedRes = true // 开启去除重复资源
+    enableFilterFiles = false // 关闭过滤文件
+    filterList = [ // 文件过滤规则
+//                   "*/arm64-v8a/*",
+//                   "META-INF/*"
+    ]
+    enableFilterStrings = false // 过滤文案
+    unusedStringPath = file("unused.txt").toPath() // 过滤文案列表路径 默认在mapping同目录查找
+//    languageWhiteList = ["en", "zh"] // 关闭语言白名单
+}
+

+ 514 - 0
app/build.gradle

@@ -0,0 +1,514 @@
+import java.text.SimpleDateFormat
+
+plugins {
+    id 'com.android.application'
+    id 'org.jetbrains.kotlin.android'
+    id 'com.google.gms.google-services'
+    id 'com.google.firebase.crashlytics'
+    id 'org.jetbrains.kotlin.kapt'
+    id 'com.tencent.vasdolly'
+    id 'kotlin-parcelize'
+    id("com.dropbox.dependency-guard") version "0.5.0"
+    id("com.google.devtools.ksp") version "1.9.10-1.0.13"//引入ksp插件
+}
+
+apply plugin: 'com.wenext.bundle.local.test'
+apply plugin: "android.aop"
+
+def isEnableTrace = project.hasProperty("ENABLE_TRACE") && project.ENABLE_TRACE.toBoolean()
+if (isEnableTrace) {
+    apply plugin: "LancerTracePlugin"
+}
+println("isEnableTrace: "+ isEnableTrace)
+
+def isOfficial = project.OFFICIAL == "true"
+if (!isOfficial) {
+    apply from: "proguard-log.gradle"
+}
+println("isOfficial:" + isOfficial)
+println("IS_RELEASE:" + project.IS_RELEASE)
+//apply from: "aab_res_guard.gradle"
+
+apply from: "zip-audio-to-assets.gradle"
+apply from: "dependencyGuard.gradle"
+
+def hookConfigByLocalProperties(String localKey, String defaultValue) {
+    String config = readLocalProperties(localKey)
+    if (config != null) {
+        System.out.println("hookConfigByLocalProperties($localKey) ==> $config")
+        return config
+    }
+    return defaultValue
+}
+
+def readLocalProperties(String key) {
+    File file = rootProject.file('local.properties')
+    if (file.exists()) {
+        //加载资源
+        InputStream inputStream = rootProject.file('local.properties').newDataInputStream();
+        Properties properties = new Properties()
+        properties.load(inputStream)
+        if (properties.containsKey(key)) {
+            return properties.getProperty(key)
+        }
+    }
+    return null
+}
+
+android {
+    namespace 'com.adealink.weparty'
+    compileSdk libs.versions.compileSdk.get().toInteger()
+
+    defaultConfig {
+        applicationId "com.partyjoy.yoki"
+        minSdk libs.versions.minSdk.get().toInteger()
+        targetSdk libs.versions.targetSdk.get().toInteger()
+        multiDexEnabled true
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+//        resConfigs "zh", "zh_TW", "en", "ar", "hi", "tr", "th", "bn", "vi", "ur", "id", "ms", "tl", "pt", "es", "ru", "kk", "ky", "tk", "tg", "uz"
+        resConfigs "zh", "en", "ar"
+
+        ndk {
+            abiFilters "armeabi-v7a"
+            abiFilters "arm64-v8a"
+            debugSymbolLevel 'FULL'
+        }
+        //指定room.schemaLocation生成的文件路径  处理Room 警告 Schema export Error
+        javaCompileOptions {
+            annotationProcessorOptions {
+                arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
+            }
+        }
+
+
+        def jvc = project.hasProperty('JKS_VERSION_CODE') ? project.JKS_VERSION_CODE : ""
+        def jvn = project.hasProperty('JKS_VERSION_NAME') ? project.JKS_VERSION_NAME : ""
+        def localVC = hookConfigByLocalProperties("VERSION_CODE", VERSION_CODE)
+        def localVN = hookConfigByLocalProperties("VERSION_NAME", VERSION_NAME)
+        def vc = jvc != "" ? project.JKS_VERSION_CODE.toInteger() : localVC.toInteger()
+        def vn = jvn != "" ? project.JKS_VERSION_NAME : localVN
+        println("version_code = " + vc)
+        println("version_name = " + vn)
+
+        versionCode vc
+        versionName vn
+
+        manifestPlaceholders = [
+                fbAppId         : "623244786994791",
+                fbClientToken   : "d4f871dc66d01eb357146318d6b63a09",
+                deepLinkScheme  : "partyki",
+                deepLinkHost    : "yoki",
+                httpDeepLinkHost: "yoki.chat"
+        ]
+        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro', (isOfficial ? 'proguard-log-empty.pro' : 'proguard-log.pro')
+
+        buildConfigField("boolean", "OFFICIAL", project.OFFICIAL)
+        buildConfigField("boolean", "IS_RELEASE", hookConfigByLocalProperties("IS_RELEASE", IS_RELEASE))
+        buildConfigField("String", "HTTPS_WEB_HOST", '"https://web.yoki.chat"')
+        buildConfigField("String", "AUTH_APPLE_PATH", '"/web/yoki-auth/apple"')
+        buildConfigField("String", "AGORA_APP_ID", '"a5233b5e42534b77bbabf2ca8ac95215"')
+        buildConfigField("String", "QTT_APP_KEY", '"baa9f1307e2e8ee02742d960828b5c50"')
+        buildConfigField("String", "HTTP_DEEP_LINK_HOST", '"yoki.chat"')
+        buildConfigField("String", "DEEP_LINK_SCHEME", '"partyki"')
+        buildConfigField("String", "DEEP_LINK_HOST", '"yoki"')
+        buildConfigField("String", "UTM_FACEBOOK_KEY", '"c4ea5534613d8b3cdcd5b80097989f9eb2414138aa8e49ddfbc458cd5e50f1a6"')
+
+        buildConfigField("String", "TIKTOK_CLIENT_KEY", '"aw8fs7e6lnjmfipk"')
+        buildConfigField("String", "TIKTOK_REDIRECT_URL", '"https://yoki.chat/applink/login"')
+
+        //trtc
+        buildConfigField("Integer", "TRTC_APP_ID", "1721001739")
+        buildConfigField("String", "TRTC_SECRET_KEY", '"ea5b233540129b2700b059d4fbb0c55a21d8e8bd69599b1ee9ccc9ed3b9142bb"')
+        //trtc debug
+        buildConfigField("Integer", "TRTC_APP_ID_DEBUG", "1600073604")
+        buildConfigField("String", "TRTC_SECRET_KEY_DEBUG", '"f7c992cc3b8fc0273716da5ad59745cf188594c695c9191806d80532914f2aa1"')
+
+    }
+
+    signingConfigs {
+        debug {
+            keyAlias "yoki-keystore-debug"
+            keyPassword "yoki654321"
+            storePassword "yoki654321"
+            storeFile file('../keystore/debug/debug.keystore')
+        }
+        release {
+            keyAlias "yoki-keystore"
+            keyPassword "yoki654321"
+            storePassword "yoki654321"
+            storeFile file("../keystore/release/release-sign.keystore")
+        }
+    }
+
+
+    buildTypes {
+        debug {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+            signingConfig signingConfigs.debug
+            firebaseCrashlytics {
+                mappingFileUploadEnabled false
+            }
+        }
+        release {
+            debuggable false
+            minifyEnabled true
+            signingConfig signingConfigs.release
+            firebaseCrashlytics {
+                mappingFileUploadEnabled !isOfficial
+                nativeSymbolUploadEnabled !isOfficial
+//                strippedNativeLibsDir 'build/intermediates/stripped_native_libs/release/out/lib'
+//                unstrippedNativeLibsDir 'build/intermediates/merged_native_libs/release/out/lib'
+            }
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_17
+        targetCompatibility JavaVersion.VERSION_17
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_17
+        targetCompatibility JavaVersion.VERSION_17
+    }
+    kotlinOptions {
+        jvmTarget = JavaVersion.VERSION_17.majorVersion
+    }
+    bundle {
+        language {
+            enableSplit = false
+        }
+        density {
+            enableSplit = false
+        }
+        abi {
+            enableSplit = true
+        }
+    }
+    sourceSets {
+        main {
+            jniLibs {
+                srcDir 'libs'
+            }
+        }
+    }
+    packagingOptions {
+        exclude '**/*.kotlin_*'
+        pickFirst('lib/armeabi-v7a/libc++_shared.so')
+        pickFirst('lib/arm64-v8a/libc++_shared.so')
+        pickFirst('lib/x86/libc++_shared.so')
+        pickFirst('lib/x86_64/libc++_shared.so')
+    }
+    dynamicFeatures = [':module:account',
+                       ':module:room',
+                       ':module:follow',
+                       ':module:profile',
+                       ':module:setting',
+                       ':module:gift',
+                       ':module:wallet',
+                       ':module:webview',
+                       ':module:emotion',
+                       ':module:message',
+                       ':module:rank',
+                       ':module:game',
+                       ':module:music',
+                       ':module:theme',
+                       ':module:level',
+                       ':module:operation',
+                       ':module:entereffect',
+                       ':module:store',
+                       ':module:headline',
+                       ':module:search',
+                       ':module:share',
+                       ':module:image',
+                       ':module:call',
+                       ':module:anchor',
+                       ':module:party',
+                       ':module:excel',
+                       ':module:email',
+                       ':module:superadmin',
+                       ':module:backpack',
+                       ':module:pk',
+                       ':module:skin',
+                       ':module:couple',
+                       ':module:family',
+                       ':module:attribution',
+                       ':module:userprotect',
+                       ':module:medal',
+                       ':module:micgrab',
+                       ':module:moment',
+                       ':module:youtube',
+                       ':module:visitors',
+                       ':module:youtube',
+                       ':module:gamehub:uno',
+                       ':module:gamehub:carrom',
+                       ':module:gamehub:domino',
+                       ':module:gamehub:ludo',
+                       ':module:certification',
+    ]
+    buildFeatures {
+        viewBinding true
+        buildConfig true
+    }
+    configurations.configureEach {
+        exclude group: 'com.facebook.fresco', module: 'drawee'
+        exclude group: 'com.facebook.fresco', module: 'ui-core'
+    }
+
+
+    android.applicationVariants.all { variant ->
+        def variantNameCap = variant.name.capitalize()
+        def taskName = "sign${variantNameCap}Bundle"
+
+        def variantName = variant.name
+        def flavorName = variant.flavorName
+        def buildTypeName = variant.buildType.name
+
+        tasks.named(taskName).configure { task ->
+            def file = task.finalBundleFile.get().asFile
+            def projectName = rootProject.name
+            def versionCode = variant.versionCode
+            def versionName = variant.versionName
+            def timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date())
+
+            // 构造文件名:projectName-flavor-buildType-versionCode-versionName-timestamp.aab
+            def nameParts = []
+            nameParts << "app"
+            nameParts << projectName
+
+            if (flavorName) {
+                nameParts << flavorName
+            }
+
+            nameParts << buildTypeName
+            nameParts << "v${versionName}"
+            nameParts << "(${versionCode})"
+            nameParts << timestamp
+
+            def finalFileName = "${nameParts.join('-')}.aab"
+
+            def finalFile = new File(file.parentFile, finalFileName)
+            task.finalBundleFile.set(finalFile)
+        }
+    }
+
+}
+
+gradle.taskGraph.addTaskExecutionListener(new TaskExecutionListener() {
+    @Override
+    void beforeExecute(Task task) {
+
+    }
+
+    @Override
+    void afterExecute(Task task, TaskState taskState) {
+        if (task.name.equalsIgnoreCase("bundleDebug")
+                || task.name.equalsIgnoreCase("bundleRelease")) {
+
+            copy {
+                from 'build/intermediates/intermediary_bundle/'
+                into 'build/outputs/bundle/'
+                duplicatesStrategy DuplicatesStrategy.FAIL
+            }
+
+            println("afterExecute " + task.name)
+            def aabDir = project.getBuildDir().absolutePath + '/outputs/bundle'
+            fileTree(dir: aabDir, include: '*/*.aab').each { File file ->
+                println(file.absolutePath)
+                def cmd = []
+                cmd << "java"
+                cmd << "-jar"
+                cmd << "${project.rootDir}/tool/bundletool.jar"
+                cmd << "build-apks"
+                cmd << "--bundle=" + file.absolutePath
+                cmd << "--output=" + file.parent + File.separator + file.name.replace(".aab", "-split.apks")
+                cmd << "--overwrite"
+                cmd << "--mode=universal"
+                if (task.name.equalsIgnoreCase("bundleDebug")) {
+                    cmd << "--ks=" + android.signingConfigs.debug.storeFile
+                    cmd << "--ks-pass=pass:" + android.signingConfigs.debug.storePassword
+                    cmd << "--ks-key-alias=" + android.signingConfigs.debug.keyAlias
+                    cmd << "--key-pass=pass:" + android.signingConfigs.debug.keyPassword
+                } else {
+                    def keystoreProperties = new Properties()
+                    keystoreProperties.load(new FileInputStream(rootProject.file('keystore.properties')))
+                    cmd << "--ks=" + keystoreProperties.storeFile
+                    cmd << "--ks-pass=pass:" + keystoreProperties.storePassword
+                    cmd << "--ks-key-alias=" + keystoreProperties.keyAlias
+                    cmd << "--key-pass=pass:" + keystoreProperties.keyPassword
+                }
+
+                exec {
+                    commandLine cmd
+                }
+            }
+            println("after build apks")
+        }
+    }
+})
+
+dependencies {
+    api fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
+    //kotlin
+    implementation libs.kotlin.stdlib
+
+    //androidx
+    api libs.androidx.core.ktx
+    api libs.androidx.appcompat
+    api libs.androidx.constraint.layout
+    api libs.androidx.activity
+    api libs.androidx.fragment.ktx
+    api libs.androidx.viewpager2
+    api libs.androidx.lifecycle.livedata.ktx
+    api libs.androidx.lifecycle.viewmodel.ktx
+    api libs.androidx.exifinterface
+    implementation libs.androidx.multidex
+    implementation libs.androidx.vectordrawable
+    implementation libs.androidx.work.manager
+    api libs.androidx.room.runtime
+    api libs.androidx.room.ktx
+    api libs.flexbox
+    kapt libs.androidx.room.compiler
+    implementation libs.androidx.camera.core
+    implementation libs.androidx.camera.camera2
+    implementation libs.androidx.camera.lifecycle
+    implementation libs.androidx.camera.video
+    implementation libs.androidx.camera.view
+    api libs.androidx.webkit
+
+
+    //android
+    implementation libs.android.material
+    implementation libs.android.install.referrer
+
+    //google play core
+    implementation libs.play.feature.delivery
+    implementation libs.play.feature.delivery.ktx
+    implementation libs.play.app.update
+    implementation libs.play.app.update.ktx
+
+    //facebook
+    implementation libs.facebook.android.sdk
+
+    //appsflyer
+    implementation libs.appsflyer
+
+    //java
+    implementation libs.rxjava
+
+    //channel
+    implementation libs.tencent.vasdolly.helper
+
+    //firebase
+    implementation platform(libs.firebase.bom)
+    implementation libs.firebase.analytics
+    implementation libs.firebase.crashlytics
+    implementation libs.firebase.crashlytics.ndk
+    implementation libs.firebase.dynamic.link
+    implementation libs.firebase.messaging
+
+    //gson
+    implementation libs.gson
+
+    //other
+    api libs.smart.refresh.layout.kernel
+    api libs.smart.refresh.header.material
+    api libs.smart.refresh.footer.classics
+
+    api libs.media3.exoplayer
+    api libs.media3.exoplayer.dash
+    api libs.media3.ui
+
+    // media3 v1.4.1 guava 单独设置, 待media3更新后移除
+    implementation libs.guava
+
+    api libs.video.processor
+    api libs.payermax
+    api libs.rxjava
+
+    //image
+    api libs.compressor
+
+    //tencent rtc
+    api libs.tencent.tui.core
+
+    //frame
+    api platform(libs.frame.bom)
+    api libs.frame.tcturing
+    api libs.frame.animplayer
+    api libs.frame.drawee
+    api libs.frame.image
+    api libs.frame.mvvm
+    api libs.frame.util
+    api libs.frame.zero
+    api libs.frame.base
+    api libs.frame.network
+    api libs.frame.retrofit
+    api libs.frame.oss
+    api libs.frame.download
+    api libs.frame.sound
+    api libs.frame.game
+    api libs.frame.autosize
+    api libs.frame.spi
+    api libs.frame.data
+    api libs.frame.aab
+    api libs.frame.coroutine
+    api libs.frame.storage
+    api libs.frame.log
+    api libs.frame.statistics
+    api libs.frame.googleservice
+    api libs.frame.deviceid
+    api libs.frame.security
+    api libs.frame.guide
+    api libs.frame.effect
+    api libs.frame.svga
+    api libs.frame.debug
+
+    api project(":frame:room")
+    api project(":frame:imkit")
+    api libs.frame.startup
+
+    api libs.frame.locale
+    api libs.frame.push
+    api libs.frame.media
+    api libs.frame.share
+    api libs.frame.dot
+    api libs.frame.crash
+    api libs.frame.apm
+    api libs.frame.audio
+    api libs.frame.tceffect
+    api libs.frame.router.annotation
+    api libs.frame.router.api
+    kapt libs.frame.router.compiler
+
+    implementation libs.toolargetool
+
+    implementation(libs.rangeseekbar)
+
+//    debugApi "com.tuzhenlei:crashhandler:1.0.1"
+//    debugApi 'cat.ereza:customactivityoncrash:2.3.0'
+//    debugImplementation "io.github.yvescheung:Uinspector:2.0.20"
+//    debugImplementation "io.github.yvescheung:Uinspector-optional-viewmodel:2.0.20"
+//    debugImplementation "io.github.yvescheung:Uinspector-optional-fresco:2.0.20"
+//    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10'
+
+    //test
+    testImplementation libs.junit
+    androidTestImplementation libs.androidx.junit
+    androidTestImplementation libs.androidx.espresso.core
+
+    if (isEnableTrace){
+        implementation (libs.frame.trace.api)
+    }
+
+    api libs.flexbox
+
+    implementation 'io.github.flyjingfish:androidaop-core:2.7.0'
+    ksp 'io.github.flyjingfish:androidaop-apt:2.7.0'
+}
+
+
+dependencyGuard {
+    configuration("releaseRuntimeClasspath")
+}

+ 298 - 0
app/dependencies/releaseRuntimeClasspath.txt

@@ -0,0 +1,298 @@
+androidx.activity:activity-ktx:1.9.1
+androidx.activity:activity:1.9.1
+androidx.annotation:annotation-experimental:1.4.1
+androidx.annotation:annotation-jvm:1.8.1
+androidx.annotation:annotation:1.8.1
+androidx.appcompat:appcompat-resources:1.7.0
+androidx.appcompat:appcompat:1.7.0
+androidx.arch.core:core-common:2.2.0
+androidx.arch.core:core-runtime:2.2.0
+androidx.asynclayoutinflater:asynclayoutinflater:1.0.0
+androidx.browser:browser:1.0.0
+androidx.camera:camera-camera2:1.4.2
+androidx.camera:camera-core:1.4.2
+androidx.camera:camera-lifecycle:1.4.2
+androidx.camera:camera-video:1.4.2
+androidx.camera:camera-view:1.4.2
+androidx.cardview:cardview:1.0.0
+androidx.collection:collection-ktx:1.1.0
+androidx.collection:collection:1.2.0
+androidx.concurrent:concurrent-futures-ktx:1.1.0
+androidx.concurrent:concurrent-futures:1.1.0
+androidx.constraintlayout:constraintlayout-core:1.0.4
+androidx.constraintlayout:constraintlayout:2.1.4
+androidx.coordinatorlayout:coordinatorlayout:1.1.0
+androidx.core:core-ktx:1.13.1
+androidx.core:core:1.13.1
+androidx.cursoradapter:cursoradapter:1.0.0
+androidx.customview:customview-poolingcontainer:1.0.0
+androidx.customview:customview:1.1.0
+androidx.databinding:viewbinding:8.6.1
+androidx.datastore:datastore-core:1.0.0
+androidx.datastore:datastore-preferences-core:1.0.0
+androidx.datastore:datastore-preferences:1.0.0
+androidx.datastore:datastore:1.0.0
+androidx.documentfile:documentfile:1.0.0
+androidx.drawerlayout:drawerlayout:1.1.1
+androidx.dynamicanimation:dynamicanimation:1.0.0
+androidx.emoji2:emoji2-views-helper:1.3.0
+androidx.emoji2:emoji2:1.3.0
+androidx.exifinterface:exifinterface:1.3.7
+androidx.fragment:fragment-ktx:1.8.2
+androidx.fragment:fragment:1.8.2
+androidx.interpolator:interpolator:1.0.0
+androidx.legacy:legacy-support-core-ui:1.0.0
+androidx.legacy:legacy-support-core-utils:1.0.0
+androidx.legacy:legacy-support-v4:1.0.0
+androidx.lifecycle:lifecycle-common-java8:2.8.4
+androidx.lifecycle:lifecycle-common-jvm:2.8.4
+androidx.lifecycle:lifecycle-common:2.8.4
+androidx.lifecycle:lifecycle-livedata-core-ktx:2.8.4
+androidx.lifecycle:lifecycle-livedata-core:2.8.4
+androidx.lifecycle:lifecycle-livedata-ktx:2.8.4
+androidx.lifecycle:lifecycle-livedata:2.8.4
+androidx.lifecycle:lifecycle-process:2.8.4
+androidx.lifecycle:lifecycle-runtime-android:2.8.4
+androidx.lifecycle:lifecycle-runtime-ktx-android:2.8.4
+androidx.lifecycle:lifecycle-runtime-ktx:2.8.4
+androidx.lifecycle:lifecycle-runtime:2.8.4
+androidx.lifecycle:lifecycle-service:2.8.4
+androidx.lifecycle:lifecycle-viewmodel-android:2.8.4
+androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.4
+androidx.lifecycle:lifecycle-viewmodel-savedstate:2.8.4
+androidx.lifecycle:lifecycle-viewmodel:2.8.4
+androidx.loader:loader:1.0.0
+androidx.localbroadcastmanager:localbroadcastmanager:1.0.0
+androidx.media3:media3-common:1.4.1
+androidx.media3:media3-container:1.4.1
+androidx.media3:media3-database:1.4.1
+androidx.media3:media3-datasource:1.4.1
+androidx.media3:media3-decoder:1.4.1
+androidx.media3:media3-exoplayer-dash:1.4.1
+androidx.media3:media3-exoplayer:1.4.1
+androidx.media3:media3-extractor:1.4.1
+androidx.media3:media3-ui:1.4.1
+androidx.media:media:1.7.0
+androidx.multidex:multidex:2.0.1
+androidx.print:print:1.0.0
+androidx.privacysandbox.ads:ads-adservices-java:1.0.0-beta05
+androidx.privacysandbox.ads:ads-adservices:1.0.0-beta05
+androidx.profileinstaller:profileinstaller:1.3.1
+androidx.recyclerview:recyclerview:1.3.2
+androidx.resourceinspection:resourceinspection-annotation:1.0.1
+androidx.room:room-common:2.6.1
+androidx.room:room-ktx:2.6.1
+androidx.room:room-runtime:2.6.1
+androidx.savedstate:savedstate-ktx:1.2.1
+androidx.savedstate:savedstate:1.2.1
+androidx.slidingpanelayout:slidingpanelayout:1.0.0
+androidx.sqlite:sqlite-framework:2.4.0
+androidx.sqlite:sqlite:2.4.0
+androidx.startup:startup-runtime:1.1.1
+androidx.swiperefreshlayout:swiperefreshlayout:1.0.0
+androidx.tracing:tracing-ktx:1.2.0
+androidx.tracing:tracing:1.2.0
+androidx.transition:transition:1.5.0
+androidx.vectordrawable:vectordrawable-animated:1.1.0
+androidx.vectordrawable:vectordrawable:1.2.0
+androidx.versionedparcelable:versionedparcelable:1.1.1
+androidx.viewpager2:viewpager2:1.1.0
+androidx.viewpager:viewpager:1.0.0
+androidx.webkit:webkit:1.14.0
+androidx.work:work-runtime-ktx:2.9.0
+androidx.work:work-runtime:2.9.0
+cn.rongcloud.sdk:databuried_annotation:0.2.1
+cn.rongcloud.sdk:im_chatroom:5.24.0
+cn.rongcloud.sdk:im_customservice:5.24.0
+cn.rongcloud.sdk:im_discussion:5.24.0
+cn.rongcloud.sdk:im_lib:5.24.0
+cn.rongcloud.sdk:im_libcore:5.24.0
+cn.rongcloud.sdk:im_location:5.24.0
+cn.rongcloud.sdk:im_publicservice:5.24.0
+com.aliyun.dpa:oss-android-sdk:2.9.5
+com.android.installreferrer:installreferrer:2.2
+com.appsflyer:af-android-sdk:6.15.2
+com.blankj:utilcodex:1.31.1
+com.facebook.android:facebook-android-sdk:17.0.0
+com.facebook.android:facebook-applinks:17.0.0
+com.facebook.android:facebook-bolts:17.0.0
+com.facebook.android:facebook-common:17.0.0
+com.facebook.android:facebook-core:17.0.0
+com.facebook.android:facebook-gamingservices:17.0.0
+com.facebook.android:facebook-login:17.0.0
+com.facebook.android:facebook-messenger:17.0.0
+com.facebook.android:facebook-share:17.0.0
+com.facebook.fresco:animated-base:3.6.0
+com.facebook.fresco:animated-drawable:3.6.0
+com.facebook.fresco:animated-gif:3.6.0
+com.facebook.fresco:animated-webp:3.6.0
+com.facebook.fresco:fbcore:3.6.0
+com.facebook.fresco:fresco:3.6.0
+com.facebook.fresco:imagepipeline-base:3.6.0
+com.facebook.fresco:imagepipeline-native:3.6.0
+com.facebook.fresco:imagepipeline:3.6.0
+com.facebook.fresco:memory-type-ashmem:3.6.0
+com.facebook.fresco:memory-type-java:3.6.0
+com.facebook.fresco:memory-type-native:3.6.0
+com.facebook.fresco:middleware:3.6.0
+com.facebook.fresco:nativeimagefilters:3.6.0
+com.facebook.fresco:nativeimagetranscoder:3.6.0
+com.facebook.fresco:soloader:3.6.0
+com.facebook.fresco:ui-common:3.6.0
+com.facebook.fresco:urimod:3.6.0
+com.facebook.fresco:vito-options:3.6.0
+com.facebook.fresco:vito-renderer:3.6.0
+com.facebook.fresco:vito-source:3.6.0
+com.facebook.fresco:webpsupport:3.6.0
+com.facebook.infer.annotation:infer-annotation:0.18.0
+com.facebook.soloader:annotation:0.11.0
+com.facebook.soloader:nativeloader:0.11.0
+com.facebook.soloader:soloader:0.11.0
+com.getkeepsafe.relinker:relinker:1.4.4
+com.github.Jay-Goo:RangeSeekBar:v2.0.4
+com.github.yellowcath:VideoProcessor:2.4.2
+com.google.android.datatransport:transport-api:3.2.0
+com.google.android.datatransport:transport-backend-cct:3.3.0
+com.google.android.datatransport:transport-runtime:3.3.0
+com.google.android.flexbox:flexbox:3.0.0
+com.google.android.gms:play-services-ads-identifier:18.0.0
+com.google.android.gms:play-services-base:18.5.0
+com.google.android.gms:play-services-basement:18.4.0
+com.google.android.gms:play-services-cloud-messaging:17.2.0
+com.google.android.gms:play-services-location:21.3.0
+com.google.android.gms:play-services-measurement-api:22.0.2
+com.google.android.gms:play-services-measurement-base:22.0.2
+com.google.android.gms:play-services-measurement-impl:22.0.2
+com.google.android.gms:play-services-measurement-sdk-api:22.0.2
+com.google.android.gms:play-services-measurement-sdk:22.0.2
+com.google.android.gms:play-services-measurement:22.0.2
+com.google.android.gms:play-services-stats:17.0.2
+com.google.android.gms:play-services-tasks:18.2.0
+com.google.android.material:material:1.12.0
+com.google.android.play:app-update-ktx:2.1.0
+com.google.android.play:app-update:2.1.0
+com.google.android.play:core-common:2.0.3
+com.google.android.play:feature-delivery-ktx:2.1.0
+com.google.android.play:feature-delivery:2.1.0
+com.google.auto.service:auto-service-annotations:1.1.1
+com.google.auto.value:auto-value-annotations:1.6.3
+com.google.code.findbugs:jsr305:3.0.2
+com.google.code.gson:gson:2.8.8
+com.google.errorprone:error_prone_annotations:2.28.0
+com.google.firebase:firebase-analytics:22.0.2
+com.google.firebase:firebase-annotations:16.2.0
+com.google.firebase:firebase-auth-interop:20.0.0
+com.google.firebase:firebase-bom:33.1.2
+com.google.firebase:firebase-common-ktx:21.0.0
+com.google.firebase:firebase-common:21.0.0
+com.google.firebase:firebase-components:18.0.0
+com.google.firebase:firebase-config-interop:16.0.1
+com.google.firebase:firebase-crashlytics-ndk:19.0.3
+com.google.firebase:firebase-crashlytics:19.0.3
+com.google.firebase:firebase-datatransport:19.0.0
+com.google.firebase:firebase-dynamic-links:22.1.0
+com.google.firebase:firebase-encoders-json:18.0.1
+com.google.firebase:firebase-encoders-proto:16.0.0
+com.google.firebase:firebase-encoders:17.0.0
+com.google.firebase:firebase-iid-interop:17.1.0
+com.google.firebase:firebase-installations-interop:17.2.0
+com.google.firebase:firebase-installations:18.0.0
+com.google.firebase:firebase-measurement-connector:20.0.1
+com.google.firebase:firebase-messaging:24.0.0
+com.google.firebase:firebase-sessions:2.0.3
+com.google.guava:failureaccess:1.0.2
+com.google.guava:guava:33.3.1-android
+com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava
+com.google.j2objc:j2objc-annotations:3.0.0
+com.google.zxing:core:3.3.3
+com.googlecode.libphonenumber:libphonenumber:8.13.47
+com.gu.android:toolargetool:0.3.0
+com.parse.bolts:bolts-tasks:1.4.0
+com.payermax:payment-easy:1.0.08
+com.qcloud.cos:cos-android-lite-nobeacon:5.9.6
+com.qcloud.cos:qcloud-foundation:1.5.49
+com.squareup.okhttp3:okhttp:4.12.0
+com.squareup.okio:okio-jvm:3.6.0
+com.squareup.okio:okio:3.6.0
+com.squareup.wire:wire-runtime:2.3.0-RC1
+com.tencent.imsdk:imsdk-plus:8.4.6667
+com.tencent.liteav.tuikit:tuicore:8.4.6667
+com.tencent.liteav:LiteAVSDK_Professional:12.3.0.17115
+com.tencent.liteav:LiteAVSDK_ScreenCapture:12.3.0.17115
+com.tencent.mediacloud:TCEffectPlayer:3.1.0.247
+com.tencent.mediacloud:TCMediaX:3.1.0.247
+com.tencent.mediacloud:TCXMagicAuth:3.1.0.247
+com.tencent.vasdolly:common:3.0.6
+com.tencent.vasdolly:helper:3.0.6
+com.tencent.vasdolly:reader:3.0.6
+com.tencent:mmkv:1.3.14
+com.wenext.android:SVGAPlayer:5.1.7-yoki-beta
+com.wenext.android:animplayer:5.1.4
+com.wenext.android:autosize:5.1.4
+com.wenext.android:drawee:5.1.5
+com.wenext.android:frame-aab:5.1.8-yoki-beta
+com.wenext.android:frame-apm:5.1.5-yoki-beta
+com.wenext.android:frame-audio:5.1.4
+com.wenext.android:frame-base:5.1.4
+com.wenext.android:frame-bom:5.1.29-yoki
+com.wenext.android:frame-coroutine:5.1.4
+com.wenext.android:frame-crash:5.1.5-yoki-beta
+com.wenext.android:frame-data:5.1.5-yoki
+com.wenext.android:frame-debug:5.1.4
+com.wenext.android:frame-deviceid:5.1.4
+com.wenext.android:frame-dot:5.1.10-yoki-beta
+com.wenext.android:frame-download:5.1.7-yoki-beta
+com.wenext.android:frame-effect:5.1.6
+com.wenext.android:frame-game:5.1.5-yoki
+com.wenext.android:frame-googleservice:5.1.4
+com.wenext.android:frame-guide:5.1.19-yoki
+com.wenext.android:frame-image:5.1.8-yoki-beta
+com.wenext.android:frame-locale:5.1.8-yoki
+com.wenext.android:frame-log:5.1.6-yoki
+com.wenext.android:frame-media:5.1.10-yoki-beta
+com.wenext.android:frame-mvvm:5.1.5-beta
+com.wenext.android:frame-network:5.1.9-yoki
+com.wenext.android:frame-oss:5.1.7-yoki-1
+com.wenext.android:frame-push:5.1.4
+com.wenext.android:frame-router-annotation:5.1.4
+com.wenext.android:frame-router-api:5.1.5
+com.wenext.android:frame-security:5.1.5
+com.wenext.android:frame-share:5.1.18-yoki
+com.wenext.android:frame-sound:5.1.4
+com.wenext.android:frame-spi:5.1.4
+com.wenext.android:frame-startup:5.1.1-yoki
+com.wenext.android:frame-statistics:5.1.7
+com.wenext.android:frame-storage:5.1.7-yoki
+com.wenext.android:frame-tceffect:5.1.8-yoki-beta
+com.wenext.android:frame-tcturing:5.1.5-yoki-beta
+com.wenext.android:frame-util:5.1.6-yoki-beta
+com.wenext.android:frame-zero:5.1.4
+com.wenext.android:retrofit:5.1.4
+id.zelory:compressor:3.0.1
+io.agora.rtc:agora-special-voice:3.7.3.5.XYHZ
+io.github.flyjingfish:androidaop-annotation:2.7.0
+io.github.flyjingfish:androidaop-core:2.7.0
+io.github.scwang90:refresh-drawable-paint:3.0.0-alpha
+io.github.scwang90:refresh-footer-classics:3.0.0-alpha
+io.github.scwang90:refresh-header-material:3.0.0-alpha
+io.github.scwang90:refresh-layout-kernel:3.0.0-alpha
+io.reactivex.rxjava3:rxjava:3.0.4
+javax.inject:javax.inject:1
+org.checkerframework:checker-qual:3.43.0
+org.conscrypt:conscrypt-android:2.5.3
+org.jetbrains.kotlin:kotlin-android-extensions-runtime:1.9.0
+org.jetbrains.kotlin:kotlin-annotations-jvm:1.3.72
+org.jetbrains.kotlin:kotlin-bom:1.8.22
+org.jetbrains.kotlin:kotlin-parcelize-runtime:1.9.0
+org.jetbrains.kotlin:kotlin-stdlib-common:2.0.0
+org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.0
+org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.9.0
+org.jetbrains.kotlin:kotlin-stdlib:2.0.0
+org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3
+org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3
+org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3
+org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3
+org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3
+org.jetbrains:annotations:23.0.0
+org.jspecify:jspecify:1.0.0
+org.reactivestreams:reactive-streams:1.0.3

+ 4 - 0
app/dependencyGuard.gradle

@@ -0,0 +1,4 @@
+//编译时自动运行 dependencyGuard 任务,监控依赖项变化
+tasks.named("preBuild") {
+    dependsOn("dependencyGuardBaseline")
+}

BIN
app/libs/R_TuringSDK_v90_c0_lcD97A2DB9093760EF_release_20250220154612_pri_mini_nolog.aar


BIN
app/libs/wenext_jni-release.aar


BIN
app/libs/xcrash_lib-release.aar


+ 0 - 0
app/proguard-log-empty.pro


+ 176 - 0
app/proguard-log.gradle

@@ -0,0 +1,176 @@
+// generate proguard config for log
+class LogProguardRuleTask extends DefaultTask {
+
+    @Input
+    String projectName
+
+    @Input
+    String[] proguardClassList
+
+    @Input
+    String currentLogLevel
+
+    @OutputFile
+    File logProguardFile
+
+    private ArrayList<String> removeLogLevels
+    private HashMap<String, String> allowedLogLevels
+
+    LogProguardRuleTask() {
+        removeLogLevels = new ArrayList<>()
+        allowedLogLevels = [VERBOSE: "v",
+                            DEBUG  : "d",
+                            INFO   : "i",
+                            WARN   : "w",
+                            ERROR  : "e"]
+    }
+
+    private void updateProguardFile() {
+        File file = logProguardFile
+        if (file.exists()) {
+            file.delete()
+        }
+
+        file.createNewFile()
+        file.append("\n" + genProguardRules())
+    }
+
+    private String genProguardRules() {
+        def rules = ""
+        for (String logClass : proguardClassList) {
+            def rule = genLogRules(logClass, removeLogLevels)
+            logger.lifecycle(rule)
+            rules += rule
+        }
+        return rules
+    }
+
+    private String genLogRules(clazz, levels) {
+        def sb = new StringBuilder()
+        sb.append(String.format("-assumenosideeffects class %s{\n", clazz))
+        for (String level : levels) {
+            sb.append(String.format("   public static *** %s(...);\n", level))
+        }
+        sb.append("}\n")
+        return sb.toString()
+    }
+
+    @TaskAction
+    void doTaskAction() {
+        println("proguard.gradle, LogProguardRuleTask, doTaskAction()")
+        logger.debug("proguard.gradle, LogProguardRuleTask, doTaskAction()")
+
+        if (project.name != projectName) {
+            return
+        }
+
+        if (!project.hasProperty("LOG_LEVEL")) {
+            return
+        }
+
+        logger.debug("gradle param log level: ${currentLogLevel}")
+
+        for (def i = 0; i < allowedLogLevels.size(); i++) {
+            if (allowedLogLevels.keySet()[i] != currentLogLevel.toUpperCase()) {
+                removeLogLevels.add(allowedLogLevels.values()[i])
+            } else {
+                break
+            }
+        }
+
+        logger.debug("proguard log level: " + removeLogLevels)
+
+        updateProguardFile()
+    }
+
+}
+
+def genLogProguardFileTask = tasks.register("genLogProguardFile", LogProguardRuleTask) {
+    projectName = "app"
+    proguardClassList = ["android.util.Log",
+                         "com.adealink.frame.log.Log"]
+
+    currentLogLevel = System.getenv("LOG_LEVEL")
+    if (currentLogLevel == null || currentLogLevel == "") {
+        currentLogLevel = project.hasProperty("LOG_LEVEL") ? project.property("LOG_LEVEL") : "NONE"
+    }
+
+    logProguardFile = new File(project.buildFile.getParent() + File.separator + "proguard-log.pro")
+}
+
+// remove low level log before compile, since proguard can't remove the concat log clearly
+class RemoveConcatLogTask extends DefaultTask {
+
+    @Input
+    String projectName
+
+    @Input
+    String[] proguardClassList
+
+    @Input
+    String currentLogLevel
+
+    private HashMap<String, String> allowedLogLevels
+
+    RemoveConcatLogTask() {
+        allowedLogLevels = [VERBOSE: "v",
+                            DEBUG  : "d",
+                            INFO   : "i",
+                            WARN   : "w",
+                            ERROR  : "e"]
+    }
+
+    @TaskAction
+    void doTaskAction() {
+        if (project.name != projectName) {
+            return
+        }
+
+        def paramLevel = ""
+        for (int i = 0; i < allowedLogLevels.size(); i++) {
+            if (allowedLogLevels.keySet()[i] != currentLogLevel.toUpperCase()) {
+                paramLevel += allowedLogLevels.values()[i] + " "
+            } else {
+                break
+            }
+        }
+
+        def paramClass = ""
+        for (def logClass : proguardClassList) {
+            paramClass += logClass.split('\\.')[-1] + " "
+        }
+
+        if (paramClass.length() > 0 && paramLevel.length() > 0) {
+            println("proguard.gradle, RemoveConcatLogTask, doTaskAction, python proguard-log paramClass:" + paramClass + " paramLevel:" + paramLevel)
+            project.exec {
+                workingDir "."
+                commandLine "python3", "proguard-log.py", "-c " + paramClass, "-l " + paramLevel
+            }
+        }
+    }
+}
+
+def emptyConcatLogStringTask = tasks.register("emptyConcatLogString", RemoveConcatLogTask) {
+    projectName = "app"
+
+    proguardClassList = ["android.util.Log",
+                         "com.adealink.frame.log.Log"]
+
+    currentLogLevel = System.getenv("LOG_LEVEL")
+    if (currentLogLevel == null || currentLogLevel == "") {
+        currentLogLevel = project.hasProperty("LOG_LEVEL") ? project.property("LOG_LEVEL") : "NONE"
+    }
+}
+
+android.applicationVariants.configureEach {
+    if (it.buildType.name != "debug") {
+        it.preBuildProvider.configure {
+            dependsOn emptyConcatLogStringTask
+        }
+    }
+}
+
+tasks.named("preBuild").configure {
+    dependsOn genLogProguardFileTask
+}
+

+ 9 - 0
app/proguard-log.pro

@@ -0,0 +1,9 @@
+
+-assumenosideeffects class android.util.Log{
+   public static *** v(...);
+   public static *** d(...);
+}
+-assumenosideeffects class com.adealink.frame.log.Log{
+   public static *** v(...);
+   public static *** d(...);
+}

+ 103 - 0
app/proguard-log.py

@@ -0,0 +1,103 @@
+import argparse
+import os
+import re
+
+log_class = []
+log_level = []
+log_pattern = []
+
+
+def scan_dir(dir_path):
+    file_list = os.listdir(dir_path)
+    for filename in file_list:
+        file_path = os.path.join(dir_path, filename)
+        if os.path.isfile(file_path) and (filename.endswith('.java') or filename.endswith('.kt')):
+            process_file(file_path)
+        elif os.path.isdir(file_path):
+            scan_dir(file_path)
+
+
+def process_file(file_path):
+    new_lines = []
+    record_lines = []
+    stack = []
+
+    # 使用 utf-8 进行文件读取,以防编码问题
+    with open(file_path, 'r+', encoding='utf-8') as file:
+        hiting = False
+        match = None
+
+        for line in file.readlines():
+
+            if hiting is False:
+                match = re.match(
+                    r'\s+(%s)\s*\.\s*(%s)\s*\(' % ('|'.join(log_class), '|'.join(log_level)), line)
+                if match is not None:
+                    hiting = True
+                else:
+                    new_lines.append(line)
+                    continue
+
+            record_lines.append(' - ' + line)
+
+            for letter in line.strip():
+                if len(stack) == 0:
+                    if letter == '(':
+                        stack.append(letter)
+                elif stack[-1] == '"':
+                    if letter == '"':
+                        stack.pop()
+                elif stack[-1] == '(':
+                    if letter == '"':
+                        stack.append(letter)
+                    elif letter == '(':
+                        stack.append(letter)
+                    elif letter == ')':
+                        stack.pop()
+
+                        if len(stack) == 0:
+                            replace = match.group()
+                            if file_path.endswith('.java'):
+                                replace += '"TAG", "");'
+                            elif file_path.endswith('.kt'):
+                                replace += '"TAG", "")'
+                            replace += '\n'
+
+                            # new_lines.append(replace)
+                            # record_lines.append(' + ' + replace + '\n')
+
+                            new_lines.append('\n')
+                            record_lines.append(' + \n')
+
+                            match = None
+                            hiting = False
+                            break
+
+        if len(record_lines) > 0:
+            record_file.write(
+                '--------------------------------------------------------------------\n')
+            record_file.write(file_path + '\n')
+            record_file.writelines(record_lines)
+
+            file.seek(0)
+            file.truncate()
+            file.writelines(new_lines)
+
+
+if __name__ == '__main__':
+    parser = argparse.ArgumentParser()
+    parser.add_argument('-c', '--log_class', type=str, help='log class', required=True)
+    parser.add_argument('-l', '--log_level', type=str, help='log level', required=True)
+    args = parser.parse_args()
+
+    log_class = args.log_class.split()
+    log_level = args.log_level.split()
+
+    print("remove class: " + str(log_class))
+    print("remove level: " + str(log_level))
+
+    with open('proguard_log.txt', 'w', encoding='utf-8') as record_file:
+        # 获取父目录,确保路径跨平台可用
+        cwd = os.path.abspath(os.path.join(os.getcwd(), os.pardir))
+        print("cwd: " + cwd)
+        scan_dir(cwd)

+ 413 - 0
app/proguard-rules.pro

@@ -0,0 +1,413 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
+
+-obfuscationdictionary s-proguard.txt
+-classobfuscationdictionary s-proguard.txt
+-packageobfuscationdictionary s-proguard.txt
+-dontusemixedcaseclassnames
+-dontskipnonpubliclibraryclasses
+-verbose
+-optimizationpasses 5
+-dontskipnonpubliclibraryclassmembers
+-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
+-ignorewarnings
+
+-keepattributes Exceptions
+-keepattributes InnerClasses
+-keepattributes *Annotation*
+-keepattributes JavascriptInterface
+-keepattributes Signature
+-keepattributes RuntimeVisibleAnnotations
+-keepattributes SourceFile
+-keepattributes LineNumberTable
+
+#native
+-keepclasseswithmembers class * {
+    native <methods>;
+}
+
+#context
+-keepclassmembers class * extends android.content.Context {
+   public void *(android.view.View);
+   public void *(android.view.MenuItem);
+}
+
+#Parcelable
+-keep class * implements android.os.Parcelable {
+  public static final android.os.Parcelable$Creator *;
+}
+
+-keepclassmembers class * implements android.os.Parcelable {
+  public static final android.os.Parcelable$Creator CREATOR;
+}
+
+-keepclassmembers class * implements android.os.Parcelable {
+    static ** CREATOR;
+    <fields>;
+    <methods>;
+}
+
+#Serializable
+-keep class * implements java.io.Serializable {
+    *;
+}
+
+-keepclassmembers class * implements java.io.Serializable {
+    static final long serialVersionUID;
+    private static final java.io.ObjectStreamField[] serialPersistentFields;
+    !static !transient <fields>;
+    private void writeObject(java.io.ObjectOutputStream);
+    private void readObject(java.io.ObjectInputStream);
+    java.lang.Object writeReplace();
+    java.lang.Object readResolve();
+}
+
+-keepclassmembers class * {
+    public <init>(org.json.JSONObject);
+}
+
+#View相关
+-keepclassmembers public class * extends android.view.View {
+   void set*(***);
+   *** get*();
+}
+
+-keep public class * extends android.view.View {
+      public <init>(android.content.Context, android.util.AttributeSet);
+      public <init>(android.content.Context, android.util.AttributeSet, int);
+      public <init>(android.content.Context, android.util.AttributeSet, int, int);
+      public void set*(...);
+}
+
+-keepclasseswithmembers class * {
+    public <init>(android.content.Context, android.util.AttributeSet);
+}
+
+-keepclasseswithmembers class * {
+    public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+-keepclassmembers class * extends android.app.Activity {
+   public void *(android.view.View);
+}
+
+#Enum
+-keepclassmembers enum * {
+    public static **[] values();
+    public static ** valueOf(java.lang.String);
+    public static ** valueOf(int);
+}
+
+#Resource
+-keepclassmembers class **.R$* {
+    public static <fields>;
+}
+
+-keep class **.R$* {*;}
+-keep class **.R{*;}
+-dontwarn **.R$*
+
+#keep
+-keep class androidx.annotation.Keep
+
+-keep @androidx.annotation.Keep class * {*;}
+
+-keepclasseswithmembers class * {
+    @androidx.annotation.Keep <methods>;
+}
+
+-keepclasseswithmembers class * {
+    @androidx.annotation.Keep <fields>;
+}
+
+-keepclasseswithmembers class * {
+    @androidx.annotation.Keep <init>(...);
+}
+
+#android component
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.preference.Preference
+-keep public class * extends android.app.backup.BackupAgentHelper
+
+#LOG
+#xlog
+-keep class com.tencent.mars.xlog.** {*;}
+
+-assumenosideeffects class android.util.Log {
+    public static *** println(...);
+    public static *** wtf(...);
+}
+
+-assumenosideeffects class * extends java.lang.Throwable {
+    public void printStackTrace();
+}
+
+#Rxjava@1.1.9
+-dontwarn sun.misc.**
+-dontwarn rx.**
+-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
+   long producerIndex;
+   long consumerIndex;
+}
+
+-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
+    rx.internal.util.atomic.LinkedQueueNode producerNode;
+}
+
+-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
+    rx.internal.util.atomic.LinkedQueueNode consumerNode;
+}
+
+#supportV4
+-keep public class android.support.v4.content.LocalBroadcastManager{*;}
+-dontwarn android.support.v4.**
+-keep public class * extends android.support.v4.app.** { *; }
+-keep public class * extends android.app.Fragment{ *; }
+-keep public class * extends androidx.fragment.app.Fragment { *; }
+-keep public class android.support.v4.app.**{ *; }
+-keep public class android.support.v4.media.**{ *; }
+-keep class androidx.annotation.** { *; }
+-dontwarn androidx.annotation.**
+
+#okhttp
+-dontwarn okhttp3.**
+-dontwarn okio.**
+-dontnote retrofit2.Platform
+
+# Gson specific classes
+-keep class sun.misc.Unsafe { *; }
+-keep class com.google.gson.stream.** { *; }
+-keep class com.google.gson.examples.android.model.** { *; }
+-keep class * implements com.google.gson.TypeAdapterFactory
+-keep class * implements com.google.gson.JsonSerializer
+-keep class * implements com.google.gson.JsonDeserializer
+-keepclassmembers,allowobfuscation class * {
+  @com.google.gson.annotations.SerializedName <fields>;
+}
+
+#gms
+-keep class * extends java.util.ListResourceBundle {
+    protected Object[][] getContents();
+}
+
+-keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable {
+    public static final *** NULL;
+}
+
+-keepnames @com.google.android.gms.common.annotation.KeepName class *
+-keepclassmembernames class * {
+    @com.google.android.gms.common.annotation.KeepName *;
+}
+
+-keepnames class * implements android.os.Parcelable {
+    public static final ** CREATOR;
+}
+
+#webkit
+-keepclassmembers class * extends android.webkit.WebChromeClient {
+    public void openFileChooser(...);
+    public void onShowFileChooser(...);
+}
+
+-keepclassmembers class * {
+    @android.webkit.JavascriptInterface <methods>;
+}
+
+#来电浮窗代码
+-keep interface com.android.internal.telephony.ITelephony {* ;}
+
+#google installreferrer
+-dontwarn com.appsflyer.**
+-dontwarn com.android.installreferrer
+-keep class com.appsflyer.** { *;}
+
+#AppFlyer
+-keep class com.appsflyer.** { *; }
+-keep class kotlin.jvm.internal.** { *; }
+
+# thinR
+-keepclassmembers class **.R$* {
+	 public static <fields>;
+}
+-keep class **.R {*;}
+-keep class **.R$* {*;}
+-keep class **.R$*
+-keep class **.R
+
+-keepclassmembers class **.R2$* {
+	 public static <fields>;
+}
+-keep class **.R2 {*;}
+-keep class **.R2$* {*;}
+-keep class **.R2$*
+-keep class **.R2
+
+-dontpreverify
+
+-keepclassmembers class * {
+    void *(**On*Event);
+}
+
+-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
+
+#router
+-keep public class * implements com.adealink.frame.router.IBinder
+-keep public class * implements com.adealink.frame.router.IInterceptor
+-keep public class * extends com.adealink.frame.router.handler.UriHandler
+-keep interface com.adealink.frame.router.IRouterInit
+-keep class * implements com.adealink.frame.router.IRouterInit
+
+#spi
+-keep class * implements com.adealink.frame.aab.IService
+
+# com.opensource.svgaplayer
+-keep class com.squareup.wire.** {*;}
+-keep class com.opensource.svgaplayer.proto.* {*;}
+
+#fresco
+-keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip
+-keep,allowobfuscation @interface com.facebook.soloader.DoNotOptimize
+-keep @com.facebook.common.internal.DoNotStrip class *
+-keepclassmembers class * {
+    @com.facebook.common.internal.DoNotStrip *;
+}
+-keep @com.facebook.soloader.DoNotOptimize class *
+-keepclassmembers class * {
+    @com.facebook.soloader.DoNotOptimize *;
+}
+-keepclassmembers class com.facebook.** {
+    native <methods>;
+}
+-keep public class com.facebook.soloader.SoLoader {
+    public static void init(android.content.Context, int);
+}
+-dontwarn okio.**
+-dontwarn com.squareup.okhttp.**
+-dontwarn okhttp3.**
+-dontwarn javax.annotation.**
+-dontwarn com.android.volley.toolbox.**
+-dontwarn com.facebook.infer.**
+
+#aliyun oss
+-keep class com.alibaba.sdk.android.oss.** { *; }
+-dontwarn okio.**
+-dontwarn org.apache.commons.codec.binary.**
+
+#refrofit
+-keepattributes Signature, InnerClasses, EnclosingMethod
+-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations
+-keepclassmembers,allowshrinking,allowobfuscation interface * {
+    @retrofit2.http.* <methods>;
+}
+-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
+-dontwarn kotlin.Unit
+-dontwarn retrofit2.KotlinExtensions
+-dontwarn retrofit2.KotlinExtensions$*
+-if interface * { @retrofit2.http.* <methods>; }
+-keep,allowobfuscation interface <1>
+
+#websocket
+-if interface * {
+    @com.adealink.frame.network.socket.annotation.* <methods>;
+}
+-keep,allowobfuscation interface <1>
+-keepclassmembers,allowshrinking,allowobfuscation interface * {
+    @com.adealink.frame.network.socket.annotation.* <methods>;
+}
+-keep class com.adealink.frame.network.socket.annotation.* { *; }
+
+#agora
+-keep class io.agora.**{*;}
+
+#error
+-keep public class * extends com.adealink.frame.base.IError { *; }
+
+#java mail
+-keep class javamail.** {*;}
+-keep class javax.mail.** {*;}
+-keep class javax.activation.** {*;}
+-keep class com.sun.mail.dsn.** {*;}
+-keep class com.sun.mail.handlers.** {*;}
+-keep class com.sun.mail.smtp.** {*;}
+-keep class com.sun.mail.util.** {*;}
+-keep class mailcap.** {*;}
+-keep class mimetypes.** {*;}
+-keep class myjava.awt.datatransfer.** {*;}
+-keep class org.apache.harmony.awt.** {*;}
+-keep class org.apache.harmony.misc.** {*;}
+
+#qttaudio
+-keep class com.qttaudio.**{*;}
+
+#trtc
+-keep class com.tencent.** { *; }
+
+#apm
+-keepclassmembers public class * extends com.adealink.frame.apm.core.base.MonitorEvent {
+   <fields>;
+}
+
+#fcm
+-keep public class com.google.firebase.messaging.FirebaseMessagingService {
+    public *;
+}
+
+#cocos
+# Proguard Cocos2d-x-lite for release
+-keep public class com.cocos.** { *; }
+-dontwarn com.cocos.**
+-keep public class com.google.** { *; }
+-keep class com.adealink.frame.game.GameNativeBridge { *; }
+
+# Banner
+-dontwarn androidx.viewpager2.**
+-keep class androidx.viewpager2.** {*;}
+-dontwarn androidx.recyclerview.widget.RecyclerView
+-keep class androidx.recyclerview.widget.RecyclerView{*;}
+-dontwarn com.youth.banner.**
+-keep class com.youth.banner.** {*;}
+
+#rong cloud
+-keepattributes Exceptions,InnerClasses
+
+-keepattributes Signature
+
+-keep class io.rong.** {*;}
+-keep class cn.rongcloud.** {*;}
+-keep class * implements io.rong.imlib.model.MessageContent {*;}
+-dontwarn io.rong.push.**
+-dontnote com.xiaomi.**
+-dontnote com.google.android.gms.gcm.**
+-dontnote io.rong.**
+
+-ignorewarnings
+
+#tceffect
+-keep class com.tcmediax.** { *; }
+-keep class com.tencent.** { *; }
+-keep class com.tencent.xmagic.** { *; }
+# 腾讯礼物动画特效SDK会通过 exifinterface 解析动效资源元数据,如果项目中引入了exifinterface, 必须加入下面的混淆配置
+-keep class androidx.exifinterface.** {*;}

+ 4688 - 0
app/s-proguard.txt

@@ -0,0 +1,4688 @@
+5SS55
+55SSSSSS
+sss55SSsS5
+S5sSss5Ss5
+5sS5SSS5ss
+s5SS55s55
+SSSsS
+55s55S55
+Ss55sSsSs
+sSSSSSs
+5Ss5sss5S
+S55ss5ssS
+5s55S5
+SS5s
+SssssSsSss
+S55s
+S55s55s5
+S5s5SS
+ss5ss5s
+Ss5ssSS
+5s5s55S5s
+s5sS55SSS
+55s55
+SssSSSS5
+5ss5Ss5Ss
+SSSss
+sSSSSSS
+Ss55
+sssssS5
+s55
+S5SsSSsSSS
+SS5S
+s5SSsSSSS5
+sS5s5SSS55
+ssSs5SsS
+SS5s5s
+sSSSs5sS
+sSSSsssS5s
+55s5s
+S5Ssss55
+ssSss5s5S
+SSs5SSS55S
+55Ss5Ss
+5Ssss5sss
+5S555
+s55Ss555S5
+SS55
+s5S
+Ss5S
+s5sssSsS
+s5Ss5S5Ss
+S5s55S55s
+SSSSSS5
+sSS5s55
+5s5Ss555SS
+5SssSs5ss
+55s5S
+55S5S
+5sS5Ss
+sSS5s5sSSs
+S555S5s5SS
+5SS5S
+55SS5S55
+SS5SssSSs
+S5S5SS
+5SSs5S5SSS
+SSSs5
+Ss55sS5SS5
+SsSS5S
+SSSSSs55sS
+5ss5SssSS
+s5s
+sssSSssS5S
+Ss5sss5s5s
+sssssSssSs
+5sSsSSS
+555sSSssss
+5SSSsS5sss
+5s5s5ss5s
+SSS5Sss
+5s5S5S5SSS
+SSS555S555
+5Ss5SsSS5
+s55s555sS
+S5sSSsS5
+S5SSs5sS5s
+s55S5S5s5S
+sSSS55
+SSsS55S555
+sSSS5S55s
+5555ssS
+s55sSss5S5
+5S55s5
+s5ss55sS
+S5ssSSS
+SSS555sS
+Ssss55S5
+5SSSS5Ss
+S5s55SsSSS
+ss5s5S
+sSS5SSSS55
+sS5s5Ss5S5
+5sSss5ssss
+55ssSSSsSS
+s5Ss555s
+ssS55
+SS5s5S
+ssSSs5s5S5
+SSsS5S
+s5SS5s5
+5sSSs
+sS5SSS555
+ss55Ss5S
+5SSsS5555
+55sssS
+S555sSSsS
+ssSSs55
+S55S55sS55
+ss5s5s
+55SS55ssS
+55sS55S
+ssS5S
+SsssSSss
+Ss5SS5S
+5ssssSS5
+S55SS55
+5ssssS
+SSsS55
+SSs55s5SS
+sS5Sss55s5
+S5sSSSSs
+sSs5Ss
+SsSsSs
+5s55s5S55
+5SsSs5SsS5
+ss5ssS5s
+555S5sS
+S55SsssS
+5S5SSs5S
+SS5555S
+5S5ssS5S
+ssS5s
+55SSsS55
+s55555Ss
+s5sssSs5
+Sss55s5
+S555sss5SS
+sSSS5S
+55ss5
+5sSS5
+s5ss55s5
+ss5s55
+S5SS5sss5
+sssSs5SS
+5sS55S55
+SS5s555S
+S5SS5sSSsS
+SS55s5sSs5
+sSsSS5S5SS
+SsS55ssss
+sSsS5sS
+s55S5S5s
+55Ss5SS
+5Ssss5SsS
+Sss5Ssss5S
+sSSs5sss5S
+5sssSS5
+sSSs555s
+S5S55sS
+5S5ssSS5sS
+S5sSS5SS
+5s55ssS
+sSsss55
+sSSs5SsS
+5S5S5SsS5
+55SsSs55
+S5S5Ss
+S5sSsS
+55555SS5S
+sss55S55
+ssss5Ss
+Ssss55sss
+sssS5S
+sSs5sss
+sSSs5SsS5
+5SsSssssSs
+SS55sSs55
+5S5Ss55555
+5ssSsS5Ss
+5SSSs55
+Ss5s55s5S
+55sSssss
+5SSSss5
+sssS55
+55SsSss5S
+5S5sSSs
+5s5SSS5ss
+55s5SssSSS
+SSSs555sss
+5sssSSs
+s5s5S5Ss5
+SsSSSSSs5
+5SsS5ssss
+sSs5SsS5s
+5s5SsS55
+SSSS55s
+SssSsSSS
+5sS5sSSssS
+SSSs5sS5S
+5SS5s5s
+SS5s55S55
+s55SSs5
+S5S5sSs
+s5s55SsS
+SSSS555ss5
+Ss5s5sSsS
+ss5sss5s
+S5SSsSs
+5S5SSsS5
+SS5sSS5
+S5s5sss5s
+S5sSS5S5
+Ss5sS
+SS5sSSs
+Ssss55s5sS
+S5SSSsSs
+ssSsssss5
+SSs5
+55SsSss5
+555
+S5SsSSss
+5S55sSs5S
+s5SssS5
+5s55SsSsS
+sSS5sSsS
+SSSsSs
+5SS5SS5
+5sssSs5Ss
+Ss5ss
+55Ss55ss
+SSsSs5Ss
+S5SsSS55s
+55sS5sS5s
+555S55S55S
+5sSSs5Ss5s
+Ss55S5555
+SSS5s5Ss
+s5Sss5s
+5SsSs55s
+s5s5Ssss5
+S5S5sS5S55
+ss55s5SSsS
+5ss5SSs5
+sssssS5S
+S5s55s5S
+5sssssS
+555SsSS5s
+5SSSsS5SsS
+s5s55S5ss
+ss555S5S5
+sS55ssss
+5S5ssSS
+SSs5S5SSss
+sS5ssS5
+Sss5sS5S5
+5ssss5ssS
+sSS5s5sS
+55s
+SsSSSsS5S
+5SS555555S
+SSsSss5S
+sS55S555s
+ssss55Ss5s
+SsS5Ss
+5sS5S5
+S5sSSSSSSs
+ss5SS5S
+ss5sSSS
+5sssssSS5
+SSs55SSS
+Ss5s5
+SSSssS5
+SS5SsSS55
+5Ss5sS55
+55S
+S55SSSS
+S5SsSSS
+ssSss5
+5s5S5sS5
+SSs555
+Ss5s5555ss
+SSsSSS5sS
+s5SS555
+S5ss5SS
+5SSSSS
+SsssS5S5
+sS55SSss5
+s5SsS55s5S
+55sS55Ss
+SSSS55sSs
+55sS5S
+s55sSs5SSS
+sS555s
+ssSSSSsS5
+s55SsS5SS
+ssSSS5s
+S5S5S5S5Ss
+5ssSsSSs
+sS5SS5s55
+s55SsssSS
+55S55
+S5ss5S5
+s5SS5s5Sss
+S5ss5sSS5
+5s555sss5
+5s5sssSs
+s5Ss5S5
+SS5555SS55
+SSsS5s5SS5
+5s5ss5Ss5
+55ss5S55
+55S5S5
+Ss5s555s5s
+S5SSSss5S
+5SsSS5
+s55sSS555
+5SSsssSS
+5ssSSsSSS
+sSSSs55Ss
+S555S5Ss5s
+S55SSS
+sS5ss55s
+Ss555Ss
+sssSssssS
+SssSs55
+55sssss5s
+55SSSs
+5ssS55sS5
+SSSS55sS5
+sSsS5S
+SsS5S555S
+S5sSs5s5s
+55S5SS
+ssSsS5SSS
+5ssS5sSSs
+ssSsS5
+5SSSS5
+Ssssss55
+Ss55SSSSS
+s5SS55S
+sssSSsS5
+SSSsss5s5
+SS5ss5
+sS5ss55S
+Ss555SS
+5SS5S5SS5
+ssSs55ss
+S5SS5ssS
+ssS55s
+5SSS5sS
+sSS5SS
+sSs5Ss5SS
+55sssSsSs5
+ss55Ss5s
+S55555Ss
+s55sssS
+S5S5555sss
+SS5
+5s55S55ss5
+5s5SS5
+SSsss5s5
+55555s5S5s
+5s5ssSS5s5
+S5SSss5S5
+SsSsssS5
+S55s55ssSS
+Sss5Ss5S5S
+SsS5S
+SS55SS
+5SSss55sSs
+SSsSssSS
+s55sSSS5
+sSS5ss
+s5sS5s5
+5sssS5S
+SSSSSS5S5
+sSSsSSsS
+555555
+5SSSSS5SsS
+55sS5s5
+55S5Ss55S5
+s555Ss55
+Sss55sss5
+5sSs5SSs
+SssSS5S55S
+sssSssSs5
+55sSss
+s5S5ss5s5
+5ss5sS5
+Ss55S
+Ss5ssSSSs
+sSssSs5Ss
+SSS5sss5S
+sSs5
+ss55S5SsSs
+SsSsSS55sS
+s5s55S55Ss
+SsSSS55s
+sSSsSSSs55
+SSs
+SsSSsSs5S5
+Ss5555SS
+S5Ss55sss
+S5SS5SS5
+s5s5SsSss
+5ssSsSsSS
+5sS5Sss
+ss5S5s
+S5s5S5
+SSSsS555s5
+5S5s555S5s
+sSs555
+S55SSSSsS
+sS5SsS5
+S5S55SS5
+SsS55Ss5s
+SsSS5s5
+5s5SSS
+5ss5S5SS
+SSS
+sSSS55ssS
+S5Ss55ssS
+S55SSS5sSs
+SsssSS
+ssssss5s5s
+Ss5ss55
+SS55S5
+sssSSSSs
+ss5ss5S5S5
+5S5s5s5sSS
+S55sSSsS5s
+s5SSSs
+sS555ss
+s55S5s5S
+SS555Ss
+55ssS5Ss
+s55Sss5
+Ss55sSSSS
+S55Ss5s
+sSSSsSsssS
+5s5SsSsSs5
+5s5s55s55
+s55S55SssS
+SS55SSs
+ss55SS
+55S5ssssSS
+S5s5Ss5
+S55ssSSssS
+ssSsSSSS
+ss55Ss5sSs
+s5SSSS
+5S55S5SSs
+s5s55ss5
+ssS5s5
+S55ssSs
+S5S5Ssss
+5s5ssSs5S
+5s55S55
+ssssSs55SS
+555SS5ss5s
+SSsS5SS5SS
+SsS55SSsS
+S55SS5S5
+sS5S55
+Ss5sS5s5S
+SssSsSSs
+ssSSsSss
+s5ss5ss
+s55ssS55s
+s555sSss5S
+s55SsSs5S
+s55Ssss
+55ssS5S5
+s5SsSS55ss
+55SSSS5S
+55SssSS
+S55Ss55
+sSssSs
+SsssSSsSss
+S5S5sSS
+5sSss5sSsS
+5SSSss5SSs
+sssss55
+s55555s5s
+sssssssS
+5SSs5S5
+s55S55S5ss
+SsSs5SS5S
+55ssssS5
+ss5S55S5
+S55ssS55
+s5555SssS
+sssSSsS5s
+sS5S5s55
+55sSSss5sS
+555s5sS5S
+5S5SSs5ss5
+S555SsSSS5
+S555SS55
+5s555Ss55
+sSssSS5S
+sSssSS
+5sSSS
+SS5s5sS
+sS5S5s
+S5s55ssS55
+sS5sSSS
+sSS55SS
+SSS5S
+S5SSsSSS
+55sSsSsS5
+5sSS5S55s5
+ssSs5
+sSs5S55S
+s55Sss
+sSSs5
+sSS
+ss5sSss
+s55s5SS
+5s5S5SS5
+SsSsS55sS
+SS55SS5S5S
+Sss55SS
+SS5S5SsSS
+S5s5SSssss
+SSS55Ss
+SS5S5s5
+5s55ss5s5s
+sSSSSss5
+555555s5s
+sSsSSSS5s
+SSsS5S5S5S
+5SSSsssSs
+s5s5s
+55s5sSSSs
+s5s5Ss
+555Ss55S
+SS5Ss5Ss
+Sss5SSSs
+s55s5Ss
+SsS5s5ss55
+s5sssSSSss
+S5ssS
+s5S55S
+Sss555ssS5
+5s5S55s
+5SSSssSsss
+55SsSSsS
+5ssss5
+sS555S5ss5
+SSSssSSs5
+ss5sS5Sss
+5sSss5S
+SsSS5Ss
+sSSss
+SS555ss
+s5S5s
+5S55SS555
+5SSsss
+s5ss5Ss
+5sS555S
+SSS55S5
+ss5sS5s
+sSSs5SS5
+sS5sss
+Ss55Ss5sS
+sSssSsSsS
+S5SSsSS5
+5sSss5s
+SS5sss5555
+sS5
+s5Sss5s5SS
+S5s5SsSS
+SsSsSSs
+sS555SsSS5
+S55sS5SS
+s55s5S5
+5sS555s
+S55ss55SS
+55s5s55sS
+5Ss5sS
+S5Ssss5Ss
+5ssSS5SS5s
+555Ss5Ss
+SSs5sSss55
+55S55Ss
+SsSSs5S5
+5s5S55sS5s
+5S555S5s5
+5ssssS55s5
+SsSssSs5ss
+Ss5555ss5
+sS55ssS
+S5sS55
+Ss5sSs5S
+s5ssSSs5
+SSSs5SSS
+sSSs5s5s5
+Ss5s5s
+SS5sSsS5s
+5SSSsS5ss
+S5sss55
+SSsSsSS5s
+SSsssSsSs
+Ss5sSs5
+55s5sS5
+S5sSS5sss
+s5s5s5Sss5
+sssSS5Ss
+sSSS5ss
+SsssSSS
+5sSs55S5
+sSSsSSS55
+SSssSs
+55s5SS
+s5Sss55s
+Ss5s5S
+S5ssss5SS
+SSssSSsS
+5SS5SSs
+5sS5S55s5S
+SsssSS5
+SsS55SSS
+SSSss5sS
+s55S555SS
+SSSSs5S5s5
+55ss5S
+S5sS5s
+SS55555
+Ss5555sss
+55s555sSs
+S55SSSs5SS
+SSsS5sSs
+s555ss5s55
+5ss55s
+5ss5555ss
+SsSSss5ss
+55s5Ss5
+SSs5SSS5s
+sssSSs5
+sS5Ss5S5s
+5sS5SS5S
+s5ss5S
+sSS5SS5s5
+S5sss55S
+SS5s5sSs5
+sSsS55
+5555s5S55s
+ssSss5S5
+55ss5SSs55
+S5SsssSS5s
+5SssssS
+S5s555s
+sSs5S5S55S
+5ss55s5SS
+SSssS5
+SsssS5Ss5s
+Ss5SsSS555
+SSs55ss
+5Ss5SSSSsS
+s5SsSSSSs
+SSSsS5Ssss
+SsS5SSsss
+ss5sS5sS
+55S55s5s
+ss5SS5sS5
+5sSs55s5ss
+5Ss5S5sS
+sS5S55s55
+s5SS
+5s555s5s
+S5ssS5s
+ssssSS
+S555555S
+sS5s5s
+Ss5SsS
+5s55s5s5
+s5ss5s5
+5sS5SsSsS5
+sS5SS5Ssss
+5SS5sS5sSs
+SSsss5ssSS
+sSsss555
+5S5SSsS5s
+5sSS5s55s
+5Ss5S5ss
+sS55ss5
+s5Ss
+S5ss55
+sSsSsSsS5
+ssssSs
+sSSS5sSS
+5sS5ss5
+SssS5sS55
+Sss5Sss
+5555S5ssS
+5S555s
+S5555SSSS5
+Sss55sSS
+s5SSs5S5Ss
+SS55ss5SsS
+SSsSsss
+5s5Ss5S
+S5ssS5SS
+55SS5sS
+SSSsSSs
+5S555s555s
+S5ssSSSSs5
+S55555SSS
+sS5s55
+SSsssSsSS5
+sss555s5Ss
+55Ss5555S
+sSSss555s5
+ss55SS5S5
+sssS55S55S
+s5ss55s5SS
+S55s55
+5S5S5ss5Ss
+s5Ss5Ss5S
+555Sss
+s5S5
+ssS5Ss5s
+Ssss55SS5
+Sss5Ssssss
+SsSSSsSSSS
+ssssS5
+SS5SSss
+ssS5S5s55s
+sS55SSsss
+5S55S
+ss55SS5S
+ssS5sSS
+s55s55ss5s
+SsS5SSsS
+55ss55SS
+5sSS
+s5sSssSS
+5ssSsSs
+555sssss
+SsSs5S55SS
+s5S5S5S
+5sssSssS5S
+ssS55Ss
+sS5Sss5sS5
+5Ss5SS
+sS5s5Ss
+SS5S55s
+55SsSsSsS
+5S55s
+SSSSSSs5
+5sSsS5S
+555sSSs55
+5s5Ss5SSss
+SsSSS5S5
+55sssss
+S5sSs5SSsS
+5sSs
+ssSS5SSS
+5ssSsSS
+55sss5
+ssss5sSS55
+5S5S5SS5
+SSS5sS
+S5s5ss5
+SSSs5S555
+SsSsS5
+SS55s5Sss5
+5SSS5sSsSs
+55SsSsSs5
+ss55S555
+sSSs5Ss5S
+s55ss5
+5S5SsS5
+ss5sSSsS
+ssss55ss
+5ss5s5sSs5
+SssS555s5
+5s555sSs
+5ss5S5
+5ss5555
+S5S55s
+Ssss55s
+sSss5SS55S
+S5SSssssS
+ssS5555
+Ss55s5sS55
+5SS5s5Sss
+ss5ss5
+SSSS5S5
+5sS555sS
+sS5sSSsS
+ssS5s5ss5
+55sSsss5sS
+5sS5
+5SsssSss55
+5s5sSSS5ss
+55SSSSs5
+5s555sSS
+555s55
+sssS5S5S
+sS5SSSS5s
+s5555S
+s5s5ssSSSs
+Sss55sS555
+5S5sSsss
+5ss555ss
+55SsS5s5
+5SS5s
+55sS5s
+SssS55SssS
+S5ss5
+Ss55ssS5S5
+S5s55Sss
+5SsS5Ss
+Ss5ssSs
+Ss5SS55S5
+5Ss5SSss
+ss5Ssss
+s5s5ss5SSs
+S5sSss
+5S5ssSSS
+Ss5SSsSSSS
+sSs55sS5s
+s55sss5s5
+sSss5
+S5ssSS5
+sSs5SSSS5
+s5SS55SS
+sssS5Ss5sS
+SSSSs5s5S
+5SsS5SS
+5sSss5sS
+S5S555S
+ss5sS55
+s55s55S
+sSSsS5
+ssSssSss
+S55s5s5sSs
+ssSSsssSs
+ss555S5Ss
+sSssSS5
+sSssS
+sSsS5s55s
+s5555SS
+ss5SsssSss
+5s5SSssSS
+5SsS5S5
+sss5555s5
+555SS5
+S5sss
+sSSsSS
+ss5sS5S
+Ss5s55S55
+555S5ss
+ssss5S555
+S5sSs5
+SSss5SSSs5
+5s55ss5
+55sSsSS55S
+5SS5SS5SSS
+55s55s5sSs
+Ss5ss5s
+s5ss55SsS5
+s5S5Sss5S
+sSsss
+sSss55SsS
+SSSS55
+SssSSsS
+5Ss5sSSS55
+5Ss5sssSS
+555ss55S5s
+Sss55
+5ssS5SsSs
+SsssS5s
+SsS55s
+5s55sS5SS5
+Ss5sSsSs
+s5s5sSs55S
+555ss55ss5
+SSSsSSsSS
+S5SsssSSS
+5sSs5s55
+s5S5sSs
+S55s555
+sS5sS5ssS5
+5S
+5SS5SSsS5s
+Ss5SSs5Ss
+S5Ss5ssSss
+5S555Sss5S
+S5S55S
+s5S5SS
+ss5ssS
+S5555sSS55
+s5sSss5s
+s5s5s55
+S5sS5SSSs5
+5ss5S5sSS
+sSS55S
+5SssSs5s5S
+s55s555
+5s
+5Ssss555
+SSsss
+sSS555
+s5S5Ss
+SS5sS55s
+5S5s5Sss
+SsssSs5ss
+sSs5SsS
+ss55s5s
+SSSS5s5SS
+s5s5sSSS
+S55s55s
+SsSs5ssss
+55SS5sSS5S
+SSsssS
+5SSSSSS
+ssssSSS
+5sss5s55S5
+ss5SSS5s
+Ss55S5S5ss
+S5SsSss5s5
+s5S5S5
+5SSS55s555
+SSSsS55
+5ssSsss5SS
+sss5SSS
+sssSsSS5
+sSSSssSSS
+S5Ssss
+5SsSs5S
+55
+5s5s5SSSss
+sSs5sS5s5
+555sSs5sss
+5SS5s5ssS
+5s5sss55s5
+sS5sS5ssS
+55SSSsSsSS
+SSSsss5S5s
+ss5s5ss
+sSS55s
+Ss5s55ss
+5ssS5SSSss
+555SsSs
+5sSssssS
+s5sssSSSs
+5S55SS
+SSSSS5Ss
+55S5Ss5555
+SS5S5sSS
+sSS5s55SS5
+sSSS555
+5s55SSss
+5SSSs
+SSs5ss5SS5
+55SsSs
+5ssSSs5s
+Ss5s5sS
+ss5S5s5Ss
+ss5SSsS
+sS555S555s
+sssSs55SSs
+SSS55sS555
+Sss5ss5
+555sS55
+55sS5sS5
+SSsSs
+55ss5sSss
+s5SS55Ss
+5S5SS55S
+5SSSS
+5s555SsSS5
+55SsSS
+555ssSSsS
+SssS5Ss5
+SSS5ss5SS
+5sSSSS5Ss
+ss5SS
+S5sSSSSSS
+ssssSS5s5
+s55ss55s
+5S5s5sssSs
+s55SsS
+s555sSSsss
+Sss5S55
+SSsSSSS5sS
+Sss5S555S
+SssS5SsS
+Ss5s
+55SsS5
+ss5Ss
+sSsSs5S
+s55S55ssS
+5Ss55s5
+S5S555
+Ss5sSS55ss
+SSsS5
+ss5ssS5SS5
+ss5ssSsSS
+5sSsss5
+sS5SSs555
+ss5SSs5s
+55s555
+5S5S55SS
+SS5sS55S
+ssss55s
+55ssSs5sS
+555sSsS
+S5sssSSs5
+s55s5SS55s
+SS5SSsS5
+S5s5ssSs
+5s5Ssss55
+S5S5S5ss
+s5sS5S55s
+5Ss5Ss555S
+S5s5
+5s5SS5s55s
+Ss5Ss5s5s
+SSSSSSSs55
+555S55
+ssS5SSss5S
+SsSssss5S
+5ss5SSSsS5
+5sssSSS5
+SSS5sS5
+s5SSS5s
+5SSsSs5SSS
+Ss55sS5
+SsSSS5ssS
+S5S5S5sS
+5S5s5
+5ssS55Ss
+S5sssss
+5ssS5s5S
+SSSs5s5s
+5s5s5SS5
+ss5S5S5555
+5SssssS5s5
+SSsSS
+s5SssSSSss
+s5SSs5
+sSSsSSSss
+sS5SS5sSS
+SSSs5s5sS
+5sS55SS5
+sS5SSS5s5
+s5S5SSSS
+s5s5Ss55S5
+55ssSss
+S5s555S5
+5Ss5ss555s
+sS5S5SSS55
+sS5S5ssss
+SS5sSs55S
+555s55S5S
+555S5s
+5S55s55s
+s5SSS55
+sS5S5sS5S5
+sssSS5sSsS
+S5s5SS5
+S5s55SSss
+S5sSS5SSS
+5sS5sSss
+sS5sSsss
+SsSSsSS5s
+SSSsSSS
+sS5SSSs
+Sss5S5
+5S5ss
+5s5s5SsSS
+555s5sSSS
+5S5S5ss
+5s5sssSS
+5S55s55S
+SS5S5ss55S
+SS55SssS
+5s5S5S5
+5sSSSs
+5S5s5S5s
+S5Sss5Sss
+SS55SS5S
+SssS5
+SsS5sSS5Ss
+5s55SS5Ss
+sSSs
+s5Ss5
+sSSSSs5s5s
+SSSS5s
+SS5SsS5S
+s555S55Ss5
+SSsSs5s
+555sssS
+s5sSs5SsSs
+ss55S5S5S5
+55Ss55s5s5
+5sSSSS
+5SSs55Ss5S
+SS55S5s
+S5Sssss55S
+5ss5Ss5
+sssssS55s
+S5sSS55S
+5s5sSsSS
+sSSS
+ssSSSSSSSS
+S5Ss5
+sS5S5S5
+5SsSssS
+55SSSss55
+555S5Ss5sS
+5SssSSsSs5
+sS555s55
+SS5sSs5SS
+SsSSSsS5
+5sSSS5
+555sss5
+55SSSSSs
+55s5S5ss
+SssSs
+sS5sSsSs5s
+S5555S5S
+s5S555Ss5
+s55555SS5s
+S5SsS
+sSS5
+555S5555S
+Sss5ss
+SsSss55
+5ssSS5
+s555S5
+5SS5Sss5SS
+S5sssS
+s5sSS55Ss
+ss5s5S5SS
+SssSS
+Ss55S55S
+s5SSSS5
+5sssS55s5
+s5SsS
+5ssS5s5s
+Ss555s5s
+Ss5ssSs5S
+sS5Ss5s5
+5555S5
+s5ss5ssSSS
+5SSSsSss
+5S5ssSSS55
+5SsS55Ss
+s5SS5SSS
+s5s5SSS5
+S55S55
+sS5s5SS5ss
+SSSsSsS
+5s5ss5s
+5S5Sss5SS5
+sS5SSS5ss5
+SSSSss
+5SS5s5S5S
+sSs5s5
+s555sssS
+5SS5Ssss5
+s5sS55s
+555S5
+5sSS5SsS
+55Ss5s5
+5Ss5Ssss
+Sss555sS
+555s55SS55
+SsSsss5s
+SSSSsS
+55SSs5ss
+5sS55S
+5sSSSS5Sss
+555SS
+SS5Ss
+s5S5SSS
+S555SSs
+s5ssS5S
+5SSSs555
+SsS5sS55sS
+SsS5S5
+55Ss5sS
+SSssSsSss
+5sSS5Ss5
+Ss5s5ssS5
+5SS5sSSS
+5sss55SS5
+SSsS5sSS55
+SSs555sSsS
+SsS5Ss5s
+SSs5s5s
+555sSSssS5
+sS5Sss
+SSSSs5
+S5sss5
+555Ss
+5ss5SsSs5
+s5sS555
+sSsSS55
+s5ssS5s
+S5S5S5
+Ss5S5S5SS
+S55S5S
+5s5SsS5
+S5s5SsS
+5SSs5sS55
+ssssSS5
+5SsS5555
+5SssS5SS
+Ss5sSs5555
+sS5s5s5
+s5s555S55
+5ss5S5555
+S555S5sSSS
+5s55S5ss55
+5SSsSS55S
+S555SS
+S5s5S
+SSS5S5SSss
+s5sssS5
+SS55S5Ss
+SSs55ssS
+sSs5S5
+SS5sSSSs
+5SS5sS
+5s5s5ss
+5sSSSs5S5
+5sSsSs5
+SssS5s5sss
+ssS5sS55s
+5S5S5s5S
+S5s5s5
+5SS5sssSs
+555555Ss5S
+s5SssSS5
+sSsSs5sssS
+S5s5s
+s555ssSss
+SsS555s5
+5SSs5SSs5
+SS5ssS5sS
+sSS5ssS
+SSSs5Ss5
+sSssSsSS
+SS5sSSSS
+SSSS55Ss
+S5SS55Ss
+5s5SS5sS
+ssSSsS
+5sS5S5ssss
+5s5S55S5S5
+SSs5S55
+SS5ss
+Sss5s
+Ss55sS5ss
+5sss5SS
+5S5SsS5SS
+55S5Ssss5S
+55sSS555
+555sS5s
+sS55sS5s55
+s5Ss555
+sS55sSS
+s5SS5S5
+5S5S5SsSS
+ssssSS55s
+ssS5ssss
+5S5Ssss5SS
+5Ss555sSss
+S5s5ss5S5
+S5SSSSs5ss
+55555ssS
+ssSSs5
+SS5sS
+SssSsss55
+5sSssSsS
+S5Ss5S55S
+S55Ssss
+sSsss5S
+S55s5SS5S
+SSS5S5SS
+Ss5S5Ss
+5S55s5s5
+5sss5s5
+5SS5S5S5
+sSSS55S
+S5sSsSsS5s
+5ssSs5s
+Ss5SssSsSs
+S5s5sS
+sS5sSs55
+S5S55
+55S5SSSs
+5SsSSssSS
+SSs55
+s5sS5s5sSS
+55sS55ss5S
+sSsS55S
+5sSs5
+s5s5SS
+S5Ss5sS
+55sss55
+55ssSS
+sS5s5S5s5
+S55SS
+sSS5Ss5Sss
+5SSSsSsSSS
+55Ss5SSsSS
+sSss5sSSS5
+5Sss555
+Ss5s5ss5
+s5S5S5S5S5
+ss5S55
+ssSss5sS
+s5S5S5SS5s
+5sssS5SS55
+5S55sS55s5
+S5s5SSs
+S555SsS5sS
+5sssss5ss
+55ssSs
+SSS555ssSs
+SSs5s
+Sss5sss
+555s5sssS5
+S5SsSSSss
+S5S5s
+Ss5s5ssss
+sSs5SssS55
+sS5SSs5s5
+s555S55ssS
+5SsS5s
+55ss5S55Ss
+5sSss
+SSSssSS
+s555sS
+SssSss
+sssSs55
+s5s55s
+5SssS5s55
+5s5sSs5s
+SsSsS5Sss5
+55SS5Sss5S
+S5SSSSss
+5Sss55s
+5sSSs5S
+S5S5S
+55sS55ss55
+sSs55ss5s
+5s5sSssSS5
+5555sSS
+5SsS5S
+Sss55SSs5S
+s5SSSss
+sS55s5
+SSS5S5sSs
+SssSsS
+5SSssSS
+sS55S5SsS
+ssSS5S5
+Ss5s55S
+S5sSss55
+sSSsssS55s
+55s55ssss
+5S55SsS55
+sS5Ss5ss
+55ssS55S
+5s5sss
+ss5sS555s5
+sS5SssSs
+SsSS5SSSS5
+S55S5SssS
+ssS5ss5s
+5sssSssSS
+SS555SSs
+sss5s55
+sSsSs5
+s5sSsSss5
+SsSssss55s
+s5sssS
+ss555Ss5s
+SsS5sSS55
+5s5ssS
+S5Sss55SSs
+s5
+s5ss5
+s5sSS5s
+S5s5SSsS55
+SSssSs5S
+5Ssssss5sS
+5ss5Sss
+ss5ss55S
+5sssSss5s
+5ssSs555SS
+ss5S5S555
+s5sSsSssS
+s55s55sS
+sS
+SSSS5s555
+s5sss5Ss5
+s5S55555S5
+5s5S5S55
+5SsSS5sS
+s5ssS
+ssSS5S
+SSssSS5S
+S5sSS5ss
+55S5s5S
+55S555sssS
+ssS5ss55
+Sss55s5sS5
+5s5sss5s5s
+5S55sS5
+ss5S5SS555
+SsSSSsss
+ss
+sS5Ss5
+Ss5s5s5S
+S55Sss5SS
+s555ss55
+ssS5SsSS
+SssssSSSSs
+sSsSSS55
+5SSs5sss5s
+SsSS5S5Sss
+s5sss5S55
+5s5s5SSs5S
+s55Ss5S5
+55S5s5s
+sss5s5S5s
+sSSSS5sS
+SsS5sSS
+S5sSs55
+sss5S5SSs5
+5ssSSsSss
+ssSSss
+S5sss5sS5
+s5555sSss
+s5s5Ss55
+ssssss5ssS
+ss55S5s
+5s5sS5S
+5S55s55
+SsS5555S
+sSs55s5S5
+Sss5s5S5s
+5ss55SsSsS
+5s555s
+ssSSss5
+ss55Sss55S
+s55Ss5
+s5Ss5ss5s
+5Ss5sS5
+SsSsS555
+SsS5ss5ss5
+Ss5Ss5SS
+55S5SssS5
+sSss5s5SS
+s5Ss5sS
+SS5S555S
+55S5sS
+sss5S55
+55SsS
+sSsssSSS
+5SS55Ss5ss
+s55s55sS5S
+55ssS5sss
+5sS55S5sS5
+sSSssSsS
+5sss5Sss
+5Ss55ssS
+Ss5Ss5S5
+s55SSSS5S
+s5Ss5s5
+SSSsSS
+5SSssss
+sS55SSs
+555sS5SSS
+5sS5SSS
+S5s5sS5s5
+s5ssSSS5ss
+5S5Ss5sS
+SssSS5Ss5s
+Sssss5S5S5
+5SSsS5S
+SSs5Ss5
+SsS5sS5
+Ss55SS55Ss
+SSs55s
+Ss55S5Ss
+S5sSsS5s55
+5ssS55SS
+SsSssss
+S55s5ss555
+sSS5ssssS
+5Sss55ss5
+55555S5S
+S5SssSS5S
+sSsSSs5S
+ssssssSS55
+5S5555S
+sss5S5s
+SS5sSSS55
+s5S5Ss5s
+SSSss5
+55s5SS5Ss
+ssSS5SS55
+s5S5SsSSS5
+sssSSS5s
+s5S5S
+sSSs5ssSs
+ssS5Ss5sS
+Sss55SsS
+S5s55
+SssS5S
+Sss5sS
+s5SSsss
+ssSS5s
+SssssS5S
+SSsSssSS55
+55s555SsSS
+Sss5SS555s
+S5sSS5s555
+S55ssSS5s5
+S5SssSss
+5SS5ssS
+555s5SSsss
+5S5SS55S5S
+sssSSSs5
+55s5s55
+Ss5SSsS55s
+s5SSs55s
+SS5555Ss
+sSsSss5s5S
+s5Ss55
+ssSsss
+5ss5ssS
+SsSsS555S
+ss5SS55S
+55SSSSsS5S
+Sss555S
+sS5ss
+sSS55s55S
+sSS5Sss5
+555SSs5S5
+ss55s5ss5S
+sssSssSSS
+ss5SsSsS
+s5Ss5S
+s555sS5S
+SS5sssSs
+55SSS55SS
+5sSs5S55
+S5S5SSsSSs
+5sS55S5S
+5sS5S5s55s
+5ss5ss5
+55s5ss
+SSsSssSS5s
+s5S55
+SS5S5ss
+5S555sS
+S5sSSsssS
+Sss5SsS5s5
+5Ss55S
+s5SSs555
+ss555S5s
+55sSss5S55
+55s5s55S5S
+S5SS5S5
+sssss55S55
+5SS5s555
+55S555S
+sss5sSSsss
+SS55sSs
+s5SsSs5s5S
+SS5s5
+SsS555
+s5s55
+SS5sSSs555
+5sssSSsS5
+sSs5s555SS
+SSsS555S
+sssssSSs5
+SsSS5S5
+ssSSs55s
+555555sss5
+SsSs
+55s5s5ss5S
+55SSSssSs
+sSsS55Sss5
+ss5Ss5ssS
+5s55Ss55s
+5S5SS5s5
+Sss5s55
+5SS55S
+5SSsSSs5s
+ss5SS5s
+sSs555S5sS
+Ss5SssSSs
+sssS5S5ss
+SSS55
+ss5S555Ss
+55SssSs5
+SssSs5S
+sS555
+sSsS5s
+SsSS
+5sss5
+SSSs5ssS5
+ssSSsSsSS5
+sSS5ss5sS5
+5S5SssSssS
+S5S5ssS
+sSSS5S5
+5SSSSsS5s
+sS5Ssss
+SsSssss5
+Ss5SSss5
+ssS55SS55
+sS55S
+5s5SsS
+sS5SsSsss5
+Ss5SsS5SSS
+5sssS
+ss55SSS
+SsS5
+SssssS
+s55S555
+ssSSs5S5sS
+SSsS5Ss5
+SsS555SSss
+5ss5SsS
+Ss5S5sSSS
+sS5SssS
+Ss55555Ss
+S55555s5
+Ss5ss55S
+s5s5S
+SS55SS5
+sS55s
+5s5Sss
+5Sss5
+s5SsSssSs
+S5s5sSss5s
+5ssss
+S5555ssS5S
+Ssssss
+5SsSSs5Ss5
+s55sSSSS
+55SsSSSs
+ssssssssS
+ssSsSs5
+S55sSSSsS
+55s555sss
+SS55SsS
+S55sS5
+S55sss5
+ssss55ss5s
+ssSsssS
+s5SSs55S
+5SsS
+ssSs5Ss
+ssSSs5Ssss
+s55sSS5SSs
+5SssSsSSs
+S55ss5s5
+5S55SSs
+S5S5SsS
+SS5SssS55
+Sss55s5S5
+sSsSSsss
+S55s5SS
+S5Sss5555s
+s5SSss
+5s5S5s5s
+5ss5Ss5sS
+Ss5S5s
+55s55SS
+SsSsSsS
+s55S5
+S5S5S5s5
+5Sss
+5SSSsS5SS
+55SSs55SS
+ss5Sss5S
+5555555SS5
+s5s5s5sSSs
+S55SSssS
+sSsss5
+Ss55S5ss
+SsSss5SS
+55s5555
+5S55sSsSS5
+55ss5ss
+S55s5sSsss
+55s55SSS
+SSSS5S
+5Sss5s55ss
+55ssSsSsSS
+5S55sSss
+Ss55S5S5S
+5555ssS5s5
+5s55SsS5S
+s55SS
+5s5S5s5Ss5
+sSsssS
+ss55sS5s
+ss5sS5sSS
+SSSsS5S
+SsS5SS
+s55555Ss55
+Ss5SSss
+s55Ss5S
+5sSSsSSS
+SSSSSsSSs
+ss5s55S5s5
+Ss5Ss5S5S
+S55sssS
+SSs55sSs5s
+sSSssss
+S5555s5
+5S55sSsS
+Ss555s5ss
+5Ss5
+s55Ss
+sSssss
+S55SsS5S
+SS55s5Ss
+S55ss5sS
+55ss55s5S
+sSsssSss
+55sSssSS
+5S5sS
+5sS5S
+ss5
+55s5SS5SsS
+5Ss5SsS5SS
+sss5sSss
+SSSSsSss55
+55sSSSs5SS
+S5ssS5s55
+5Ss5SSSS55
+ss55SSs
+5ss5ssSSs
+s55SS5S5
+Ss5s5ss5S
+5Ss5S5S5S
+S5sssss5S
+5sSssS5S
+55s5ssS
+sS55
+5sssSssS5
+5SsssSsss
+5SsSSs
+5sS5s
+ssSS55
+55SsSsss5s
+sSSS5
+SSSss55S5S
+S55S5Ss
+sSS5SSsss
+55ssS55s5s
+S555Ss
+ssSS555
+sS5S5S5s5
+s5sS5SsS5
+5sSs5SS555
+S5ss5S5S
+sssS5Ss55
+S55Sss555
+55ss5sS55s
+ss5SS5SSSs
+sss5s5ssS
+s5Sss5ss5
+sss5ssS5s
+sSSSS
+sss
+ssS55sSs5
+55s5SS5s
+ssssSss5
+55S555sSs
+s55SSS5SSS
+S5SsSsS55
+s5ssSss5s5
+sssSSsS
+55ssSS5ss
+5sS55
+s55SsS5s
+S5sSs5S
+sSs5sSsSs
+ssS555s55
+sSSSs
+s555sSss5
+ssS
+555Ss5ss55
+S555S5
+SS5sss55
+S55S5S5
+SS5SS55sS5
+s5s555
+55sSsSSsS5
+S555S55
+5ss55
+SsSs55s5S
+55SssSssS
+SssS5ss5s
+ssss55s5S
+Ssss55
+55Ss5
+5S5s5S
+5S5sSsS5s
+sSs555ss
+Ss5555sS
+ssSsSs
+SsSsSS55s
+SSSs5S5SSs
+Ss55sSs
+ss555Sss55
+S5S5S555S
+5sSs5ss5SS
+55S5ssS55
+S5ssss5sS
+55s5sS5sSS
+5SS5ssSSs5
+ssS5S555SS
+5Sssss
+5555Sss5
+sss55S
+5S5s5s
+s55ss5S5S
+S5sSs5SS5
+5S55555
+555SSSsS
+S555Ss5S5
+sS5SSss
+sSSsSSs5SS
+SsssSSSsSS
+s5S5SSs
+SSsS5ss5
+sSSss55
+SSSsss5ss
+sSSSS55SS
+5ss5s
+555SSSs5
+S5sSs5sS5
+s5Sss55s55
+5ss5sS55
+5s5s5S5s
+55SSSss
+555s5SSs5S
+5SSSssS
+5sSs5ss5
+5SS5SSsSS
+5ssSS555s
+55ss5sSs
+ssSsSsssSs
+SS5SsSS
+sS5s5S555
+s5SsSsss5
+5Ss5SSs5
+S5ss5sSS5s
+sS5S5SS5Ss
+5s5sSSs
+55ssS555
+s5sSs5S5S5
+5ssS5Ss5
+5sSSs5s
+ss5SSSSSs
+55ss5Ss5s5
+SsS5SssSS
+5s5ssSSSSs
+5sss5S5s
+S5s5S55S5s
+55ssss5s5
+sSSSsssSss
+5s5SSs5S
+s5S5SS5
+sSS55ss5
+ss55SssSs5
+SS5ssSS5SS
+S5sS5ssSsS
+5SsS5SssS
+5S5s
+Ss5SsSS5s5
+sSss55
+ssSSs
+ss55s55s
+sSSsSss
+S5SSsSs55
+5S5
+s5s5
+55Ss5S
+55SSss
+S5s5sSs
+SSSss5s
+s5Ss5Ss5Ss
+5S5S5Sss
+5S5S
+SSS5s55SsS
+sS5S5s5
+ssSSS
+S5sSSS5sSS
+sSSsSsS
+s555S55SsS
+5s5S5
+5Ss5s5Sss
+ssS55sSs5s
+SsssS
+5sSSS55ss
+SssS5sss
+SsSss5ss
+ss5Ss5s5s
+5S55
+5s55SSS
+ssSS55S5
+Ss5sSsSsS
+S55sSss5S
+5S5S5s
+ssSS5
+S5SssssS
+sS5sssSS
+SsSSssS5s
+SSSSSssSss
+s5ss
+5Ss
+5s5SS
+sSS5SS5S
+5sSSsSss5
+5Ss5sSs55
+Ss5sS5S
+S5Ss5ssS55
+S5sSsSSS
+sSSss5ss55
+Sss55S5S
+s5SSs55Ss
+SS5SS55s
+5ss5ss55
+S5s555
+sSss5S
+S5s555ss5
+s5sS
+5SS5s55S5s
+5SS
+S5555sS5
+5s5Ss
+55s555ssSS
+5S5s55sS
+ss555S5
+S5SsS5
+ssSSsS5Ss
+SSSSssSss
+SSSS555s
+sss5SsSss
+5S5s55
+s55sSS
+ssSs5s
+sS55SSss
+SsS55SsS5
+5555SS
+5ss5
+sssSSSS
+55ss55s5
+s5555SS5S
+S5sssSSs5s
+55S55S55Ss
+ssSS5SSS5
+SsSs5SS5
+5SSSSSS5S5
+55sSS55SSS
+5sSsss
+s5sSs5s
+5Ss55Ss5
+Sss5s5
+sSSs5S5
+5555S5s5
+SsS5sSSs
+ss55sS5SS
+S5ss55sS
+sS5ss5sSs
+SsSSSSs5S5
+sSS55S5sSS
+SsSSSs5s
+S5sSS5S5S
+555SSSs
+55Sss5s5s
+5SS5555sS
+SS55SsSS
+5SsSSsSs
+sS55sSsS5
+5SS5SSsSs
+S5SsSs
+s5S5sS5
+SSsS5SsSS
+sSSs5SS
+555sSSsS
+55S5SS5S
+5sss
+SsSsSS5
+SS555ss5ss
+s5555S5s
+5s55sS
+Sss55s5s
+ss5s55S5S
+55sS5S5s
+Ss55SsSs
+SSSsSSSss
+5S555S55Ss
+5ssSSs
+s5SssS
+SsSSS5SsS
+sSs5s55S
+sSS5s555
+5s5s5sssS5
+555sSSss
+ssS55s555
+s555sSssSS
+5ssS
+sss5Ss5sS5
+5s55ss
+555s5S5555
+5S5Sssss
+5sssS5Sss
+S5sSSssssS
+Ss5s5s5Ss5
+sS5sssS555
+S5S5Ss55s5
+S555S
+5SsS5
+55S55S5s55
+Ss5SS5ssSs
+5S5SsS
+SsSs5
+ssSSSS5Sss
+SsSSSS55
+s5Ss5S55
+S55
+5SSSSss
+S5Ss55s5S
+sSssssSssS
+5S5555SSs
+5SsSS555S
+S5ss5SSS5
+s5S5S5S5S
+5SsSssSsss
+SsS5SS5
+Ssss5Ss55s
+5ss55sSSs
+S555s
+SsSs5sS5S
+555s5s5S55
+555sss5Ss
+S5ss5ssS
+5SS5SS5ss
+SS5S5Ss5
+SsS5s5s5S
+5Ss55SSs5
+S5ss5s55Ss
+5S55SSs55
+55s5s5555s
+5SsSSSS
+5S5sS5SS5
+S5S55S5S
+ss55SSS5S5
+5SsSS555s
+sSss55S55s
+555SSss5ss
+55S5s
+ss55SSS55s
+5s5SsSssS
+55S5S5sS
+SsSss
+5sSSSsS555
+Sss5SssS
+S5S55sS5s
+5SSSSs5
+S5s
+SSSs5s
+SsSSSS5s
+5S5s5SSsS
+5ssSSsSSs
+S555SSS55s
+sS555Sss5
+SSSS5SSS
+sssS5SS55
+S5555
+Sss5s55SSs
+SSS5ss5
+55s5S55
+SsSsS
+s555S5s5
+55555Ssss
+s5ssss5
+55SSs5s5
+5s5ssssSS5
+S5S
+55SS5SSsss
+5S5s5SSss
+ss55sSs5s5
+sss5ssssSS
+5s5sS5
+SsSSSSs
+Sss5sSs
+ss55sSsS
+5ssSSsSS
+s55S
+5S5SSSsS
+S5ss55SSs5
+sSsSs55S
+55SSs
+SSs55ssSs
+S5sS
+55S5SsSS
+5SS5SssSs
+s5S5s5
+5sSSsssss5
+55ss5ss5
+555SSS55
+sS5SS
+s5S5sSSs55
+s55s
+55sSssS5
+55sS5S5Ss
+sSSss5S
+55SSS
+5SSss5SSs
+sSSsssSss
+55SSsSssS
+Ss5SSsss5
+ss5S5
+sSS5sS5SsS
+S5ss
+sSSss5
+5S5ss55
+S5S55s5s
+S5SssSs5S5
+SsSsS5ssS
+sSss5Sss5
+s5SSsSsS55
+S5SSSsSs5
+5sSSss55S
+sSsS5sSS55
+sS5S5
+5ssS5sss
+S55SSS5sS
+SSs5Ssss55
+SsS55Sss
+sS55sssS
+sS5SSsSs55
+ss55sSs5S
+sSsSSs
+SSsSSS55S
+5sSs5SSSs
+SssssS555
+sssSS5s5S
+SSssS5SsS5
+555SSS5s
+5SSSss5s
+SSS5Sss5s
+Ss5Ss55SS
+sss555
+555sSs55
+SsssSSSs
+5SS5S5s
+SS55ssSSs
+s555
+5ssSSsS5
+S5sSSS
+ssSssSs5
+S55SS55s
+55S5s5S5
+5S5ss5s
+ss5S55ss5
+s555ssS5
+5sSSs5sSs5
+555SSS5S
+s5Sss
+ssSs5s55
+sSS55555
+SSsss5sSs5
+SsSss555S5
+5ssSsS55
+5s5S
+5SSs55
+5Ss55S5
+ssSS5s5
+555sSs5S
+5sS5sSSs
+5SSS5s55ss
+Ss5s55
+SS5SssS555
+5S5Ss
+sssSsssSSs
+5s5S5sSS55
+SSS55S55
+Ss55S5S
+S5ss5Ssss
+SsS555s5sS
+55Ssss5sSs
+5s5s
+SsS5ss5
+Sssss5Ssss
+S5s5555sS
+sSs55Sss
+Ss55ssS
+SS5SSsS55s
+SSsSsS55
+5S5SS
+Sss5SS5S
+5SS55SSSsS
+Ss55S5s
+SsSSS555
+555SSs55
+ssSs5s5S
+5S5Ss55S55
+sSSsssSs
+5Ss5s5S
+SS5ssS5sss
+555S
+5sSS5s5Ss5
+sss5ssSS5S
+s55sSss55
+ss5SS5Ss
+SSsS5ss
+sS55SsS
+sS5sSSsSs
+5S5S5
+5sSssSs
+sSsSSss
+SSs5sS5s5S
+SS5SSS
+5sSSS5ss
+sSS5ssssS5
+sSSs5sSss
+SssSsss5s
+5s55
+sS555Ss555
+5sSSS55s5S
+5ssS5SSSS5
+s5S5S5SsS
+SsS5SSSSS
+S5s5S55s5
+sSs55Ss5
+ssSS5sS
+55S5sssS
+555S5S5Ss5
+ssssss5
+55SSsSs5S5
+S5Sss5
+sss5S5sSs
+5S5sSss5
+555sSS55
+ssSsssSs
+5sSS555
+SSss5Sssss
+ssS5ssSss
+555SS5SS
+5ssSs5sSS
+555sSs5s5
+SSssSSS
+Ss5sSs5SSs
+55sSsS
+S5sS55Ss
+S5SSs
+5sSS5SsSS5
+55SS5SSSss
+5ssSSS55s5
+ss5s5sSS5s
+Ss5sSS
+5SSs555S55
+555sSs
+sSSSSSsS5S
+5sSS5ssS55
+SSS5s
+5S5555SS
+5sSsS5SS5S
+s555sSS
+5S555ss555
+Ss5S5s5sSs
+555ss55
+5s55ss5Ss5
+S5ss5Ss5
+SS55ss55
+SSSs55
+s5sSss5
+55ss55ss5
+s55555sssS
+SS55sS5S
+sSSsssS5
+s5SS5s
+S5Sss
+Ss5sSs
+ss5SS5
+5S5SssS
+S5sS5ss5
+S55S555555
+ssss5SSs55
+sSS55
+s5sss5S
+s5SS5s555
+S5S55s5Ss
+5SSss5SS5
+SssS555sS
+5sSs5SSs5S
+SSsssSssSs
+S5SS5
+sSss5s
+s5s5ss55s
+55S5SSss
+Ss5sSss55
+55s5S5s
+55sSS5555s
+sS5SSS5s55
+ssS5sSS5s
+555sS5
+Ss5S5SS
+SSssSS5
+SsSS5s5SS5
+SssS55sS
+sSSS5ssSS
+SSSs55Ss
+5SSS5SSS
+SS5s55
+s555ss55s
+5s55SsS5
+5Sss5SS5Ss
+ss5sSSss
+Ss5sS5
+sS5ssSs5S
+555555s
+sSs5sSs
+5sssS5
+S5sS555
+S5S5ssSsSS
+5S5sSS5ssS
+5sSSsS
+S5S5S5sss
+s5ssS555
+ssSs5SS
+SSSs5ssSs
+S555SS5
+sSsS
+S555555S5
+5SSSssSS5s
+s5sS55S
+sS5sSS
+SS5SS55
+ssS55sS
+5S5ss5SS
+S5ssS5
+55Ss55SS
+SsSSssSss
+s55sSssS5
+S55S
+5s5sSSSss
+ss55S5sS55
+SsSSsS5
+S5s5SS5s
+Ssss5
+sSss
+5sSSsS5
+S55Ss5sS
+5S5SS5S5
+sss5sS5S
+Ss5sSsS5s
+Sss5sSS
+Sss5s55ss
+ss5SSs5Ss
+SSSs5Ss55
+SS5SSSs
+S555
+S5sS55s
+S5ssSS
+S5SS5sss
+Ssss5sS
+s5s555S5
+s55SSS5SS
+SsSSsSS
+55Ss5sSs5S
+SS55Ss
+555Ss5SS55
+Ss5sSSsS5
+S55sS
+SSS55s
+sss5SSsSSs
+5sSSs5
+5SSS5S
+sSs55SSSs5
+S5sS55S
+s5ss555SSs
+s5S5ss5sss
+555S55SSs
+Ss5Ss5s5S
+Sssss
+s5SSSSs5
+5S5Ss55S
+55SsSSS5ss
+5sSSsSs
+55S5S5sSs
+sSsSSSssSS
+SSSS5Ss5
+SSS5SS555
+sSsssS5SS
+sSsSSs5sS
+S5SSsS55
+5s5SssS55s
+55SssS55S
+sS5S555
+sss5Sss5
+5s5S5SsSsS
+ssSsS55s
+sS55sss
+555SsS55s
+555s55SS
+sSssSSS
+SSSSSS
+5SSsSsS5
+5ss55S
+sS55S5S
+5sss5ss55
+S5sSss5SS
+SS5sss5SSS
+55ssss
+5ssSSssSS
+sssSSSS5
+5s5s55sSS
+55SS55
+5ssSss5SsS
+sSsss5ss
+5sSs55SsSS
+55SS5s55S
+5sSS5sS55
+SSs5SS5ssS
+S5sSsS5
+SsS55ss5S
+5555ssSsSs
+s5ssssS
+SsS5ssS5s
+Sssss5ssS
+ssSs5S5
+ss55sSSsS
+S555SsS
+sS55s55sS
+5sSSSS5S55
+5S5S5S5S
+5sSSS5s5S5
+S5s5sSS5
+5S5sSs
+55SS5S
+sSSssS
+s555SsSSs
+55555S
+5Ss5SS55Ss
+SS5Ss5S
+5sssSss5SS
+55sS5s55
+5s5S5SSs
+sSS5S5
+SSsSsSsS
+5s5ssSSSS
+5s5s5s5S
+ss5SS55Ss5
+Ssss5SS
+55SS5s
+s5Ss5sSss5
+Ss5Ss5ss5s
+sSsSSSs5
+S5sSsSs
+SS5SsSSss
+SsSS55
+s5SSsSS
+sS55S55
+ssS55S
+5s5SsSs
+SSSSS5
+sSsssssS
+s5Sss5
+ssSSSs5sSS
+5ss5Ss5SS5
+5S5S5s5
+Ssss5ss5S
+5s55Ss5s5
+55SS5sSss
+SS5ssS5SS
+5S5ssS5Ss
+S5
+sS5sS5
+s5SS555S
+sSs5SSSs
+s555Ss
+sssssSSS55
+Sss5s5S
+5s5SSssS
+Sss5SssSss
+S55sS5ss5
+sS5sSs5SSS
+sSSSSsss5
+5ss5sS5SSS
+SS5S5
+sSsSss5
+5Ss555
+sSs5ss55SS
+55SsS5SsS
+S555S5S5sS
+ssssSSs
+Ss5SS5555
+5s55sSSs5s
+s5SSSSS
+s555SS
+ssSSSs
+5s5SSsss
+55sSS
+S5ssSSsssS
+SSsS5S55
+SS5SS
+5sS5s5S5Ss
+5sSssSSS
+sssSSS
+SSssS55sS5
+S5ss5sS
+Ss
+SS555ssSS
+5S5ssS5S5
+s5SSSs55
+SSSssss5S
+s5S55S5s
+s5sSSs5
+555ss5sS55
+SsS5sSSS
+sSs55sSss
+S5Sss5Ss
+s555sss55
+SSs5SSSSSs
+sS5s5ss
+Ssss5ss55
+5sSss5SSsS
+5sSssSSs
+SsssSSS55s
+SS
+s5s5sSS5
+5SSSS5s
+5S5SSS
+5S55S5
+SSsSSs5
+S5SS55sS
+s5S55S5s5S
+s5SsS5SS
+S5sS5sSsS
+55sS5sSS
+5s5s55ss
+5555S555
+sSs5S
+sSs5ssS
+ss55sSss5
+S55sS55
+sSSsss5s5
+S5s5555s
+S5Ss5SS5s
+S5ss5s555
+ssS55ss5
+SS55ssS
+SssSS55s5
+5SsS5sSSS
+s5sSs
+sSS5SsSs
+5sSsSSSs
+sssS5sS
+s55sss5S
+sS5Sss5sS
+ss5SsSSSS5
+5s5ss55sS
+S55s5sSs5
+sSs5s
+5ssssSsS5
+ssS5S5SSss
+5ssS5ssSS5
+s5ss5SsS5
+SssS5S5
+SS55SS55S
+55sSSss
+sSs5SSSSSS
+5Ss5SSss5S
+s5sSS
+s55SsSSsSs
+555ssssSS5
+S55S5s
+SssSS5
+ssssSssS
+Ss5Ss5ss
+5SS555sSs5
+sSSSS55sSS
+ss5sss5S5s
+s5s5sSS5ss
+SSss5ss
+s5SSSSS55S
+SssS5SS
+s5sSSssS
+S555sssSSS
+s55S5sS
+SS5555S5s
+s5sS5
+S5sS55SSs
+sSssss555s
+S5555SSs
+55sSssss5
+Ss5SsSSsSs
+5S5SSSSs
+SSs5sSS5S
+S55sSSS5S
+s5s5S5s
+S55sssS5s5
+Ss55SsS5
+sSs55
+S5S5ss
+S5s5s55sS
+ssS55ssS
+ssS5ssss5
+s55S5ss
+Ss55Ss5
+sSSs5sSSSs
+55sSs5
+5sSsSS555S
+s555SSsSS
+s55S5ssS55
+5sssS5S55s
+55sSs
+5sSSsS5SS
+Ss55s5sSS5
+s5s55SS
+s5555
+Sss55s55S
+SSs5S5sss
+S5sS5SSS
+s5s5SssS
+sS5sSs55ss
+sS5SSSSS5S
+SSs5ss
+55Ss5s
+5S5Ss5s
+sssSSsSSsS
+ssSs5S
+sss5
+5ssSS5Ss
+ss555ssS
+sSs5S5s
+5SS5SsS5s
+Ss5sss
+5Ssss5
+SSSsssS5
+sSsssSsS
+SS5sSS5SSS
+S555ssS
+5ssssSS5Ss
+5SS5sSs
+5S555SSSSs
+SS5ssSS5
+sS5s5Ss5s
+5S5ssSs
+5s5sSs
+s5SS555Ss
+5S5Ss5S
+5ssSS5SS
+sS55sSSs
+sSsSS5
+Ss5sSSS555
+55ssSSsS
+SsssSSsSS
+ss5sS5Ss
+s555s
+sSs55s5
+sSss55s55
+55Ss55
+s5s5s55S5
+S5S5SSSs
+5555SSSSs
+55S5SsSs
+ssss
+sss5s5ssS5
+Ss55s55SS
+S5S5sSSS55
+ss55ssS
+5SsS5s555S
+sSsSSS
+5SS5sSS
+sSs5SsSss5
+SSsss5s
+ssSSSssSS5
+s55SSSs
+s5S55SS
+s555S
+5sSssS
+5SSsSS5sS5
+sSS55S5
+ssS5555Ss
+S5S5SSSS
+ssSsSSS
+sssS
+S5s5ss555S
+5S55Ss5S
+5ss5Ss
+5ssssSssS5
+5s555sss55
+sS5S5sS55
+s55s5SS5s
+S5555s
+sS5s55S
+s5SSssSS
+s5sSSS5S
+SS5555s5
+s55ssssSs
+ss555ssSs
+ss5s5sSS
+55555s5
+SSs55S
+S55Sss
+s5sS5Ss55
+5sS55S5
+S5Ss5s
+S5SssSS
+55s5
+S5SSSs55S
+sS55s55
+S5S5SS5
+Ss5sss55
+sSsSS5SS5S
+5S5ssssS5
+Ssssss5S5
+sss5SSs5
+s5sSSs5Sss
+S55SsS
+S5Ss5S
+5Sss55S5
+SsSS5ss5S
+s5s5sS5
+S5ssS5ss
+S5S5s5
+sSsSs5s
+5S5S55S
+55S55sss
+s5s55Ss
+ss5Ss55s
+S55555
+S5sss55Ss
+s5sSSS5S55
+S55Ss5
+55555ss
+s5555s5S
+55ss
+S5sS5S55
+s5sS55555S
+SS555S
+S5Ss55
+s5SSs5sS5S
+S5S5SSs
+55S55S5
+sSs5sS
+ssSsS
+s55S5s5S55
+5S55S55
+5ssSssSS
+s5ss5ssS5s
+5SSsSS
+Ss5SsSS5
+5ss5Ss5S
+Ss5S5sss
+55ss55Sss
+SsSs55SSS5
+SsssSSs5
+55sS
+5Sss55Ss
+SssssSS5S5
+sS5555S
+5ssS5sS
+SSsSss5sS
+SsSs555
+ssSSSSS5s5
+sSsSs55
+sS55s5S
+SSs5S5S5sS
+S5ssS5s5
+SsSS55s5s
+s5SSSS55
+5Ss5s5s
+s5sSS5ssss
+5SsS5sS
+sS5sSssSS
+Sss555Ss5s
+ss55s
+SSs55S55
+5sSSs55S
+S5SS5S
+Ss5s5Sss5S
+5ssSs
+SS555SSsSs
+ssSs5SSs
+5555SsSsSs
+SSSsSssS5
+5sS5s5
+sSs5SSSsS5
+Sss55Ss
+SssSssss5S
+55s5s55sS5
+S5SS5s
+ssSssSsss
+5sSS55555s
+55sS55s5s5
+sssSSss5
+SS55sSs5
+S555s5SS
+SS5sS5
+ss55S
+5ss5SS55
+SSSsSs55
+ssS5Sssss
+sSssSS5Ss
+5ssSS
+sSsS5sssSS
+5ssSs5Ss
+s55s555s
+5Ss5555
+s5sSs55
+ssS555Ss5S
+S5sSss55S
+55s5Ss5s5s
+5ssS5Ssss5
+55s5S5SS
+5s55SSs5s5
+ss555
+sSssS55
+S5SSS5SssS
+5ssS5
+s5S5SS5555
+Ss55SS5s
+555sS5S
+SssSS5s
+s5S5sss55
+ssSSSS
+SsssSSs5S
+s5sSSs5S
+sss5SSS5s5
+ss55ss5
+Ss5S5S5
+S55Sss5S
+5sss5SSss
+sSsS5Ss
+55SSsSS
+s5S55Ss
+S5s5sSsS5
+ssSsss55s
+S5SS55
+5Sss5SsS5
+5Ssssss55
+s5SS5S
+5Ss555s
+sSss5s5s
+sS5sSs5SS
+s5ssS5
+s5SSS5
+sSs5s555s5
+S5SsSS5s
+Sss5sS5ss5
+55s5SS5
+SS55Ss5
+s55Ssss5S
+sssss
+sSsSS5S5
+55s5sssS
+SssS
+SS55Ss5Ss
+Ss55sSS5s5
+SS555s
+s55ss
+Sss55s
+ssSs5SSS
+s5S55SSs
+Ss5SSSS
+S55sS5S
+55S55SsSS5
+SSs55sSssS
+55SSs5sS55
+555S55S
+s5s5s5s5
+ssssS
+5s5s555
+sSSssSS
+Ssss
+Ss5ssS5s
+S5ssss
+5sS55s5
+s55sS
+55sss555
+55s5SSs
+SSSsSsS55
+5s55s
+S55SssS5ss
+SSSSS5ssSS
+sSSssS5
+55Ssssss
+55s55Ss5
+ssss5
+sss5ssSs
+s55s5
+S555sS55
+555s5sSSs
+sS55Ss
+SS55SsSs5
+ss55S5SS5
+5s55sss555
+sSS55Ss
+SSsS55S5s
+5s55S
+55Ss555s
+s5ssSS
+ssS5SSsss
+sS5S5s5Ss
+5s5s55s
+Ss5sS555
+5SSS5555
+sss5ssSS
+5Ss5SSSS5
+sS55sS5
+ssS5S5SS5s
+5s55sS555S
+5S5SSs5s5
+5s5s5sss
+Sss5
+sS55SS
+s5ss5sS5sS
+5sSSss55
+55sSs5SS
+sSSSSSsS
+sSS5SSs
+5sssSsSs
+sss5s5sSSS
+ss5sss
+SS555S5Ss
+555SSsS5
+S55sS5Sss5
+S55sS5s5S
+S5sS555S5S
+55SS5SS
+sSSsSSSsSS
+5SS5S5ss
+sS5s555s
+sssSs5s55S
+5SS5
+5Ss555ssS
+s5555ssS
+sSs55s
+sSS5sSssss
+5sssSS55Ss
+5S555s5
+S5S5ssSs5
+5sssSsSS
+5sssSsssS
+ss5SsS5
+SS5SSSsSS
+SsSSs
+S55ss5
+SSS55SSS
+ss55ss55
+5SsSs
+SSSssSSss5
+s5S5S55s5S
+s5ss55S55
+555Sss5S
+SS5SSs5555
+sSs55S
+S5S5555S
+sSS5sss
+s5SS5sSSsS
+SsSsssSss
+5SSss5sS5s
+S5s5Sss5SS
+Sss5S5ss
+S55ssS
+SssSs5Ss
+S5s5ssSs5
+5SSs
+S55s555ssS
+sSs55Sss5
+s5sSSSSSS
+SS555sss
+55SSs5S
+SsSS5sss
+55ss55S
+5555s555
+SSSsSS5Sss
+5Ss5S5
+SSsSsSss5
+s5ssSs
+5S555ss
+55S5ss
+S5Ss5SSs
+5S5SSssS
+S55sss
+5SSss55s
+5SSs5s5555
+ssSssS5
+5SSS
+ssSSS55s55
+SS5s5Ssss
+ssSsSsSSSS
+S55SSsS5
+5SSsS5S55s
+sss5SS55
+S5s5SS5S5s
+5SS55ss
+55SSssSss
+SsssSs5S
+5SSsS5Ss5
+5555s
+s55sSs5ss
+S5SsSsss5
+S5SsS55S5
+S55ssSS5S5
+SsSsSss5ss
+55SssSss
+s555SSssS
+Ss555SsSss
+5SS5SSsss
+SSS5sSS5S
+SS5S55S55
+sSS5ssS5S
+ss5S5S
+S5SSsS
+5S555S55
+S5SsSsSsss
+5s5sSsS
+sSSSSSs5s
+5SSSsSS
+ssssssSSS5
+sSSs55S5
+SSS5sSS5s
+55555s5S
+SS5SsSsS55
+SSSsSsss
+s55sS55
+ss5SsSs
+S5SS5sS
+s55s5s5
+SsS5s5S
+55s5sSs5
+ssSS5SS
+55555
+SsSSs55Ss
+5sss55
+5s55s555SS
+sS5s555S
+s5sSSS
+S5sS5sss
+SsSs5ss5
+ss5s5S5s5
+5ssSSS5sS
+s5SS5ssS55
+5sSSS55
+S5S555sSSs
+Ss55SSs5
+Ss5S55
+S5S5s5Ssss
+5sS5sS
+sSSSsSS5s
+ss5555
+ss5sSssss5
+ss55S5
+sSSSSSs55
+55SSSs55S5
+Ss5s5S5ss
+55S5s555
+5SSSS5sSS
+5SS55s5
+S555S5SsSS
+5ssS5sSSsS
+5SsssssS
+ss555ss5s
+5sSsSss5
+5S5SssSSS
+55sss
+S5s55sss5s
+5s555
+sSS555Ss
+sss55ssS
+S5s5SSSs5
+sssS555ss5
+555sSSSs5
+5s5ss
+SSsSSs
+5SSSsssSS
+55S555
+55s55S5sS
+ss555sss
+SsSsSsSS5s
+5SssS5
+Sssssss5s
+ssSSsss5
+S5sSS5s
+S5s5ss
+sssS5s5
+5s555SS
+Ssssss5Ss
+s5S5S55SS
+5S5s55s55s
+s5S5ssss5S
+5SSS5S55s
+5s5sS
+s5s5s55s
+555sS5sS55
+SS5SSsssss
+5s5SsSSS
+SS555s5
+S5SsS5S5
+SSss555S
+SsssssSs
+ssS5SSs
+Ss55sssSS
+sSSS55555
+sSSss55ss
+Ssss555S
+5S5s55s
+SSSSsSS
+5s5S555ss
+5s5s5
+55SssSssSS
+5S5s55SsS5
+SsSsSs5s
+S5SsS5SS
+5S555SSS5s
+SS55s5S
+SSsSS5
+5ss555ss5
+55Ss5ss55
+S5SssssSS
+S5sS5sS5s
+SSs5s55s
+sS5ssSss5s
+s5Ss5ss
+ss55SsS
+S55sS5S5ss
+5S5sSSsSs5
+SS55SsS5Ss
+s55SSssss
+s555s5S
+s5SsS5
+s5Sss5Ss
+sssSSs555
+5s55SSs55S
+55S55S
+sSs5Ss5ss
+S5sSSSssSS
+5555ssSS
+SSSS5S5S5
+sssSss5s
+sSs55555ss
+55S5S5S5
+5SsS5Sss5s
+SS55S55S
+5Ss55SSs
+55sS5S5SsS
+5s5S55S555
+sssSSs
+Ss5Ss5s
+Ss55SssS
+SSs5SS5sSs
+5sS5SS55Ss
+5ss5s5S55S
+S5s5sS5sSs
+ssSss
+s55sSs
+Ss555
+sssssSss55
+5s5
+5S55SsSsS
+sS5s55Ss
+5s55SS
+5SSss5ss
+SSssS555SS
+Ss5Ss5S
+sS5ss5s5s
+5SSsSs5ss
+ssSSsSsS
+S5ssSs5SS
+SssSssS555
+5s5ssSSs
+Ss55SS5S5
+55S55ss
+Ss5S55S5
+s55SSs
+sS5ss5s5
+SssssSS
+SSss
+SsS5SS5s
+5ssS5S
+5sS
+SsS5SsSS
+5s5s5sS5
+ssSSs5S5ss
+SS5ssS
+Ss5Ss55
+SSs5S5s
+sssSS5
+Ssssss5ss
+s55sS5
+5SS5SsSsSS
+SssSssS5
+5sssS5s
+SSSss555S
+Ss55s
+sss55sSS
+SssssSs
+SSsS
+Ss5SSSssS
+SSsss5sss
+5ss
+sss555s55S
+SS5sss
+S5SSs55Ss5
+s5sSss5ss
+S5SS5Sss55
+sS55SSsS
+S5SS55S
+s5s5SsS5ss
+s5S5S5S5
+SSssssS
+555S5ssS5
+sss5sS5s
+SsS5S5ss5s
+ss5s55Sss
+SsSSss5s5S
+sSs
+5S5ss5S5s5
+5SS55ss5s
+5SS5Sss5s
+sSsS5
+SsSSS5
+S5SsS55
+sSSS555ss
+sS555S5s5
+S5SS55s
+S5s5SSS5Ss
+S55s5
+55Ss5S5s
+SSssS5SS
+5Ss5sSs5sS
+5s5sssS5
+Ss55SSs5s
+5sSssS555s
+sSss5555ss
+Ss5Sss
+s5S5555
+555Ss5s5
+SSsS5555S5
+55S5SSS
+SSs55S5s5s
+5s5sSSs5S
+Sss5Sss55s
+s55SSsS5s
+5S5SSS5Ss
+sSSSSSsSs
+5Ss55SSS5
+SSSSSSS
+5sSssss
+s5ss555S
+S55S55sS
+s5S555S
+555sSssSS5
+sS5sS5S
+55ssSSss5S
+sSS5Sss
+ssSs5sss
+S5SS555
+sSsss555sS
+5Sss5ssSS5
+s5SsSs
+5sS5555
+S555SSs5S
+5S55s55555
+5Sssss5
+S5S5sss5s
+S5SsSSsss
+SsSSSS
+s5S555s
+ssSss5Sss
+5Ss5ssS5
+SsSSSSSs5s
+5sss555S
+s5SSSsS5
+5SsS55
+SSSS5S5sS
+5SSs5ssS
+S5SS55s5sS
+SS5Ss55s
+ssSSS5ssss
+s5ssS55S5
+SsS5sS5s
+5SSSs5
+sSsS55sSs5
+S5sS5Ss
+sssS55555S
+s5SsSSSs5S
+s5SS5SS5Ss
+ssSS5ss5
+55SssssS
+sSsS5Ss55
+SSsSssSSs
+s5s5sS55S5
+5SSSsSsSS
+SssS5S5sS
+sSS55ss5sS
+s5S55Ss5
+55ss5Sss5
+SsSS5
+S55SSS5s5
+55SsSSS55
+55sss55s5S
+Sss5ssS
+sSSS5SSs5
+5SS5s5S
+sSs55ss
+SS55S5S
+55ssssSS5s
+sss5sSS
+5s5ssSSs5
+sS5sS5s5Ss
+Ss5555555
+s5sS5s
+sss55S5S55
+5SS55Ss
+SS5s5sSs
+SsSSS
+SS5Ss555
+ss5ssSSS
+S5sS5S5
+5s5s5s
+sS5sS5555S
+5sSSSsS
+s5ss5SS55
+Ss5SSSsSss
+sSSSSs
+S5SsSs55S
+5sS55sS5s
+SsS5S55s5
+S5sssSsSss
+5s5s5Ss5Ss
+ss55SSs55s
+ss5Ss5s
+SsSs555S
+ssSSsssSsS
+5sssS5ssS
+SsS5SS5s5
+5s5SSS55s5
+s5S55Sss
+55s5s5
+5SS555sS5
+5s555SS5SS
+ssS55SSS
+5s5SsSSssS
+s5ss5s5S
+sSSSSS
+5sS55sS5S
+5ss5SS5sS
+5sssSS5s5S
+s5SsSSSs55
+sS5sS
+sS5Ss
+sssSssSSs
+SsS55SSsSs
+S55S5s5
+SssSS5SsSs
+55ss5s5
+S5SsssSs5
+S5sSS
+5s5sSs5Ss5
+s5sssSS55
+S5S5
+sss55SS
+SSs5S5ssss
+SS5Sss
+sSs5S55ss
+5SsSS55SSs
+5sS55ss
+5s5sss5sS5
+5SssSS55
+ssS5s5SsSS
+S5s5sSS5sS
+5ssss5S
+55Sss
+sSssS5Sss
+sS55SSS
+ss55S55S5
+SsSs55SS5
+5S5sSS
+s55Ss55
+555S555S
+sS5Ss5S
+SSs5s5
+5S5SSss
+SSSs55S
+sSSssSSs
+SSsSSssSs
+s555Sss
+sSSsSS5s
+S5sSs5s
+sSsssssSss
+Ssss5S
+sss5s5Ss
+sS5SSSSs
+55s5s5555
+sSs5ss55s
+S5Ss
+5SSsSSs5
+S5SSSSS
+sSSs5S
+55sSSssS55
+SSs5sS
+SS5Ss5
+ss5sSSsSSs
+SsS5ss555
+s5SS5SssSS
+SSSs5ss5s
+5SssSS5s
+sS5s5
+Ssss55ss
+55SsSs5
+ss5SsSssS
+55ss5sS
+55Ss55sss
+sSsssSs
+ss5SSs5
+s555SsSSs5
+S5SS
+SssSSs
+5sS5sSssS
+5ssS5s5Sss
+s5S5sss
+5SsSs5SSs
+S5SSssS5S
+Ss5ss5SS
+SsSsSSss
+ssssss5SS
+5sSsSSsss
+5s5Ss55SS5
+s5S5Ssss5
+5Ss555sS
+SSSSs
+5S5Sss
+SSSSssS55
+SssSsSs
+5SSs5s
+SSS55S5S
+s5Ss5555
+s5sSsSs
+55sSssS
+5sSs5ss
+ss5Sss
+5Sss5SsS55
+s5sSsSs5Ss
+s55sssS5
+5S555S
+55SssS
+Ss55SSssS
+s5SSs5s
+sS5555SsS
+SSSSS
+5sS5SSsS
+sssssss
+S5sS5s5Ss5
+s55SssSsss
+ssSsSS5S
+5sSssS5S5
+SSSS5s5sSs
+sS5SS5s
+sss5s5
+sS5sSss
+S5S5SSs5
+Sssss5S55s
+SSsss55555
+sSsSS5S5s
+5S5555
+sSS55s5S5
+SssSsS5
+5S5ssS
+5S5Ss5
+555SS5Ss
+SSSssSsSsS
+55ss55sS5
+Ssss5SSsss
+5sssss5S
+55s5S5S
+ss5Ss5
+SS5S55sssS
+5SSSSS5Ss
+55SSSSs5s
+sSssssSSS
+SSsss5SsS
+sSS55SSsss
+ssSSSSS5
+s555S5SS
+sSSsSSSs
+55SSs5S5
+55ss5sS5Ss
+sS5ssS5Ss5
+555SssS5Ss
+sss5ss
+S55ssss555
+ss5SSS555S
+5s5S5s
+sSS5sSS5s5
+S5s5S5s
+SS5sssSs5
+s55Ss5555S
+s555ssS
+SsSSSSS5
+SSssSs5
+s55sS5ss55
+Ss5SS555
+sSs5SSsS5
+SSss55SS
+sS55Ss5sS
+5SssSs5
+5sSSsss
+S55Ss
+5S5S5sS5S5
+sS55555
+555S5sS55
+SsSSS5S
+S5s5S5S
+S5sS5s5
+sSSSSSSss
+Ss5s5SssSS
+Ss5sS5s5sS
+555S55SsS
+S5S5S55Ss
+SSsSS5ssSS
+s55SSs5SS
+s5sS55Ss
+SsSSssS
+5SSS5s
+555S55s5
+S555ss5S
+5SSs5ss
+sS55sssssS
+5sSSSSS
+s55ssSsS
+SsSSS5s
+s5sS55s5s5
+5SsSSssSSS
+SsS555s
+S555S5S
+SS5s555
+SsSSss5
+SsSs5ss
+Ss5SS55s
+S55SSSs5S
+5SS55Sss
+S55S5
+S5SSSSs55
+55SsSSS5S5
+5Sss5SS5
+s55Sss5Ss
+5S5sS5sss
+S55SSs5s
+55sS5Ss5
+sS5S55S5s
+55ssS5S
+Ss5ssss
+s55555sS
+Ss5S5SS5s
+ss5Ss55sss
+5sssSS
+S5S55s5ss
+55sssSs
+ssSs55SsS
+S55SSSs55s
+SSSS5ssss5
+5SssSsS
+SSS5555
+5SSS55
+Ss55SSSSSs
+5SsSS
+s5S55S55
+SssSs5ss5S
+sss5s
+SsSSssSS5
+Ss5S5SssS
+5sssS555
+ssS55SSs
+5SSs5Ss5
+s5ss5s
+sS5sSs55s
+s5sSSs
+5sssSsS
+55SsssS
+Ss5s5sSSS
+sS5ss5
+55s5Ss
+S5sssSSSSs
+S5s5s5s
+Sss5sS5
+SS55s5ssS
+55s5S5Ss
+s5sssSSs
+sssss55s5
+SsSs55SS55
+5sssSss
+ssSsSsSSS5
+Ss5s5sSSs
+5sSsS
+s5s5s5
+sssSS5SsS
+s5SSs5SSS
+5ssSs5SSss
+sSSSSSss
+sSsSSSSss
+5S5Sssss55
+S5SSs5sss5
+555ss5s555
+55SS5
+ssS55SS5
+sss5S5ss
+5SSSSsss5
+ssS5Ssss5
+5sssSS555s
+sS5ssSS
+s5ss55
+SSssssSS5
+5S5SSss5
+s5s5sS
+55s5Sss555
+5sSS5s
+55ssSsS555
+Ss5S5S
+S5SSSsSSs5
+5s5s5SsS5s
+5S5sSs5
+S55SsssSs5
+sSs5ssSs
+5Ss55sss5
+ss5S5SSss
+5SsssSs
+555SSsSssS
+s5s5ss
+s5SS5SSs5S
+Ss5SSs555
+Ss5Ss
+ss55SSSS
+55S5ssSSss
+S5sS5sS
+sS55S5SSsS
+SSS555
+555SsssSS
+SSs5Ss
+55SsS55s
+55ss5s
+sSsSsSs
+s5sS5S
+sSSsSSSsss
+sS5s5sss5
+5Sss555S
+SS5s555ss5
+sSSSS555s
+ssSSS55SSS
+SsS5sS
+5SS55ssSss
+Ss5SS
+ssSsSSs55S
+sSS5SsSSS5
+S5ssSsS5ss
+5S5ss5
+s5S55S55S
+5SssSsSss5
+S5s5s5Ss
+s55S5Ss5Ss
+S5sS5Sss
+55S5ss5s5s
+SSs5SS55S
+S55S5555SS
+Ssss5sss
+s5555sSs
+5sssSssssS
+S55ss55SsS
+SsSss5SS5s
+S5SsSs555
+55s55s
+Ssss55555
+Ss5S5
+sSsSsS5
+sSsss5Ss
+55sSSSS5
+SSSs5sS
+SSssSs5Sss
+55Sssss
+sssSSss5s
+S55S5SsSss
+sSS5sssSs
+55SSsS55s5
+SsSss55Ss
+ss5555555
+s5SS5s55S
+SsS55sS
+5SSsS5
+5ss5S
+55SsSsS
+ss5sSs5
+5S55sS55S5
+SSs5s55Ss
+5ssss5sSS
+5S5sss
+55s5SssS
+S5S5S5SS55
+sSSsSS55
+s5sS5SS55
+sS55ss55S
+S55SsSssSs
+s5s555Sss
+S5S5S55
+ssSSs5s5
+SSSsS5555
+SsS5s5
+S5ssSs
+s5sSssS5S
+ssSSsS5sS
+ss555555
+s5sSsSSS
+SS5sSsssS
+SsS5S5Ss
+5sS5SssS
+5Ssss55S5
+SSs5s5SS5
+s5sSS5Ss
+5ssS5s
+5sSS555S
+5s5SSSS555
+sSS5sS
+sS5s5ss5S
+SS5S5Ss
+5sSs5S5S5
+ssssSsSS
+5sSS5S
+sSSsss5ss5
+s5s55sS
+5SSSsS
+S5ss55Ss5s
+5SSS5sSs5
+sS5sss5sS
+S5SsSsS5S
+Sss5SS5S5
+sSSSssS
+ssSSssS5s
+ssssSSsS5s
+5SSS5Ss
+s55s5sssS
+sssS5sS5Ss
+5s5S555S
+ssss5s5S
+S5sSs5Sss
+S5ssSSssSs
+5s55Sss5S5
+S5S5S5S5sS
+SS5sSsS
+555sS5555
+5S5SsSsSS
+SssS5s5
+s5s5sSss
+SSsSss5
+5SsSs5sSSs
+ss55sS5S55
+55sS5SSs
+5S555s5s
+Ss5S5ssss
+55s5SSsssS
+SS5S5S5
+sss5ss5sS5
+sSSSsSSs
+Ss5ss5sS5
+SSssSSSSs
+555ss5
+55sss5ssSS
+ssS5sS5s
+S5ssS55sS
+ss5s55sss5
+s5SsSS
+SsS55S5
+Sssss55S5
+5ss5sS5s
+5sSS5555
+SssSSs5S5S
+5S55sS
+5Sss5s55
+S55S55SS
+SssS5sS5
+5SSS5S5S
+ssS5sS
+SsS5s
+5SsS5SSs55
+SS5s5sSsss
+Ss5s5sS5SS
+55sSSS5
+5Ss55S5s5
+SS5S5S5S
+sSS55sSs
+ss55s55ss5
+SsS
+s55ssSSSS
+s5s555ss5
+5s5s5SsssS
+sSSS55sSS
+ssSS5ssSSS
+555ssSssSS
+55sSSs
+5SS5sss
+SSS5S55
+S5s555S
+S555s5sssS
+5sss5SSss5
+55s5sSSSs5
+55Sss5s5SS
+S5S5s55
+55s5S5
+Sss
+555SSS
+SS55sS
+5sS5sS5555
+5sS5SSs
+5sssSs5ssS
+sSS5Ss5S
+5SS5555s5S
+s55SSs555s
+sSsSsS
+sSsSSS5s
+SsS55
+5sSS55ssS5
+555SS55S
+5555SsS
+s5sSs55S5S
+55sSSSs
+S55s5sS
+ssSSSS55s
+SSSSssSSSs
+sSssSssS5S
+SSS5S5s
+ssS55S5SS
+SS5S5SS
+SS55s5
+5Ss5s5s555
+ss5555SS
+5sS5sSS5Ss
+SSsSsssS5
+55ssS5ssSs
+SS5ssSS55S
+SSsS5s
+Ss5
+S55sSsSS
+555SS55s
+5SsSs5s
+5SS55sss
+Ssss5ss
+55S5555SSs
+55SSSS5
+S5s5SsS5S5
+S555SSsSs
+5Sss5s5s
+SSS5Ss55S
+S5sSSSs
+SSs5SsS5
+SsSSs5S
+55S5sS5ss
+S5S55sS555
+sSS555s
+S555sSS
+SSsSss5s
+s5sSss
+ss555ssSS
+s55S5sSSs5
+s55SsSS
+5S5sss5
+55sSSS5S5
+S55s5SSSs
+5sssss5
+5SsSSS55Ss
+SSS5ssss5
+ss55ss
+SS5s5SS
+5s5ssS55
+Sss5SS
+55ss5S5
+5sSsS5
+S5sS5S
+sS5S5S55S
+SSss5S5S55
+ss5s555
+s5sSsS
+5SsSss5SS5
+sS5SsSSsS
+sS5s5S5S
+SSSS5
+SsSssS5s55
+ssS5
+ss55sS
+SS5s5Ss
+SSS5s55
+sss5s55S
+s5sSs5
+sS5sSSs
+55sssSSS5
+SSsSssS5
+sss5SSS5
+5s55sSs5Ss
+5S5ssss
+5SSssSSS
+Sss5s55sS
+ssSS
+sSS5s5S
+555S55s
+ssSss5S5sS
+SSS5s5S
+55SsS5S
+S5s5s5S5S
+ss55s5
+sSSSS5sSS5
+5s55S555Ss
+5ss5S55
+5sSsSs
+sS5ss55
+SSSs5Sss5
+SssSS55s
+s5S5ssss5s
+5s555S5S
+ssSs
+sSsSSSS55s
+5s55SS5S
+5ssSs5s55
+5s5ssss5Ss
+Ss555sS5
+555sss
+Ss5s5Ss5S
+5s5sss5s5
+5SSSss5ss
+ss5sSs
+5SSs55s
+sSSsSs5s
+s55SS5S5S5
+sssSs5S55
+5SS5sS5s
+SS5s5ssS
+SsSs55SSs5
+5ssssS55S
+s55s5ss
+sSsSss
+s5SsSs5
+S5sS5s555
+sss5S555
+555ss55s5
+sS5ss55sS
+sss5s5s
+5SSss5
+Ss555ssSs5
+S5SSsSsS
+SsSSSss55s
+sS5Sss5
+55SS5SS5S
+5S5sSSSS
+5S5sS5s5s
+S55SSs
+S5Ss5555ss
+s55s5sS
+ss5s55S5
+S55sssSSsS
+55S5
+55sS555S
+Ss5S5sS
+S5Ss555s55
+5SSssS5ss
+sSssssSS
+SSSsssSS5
+5sSs55
+5SSssS
+sSsS5S55
+sSsS5s5
+555S5SsS
+sSSSSsss5S
+55SS
+S5S5S555s5
+55sSss55S
+s5S55SsS
+5s55sSsssS
+55Sss55SS
+ssS5S555S
+S5ssS5Ss
+Ss5SSSS5SS
+5sssS5s555
+sssss5S
+S5s5SsS55
+5SssSSSssS
+5ssSs55
+S555s5
+sss5S
+S5s55sS55
+ss5s5S5SSs
+SS5SS5
+S55SS5
+s5ss5SS55s
+5SsS5SsSss
+55Ss
+5sSS5sss
+SS55Ss55s
+s5SsSsS
+5S5S5S5
+s5sSSsSs5S
+5SSSsSs
+Ss55S555s
+Ss5SSSSS55
+S5Ssss5sS
+Ss555Ss55
+5ssSSs55
+5SssS5SsS5
+555SsS5
+S55sss5sS
+555sSsSS55
+55SSsssSs
+SS5SssSS
+ss5SsS
+5S5s5s55
+sSsSSsS55
+SSssss
+sSSSs55
+55S55S5ss
+55SS5SSs
+5Sss5s
+S5Sssssss
+SSS5Ss5
+S5SSs5s
+s5s5SS5
+s55S5ssSSs
+s5SS5S55s
+5s5SSSSS
+5555SS55S
+SS5SssSs
+s5S5SS555
+Ss555sSs
+5S5s55SSsS
+SS5Ss5SsS
+5s55s555
+5ss5s55
+SSSs5SsS
+Ssss5S55
+sS5S5sS5
+Sssss5Ss5S
+5ss5sSsS
+5SsSsS5s5
+5555SS5Ss5
+SSSs555S
+SssSsss5
+SS5ssSssS
+ssSS5ss
+SSsSS555
+ss5S5ss55
+SSsSs5S5
+sSs5Sss5S
+sSsS555
+ssSSSss
+SsSS5sssSS
+5SSS5
+SSS55sS
+SSs5s5s5s
+sS5SsssSS5
+SSsss5
+S5SSs55
+55sss5S5s5
+SSSSS5s
+sS5sSSs55s
+55ssss5S
+sSS5s5
+s55S555sS
+SSs5sS5SsS
+SsssS55Ss
+SSss5SsS55
+Ss55ss
+5S5sSs5s
+sSSSs5S
+5555S
+SsssSSS5
+Sss5S
+sSsSS55sS5
+S5SSss55
+SS555Ss5S
+5s5Ss55S
+ssS5S5ss
+sssSS
+SsssSsS
+Ss5SsSS
+SsSs555SS
+5555SSS5ss
+Ss55ssS55
+ssssss
+5Ss5sSs
+5sSS5sSS5
+s5sss5
+sS5S555ssS
+5SsSSSsSS
+5S555SS
+SSs5S5
+sssSs
+ss55ssS5
+SSsSsSs
+Ss555SSs5S
+S5SSSs55sS
+sssssS
+555SsS
+55Sss5S
+s5S5ss5s
+5SsSsSSsSS
+5S555S5
+555s5555s5
+ssS5sss
+5s5Ss55
+sSSsSSs5
+ss5S5ss55s
+SSSSss5s
+S5Ss5s55s
+5sSSSsSS
+55SS5s5
+s5s55s5s
+sSsssS555
+5555SSS
+SSsS5sSSss
+sS55sS
+SsS55SS
+555Ss5
+55555S55
+sssss5
+Ss5s55s
+SSS55S555
+sssSsSs5
+SS5s555ss
+sS5sSS5
+s5ssss
+SSSS5sSS
+sssS5s5s
+S5sSssS55s
+sssS5
+SsssSss
+SS555S5555
+sSSS555S5
+5S5SS5
+55sSsS5s
+sS55ss
+ssS5s5s
+Ss5s5Ss5
+S5sssS5Ss
+SsS5SSs
+S5SSs5
+5sssSssS
+ss5s5SSSS
+sSs5S55sSS
+S55s5s5S
+5ss5555S
+SS5SsS
+SSS555SS
+5Ss555sS5
+ssS5sS5S
+S555sssS
+5s5Ss5sSsS
+SS5SS5Ss55
+SSss5s
+ss5s
+ss555s555S
+5sSsSSs5
+Ss5SSSsssS
+5SS5Sss
+5s5SS5SS5S
+SsSSs55
+s555S55s
+SSs555S
+5sssSsss
+sSS55ssSsS
+S5sS5S5ss
+5S55s5SsSS
+Ss555s
+Ss5SssSs5
+sSss55ss
+5SsS55SS
+5S55SSs5
+ss5S
+s5S5S5ssSs
+sS5S5sS
+S5s5s55S
+ssssS5S
+s555S55S
+sss555S55
+S5S5SsS55s
+SsS5sSs
+5SS5SSSSS
+SSs5555
+SsS5SSsSS
+SSS5s555
+5Ss5S5s
+sSsS5Ss5S
+55SS5ss
+SSS555s5s
+S555SssSSs
+ss55
+sSSSss
+5Ssss
+sS5S
+S55s55SS
+SsSsss
+SsSsSSs55s
+ssssS55
+S555S5sS5
+SS5SSs
+5ss555sss
+SS555S5s5
+S5ss5S
+S5SSssss
+5Sss5S5s5s
+SSSsSS5s5
+SssSS5S
+555sS5s5s
+s55sS5SSss
+ss5s55sS5S
+SSSssS
+5S55SSss
+5SssS
+55Sss5
+sS5s
+s55ss5SSS
+55sSsSs
+SS5sss5SS
+5sSsSSsSSS
+SSs5sss
+sSS5s
+s55sSsss5S
+SSSSsSs5
+S5SSSS
+Ss5555
+s55sSs5SS
+5S5555Ss
+5ssS5555SS
+5sss5s
+sSsSS5SS
+ssSS55SSS
+Ssss5ss5sS
+sss555sS5
+555s
+5SS55s55s
+5SSs5SSS
+s5sS5sSss
+55sssSSs5S
+55S5s5s55
+ssSssSSs
+5ss5SSs
+555s555S
+5s55sSS
+5Ss5S
+S5SSSs
+s5sss555
+5SS5555
+55SsS5Ss
+ssSSsss
+5sss5S
+s5SS5Sss
+5ssSss5
+S5sSs
+5sS55s5SS5
+S5SsS5sS
+s55s55SssS
+5ss5s5
+SSS5S5
+5S55S55s
+5555sS5S
+S5sS5sSSss
+5Ss5SsS5s
+5s55sS5
+sSsSs
+SsS5s5sS
+SSsSSsSSs
+sss55SSs
+5555
+SSSsss
+55s55s5s5
+5ssSssS
+Ss5SSsSs5
+SSssss5S
+s55Sss5ss
+Ss5Ss5SSss
+Ss5sSssssS
+s5s5ss5
+5SSSSs5SS5
+555s5SS
+S5SSS5
+s5s55sS55s
+555ssS
+S5SSSS55
+sSsSS
+5S5sS5sSs5
+ssSS55sSs
+Ss555S
+SssS5SsS5s
+S5sS5
+SSSss55s55
+SsS5s555S
+s5sssSSS5S
+sssSsS5s
+5ss5ss
+5Ss5s5S5s5
+sSs5SSS
+55ss5555
+sssSSS5sSS
+SS5sSss
+5ss5ssSs5s
+sS5555
+S5s5SS5ss5
+SS55s
+5SsSSS
+5S55s55sS5
+SSsS5S5
+s555sSS5
+5S55S5s
+55SSs5sS5
+sS55SsssSS
+SsssS5S
+55Sss5555
+5S5S555s
+sSs5SSs
+S5ss5s
+S5ssss55
+ss5sss5
+SSs55s5SSS
+sSssSs5
+5SS5Ss
+SS55S
+S555S55sS5
+5S55Ssss5
+S5SS5ss55
+SsS5S5S
+5SsS5s5
+S5SSss5
+sssssSS5s5
+5S5s5sSS
+s55SsSss
+SSsSss
+5Ss55sSsss
+5s5SsSss
+s5SS5s5S
+SSs5sssSS5
+SsSS5sS55
+sSS5S
+5SSSsS5s
+S555sS
+555sSSS5S5
+s5S5ss55
+5SsS5s5Sss
+SS555
+5S55Ss5
+sSssSsS
+S55sS5s
+s5SS55sS
+S5SSssS
+5SsSs55s5
+5S5sss5ssS
+s5S5555S5
+5Ssss55sss
+SsssS555S
+55s5ssSs
+5SsSsSss55
+Ss5SsSsS5s
+5SS55S5ss
+55sssS55
+SsSS5sS55s
+555ssS5
+sss5Ss5
+5s5sS55s
+sSsSS5S
+SS555ssS5
+55sS5
+SSSSSsS
+55S5sS5S55
+s5ss5S55S
+sSssSSss5S
+5ssS55
+s5SSSSsSS
+ssS5Ss
+SSS5S5s5S
+S55SSsS
+sss555s
+55sSsSSS
+sS5sS555s
+ss5s5
+SsSss5S5
+s55SS5
+s5Ss5s
+SsSs5S
+sssSsSS5S
+5s5SssssSS
+Ss5ssSss
+5S55s5S
+sssSsS
+5SSS55S
+SsssS5Ss5
+sSSsS5SsS
+5sSSSS5ss
+sss555S
+5sSs5S5
+s5S5SSs5Ss
+SSSsss55Ss
+SsSSs5s
+SSssS
+SsSs5s
+5SS555Ss5
+55sssss5
+sSsS5sS5ss
+SSS5
+sssSss
+S5SSS
+ss5sSS
+S55ss
+s5ss5S5S5
+ss5ss
+55ssSsss
+s5ss555
+S55SSsSSS
+sss5555
+5S5s555
+SSss5
+SsSSsSSsS
+ss555S
+SsS55S5s5
+SSSS
+SSs5SS5s
+55S5s5SS55
+sS5sssss
+5SSSSs5ss5
+5SsSSS5S
+SsSS5S55
+ss5sS5
+s55SssS5
+5sSsSs5ssS
+ss5sS
+SssS555ss
+5Ss5sSsss5
+s5555Ss
+55sSSS
+s5sSS555
+sSS5sSs55
+SS5sS5S5
+s55SSS
+ss555s
+SSSs
+5sss5SsS
+SS5555SsS
+5sss55ssS
+5s5SS5ssS
+SS5S5sss55
+55s55ss5S
+555SsS5ss
+sSS5ss55
+5sSss5Ss5S
+sss5sss
+5sS55Ss
+5sssSSs5
+sss55
+5S55S5ss
+s55S55
+5s5S5S
+5SSs55s5s
+5SS5s5
+s5s5s55S
+5sss55555
+S555sSs5
+5sSS55S5s
+5sSS55s
+ss5s55sssS
+Ss5SS5
+S55S5s5sS
+Ss5s5555
+S55s5ss
+SS5Ss5ssS5
+55S5s55Ss
+S5sssSSSs
+5sS5Sss55
+sS55sSS5
+555s5S
+5S5SSsS
+sSSs5s
+5SSs5
+5ssSSsS
+ss5Ss5S5
+sSSsssS55
+S55S5ss5
+5555S5SS
+55s55sS55
+5ss555S5s
+5sSSs5s5ss
+SsSSs5
+SSssssss
+Ssss5555
+5sssss55
+SSSss5S5SS
+sss5ss5
+5sss5ss5s
+55s5s5Ss
+5S5SSs5
+SSsssSs
+SSSSsssSSs
+5sSSSS5
+5SSsS
+SsssSS5s
+SsSSsS
+Ss5SSs
+555sS55sS
+s55s5SSS
+5ss5s5S
+SSSS55S5SS
+55SsSS5sS5
+SSs5sS5
+sSSs55
+5SSsSss55s
+5s5S55
+5sS5S5SS
+5SSss
+Ss5SSS
+5SsS5S5S5S
+55ssS
+s55S5S
+S5s5sS55s
+5ssSss
+Sss5S5SS
+SSSS5Ss
+55S5SSss55
+5s5s55
+5s5555Ss5
+5Sss5s5s55
+s5SSS
+SSs555ss
+55sSs5ss5
+SsSsSs5s5
+ss5s5S55
+5SS5SS5SS
+5SSsS555SS
+5Ss5sss55s
+sSsSsSss
+SSsS55SS
+sSSsS55sss
+5SsSsS
+SS5Ss55SsS
+555S5S5Ss
+sssS5Ss5
+S5sSSS55
+55sssSS5S
+555Ssss5S
+ss5ss5ss5s
+SsS555Ss5
+sSss55Ss
+sSS5s5ss
+s5SSs
+5s5S5s5
+5SSSSsSSs5
+Sss5Ss
+ssssS5Ss
+sSSSs5
+5ssSs5
+5Ss55
+5ssSSSSs
+ss5ss555S
+ssss5ss5s
+s55sSSS
+SSss5S
+555SS5555
+SSsSSS
+55ss55s55S
+55SSssS
+5s5sSsS555
+sSSSsS
+s55sss5
+S5SsS5S55S
+5ss555S
+5555s5
+5ss5S5s
+ssS5s5sSs5
+Ss5s5ssssS
+5sSssSs5s5
+5Ss5ssS5Ss
+s55SSSSS
+555s5s
+s55sSSs
+sSs55SSS
+S5S55Sss5
+S5ss5Sss
+s5SS5
+5s5S5Ss55S
+55ss55s55s
+5SS5Ss55
+s55sS5SSs
+sSs55SS
+SSs55S5
+5sSS5S5ssS
+ss55sSSs
+5sSsS55
+5S5SssS5Ss
+555s5
+55SsSS5
+55S5SSsS5s
+sSs5sSsS
+ssSssS
+S5S55ssS
+55SsSSS
+55sSS5SS
+55s5SsSS5s
+SSssS55Sss
+sSs5SS
+sS555sS5sS
+ss55s5Sss
+5sSSSSS5S
+ss55sSSS
+S5ssssSS
+5SsssSS5
+SS5SSs5
+5Ss5s
+5SssS555
+ssss55
+5s5sSsss
+5Sss5S55S
+SSss5Ss
+sSs5sSss
+S5sSss5sSS
+ss5s555Sss
+Ss5S55SSS5
+Sss5SSS5
+SSSsss5S
+s5s5ssS5
+s5sSsss
+sss5SS
+SSs5s55S
+Ss5S55sSs5
+555ss
+SssSsSss
+ssssss5sSs
+5S55SsS
+ssSsS5S5S
+ss55sSS5
+5ssS5ssssS
+5s5s5sS55
+sSs5S5S5
+55SS5SsSs
+5Sssss55
+sS5SsSss
+SSsSsss5S
+555Ss5sS5S
+s55SSsssSs
+Ss55ss5s
+5sssSsSSS
+555Ss5S
+Ss5Ss5
+ss5SSs
+SsSS55s
+555sS
+S55S5SS5SS
+s555SSs
+5SsSss
+S5ss55sS5s
+s5SsSssS
+5sS5ssS5
+55s5s5SS
+55sS55sSS
+ssss5s
+S55ss5s
+S55s5S5S
+555Ss5sS5s
+5SsSsSs
+SSSssS5s
+s55S5Ss5
+Sssss5S
+SsSS55S

+ 24 - 0
app/src/androidTest/java/com/adealink/weparty/ExampleInstrumentedTest.kt

@@ -0,0 +1,24 @@
+package com.adealink.weparty
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+    @Test
+    fun useAppContext() {
+        // Context of the app under test.
+        val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+        assertEquals("com.adealink.weparty", appContext.packageName)
+    }
+}

+ 11 - 0
app/src/debug/assets/timpush-configs.json

@@ -0,0 +1,11 @@
+{
+  "version": "1.0.1",
+  "com.partyjoy.yoki": {
+    "manifestPlaceholders": {
+      "VIVO_APPKEY": "",
+      "VIVO_APPID": "",
+      "HONOR_APPID": ""
+    },
+    "fcmPushBussinessId": "8218"
+  }
+}

+ 47 - 0
app/src/debug/google-services.json

@@ -0,0 +1,47 @@
+{
+  "project_info": {
+    "project_number": "968451835912",
+    "project_id": "yoki-debug",
+    "storage_bucket": "yoki-debug.firebasestorage.app"
+  },
+  "client": [
+    {
+      "client_info": {
+        "mobilesdk_app_id": "1:968451835912:android:9f3093ae99d06fd5a629bd",
+        "android_client_info": {
+          "package_name": "com.partyjoy.yoki"
+        }
+      },
+      "oauth_client": [
+        {
+          "client_id": "968451835912-ni8f8ith97nv5obgq1ttt3qvbajplku3.apps.googleusercontent.com",
+          "client_type": 1,
+          "android_info": {
+            "package_name": "com.partyjoy.yoki",
+            "certificate_hash": "a596581469039c165a4107e2a7224c22bb07b404"
+          }
+        },
+        {
+          "client_id": "968451835912-2kqb2tm5khcp59ukg528fbgclbvsicia.apps.googleusercontent.com",
+          "client_type": 3
+        }
+      ],
+      "api_key": [
+        {
+          "current_key": "AIzaSyCWc4fzoYDW7T9LQqm96ijJU7zb0gbY44I"
+        }
+      ],
+      "services": {
+        "appinvite_service": {
+          "other_platform_oauth_client": [
+            {
+              "client_id": "968451835912-2kqb2tm5khcp59ukg528fbgclbvsicia.apps.googleusercontent.com",
+              "client_type": 3
+            }
+          ]
+        }
+      }
+    }
+  ],
+  "configuration_version": "1"
+}

+ 199 - 0
app/src/main/AndroidManifest.xml

@@ -0,0 +1,199 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:dist="http://schemas.android.com/apk/distribution"
+    xmlns:tools="http://schemas.android.com/tools"
+    tools:ignore="LockedOrientationActivity">
+
+    <dist:module dist:instant="true" />
+
+    <uses-permission
+        android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION"
+        tools:node="remove" />
+    <uses-permission
+        android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"
+        tools:node="remove" />
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
+    <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="com.google.android.gms.permission.AD_ID" />
+    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+    <uses-permission
+        android:name="android.permission.READ_EXTERNAL_STORAGE"
+        android:maxSdkVersion="32" />
+    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
+    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <!--      后续需要再打开-->
+    <!--    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />-->
+    <!--    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />-->
+
+    <queries>
+        <package android:name="com.google.android.gms" />
+        <package android:name="com.facebook.katana" />
+        <package android:name="com.whatsapp" />
+
+        <package android:name="com.zhiliaoapp.musically" />
+        <package android:name="com.ss.android.ugc.trill" />
+    </queries>
+
+    <uses-sdk
+        android:minSdkVersion="21"
+        tools:overrideLibrary="com.hw.videoprocessor" />
+
+    <application
+        android:name=".App"
+        android:allowBackup="false"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:largeHeap="true"
+        android:networkSecurityConfig="@xml/network_security_config"
+        android:requestLegacyExternalStorage="true"
+        android:supportsRtl="true"
+        android:theme="@style/Theme.Weparty"
+        tools:replace="android:allowBackup"
+        tools:targetApi="n">
+
+        <meta-data
+            android:name="android.app.lib_name"
+            android:value="cocos" />
+
+        <!-- Facebook AppId -->
+        <meta-data
+            android:name="com.facebook.sdk.ApplicationId"
+            android:value="fb${fbAppId}" />
+        <meta-data
+            android:name="com.facebook.sdk.ClientToken"
+            android:value="${fbClientToken}" />
+
+        <meta-data
+            android:name="android.max_aspect"
+            android:value="3.0" />
+
+        <!-- Set custom default icon. This is used when no icon is set for incoming notification messages.-->
+        <meta-data
+            android:name="com.google.firebase.messaging.default_notification_icon"
+            android:resource="@drawable/notification_ic" />
+
+        <receiver
+            android:name="com.appsflyer.SingleInstallBroadcastReceiver"
+            android:exported="true">
+            <intent-filter>
+                <action android:name="com.android.vending.INSTALL_REFERRER" />
+            </intent-filter>
+        </receiver>
+
+        <activity
+            android:name=".MainActivity"
+            android:exported="true"
+            android:launchMode="singleTask"
+            android:screenOrientation="portrait"
+            android:theme="@style/MainActivityTheme">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="${applicationId}.GO_MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <intent-filter>
+                <action android:name="DISPATCH_ROUTER" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+
+        <activity
+            android:name="com.adealink.frame.router.RouterDeepLinkActivity"
+            android:configChanges="keyboardHidden|orientation|screenSize"
+            android:exported="true"
+            android:theme="@style/TranslucentActivityTheme">
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+
+                <data android:scheme="${deepLinkScheme}" />
+                <data android:host="${deepLinkHost}" />
+            </intent-filter>
+            <intent-filter android:autoVerify="true">
+                <action android:name="android.intent.action.VIEW" />
+
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+
+                <data android:scheme="http" />
+                <data android:scheme="https" />
+                <data android:host="${httpDeepLinkHost}" />
+            </intent-filter>
+
+            <intent-filter android:autoVerify="true">
+                <action android:name="android.intent.action.VIEW" />
+
+                <category android:name="android.intent.category.DEFAULT" />
+                <category android:name="android.intent.category.BROWSABLE" />
+
+                <data android:scheme="http" />
+                <data android:scheme="https" />
+                <data android:host="yoki.chat" />
+                <data android:path="/applink" />
+            </intent-filter>
+        </activity>
+        <activity
+            android:name=".debug.DebugActivity"
+            android:screenOrientation="portrait"
+            android:windowSoftInputMode="adjustPan|stateVisible" />
+
+        <provider
+            android:name="com.facebook.FacebookContentProvider"
+            android:authorities="com.facebook.app.FacebookContentProvider${fbAppId}"
+            android:exported="true" />
+
+        <provider
+            android:name="androidx.core.content.FileProvider"
+            android:authorities="${applicationId}.android7.fileprovider"
+            android:exported="false"
+            android:grantUriPermissions="true">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/file_paths" />
+        </provider>
+
+        <activity
+            android:name="com.adealink.weparty.imageselect.ImageSelectActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name="com.adealink.weparty.imageselect.clip.ClipImageActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name="com.adealink.weparty.imageselect.selectpreview.SelectPreviewActivity"
+            android:screenOrientation="portrait" />
+        <activity
+            android:name="com.adealink.weparty.imageselect.takePhoto.CameraActivity"
+            android:screenOrientation="portrait" />
+
+        <activity
+            android:name="com.adealink.weparty.videoselect.VideoSelectActivity"
+            android:screenOrientation="portrait" />
+
+        <!-- Trigger Google Play services to install the backported photo picker module. -->
+        <service
+            android:name="com.google.android.gms.metadata.ModuleDependencies"
+            android:enabled="false"
+            android:exported="false"
+            tools:ignore="MissingClass">
+            <intent-filter>
+                <action android:name="com.google.android.gms.metadata.MODULE_DEPENDENCIES" />
+            </intent-filter>
+            <meta-data
+                android:name="photopicker_activity:0:required"
+                android:value="" />
+        </service>
+
+    </application>
+
+</manifest>

BIN
app/src/main/assets/cocosgame_coins_collect_effect.svga


BIN
app/src/main/assets/common_svip_ic.svga


BIN
app/src/main/assets/common_task_reward_dialog.svga


BIN
app/src/main/assets/couple_love_play.svga


BIN
app/src/main/assets/custom_refresh_loading.svga


BIN
app/src/main/assets/game_downloading.svga


BIN
app/src/main/assets/home_float_income_bg.svga


BIN
app/src/main/assets/home_float_income_fg.svga


BIN
app/src/main/assets/home_say_hi.svga


BIN
app/src/main/assets/home_say_hi_rtl.svga


BIN
app/src/main/assets/home_user_in_room.svga


BIN
app/src/main/assets/ic_greedy_personal_out_room.svga


BIN
app/src/main/assets/ic_home_entrance_new_user_guide.svga


BIN
app/src/main/assets/ic_home_entrance_new_user_guide_ar.svga


BIN
app/src/main/assets/im_user_in_room.svga


BIN
app/src/main/assets/income_message_reminder_reply_btn.svga


BIN
app/src/main/assets/level/wealth_level_0_to_29_bg.svga


BIN
app/src/main/assets/level/wealth_level_100_bg.svga


BIN
app/src/main/assets/level/wealth_level_30_to_39_bg.svga


BIN
app/src/main/assets/level/wealth_level_40_to_49_bg.svga


BIN
app/src/main/assets/level/wealth_level_50_to_59_bg.svga


BIN
app/src/main/assets/level/wealth_level_60_to_69_bg.svga


BIN
app/src/main/assets/level/wealth_level_70_to_79_bg.svga


BIN
app/src/main/assets/level/wealth_level_80_to_89_bg.svga


BIN
app/src/main/assets/level/wealth_level_90_to_99_bg.svga


BIN
app/src/main/assets/match_entrance_bg.svga


BIN
app/src/main/assets/match_entrance_bg_rtl.svga


BIN
app/src/main/assets/message_chat_cp.svga


BIN
app/src/main/assets/message_chat_guard.svga


BIN
app/src/main/assets/message_income_diamond.svga


BIN
app/src/main/assets/message_reminder_reply_btn.svga


BIN
app/src/main/assets/new_user_reward.svga


BIN
app/src/main/assets/profile_custom_id_bg.svga


BIN
app/src/main/assets/profile_sid_1_4.svga


BIN
app/src/main/assets/profile_sid_5_7.svga


BIN
app/src/main/assets/profile_sid_8_10.svga


BIN
app/src/main/assets/room_rocket_entrance_level1.svga


BIN
app/src/main/assets/room_rocket_entrance_level2.svga


BIN
app/src/main/assets/room_rocket_entrance_level3.svga


BIN
app/src/main/assets/room_rocket_entrance_level4.svga


BIN
app/src/main/assets/room_rocket_entrance_level5.svga


BIN
app/src/main/assets/skin.zip


BIN
app/src/main/assets/sound_wave_loading.svga


BIN
app/src/main/assets/svip_6_digit_good_id.svga


BIN
app/src/main/assets/svip_7_digit_good_id.svga


BIN
app/src/main/assets/svip_8_digit_good_id.svga


BIN
app/src/main/assets/today_fate_btn.svga


BIN
app/src/main/assets/today_fate_content_animation_bg.svga


BIN
app/src/main/assets/today_fate_head_animation_bg.svga


Diferenças do arquivo suprimidas por serem muito extensas
+ 0 - 0
app/src/main/assets/v_cube.license


BIN
app/src/main/assets/wallet_recharge_daily_task_gift.svga


+ 782 - 0
app/src/main/java/androidx/viewpager2/adapter/LazyFragmentStateAdapter.java

@@ -0,0 +1,782 @@
+package androidx.viewpager2.adapter;
+
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+import static androidx.core.util.Preconditions.checkArgument;
+import static androidx.lifecycle.Lifecycle.State.CREATED;
+import static androidx.lifecycle.Lifecycle.State.INITIALIZED;
+import static androidx.lifecycle.Lifecycle.State.RESUMED;
+import static androidx.lifecycle.Lifecycle.State.STARTED;
+import static androidx.recyclerview.widget.RecyclerView.NO_ID;
+
+import android.annotation.SuppressLint;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Parcelable;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.widget.FrameLayout;
+
+import androidx.annotation.CallSuper;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.collection.ArraySet;
+import androidx.collection.LongSparseArray;
+import androidx.core.view.ViewCompat;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+import androidx.lifecycle.Lifecycle;
+import androidx.lifecycle.LifecycleEventObserver;
+import androidx.lifecycle.LifecycleOwner;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.viewpager2.widget.ViewPager2;
+
+import com.adealink.frame.log.Log;
+
+import java.util.Set;
+
+
+/**
+ * copy from FragmentStateAdapter
+ * modify: setMaxLifecycle(fragment, INITIALIZED) 懒加载生命周期改为INITIALIZED
+ */
+public abstract class LazyFragmentStateAdapter extends
+        RecyclerView.Adapter<FragmentViewHolder> implements StatefulAdapter {
+    // State saving config
+    private static final String KEY_PREFIX_FRAGMENT = "f#";
+    private static final String KEY_PREFIX_STATE = "s#";
+
+    // Fragment GC config
+    private static final long GRACE_WINDOW_TIME_MS = 10_000; // 10 seconds
+
+    @SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
+    final Lifecycle mLifecycle;
+    @SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
+    final FragmentManager mFragmentManager;
+
+    // Fragment bookkeeping
+    @SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
+    final LongSparseArray<Fragment> mFragments = new LongSparseArray<>();
+    private final LongSparseArray<Fragment.SavedState> mSavedStates = new LongSparseArray<>();
+    private final LongSparseArray<Integer> mItemIdToViewHolder = new LongSparseArray<>();
+
+    private FragmentMaxLifecycleEnforcer mFragmentMaxLifecycleEnforcer;
+
+    // Fragment GC
+    @SuppressWarnings("WeakerAccess") // to avoid creation of a synthetic accessor
+            boolean mIsInGracePeriod = false;
+    private boolean mHasStaleFragments = false;
+
+    /**
+     * @param fragmentActivity if the {@link ViewPager2} lives directly in a
+     *                         {@link FragmentActivity} subclass.
+     * @see androidx.viewpager2.adapter.FragmentStateAdapter#FragmentStateAdapter(Fragment)
+     * @see androidx.viewpager2.adapter.FragmentStateAdapter#FragmentStateAdapter(FragmentManager, Lifecycle)
+     */
+    public LazyFragmentStateAdapter(@NonNull FragmentActivity fragmentActivity) {
+        this(fragmentActivity.getSupportFragmentManager(), fragmentActivity.getLifecycle());
+    }
+
+    /**
+     * @param fragment if the {@link ViewPager2} lives directly in a {@link Fragment} subclass.
+     * @see androidx.viewpager2.adapter.FragmentStateAdapter#FragmentStateAdapter(FragmentActivity)
+     * @see androidx.viewpager2.adapter.FragmentStateAdapter#FragmentStateAdapter(FragmentManager, Lifecycle)
+     */
+    public LazyFragmentStateAdapter(@NonNull Fragment fragment) {
+        this(fragment.getChildFragmentManager(), fragment.getLifecycle());
+    }
+
+    /**
+     * @param fragmentManager of {@link ViewPager2}'s host
+     * @param lifecycle       of {@link ViewPager2}'s host
+     * @see androidx.viewpager2.adapter.FragmentStateAdapter#FragmentStateAdapter(FragmentActivity)
+     * @see androidx.viewpager2.adapter.FragmentStateAdapter#FragmentStateAdapter(Fragment)
+     */
+    public LazyFragmentStateAdapter(@NonNull FragmentManager fragmentManager,
+                                    @NonNull Lifecycle lifecycle) {
+        mFragmentManager = fragmentManager;
+        mLifecycle = lifecycle;
+        super.setHasStableIds(true);
+    }
+
+    @SuppressLint("RestrictedApi")
+    @CallSuper
+    @Override
+    public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
+        Log.d(getClass().getSimpleName(), "start adapter: " + this + " | onAttachedToRecyclerView " + recyclerView);
+        checkArgument(mFragmentMaxLifecycleEnforcer == null);
+        mFragmentMaxLifecycleEnforcer = new FragmentMaxLifecycleEnforcer();
+        mFragmentMaxLifecycleEnforcer.register(recyclerView);
+        Log.d(getClass().getSimpleName(), "end adapter: " + this + " | onAttachedToRecyclerView " + recyclerView);
+    }
+
+    @CallSuper
+    @Override
+    public void onDetachedFromRecyclerView(@NonNull RecyclerView recyclerView) {
+        Log.d(getClass().getSimpleName(), "start adapter: " + this + " | onDetachedFromRecyclerView " + recyclerView);
+        mFragmentMaxLifecycleEnforcer.unregister(recyclerView);
+        mFragmentMaxLifecycleEnforcer = null;
+        Log.d(getClass().getSimpleName(), "end adapter: " + this + " | onDetachedFromRecyclerView " + recyclerView);
+    }
+
+    /**
+     * Provide a new Fragment associated with the specified position.
+     * <p>
+     * The adapter will be responsible for the Fragment lifecycle:
+     * <ul>
+     *     <li>The Fragment will be used to display an item.</li>
+     *     <li>The Fragment will be destroyed when it gets too far from the viewport, and its state
+     *     will be saved. When the item is close to the viewport again, a new Fragment will be
+     *     requested, and a previously saved state will be used to initialize it.
+     * </ul>
+     *
+     * @see ViewPager2#setOffscreenPageLimit
+     */
+    public abstract @NonNull Fragment createFragment(int position);
+
+    @NonNull
+    @Override
+    public final FragmentViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        return FragmentViewHolder.create(parent);
+    }
+
+    @Override
+    public final void onBindViewHolder(final @NonNull FragmentViewHolder holder, int position) {
+        final long itemId = holder.getItemId();
+        final int viewHolderId = holder.getContainer().getId();
+        final Long boundItemId = itemForViewHolder(viewHolderId); // item currently bound to the VH
+        if (boundItemId != null && boundItemId != itemId) {
+            removeFragment(boundItemId);
+            mItemIdToViewHolder.remove(boundItemId);
+        }
+
+        mItemIdToViewHolder.put(itemId, viewHolderId); // this might overwrite an existing entry
+        ensureFragment(position);
+
+        /** Special case when {@link RecyclerView} decides to keep the {@link container}
+         * attached to the window, but not to the view hierarchy (i.e. parent is null) */
+        final FrameLayout container = holder.getContainer();
+        if (ViewCompat.isAttachedToWindow(container)) {
+            if (container.getParent() != null) {
+                throw new IllegalStateException("Design assumption violated.");
+            }
+            container.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+                @Override
+                public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                                           int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                    if (container.getParent() != null) {
+                        container.removeOnLayoutChangeListener(this);
+                        placeFragmentInViewHolder(holder);
+                    }
+                }
+            });
+        }
+
+        gcFragments();
+    }
+
+    @SuppressWarnings("WeakerAccess")
+        // to avoid creation of a synthetic accessor
+    void gcFragments() {
+        if (!mHasStaleFragments || shouldDelayFragmentTransactions()) {
+            return;
+        }
+
+        // Remove Fragments for items that are no longer part of the data-set
+        Set<Long> toRemove = new ArraySet<>();
+        for (int ix = 0; ix < mFragments.size(); ix++) {
+            long itemId = mFragments.keyAt(ix);
+            if (!containsItem(itemId)) {
+                toRemove.add(itemId);
+                mItemIdToViewHolder.remove(itemId); // in case they're still bound
+            }
+        }
+
+        // Remove Fragments that are not bound anywhere -- pending a grace period
+        if (!mIsInGracePeriod) {
+            mHasStaleFragments = false; // we've executed all GC checks
+
+            for (int ix = 0; ix < mFragments.size(); ix++) {
+                long itemId = mFragments.keyAt(ix);
+                if (!isFragmentViewBound(itemId)) {
+                    toRemove.add(itemId);
+                }
+            }
+        }
+
+        for (Long itemId : toRemove) {
+            removeFragment(itemId);
+        }
+    }
+
+    private boolean isFragmentViewBound(long itemId) {
+        if (mItemIdToViewHolder.containsKey(itemId)) {
+            return true;
+        }
+
+        Fragment fragment = mFragments.get(itemId);
+        if (fragment == null) {
+            return false;
+        }
+
+        View view = fragment.getView();
+        if (view == null) {
+            return false;
+        }
+
+        return view.getParent() != null;
+    }
+
+    private Long itemForViewHolder(int viewHolderId) {
+        Long boundItemId = null;
+        for (int ix = 0; ix < mItemIdToViewHolder.size(); ix++) {
+            if (mItemIdToViewHolder.valueAt(ix) == viewHolderId) {
+                if (boundItemId != null) {
+                    throw new IllegalStateException("Design assumption violated: "
+                            + "a ViewHolder can only be bound to one item at a time.");
+                }
+                boundItemId = mItemIdToViewHolder.keyAt(ix);
+            }
+        }
+        return boundItemId;
+    }
+
+    private void ensureFragment(int position) {
+        long itemId = getItemId(position);
+        if (!mFragments.containsKey(itemId)) {
+            // TODO(133419201): check if a Fragment provided here is a new Fragment
+            Fragment newFragment = createFragment(position);
+            newFragment.setInitialSavedState(mSavedStates.get(itemId));
+            mFragments.put(itemId, newFragment);
+        }
+    }
+
+    @Override
+    public final void onViewAttachedToWindow(@NonNull final FragmentViewHolder holder) {
+        placeFragmentInViewHolder(holder);
+        gcFragments();
+    }
+
+    /**
+     * @param holder that has been bound to a Fragment in the {@link #onBindViewHolder} stage.
+     */
+    @SuppressWarnings("WeakerAccess")
+    // to avoid creation of a synthetic accessor
+    void placeFragmentInViewHolder(@NonNull final FragmentViewHolder holder) {
+        Fragment fragment = mFragments.get(holder.getItemId());
+        if (fragment == null) {
+            throw new IllegalStateException("Design assumption violated.");
+        }
+        FrameLayout container = holder.getContainer();
+        View view = fragment.getView();
+
+        /*
+        possible states:
+        - fragment: { added, notAdded }
+        - view: { created, notCreated }
+        - view: { attached, notAttached }
+
+        combinations:
+        - { f:added, v:created, v:attached } -> check if attached to the right container
+        - { f:added, v:created, v:notAttached} -> attach view to container
+        - { f:added, v:notCreated, v:attached } -> impossible
+        - { f:added, v:notCreated, v:notAttached} -> schedule callback for when created
+        - { f:notAdded, v:created, v:attached } -> illegal state
+        - { f:notAdded, v:created, v:notAttached } -> illegal state
+        - { f:notAdded, v:notCreated, v:attached } -> impossible
+        - { f:notAdded, v:notCreated, v:notAttached } -> add, create, attach
+         */
+
+        // { f:notAdded, v:created, v:attached } -> illegal state
+        // { f:notAdded, v:created, v:notAttached } -> illegal state
+        if (!fragment.isAdded() && view != null) {
+            throw new IllegalStateException("Design assumption violated.");
+        }
+
+        // { f:added, v:notCreated, v:notAttached} -> schedule callback for when created
+        if (fragment.isAdded() && view == null) {
+            scheduleViewAttach(fragment, container);
+            return;
+        }
+
+        // { f:added, v:created, v:attached } -> check if attached to the right container
+        if (fragment.isAdded() && view.getParent() != null) {
+            if (view.getParent() != container) {
+                addViewToContainer(view, container);
+            }
+            return;
+        }
+
+        // { f:added, v:created, v:notAttached} -> attach view to container
+        if (fragment.isAdded()) {
+            addViewToContainer(view, container);
+            return;
+        }
+
+        // { f:notAdded, v:notCreated, v:notAttached } -> add, create, attach
+        if (!shouldDelayFragmentTransactions()) {
+            scheduleViewAttach(fragment, container);
+            mFragmentManager.beginTransaction()
+                    .add(fragment, "f" + holder.getItemId())
+                    .setMaxLifecycle(fragment, INITIALIZED)
+                    .commitNow();
+            if (null != mFragmentMaxLifecycleEnforcer) {
+                mFragmentMaxLifecycleEnforcer.updateFragmentMaxLifecycle(false);
+            } else {
+                Log.e(getClass().getSimpleName(), "crashing -> id:" + holder.getItemId() + ", fragment -> " + fragment);
+//                CrashSDKWrapper.postCatchedException(new NullPointerException("mFragmentMaxLifecycleEnforcer null"), false);
+            }
+        } else {
+            if (mFragmentManager.isDestroyed()) {
+                return; // nothing we can do
+            }
+            mLifecycle.addObserver(new LifecycleEventObserver() {
+                @Override
+                public void onStateChanged(@NonNull LifecycleOwner source,
+                                           @NonNull Lifecycle.Event event) {
+                    if (shouldDelayFragmentTransactions()) {
+                        return;
+                    }
+                    source.getLifecycle().removeObserver(this);
+                    if (ViewCompat.isAttachedToWindow(holder.getContainer())) {
+                        placeFragmentInViewHolder(holder);
+                    }
+                }
+            });
+        }
+    }
+
+    private void scheduleViewAttach(final Fragment fragment, @NonNull final FrameLayout container) {
+        // After a config change, Fragments that were in FragmentManager will be recreated. Since
+        // ViewHolder container ids are dynamically generated, we opted to manually handle
+        // attaching Fragment views to containers. For consistency, we use the same mechanism for
+        // all Fragment views.
+        mFragmentManager.registerFragmentLifecycleCallbacks(
+                new FragmentManager.FragmentLifecycleCallbacks() {
+                    // TODO(b/141956012): Suppressed during upgrade to AGP 3.6.
+                    @SuppressWarnings("ReferenceEquality")
+                    @Override
+                    public void onFragmentViewCreated(@NonNull FragmentManager fm,
+                                                      @NonNull Fragment f, @NonNull View v,
+                                                      @Nullable Bundle savedInstanceState) {
+                        if (f == fragment) {
+                            fm.unregisterFragmentLifecycleCallbacks(this);
+                            addViewToContainer(v, container);
+                        }
+                    }
+                }, false);
+    }
+
+    @SuppressWarnings("WeakerAccess")
+        // to avoid creation of a synthetic accessor
+    void addViewToContainer(@NonNull View v, @NonNull FrameLayout container) {
+        if (container.getChildCount() > 1) {
+            throw new IllegalStateException("Design assumption violated.");
+        }
+
+        if (v.getParent() == container) {
+            return;
+        }
+
+        if (container.getChildCount() > 0) {
+            container.removeAllViews();
+        }
+
+        if (v.getParent() != null) {
+            ((ViewGroup) v.getParent()).removeView(v);
+        }
+
+        container.addView(v);
+    }
+
+    @Override
+    public final void onViewRecycled(@NonNull FragmentViewHolder holder) {
+        final int viewHolderId = holder.getContainer().getId();
+        final Long boundItemId = itemForViewHolder(viewHolderId); // item currently bound to the VH
+        if (boundItemId != null) {
+            removeFragment(boundItemId);
+            mItemIdToViewHolder.remove(boundItemId);
+        }
+    }
+
+    @Override
+    public final boolean onFailedToRecycleView(@NonNull FragmentViewHolder holder) {
+        /*
+         This happens when a ViewHolder is in a transient state (e.g. during an
+         animation).
+
+         Our ViewHolders are effectively just FrameLayout instances in which we put Fragment
+         Views, so it's safe to force recycle them. This is because:
+         - FrameLayout instances are not to be directly manipulated, so no animations are
+         expected to be running directly on them.
+         - Fragment Views are not reused between position (one Fragment = one page). Animation
+         running in one of the Fragment Views won't affect another Fragment View.
+         - If a user chooses to violate these assumptions, they are also in the position to
+         correct the state in their code.
+        */
+        return true;
+    }
+
+    private void removeFragment(long itemId) {
+        Fragment fragment = mFragments.get(itemId);
+
+        if (fragment == null) {
+            return;
+        }
+
+        if (fragment.getView() != null) {
+            ViewParent viewParent = fragment.getView().getParent();
+            if (viewParent != null) {
+                ((FrameLayout) viewParent).removeAllViews();
+            }
+        }
+
+        if (!containsItem(itemId)) {
+            mSavedStates.remove(itemId);
+        }
+
+        if (!fragment.isAdded()) {
+            mFragments.remove(itemId);
+            return;
+        }
+
+        if (shouldDelayFragmentTransactions()) {
+            mHasStaleFragments = true;
+            return;
+        }
+
+        if (fragment.isAdded() && containsItem(itemId)) {
+            mSavedStates.put(itemId, mFragmentManager.saveFragmentInstanceState(fragment));
+        }
+        mFragmentManager.beginTransaction().remove(fragment).commitNow();
+        mFragments.remove(itemId);
+    }
+
+    @SuppressWarnings("WeakerAccess")
+        // to avoid creation of a synthetic accessor
+    boolean shouldDelayFragmentTransactions() {
+        return mFragmentManager.isStateSaved();
+    }
+
+    /**
+     * Default implementation works for collections that don't add, move, remove items.
+     * <p>
+     * TODO(b/122670460): add lint rule
+     * When overriding, also override {@link #containsItem(long)}.
+     * <p>
+     * If the item is not a part of the collection, return {@link RecyclerView#NO_ID}.
+     *
+     * @param position Adapter position
+     * @return stable item id {@link RecyclerView.Adapter#hasStableIds()}
+     */
+    @Override
+    public long getItemId(int position) {
+        return position;
+    }
+
+    /**
+     * Default implementation works for collections that don't add, move, remove items.
+     * <p>
+     * TODO(b/122670460): add lint rule
+     * When overriding, also override {@link #getItemId(int)}
+     */
+    public boolean containsItem(long itemId) {
+        return itemId >= 0 && itemId < getItemCount();
+    }
+
+    @Override
+    public final void setHasStableIds(boolean hasStableIds) {
+        throw new UnsupportedOperationException(
+                "Stable Ids are required for the adapter to function properly, and the adapter "
+                        + "takes care of setting the flag.");
+    }
+
+    @Override
+    public final @NonNull Parcelable saveState() {
+        /** TODO(b/122670461): use custom {@link Parcelable} instead of Bundle to save space */
+        Bundle savedState = new Bundle(mFragments.size() + mSavedStates.size());
+
+        /** save references to active fragments */
+        for (int ix = 0; ix < mFragments.size(); ix++) {
+            long itemId = mFragments.keyAt(ix);
+            Fragment fragment = mFragments.get(itemId);
+            if (fragment != null && fragment.isAdded()) {
+                String key = createKey(KEY_PREFIX_FRAGMENT, itemId);
+                mFragmentManager.putFragment(savedState, key, fragment);
+            }
+        }
+
+        /** Write {@link mSavedStates) into a {@link Parcelable} */
+        for (int ix = 0; ix < mSavedStates.size(); ix++) {
+            long itemId = mSavedStates.keyAt(ix);
+            if (containsItem(itemId)) {
+                String key = createKey(KEY_PREFIX_STATE, itemId);
+                savedState.putParcelable(key, mSavedStates.get(itemId));
+            }
+        }
+
+        return savedState;
+    }
+
+    @Override
+    public final void restoreState(@NonNull Parcelable savedState) {
+        if (!mSavedStates.isEmpty() || !mFragments.isEmpty()) {
+            throw new IllegalStateException(
+                    "Expected the adapter to be 'fresh' while restoring state.");
+        }
+
+        Bundle bundle = (Bundle) savedState;
+        if (bundle.getClassLoader() == null) {
+            /** TODO(b/133752041): pass the class loader from {@link ViewPager2.SavedState } */
+            bundle.setClassLoader(getClass().getClassLoader());
+        }
+
+        for (String key : bundle.keySet()) {
+            if (isValidKey(key, KEY_PREFIX_FRAGMENT)) {
+                long itemId = parseIdFromKey(key, KEY_PREFIX_FRAGMENT);
+                Fragment fragment = mFragmentManager.getFragment(bundle, key);
+                mFragments.put(itemId, fragment);
+                continue;
+            }
+
+            if (isValidKey(key, KEY_PREFIX_STATE)) {
+                long itemId = parseIdFromKey(key, KEY_PREFIX_STATE);
+                Fragment.SavedState state = bundle.getParcelable(key);
+                if (containsItem(itemId)) {
+                    mSavedStates.put(itemId, state);
+                }
+                continue;
+            }
+
+            throw new IllegalArgumentException("Unexpected key in savedState: " + key);
+        }
+
+        if (!mFragments.isEmpty()) {
+            mHasStaleFragments = true;
+            mIsInGracePeriod = true;
+            gcFragments();
+            scheduleGracePeriodEnd();
+        }
+    }
+
+    private void scheduleGracePeriodEnd() {
+        final Handler handler = new Handler(Looper.getMainLooper());
+        final Runnable runnable = new Runnable() {
+            @Override
+            public void run() {
+                mIsInGracePeriod = false;
+                gcFragments(); // good opportunity to GC
+            }
+        };
+
+        mLifecycle.addObserver(new LifecycleEventObserver() {
+            @Override
+            public void onStateChanged(@NonNull LifecycleOwner source,
+                                       @NonNull Lifecycle.Event event) {
+                if (event == Lifecycle.Event.ON_DESTROY) {
+                    handler.removeCallbacks(runnable);
+                    source.getLifecycle().removeObserver(this);
+                }
+            }
+        });
+
+        handler.postDelayed(runnable, GRACE_WINDOW_TIME_MS);
+    }
+
+    // Helper function for dealing with save / restore state
+    private static @NonNull String createKey(@NonNull String prefix, long id) {
+        return prefix + id;
+    }
+
+    // Helper function for dealing with save / restore state
+    private static boolean isValidKey(@NonNull String key, @NonNull String prefix) {
+        return key.startsWith(prefix) && key.length() > prefix.length();
+    }
+
+    // Helper function for dealing with save / restore state
+    private static long parseIdFromKey(@NonNull String key, @NonNull String prefix) {
+        return Long.parseLong(key.substring(prefix.length()));
+    }
+
+    /**
+     * Pauses (STARTED) all Fragments that are attached and not a primary item.
+     * Keeps primary item Fragment RESUMED.
+     */
+    class FragmentMaxLifecycleEnforcer {
+        private ViewPager2.OnPageChangeCallback mPageChangeCallback;
+        private RecyclerView.AdapterDataObserver mDataObserver;
+        private LifecycleEventObserver mLifecycleObserver;
+        private ViewPager2 mViewPager;
+
+        private long mPrimaryItemId = NO_ID;
+
+        void register(@NonNull RecyclerView recyclerView) {
+            mViewPager = inferViewPager(recyclerView);
+
+            // signal 1 of 3: current item has changed
+            mPageChangeCallback = new ViewPager2.OnPageChangeCallback() {
+                @Override
+                public void onPageScrollStateChanged(int state) {
+                    updateFragmentMaxLifecycle(false);
+                }
+
+                @Override
+                public void onPageSelected(int position) {
+                    updateFragmentMaxLifecycle(false);
+                }
+            };
+            mViewPager.registerOnPageChangeCallback(mPageChangeCallback);
+
+            // signal 2 of 3: underlying data-set has been updated
+            mDataObserver = new DataSetChangeObserver() {
+                @Override
+                public void onChanged() {
+                    updateFragmentMaxLifecycle(true);
+                }
+            };
+            registerAdapterDataObserver(mDataObserver);
+
+            // signal 3 of 3: we may have to catch-up after being in a lifecycle state that
+            // prevented us to perform transactions
+            mLifecycleObserver = new LifecycleEventObserver() {
+                @Override
+                public void onStateChanged(@NonNull LifecycleOwner source,
+                                           @NonNull Lifecycle.Event event) {
+                    updateFragmentMaxLifecycle(false);
+                }
+            };
+            mLifecycle.addObserver(mLifecycleObserver);
+        }
+
+        void unregister(@NonNull RecyclerView recyclerView) {
+            ViewPager2 viewPager = inferViewPager(recyclerView);
+            viewPager.unregisterOnPageChangeCallback(mPageChangeCallback);
+            unregisterAdapterDataObserver(mDataObserver);
+            mLifecycle.removeObserver(mLifecycleObserver);
+            mViewPager = null;
+        }
+
+        void updateFragmentMaxLifecycle(boolean dataSetChanged) {
+            if (shouldDelayFragmentTransactions()) {
+                return; /** recovery step via {@link #mLifecycleObserver} */
+            }
+
+            if (mViewPager.getScrollState() != ViewPager2.SCROLL_STATE_IDLE) {
+                return; // do not update while not idle to avoid jitter
+            }
+
+            if (mFragments.isEmpty() || getItemCount() == 0) {
+                return; // nothing to do
+            }
+
+            final int currentItem = mViewPager.getCurrentItem();
+            if (currentItem >= getItemCount()) {
+                /** current item is yet to be updated; it is guaranteed to change, so we will be
+                 * notified via {@link ViewPager2.OnPageChangeCallback#onPageSelected(int)}  */
+                return;
+            }
+
+            long currentItemId = getItemId(currentItem);
+            if (currentItemId == mPrimaryItemId && !dataSetChanged) {
+                return; // nothing to do
+            }
+
+            Fragment currentItemFragment = mFragments.get(currentItemId);
+            if (currentItemFragment == null || !currentItemFragment.isAdded()) {
+                return;
+            }
+
+            mPrimaryItemId = currentItemId;
+            FragmentTransaction transaction = mFragmentManager.beginTransaction();
+
+            Fragment toResume = null;
+            for (int ix = 0; ix < mFragments.size(); ix++) {
+                long itemId = mFragments.keyAt(ix);
+                Fragment fragment = mFragments.valueAt(ix);
+
+                if (!fragment.isAdded()) {
+                    continue;
+                }
+
+                if (itemId != mPrimaryItemId) {
+                    transaction.setMaxLifecycle(fragment, STARTED);
+                } else {
+                    toResume = fragment; // itemId map key, so only one can match the predicate
+                }
+
+                fragment.setMenuVisibility(itemId == mPrimaryItemId);
+            }
+            if (toResume != null) { // in case the Fragment wasn't added yet
+                transaction.setMaxLifecycle(toResume, RESUMED);
+            }
+
+            if (!transaction.isEmpty()) {
+                transaction.commitNow();
+            }
+        }
+
+        @NonNull
+        private ViewPager2 inferViewPager(@NonNull RecyclerView recyclerView) {
+            ViewParent parent = recyclerView.getParent();
+            if (parent instanceof ViewPager2) {
+                return (ViewPager2) parent;
+            }
+            throw new IllegalStateException("Expected ViewPager2 instance. Got: " + parent);
+        }
+    }
+
+    /**
+     * Simplified {@link RecyclerView.AdapterDataObserver} for clients interested in any data-set
+     * changes regardless of their nature.
+     */
+    private abstract static class DataSetChangeObserver extends RecyclerView.AdapterDataObserver {
+        @Override
+        public abstract void onChanged();
+
+        @Override
+        public final void onItemRangeChanged(int positionStart, int itemCount) {
+            onChanged();
+        }
+
+        @Override
+        public final void onItemRangeChanged(int positionStart, int itemCount,
+                                             @Nullable Object payload) {
+            onChanged();
+        }
+
+        @Override
+        public final void onItemRangeInserted(int positionStart, int itemCount) {
+            onChanged();
+        }
+
+        @Override
+        public final void onItemRangeRemoved(int positionStart, int itemCount) {
+            onChanged();
+        }
+
+        @Override
+        public final void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
+            onChanged();
+        }
+    }
+}
+

+ 514 - 0
app/src/main/java/com/adealink/weparty/App.kt

@@ -0,0 +1,514 @@
+package com.adealink.weparty
+
+import android.content.Context
+import android.content.SharedPreferences
+import android.content.res.Configuration
+import android.os.SystemClock
+import androidx.room.Room
+import com.adealink.frame.aab.AAB
+import com.adealink.frame.aab.util.getCompatString
+import com.adealink.frame.apm.initAPMService
+import com.adealink.frame.base.AppBaseInfo
+import com.adealink.frame.crash.installCrashProtector
+import com.adealink.frame.data.json.initJsonConfig
+import com.adealink.frame.debug.datasource.performanceDataController
+import com.adealink.frame.debug.floatkit.floatKitManager
+import com.adealink.frame.deviceid.createDeviceIdService
+import com.adealink.frame.download.IDownloadService
+import com.adealink.frame.download.createDownloadService
+import com.adealink.frame.download.initDownloadService
+import com.adealink.frame.effect.IEffect
+import com.adealink.frame.effect.createEffect
+import com.adealink.frame.effect.initEffect
+import com.adealink.frame.googleservice.createGoogleService
+import com.adealink.frame.image.imageService
+import com.adealink.frame.imkit.IMService
+import com.adealink.frame.initStorageService
+import com.adealink.frame.locale.language.createLanguageManager
+import com.adealink.frame.log.Log
+import com.adealink.frame.log.XLogHelper
+import com.adealink.frame.media.createMediaService
+import com.adealink.frame.network.INetworkService
+import com.adealink.frame.network.NetworkService
+import com.adealink.frame.oss.IOssService
+import com.adealink.frame.oss.alibaba.AliyunOssService
+import com.adealink.frame.oss.initOssService
+import com.adealink.frame.push.manager.pushService
+import com.adealink.frame.router.Router
+import com.adealink.frame.router.manager.deeplinkRouterManager
+import com.adealink.frame.security.createSecurityService
+import com.adealink.frame.share.initShareManager
+import com.adealink.frame.sound.createSoundPlayer
+import com.adealink.frame.startup.dispatcher.StartTaskDispatcher
+import com.adealink.frame.startup.task.MainThreadStartUpTask
+import com.adealink.frame.startup.task.StartUpTask
+import com.adealink.frame.startup.task.SubNoWaitStartUpTask
+import com.adealink.frame.startup.task.SubWaitStartUpTask
+import com.adealink.frame.statistics.report.initStat
+import com.adealink.frame.storageService
+import com.adealink.frame.tceffect.TCEffectManager
+import com.adealink.frame.util.ActivityLifecycleCallbacksExt
+import com.adealink.frame.util.AppUtil
+import com.adealink.frame.util.ScreenAutoSizeUtil
+import com.adealink.frame.util.registerNetworkReceiver
+import com.adealink.tcturing.TCTuringManager
+import com.adealink.weparty.aab.AABConfig
+import com.adealink.weparty.apm.APMConfig
+import com.adealink.weparty.apm.initAPM
+import com.adealink.weparty.base.AppBaseConfig
+import com.adealink.weparty.commonui.WEUI
+import com.adealink.weparty.commonui.widget.floatview.WindowManagerProxy
+import com.adealink.weparty.constant.TAG_TIME_APP_START
+import com.adealink.weparty.constant.TAG_TOO_LARGE_TOOL
+import com.adealink.weparty.constant.logTime
+import com.adealink.weparty.crash.CrashConfig
+import com.adealink.weparty.crash.initCrash
+import com.adealink.weparty.debug.Debug
+import com.adealink.weparty.debug.DebugPrefs
+import com.adealink.weparty.deeplink.DeepLinkConfig
+import com.adealink.weparty.deviceidservice.DeviceIdServiceConfig
+import com.adealink.weparty.deviceidservice.TCTuringConfig
+import com.adealink.weparty.download.DownloadConfig
+import com.adealink.weparty.effect.EffectConfig
+import com.adealink.weparty.effect.SVGA_CONFIG
+import com.adealink.weparty.effect.TCEffectConfig
+import com.adealink.weparty.googleservice.GoogleServiceConfig
+import com.adealink.weparty.hardware.HardwareManager
+import com.adealink.weparty.hardware.IHardwareManager
+import com.adealink.weparty.im.IMConfig
+import com.adealink.weparty.image.ImageConfig
+import com.adealink.weparty.json.JsonConfig
+import com.adealink.weparty.locale.LanguageConfig
+import com.adealink.weparty.log.LogConfig
+import com.adealink.weparty.media.IMediaManager
+import com.adealink.weparty.media.MediaConfig
+import com.adealink.weparty.media.MediaManager
+import com.adealink.weparty.module.attribution.AttributionModule
+import com.adealink.weparty.module.call.CallModule
+import com.adealink.weparty.module.message.MessageModule
+import com.adealink.weparty.module.operation.OperationModule
+import com.adealink.weparty.network.INetworkManager
+import com.adealink.weparty.network.NetworkConfig
+import com.adealink.weparty.network.NetworkManager
+import com.adealink.weparty.oss.AliyunOssConfig
+import com.adealink.weparty.push.NotificationUtil
+import com.adealink.weparty.push.PushServiceConfig
+import com.adealink.weparty.router.RouterConfig
+import com.adealink.weparty.security.SecurityConfig
+import com.adealink.weparty.share.ShareConfig
+import com.adealink.weparty.sound.SoundPlayerConfig
+import com.adealink.weparty.stat.StatConfig
+import com.adealink.weparty.storage.config.StorageConfig
+import com.adealink.weparty.storage.db.AppDatabase
+import com.adealink.weparty.storage.file.FilePath
+import com.adealink.weparty.url.urlConfigService
+import com.adealink.weparty.webview.JSBridgeConfig
+import com.adealink.weparty.webview.WebResourceConfig
+import com.adealink.weparty.webview.jsbridge.manager.createJSBridgeManager
+import com.adealink.weparty.webview.jsbridge.manager.initJSBridgeManager
+import com.adealink.weparty.webview.loader.initWebResourceLoader
+import com.adealink.weparty.webview.payermax.PayerMaxHelper
+import com.facebook.FacebookSdk
+import com.facebook.LoggingBehavior
+import com.google.android.play.core.splitcompat.SplitCompatApplication
+import com.google.firebase.analytics.FirebaseAnalytics
+import com.google.firebase.analytics.FirebaseAnalytics.UserProperty.ALLOW_AD_PERSONALIZATION_SIGNALS
+import com.gu.toolargetool.DefaultFormatter
+import com.gu.toolargetool.Logger
+import com.gu.toolargetool.TooLargeTool
+import com.opensource.svgaplayer.control.SVGAManager
+import com.tencent.mars.xlog.Xlog
+import io.rong.imlib.model.InitOption
+
+/**
+ * Created by sunxiaodong on 2021/3/25.
+ * WARN: 启动路径代码使用Lambda,aab机制会导致部分机型启动崩溃
+ */
+class App : SplitCompatApplication(), ActivityLifecycleCallbacksExt {
+
+    companion object {
+        lateinit var instance: App
+    }
+
+    val networkService: INetworkService by lazy { NetworkService.create(NetworkConfig()) }
+    private val networkManager: INetworkManager by lazy { NetworkManager() }
+    val ossService: IOssService by lazy { AliyunOssService.create(AliyunOssConfig()) }
+    val downloadService: IDownloadService by lazy { createDownloadService(DownloadConfig()) }
+    val effect: IEffect by lazy { createEffect(EffectConfig()) }
+    val googleService by lazy { createGoogleService(GoogleServiceConfig()) }
+    val deviceIdService by lazy { createDeviceIdService(DeviceIdServiceConfig()) }
+    val jsBridgeManager by lazy { createJSBridgeManager(JSBridgeConfig()) }
+    val mediaService by lazy { createMediaService(MediaConfig) }
+    val mediaManager: IMediaManager by lazy { MediaManager() }
+    val soundPlayer by lazy { createSoundPlayer(SoundPlayerConfig()) }
+    val languageManager by lazy { createLanguageManager(LanguageConfig()) }
+    val securityService by lazy { createSecurityService(SecurityConfig()) }
+    val database by lazy {
+        Room.databaseBuilder(applicationContext, AppDatabase::class.java, "weparty_app_db").build()
+    }
+    val imService by lazy { IMService.create(IMConfig()) }
+    val hardwareManager: IHardwareManager by lazy { HardwareManager() }
+
+    var appStartTime = 0L
+    override fun attachBaseContext(base: Context?) {
+        logTime(TAG_TIME_APP_START, "app attachBaseContext start")
+        instance = this
+        AppBaseInfo.init(AppBaseConfig())
+        AppUtil.init(this)
+        super.attachBaseContext(base)
+        appStartTime = SystemClock.elapsedRealtime()
+        AppUtil.registerActivityLifecycleCallbacks(this)
+        logTime(TAG_TIME_APP_START, "app attachBaseContext end")
+    }
+
+    override fun onCreate() {
+        super.onCreate()
+        val appOnCreateTs = SystemClock.elapsedRealtime()
+        Log.i(TAG_TIME_APP_START, "app onCreate start")
+        //基础任务
+        baseStartTask()
+        //APP并发初始化任务
+        StartTaskDispatcher.create().apply {
+            addAppStartTask(InitNetwork())
+            addAppStartTask(InitPush())
+            addAppStartTask(InitUI())
+            addAppStartTask(InitTCEffect())
+            addAppStartTask(InitSVGA())
+            addAppStartTask(InitImageService())
+            addAppStartTask(InitRouter())
+            addAppStartTask(InitLanguage())
+            addAppStartTask(InitSecurityService())
+            addAppStartTask(InitGoogleService())
+            addAppStartTask(InitStorageService())
+            addAppStartTask(InitShareManager())
+            addAppStartTask(InitOssService())
+            addAppStartTask(InitJSBridgeManager())
+            addAppStartTask(InitWebResourceLoader())
+            addAppStartTask(InitDownloadService())
+            addAppStartTask(InitEffect())
+            addAppStartTask(InitFirebaseAnalytics())
+            addAppStartTask(InitFB())
+            addAppStartTask(InitPayerMax())
+
+            addAppStartTask(InitIM())
+            addAppStartTask(InitCall1())
+            addAppStartTask(InitCall2())
+            addAppStartTask(InitTCTuringManager())
+
+            //其他子任务(不卡主流程)
+            addAppStartTask(InitOther())
+
+            if (!AppBaseInfo.isRelease) {
+                addAppStartTask(InitDebugKit())
+            }
+
+        }.start().await()
+        Log.i(TAG_TIME_APP_START, "app onCreate end, cost:${SystemClock.elapsedRealtime() - appOnCreateTs}")
+    }
+
+    /**
+     * 所有模块最基础启动任务,必须执行完该任务才能执行其他启动任务
+     * - 日志
+     * - 组件/模块配置
+     */
+    private fun baseStartTask() {
+        val startTs = SystemClock.elapsedRealtime()
+        Log.i(TAG_TIME_APP_START, "baseStartTask start")
+        initXLog()
+        initAPMService(APMConfig())
+        initAPM(this@App)
+        initStat { StatConfig() }
+        installCrashProtector(CrashConfig())
+        initCrash(this@App) //依赖xlog,stat,crash protector
+        AAB.init(AABConfig())
+        initJsonConfig(JsonConfig())
+        Log.i(TAG_TIME_APP_START, "baseStartTask end, cost: ${SystemClock.elapsedRealtime() - startTs}")
+    }
+
+    private fun initXLog() {
+        XLogHelper.initXLog(
+            if (BuildConfig.OFFICIAL || BuildConfig.DEBUG) Xlog.LEVEL_ALL else Xlog.LEVEL_INFO,
+            FilePath.logPath,
+            FilePath.logPath,
+            BuildConfig.DEBUG
+        )
+        Log.setLogConfig(LogConfig())
+    }
+
+    /**
+     * APP初始化任务(阻塞主线程)
+     */
+    inner class InitPush : MainThreadStartUpTask() {
+        override fun run() {
+            pushService.init(PushServiceConfig())
+            NotificationUtil.createNotificationChannel(getCompatString(R.string.channel_event))
+        }
+    }
+
+    inner class InitUI : MainThreadStartUpTask() {
+        override fun run() {
+            //UI初始化
+            WEUI.init(this@App)
+            ScreenAutoSizeUtil.init(this@App, !AppBaseInfo.isRelease)
+            WindowManagerProxy.init()
+        }
+    }
+
+    inner class InitImageService : MainThreadStartUpTask() {
+        override fun run() {
+            imageService.init(ImageConfig())
+        }
+
+        override val dependsTaskList: List<Class<out StartUpTask>>
+            get() = listOf(InitUI::class.java)
+    }
+
+    inner class InitTCEffect : SubWaitStartUpTask() {
+        override fun run() {
+            TCEffectManager.init(TCEffectConfig())
+        }
+
+        override val dependsTaskList: List<Class<out StartUpTask>>
+            get() = listOf(InitUI::class.java)
+    }
+
+    inner class InitSVGA : SubWaitStartUpTask() {
+        override fun run() {
+            SVGAManager.init(this@App, SVGA_CONFIG)
+        }
+
+        override val dependsTaskList: List<Class<out StartUpTask>>
+            get() = listOf(InitUI::class.java)
+    }
+
+    inner class InitRouter : SubWaitStartUpTask() {
+        override fun run() {
+            Router.config = RouterConfig()
+            deeplinkRouterManager.init(DeepLinkConfig())
+        }
+    }
+
+    inner class InitLanguage : SubWaitStartUpTask() {
+        override fun run() {
+            languageManager.init()
+        }
+    }
+
+    inner class InitSecurityService : SubWaitStartUpTask() {
+        override fun run() {
+            securityService.init()
+        }
+    }
+
+    inner class InitGoogleService : SubWaitStartUpTask() {
+        override fun run() {
+            googleService.init()
+        }
+    }
+
+    inner class InitStorageService : SubWaitStartUpTask() {
+        override fun run() {
+            initStorageService(StorageConfig())
+        }
+    }
+
+    inner class InitShareManager : SubWaitStartUpTask() {
+        override fun run() {
+            initShareManager(ShareConfig())
+        }
+    }
+
+    inner class InitOssService : SubWaitStartUpTask() {
+        override fun run() {
+            initOssService { ossService }
+        }
+    }
+
+    inner class InitJSBridgeManager : SubWaitStartUpTask() {
+        override fun run() {
+            initJSBridgeManager { jsBridgeManager }
+        }
+    }
+
+    inner class InitWebResourceLoader : SubWaitStartUpTask() {
+        override fun run() {
+            initWebResourceLoader(WebResourceConfig())
+        }
+
+        override val dependsTaskList: List<Class<out StartUpTask>>
+            get() = listOf(InitNetwork::class.java)
+    }
+
+    inner class InitDownloadService : SubWaitStartUpTask() {
+        override fun run() {
+            initDownloadService { downloadService }
+        }
+
+        override val dependsTaskList: List<Class<out StartUpTask>>
+            get() = listOf(InitNetwork::class.java)
+    }
+
+    inner class InitEffect : SubWaitStartUpTask() {
+        override fun run() {
+            initEffect { effect }
+        }
+    }
+
+    inner class InitFirebaseAnalytics : SubWaitStartUpTask() {
+        override fun run() {
+            val analytics = FirebaseAnalytics.getInstance(applicationContext)
+            analytics.setUserProperty(ALLOW_AD_PERSONALIZATION_SIGNALS, "true")
+            analytics.setAnalyticsCollectionEnabled(true)
+        }
+    }
+
+    inner class InitFB : SubWaitStartUpTask() {
+        override fun run() {
+            if (!AppBaseInfo.isRelease) {
+                FacebookSdk.setIsDebugEnabled(AppBaseInfo.isRelease)
+                FacebookSdk.addLoggingBehavior(LoggingBehavior.APP_EVENTS)
+            }
+        }
+    }
+
+    inner class InitPayerMax : SubWaitStartUpTask() {
+        override fun run() {
+            PayerMaxHelper.init(this@App)
+        }
+    }
+
+    inner class InitNetwork : SubWaitStartUpTask() {
+        override fun run() {
+            networkManager.init(this@App)
+            registerNetworkReceiver()
+        }
+    }
+
+    inner class InitIM : SubWaitStartUpTask() {
+        override fun run() {
+            imService.init(
+                this@App,
+                InitOption.Builder()
+                    .setAreaCode(
+                        when (AppBaseInfo.isProdEnv) {
+                            true -> InitOption.AreaCode.SG
+                            else -> InitOption.AreaCode.BJ
+                        }
+                    )
+                    .setMainProcess(true)
+                    .enablePush(false)
+                    .enableSyncEmptyTopConversation(true)
+                    .build()
+            )
+            MessageModule.appOnCreateMainTask(this@App)
+        }
+    }
+
+    //1v1呼叫主线程
+    inner class InitCall1 : SubWaitStartUpTask() {
+        override val isRunOnMainThread: Boolean = true
+
+        override fun run() {
+            CallModule.appOnCreateMainTask(this@App)
+        }
+    }
+
+    //1v1呼叫子线程
+    inner class InitCall2 : SubWaitStartUpTask() {
+        override fun run() {
+            CallModule.appOnCreateSubTask(this@App)
+        }
+
+        override val dependsTaskList: List<Class<out StartUpTask>>
+            get() = listOf(
+                *super.dependsTaskList.toTypedArray(),
+                InitCall1::class.java
+            )
+    }
+
+    inner class InitTCTuringManager : SubWaitStartUpTask() {
+        override fun run() {
+            TCTuringManager.init(TCTuringConfig())
+        }
+    }
+
+    inner class InitDebugKit : SubWaitStartUpTask() {
+        override val isRunOnMainThread: Boolean = true
+
+        override fun run() {
+            if (AppBaseInfo.isRelease) {
+                return
+            }
+            if (DebugPrefs.showPerformanceFloatView) {
+                floatKitManager.install(this@App, true)
+                floatKitManager.onMainIconDoubleClick = {
+                    AppUtil.currentActivity?.let {
+                        Router.build(it, Debug.Debug.PATH).start()
+                    }
+                }
+                performanceDataController.start()
+            }
+            TooLargeTool.startLogging(this@App, DefaultFormatter(), object : Logger {
+                override fun log(msg: String) {
+                    Log.i(TAG_TOO_LARGE_TOOL, msg)
+                }
+
+                override fun logException(e: Exception) {
+                    Log.e(TAG_TOO_LARGE_TOOL, "logException", e)
+                }
+            })
+        }
+    }
+
+    /**
+     * 其他任务(非阻塞主线程)
+     */
+    inner class InitOther : SubNoWaitStartUpTask() {
+        override fun run() {
+            deviceIdService.updateDeviceId()
+            networkService.fetchNetAntiBanConfig()
+            urlConfigService.init()
+            soundPlayer.prepare()
+
+            AttributionModule.initialize()
+            AttributionModule.start()
+            AttributionModule.reportFirstOpen()
+
+            OperationModule.init()
+        }
+    }
+
+    override fun onTerminate() {
+        super.onTerminate()
+        Log.appenderClose()
+    }
+
+    override fun onConfigurationChanged(newConfig: Configuration) {
+        super.onConfigurationChanged(newConfig)
+        languageManager.onConfigurationChanged(newConfig)
+    }
+
+    override fun getSharedPreferences(name: String, mode: Int): SharedPreferences {
+        if (name == "") {
+            return super.getSharedPreferences(name, mode)
+        }
+        return storageService.hookSharedPreferences(
+            name,
+            mode,
+            super.getSharedPreferences(name, mode)
+        )
+    }
+
+    override fun onTrimMemory(level: Int) {
+        super.onTrimMemory(level)
+        imageService.onTrimMemory(level)
+    }
+
+    /**
+     * 立即ping网络状态
+     */
+    fun pingNow() {
+        networkManager.pingNow()
+    }
+
+}

+ 227 - 0
app/src/main/java/com/adealink/weparty/MainActivity.kt

@@ -0,0 +1,227 @@
+package com.adealink.weparty
+
+import android.content.Intent
+import android.graphics.drawable.ColorDrawable
+import android.os.Bundle
+import android.view.WindowManager
+import androidx.fragment.app.FragmentContainerView
+import com.adealink.frame.aab.util.getCompatColor
+import com.adealink.frame.log.Log
+import com.adealink.frame.push.data.KEY_PUSH_DEEPLINK
+import com.adealink.frame.push.data.KEY_PUSH_ID
+import com.adealink.frame.push.data.KEY_PUSH_MESSAGE_TYPE
+import com.adealink.frame.router.Router
+import com.adealink.frame.router.annotation.BindExtra
+import com.adealink.frame.router.annotation.RouterUri
+import com.adealink.frame.router.manager.deeplinkRouterManager
+import com.adealink.weparty.AppModule.Main.Companion.EXTRA_FROM_MATCH_NOTIFICATION
+import com.adealink.weparty.AppModule.Main.Companion.EXTRA_MATCH_ID
+import com.adealink.weparty.commonui.BaseActivity
+import com.adealink.weparty.module.account.Account
+import com.adealink.weparty.module.account.AccountModule
+import com.adealink.weparty.module.call.CallModule
+import com.adealink.weparty.push.PushStatEvent
+import com.adealink.weparty.stat.manager.serveReportManager
+import com.adealink.weparty.stat.reportAppOpenIfNeed
+import com.adealink.weparty.stat.reportEnterHome
+import com.adealink.weparty.storage.AppPref
+import com.adealink.weparty.ui.MainStartUpFragment
+import com.adealink.weparty.ui.home.HomeFragment
+import com.adealink.weparty.ui.home.util.HomeLocalService
+import com.adealink.weparty.ui.splash.SplashFragment
+import com.adealink.weparty.update.updateManager
+import com.adealink.weparty.util.PermissionRequest
+import com.adealink.weparty.util.goLocalLinkPage
+import com.qmuiteam.qmui.widget.util.QMUIStatusBarHelper
+import com.tencent.qcloud.tuicore.permission.PermissionCallback
+
+@RouterUri(path = [AppModule.Main.PATH], desc = "首页")
+class MainActivity : BaseActivity() {
+
+    companion object {
+        private const val TAG = "MainActivity"
+
+        //显示splash页面
+        private var showSplash = true
+    }
+
+    enum class DispatchFrom {
+        ON_CREATE,
+        ON_NEW_INTENT
+    }
+
+    @BindExtra(AppModule.Main.EXTRA_MAIN_TAB)
+    var mainTabKey: String? = ""
+
+    @BindExtra(AppModule.Main.EXTRA_MAIN_SUB_TAB)
+    var subTabKey: String? = ""
+
+    private var dispatchRouter = false
+    override val routeSubPage: Boolean
+        get() = !dispatchRouter
+    override val forceFitNavigationBar: Boolean
+        get() = true
+
+    override fun onBeforeCreate() {
+        super.onBeforeCreate()
+        android.util.Log.d("MainActivity", "onCreate")
+        Router.bind(this)
+        HomeLocalService.homeTabVisitCount++
+    }
+
+    override fun onAfterSuperCreate(): Boolean {
+        if (!isTaskRoot) {
+            finish()
+            return false
+        }
+
+        return true
+    }
+
+    override fun initViews() {
+        super.initViews()
+
+        //还原启动图背景,清除全屏模式
+        window.setBackgroundDrawable(ColorDrawable(getCompatColor(R.color.white)))
+        window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN)
+
+        QMUIStatusBarHelper.setStatusBarLightMode(this)
+        setContentView(FragmentContainerView(this).apply {
+            id = R.id.fl_content
+        })
+        dispatch(intent, DispatchFrom.ON_CREATE)
+    }
+
+    override fun initOthers() {
+        super.initOthers()
+        reportAppOpenIfNeed(false)
+        serveReportManager.reportFirebaseAppInstanceId()
+    }
+
+    override fun onResume() {
+        super.onResume()
+        updateManager.checkUpdate(this)
+    }
+
+    override fun handleNewIntent(intent: Intent?) {
+        super.handleNewIntent(intent)
+        setIntent(intent)
+        intent?.extras?.let {
+            mainTabKey = it.getString(AppModule.Main.EXTRA_MAIN_TAB)
+            subTabKey = it.getString(AppModule.Main.EXTRA_MAIN_SUB_TAB)
+        }
+        dispatch(intent, DispatchFrom.ON_NEW_INTENT)
+    }
+
+    private fun dispatch(intent: Intent?, from: DispatchFrom) {
+        if (showSplash) {
+            showSplash = false
+            inflateSplashFragment()
+            return
+        }
+        when {
+            needGoLoginActivity() -> {
+                Router.build(this, Account.Login.PATH)
+                    .putExtras(intent?.extras ?: Bundle())
+                    .start()
+                finish()
+                return
+            }
+
+            needGoRegisterActivity() -> {
+                Router.build(this, Account.Register.PATH)
+                    .putExtras(intent?.extras ?: Bundle())
+                    .start()
+                finish()
+                return
+            }
+
+            needHandleMatchCall(intent) -> {
+                Log.d(TAG, "needHandleMatchCall")
+                CallModule.getMatchNotificationView(this)?.cancelNotifyView()
+                PermissionRequest.requestVideoPermission(this@MainActivity, object : PermissionCallback() {
+                    override fun onGranted() {
+                        val matchId = intent?.extras?.getLong(EXTRA_MATCH_ID, 0L) ?: 0L
+                        CallModule.getMatchManager()?.accept(matchId)
+                    }
+                })
+            }
+        }
+        inflateHomeFragment(from)
+        intent ?: return
+        val pushDeeplink = intent.getStringExtra(KEY_PUSH_DEEPLINK)
+        if (!pushDeeplink.isNullOrEmpty()) {
+            Log.d(TAG, "dispatchPath, pushDeeplink:$pushDeeplink")
+            PushStatEvent(PushStatEvent.Action.CLICK).apply {
+                type to (intent.extras?.getString(KEY_PUSH_MESSAGE_TYPE) ?: "1")
+                pushId to (intent.extras?.getString(KEY_PUSH_ID) ?: "1")
+                source to "new_fcm_push"
+                deeplink to pushDeeplink
+            }.send()
+            goLocalLinkPage(this, pushDeeplink)
+            return
+        }
+
+        val dispatchPath = intent.getStringExtra(deeplinkRouterManager.getDispatchPathKey())
+        if (!dispatchPath.isNullOrEmpty()) {
+            Log.d(TAG, "dispatchPath, path:$dispatchPath")
+            if (dispatchPath.startsWith(Router.getDeepLink(AppModule.Main.PATH))) {
+                return
+            }
+
+            dispatchRouter = true
+            Router.build(this, dispatchPath).putExtras(intent).start()
+            return
+        }
+
+        val splashBannerUrl = intent.getStringExtra(AppModule.Main.EXTRA_SPLASH_JUMP_URL)
+        if (!splashBannerUrl.isNullOrEmpty()) {
+            Log.d(TAG, "dispatchPath, splashBannerUrl:$splashBannerUrl")
+            goLocalLinkPage(this, splashBannerUrl)
+            return
+        }
+    }
+
+    private fun needGoLoginActivity(): Boolean {
+        return !AccountModule.isLogin()
+    }
+
+    private fun needGoRegisterActivity(): Boolean {
+        return AppPref.needRegister
+    }
+
+    private fun needHandleMatchCall(intent: Intent?): Boolean {
+        return intent?.extras?.getBoolean(EXTRA_FROM_MATCH_NOTIFICATION, false) ?: false
+    }
+
+    private fun inflateSplashFragment() {
+        var splashFragment =
+            supportFragmentManager.findFragmentByTag(SplashFragment.TAG) as? SplashFragment
+        if (splashFragment?.isAdded == true) {
+            return
+        }
+        splashFragment = splashFragment ?: SplashFragment()
+        supportFragmentManager.beginTransaction()
+            .replace(R.id.fl_content, splashFragment, SplashFragment.TAG)
+            .commitAllowingStateLoss()
+    }
+
+    private fun inflateHomeFragment(from: DispatchFrom) {
+        var homeFragment =
+            supportFragmentManager.findFragmentByTag(HomeFragment.TAG) as? HomeFragment
+        if (homeFragment?.isAdded == true && from == DispatchFrom.ON_CREATE) {
+            homeFragment.setDefaultTab(mainTabKey)
+            return
+        }
+
+        homeFragment = homeFragment ?: HomeFragment.newInstance(
+            mainTabKey, subTabKey
+        )
+        supportFragmentManager.beginTransaction()
+            .replace(R.id.fl_content, homeFragment, HomeFragment.TAG)
+            .commitAllowingStateLoss()
+        reportEnterHome()
+
+        MainStartUpFragment.inject(this)
+    }
+}

+ 16 - 0
app/src/main/java/com/adealink/weparty/Routers.kt

@@ -0,0 +1,16 @@
+package com.adealink.weparty
+
+interface AppModule {
+    interface Main {
+        companion object {
+            const val PATH = "/main"
+            const val EXTRA_ACCOUNT_LOGIN="extra_account_login"
+            const val EXTRA_MAIN_TAB = "tab" //控制首页默认展示的tab
+            const val EXTRA_MAIN_SUB_TAB = "subTab" //控制首页默认展示的子tab
+
+            const val EXTRA_SPLASH_JUMP_URL = "splash_jump_url" //Splash页面跳转URL
+            const val EXTRA_FROM_MATCH_NOTIFICATION = "from_match_notification"
+            const val EXTRA_MATCH_ID = "match_id"
+        }
+    }
+}

+ 34 - 0
app/src/main/java/com/adealink/weparty/aab/AABConfig.kt

@@ -0,0 +1,34 @@
+package com.adealink.weparty.aab
+
+import android.app.Application
+import com.adealink.frame.aab.IAABConfig
+import com.adealink.frame.aab.log.ILog
+import com.adealink.frame.log.Log
+import com.adealink.weparty.App
+
+/**
+ * Created by sunxiaodong on 2021/5/19.
+ */
+class AABConfig : IAABConfig {
+
+    override val application: Application
+        get() = App.instance
+    override val log: ILog
+        get() = object : ILog {
+
+            override fun logD(tag: String, msg: String) {
+                Log.d(tag, msg)
+            }
+
+            override fun logI(tag: String, msg: String) {
+                Log.i(tag, msg)
+            }
+
+            override fun logE(tag: String, msg: String, e: Exception?) {
+                Log.e(tag, msg, e)
+            }
+
+        }
+
+
+}

+ 13 - 0
app/src/main/java/com/adealink/weparty/apm/APMConfig.kt

@@ -0,0 +1,13 @@
+package com.adealink.weparty.apm
+
+import com.adealink.frame.apm.config.IAPMConfig
+
+/**
+ * Created by sunxiaodong on 2021/11/30.
+ */
+class APMConfig : IAPMConfig {
+
+    override val enable: Boolean
+        get() = false
+
+}

+ 173 - 0
app/src/main/java/com/adealink/weparty/apm/APMInit.kt

@@ -0,0 +1,173 @@
+package com.adealink.weparty.apm
+
+import android.app.Application
+import com.adealink.frame.apm.core.APM
+import com.adealink.frame.apm.core.base.AbstractPlugin
+import com.adealink.frame.apm.core.base.MonitorEvent
+import com.adealink.frame.apm.core.stat.AbstractPluginEventHandler
+import com.adealink.frame.apm.plugins.largebitmap.LargeBitmapConfig
+import com.adealink.frame.apm.plugins.largebitmap.LargeBitmapPlugin
+import com.adealink.frame.apm.plugins.largebitmap.LargeBitmapStat
+import com.adealink.frame.apm.plugins.memory.memoryleak.LeakStat
+import com.adealink.frame.apm.plugins.memory.memoryleak.MemoryConfig
+import com.adealink.frame.apm.plugins.memory.memoryleak.MemoryLeakPlugin
+import com.adealink.frame.apm.plugins.uiblock.ANRStat
+import com.adealink.frame.apm.plugins.uiblock.BlockStat
+import com.adealink.frame.apm.plugins.uiblock.UIBlockMonitor
+import com.adealink.frame.base.AppBaseInfo
+import com.adealink.frame.coroutine.dispatcher.Dispatcher
+import com.adealink.frame.data.json.toJsonErrorNull
+import com.adealink.frame.log.ILog
+import com.adealink.frame.log.Log
+import com.adealink.frame.log.Log.splitLog
+import com.adealink.frame.util.AppUtil
+import com.adealink.frame.util.ProcessUtil
+import com.adealink.weparty.App
+import com.adealink.weparty.BuildConfig
+import com.adealink.weparty.apm.stat.APMStatEvent
+import com.adealink.weparty.log.data.UploadLogType
+import com.adealink.weparty.log.manager.logManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * Created by sunxiaodong on 2021/12/4.
+ */
+
+const val TAG_APM = "tag_apm"
+
+fun initAPM(application: Application) {
+    if (!ProcessUtil.isUIProcess()) {
+        return
+    }
+
+    Log.i(TAG_APM, "initAPM")
+//    XCrash.init(application) 已在CrashInit中初始化
+    APM.init(application) {
+        addEventHandler(object : AbstractPluginEventHandler() {
+            override fun onEvent(plugin: AbstractPlugin, event: MonitorEvent) {
+                stat(plugin, event)
+            }
+        })
+
+        use(UIBlockMonitor {
+            releaseUploadThreshold = 3000
+            uiBlockEnable = BuildConfig.DEBUG || !AppBaseInfo.isRelease //开发阶段默认开启,线上发布需要灰度收集
+        })
+        use(LargeBitmapPlugin(
+            LargeBitmapConfig.Builder()
+                .apply {
+                    largeBitmapEnable = BuildConfig.DEBUG || !AppBaseInfo.isRelease //只在开发阶段使用
+                }
+                .build())
+        )
+        use(
+            MemoryLeakPlugin(
+                MemoryConfig()
+                    .apply {
+                        memoryLeakEnable = BuildConfig.DEBUG || !AppBaseInfo.isRelease //只在开发阶段使用
+                    })
+        )
+
+        setLog(object : ILog {
+            override fun logD(tag: String, msg: String) {
+                Log.d(tag, msg)
+            }
+
+            override fun logI(tag: String, msg: String) {
+                Log.i(tag, msg)
+            }
+
+            override fun logE(tag: String, msg: String, e: Exception?) {
+                Log.e(tag, msg, e)
+            }
+        })
+    }
+}
+
+// default not on main Thread
+private fun stat(plugin: AbstractPlugin, event: MonitorEvent) {
+    when (plugin) {
+        is UIBlockMonitor -> {
+            when (event) {
+                is ANRStat -> {
+                    reportANR(event)
+                }
+                is BlockStat -> {
+                    reportUiBlock(event)
+                }
+                else -> {
+
+                }
+            }
+        }
+        is LargeBitmapPlugin -> {
+            when (event) {
+                is LargeBitmapStat -> {
+                    reportLargeBitmap(event)
+                }
+            }
+        }
+        is MemoryLeakPlugin -> {
+            when (event) {
+                is LeakStat -> {
+                    reportMemoryLeak(event)
+                }
+            }
+        }
+    }
+}
+
+private fun reportANR(anrStat: ANRStat) {
+    CoroutineScope(Dispatcher.WENEXT_THREAD_POOL).launch {
+        val anrEventMap = anrStat.toMap()
+        anrEventMap["boot_offset"] = (anrStat.timestamp - App.instance.appStartTime).toString()
+        anrEventMap["cur_activity"] = AppUtil.currentActivityName
+        APMStatEvent(APMStatEvent.Action.ANR)
+            .apply {
+                addEventMap(anrEventMap)
+            }
+            .send()
+        val time = System.currentTimeMillis()
+        splitLog("$anrEventMap")
+            .forEach { Log.i(TAG_APM, "anr($time) = $it") }
+        logManager.uploadLongString("anr($time)", "${toJsonErrorNull(anrEventMap)}")
+        logManager.uploadLog(UploadLogType.FIVE_DAY)
+    }
+}
+
+private fun reportUiBlock(blockStat: BlockStat) {
+    CoroutineScope(Dispatcher.WENEXT_THREAD_POOL).launch {
+        val blockEventMap = blockStat.toMap()
+        APMStatEvent(APMStatEvent.Action.UI_BLOCK)
+            .apply {
+                addEventMap(blockEventMap)
+            }
+            .send()
+        val time = System.currentTimeMillis()
+        splitLog("$blockEventMap")
+            .forEach { Log.i(TAG_APM, "ui block($time) = $it") }
+        logManager.uploadLongString("ui block($time)", "${toJsonErrorNull(blockEventMap)}")
+        logManager.uploadLog(UploadLogType.FIVE_DAY)
+    }
+}
+
+private fun reportLargeBitmap(stat: LargeBitmapStat) {
+    Log.i(TAG_APM, "large bitmap = $stat")
+}
+
+private fun reportMemoryLeak(stat: LeakStat) {
+    CoroutineScope(Dispatcher.WENEXT_THREAD_POOL).launch {
+        val memoryEventMap = stat.toMap()
+        APMStatEvent(APMStatEvent.Action.MEMORY_LEAK)
+            .apply {
+                addEventMap(memoryEventMap)
+            }
+            .send()
+        val time = System.currentTimeMillis()
+        splitLog("$memoryEventMap")
+            .forEach { Log.i(TAG_APM, "memory leak($time) = $it") }
+        logManager.uploadLongString("memory leak($time)", "${toJsonErrorNull(memoryEventMap)}")
+        logManager.uploadLog(UploadLogType.FIVE_DAY)
+    }
+}

+ 71 - 0
app/src/main/java/com/adealink/weparty/apm/HookTest.kt

@@ -0,0 +1,71 @@
+package com.adealink.weparty.apm
+
+import android.view.View
+import androidx.viewpager2.widget.ViewPager2
+import com.adealink.frame.log.Log
+import com.flyjingfish.android_aop_annotation.ProceedJoinPoint
+import com.flyjingfish.android_aop_annotation.anno.AndroidAopMatchClassMethod
+import com.flyjingfish.android_aop_annotation.base.MatchClassMethod
+import com.flyjingfish.android_aop_annotation.enums.MatchType
+
+/**
+ * 定义一些替换操作
+ * Created by XiaoDongLin.
+ * Date: 2025/8/22
+ */
+@AndroidAopMatchClassMethod(
+    targetClassName = "androidx.viewpager2.widget.ScrollEventAdapter",
+    methodName = ["updateScrollEventValues"],
+    type = MatchType.SELF
+)
+class ScrollEventAdapterMethod : MatchClassMethod {
+    override fun invoke(joinPoint: ProceedJoinPoint, methodName: String): Any? {
+        try {
+//            val target = joinPoint.target
+//            if (target != null) {
+//                val mViewPagerField = target::class.java.getDeclaredField("mViewPager")
+//                mViewPagerField.isAccessible = true
+//                val mViewPager = mViewPagerField.get(joinPoint.target) as ViewPager2
+//                val viewPagerIdString = mViewPager.resources?.getResourceEntryName(mViewPager.id)
+//                Log.i(
+//                    "ScrollEventAdapterMethod",
+//                    "=====updateScrollEventValues=====${mViewPager}, viewPager:$viewPagerIdString, itemCount:${mViewPager.childCount}, currentItem:${mViewPager.currentItem}, " +
+//                            "${mViewPager.adapter}, ${mViewPager.itemDecorationCount}, ${mViewPager.getChildAt(mViewPager.currentItem)}"
+//                )
+//            }
+            return joinPoint.proceed()
+        } catch (e: Exception) {
+
+        }
+        return null
+    }
+}
+
+
+@AndroidAopMatchClassMethod(
+    targetClassName = "androidx.viewpager2.widget.ViewPager2",
+    methodName = ["setCurrentItemInternal"],
+    type = MatchType.SELF
+)
+class SetCurrentItemInternalMethod : MatchClassMethod {
+    override fun invoke(joinPoint: ProceedJoinPoint, methodName: String): Any? {
+        try {
+            val target = joinPoint.target
+            if (target != null) {
+                val mViewPager = target as ViewPager2
+                val viewPagerIdString = mViewPager.resources?.getResourceEntryName(mViewPager.id)
+                val args = joinPoint.args
+                val item = args?.getOrNull(0) as? Int
+                val smoothScroll = args?.getOrNull(1) as? Boolean
+                Log.i(
+                    "SetCurrentItemInternal",
+                    "=====setCurrentItemInternal=====${mViewPager}, viewPager:$viewPagerIdString, currentItem:${mViewPager.currentItem}, " +
+                            "${mViewPager.adapter?.javaClass?.name}, setItem:$item, smoothScroll:$smoothScroll"
+                )
+            }
+        } catch (e: Exception) {
+
+        }
+        return joinPoint.proceed()
+    }
+}

+ 18 - 0
app/src/main/java/com/adealink/weparty/apm/stat/APMStatEvent.kt

@@ -0,0 +1,18 @@
+package com.adealink.weparty.apm.stat
+
+import com.adealink.frame.statistics.BaseStatEvent
+import com.adealink.frame.statistics.IEventValue
+
+/**
+ * Created by sunxiaodong on 2021/12/5.
+ */
+class APMStatEvent(override val action: IEventValue) : BaseStatEvent("apm") {
+
+    enum class Action(override val value: String, override val desc: String) : IEventValue {
+        ANR("anr", "ANR"),
+        UI_BLOCK("ui_block", "UI卡顿"),
+        MEMORY_LEAK("memory_leak", "内存泄露"),
+        CRASH("crash", "崩溃"),
+    }
+
+}

+ 24 - 0
app/src/main/java/com/adealink/weparty/base/AppBaseConfig.kt

@@ -0,0 +1,24 @@
+package com.adealink.weparty.base
+
+import android.net.Uri
+import com.adealink.frame.base.IAppBaseConfig
+import com.adealink.weparty.debug.DebugPrefs
+import com.adealink.weparty.network.RELEASE_HOSTS
+import com.adealink.weparty.BuildConfig
+
+class AppBaseConfig : IAppBaseConfig {
+    override val debugLog: Boolean
+        get() = BuildConfig.DEBUG
+    override val isRelease: Boolean
+        get() = BuildConfig.IS_RELEASE
+    override val platform: String
+        get() = "android"
+    override val deeplinkScheme: String
+        get() = BuildConfig.DEEP_LINK_SCHEME
+    override val deeplinkHost: String
+        get() = BuildConfig.DEEP_LINK_HOST
+    override val isProdEnv: Boolean
+        get() = isRelease || RELEASE_HOSTS.contains(Uri.parse(DebugPrefs.httpUrl).host)
+    override val appName: String
+        get() = "yoki"
+}

+ 66 - 0
app/src/main/java/com/adealink/weparty/cache/CacheRepository.kt

@@ -0,0 +1,66 @@
+package com.adealink.weparty.cache
+
+import android.util.Log
+import com.adealink.frame.base.IError
+import com.adealink.frame.base.Rlt
+import kotlinx.coroutines.async
+import kotlinx.coroutines.flow.channelFlow
+import kotlinx.coroutines.supervisorScope
+
+/**
+ * 数据仓库,本地+网络,谁快就先用谁来显示。
+ * ChannelFlow:生产者消费者两端能够实现异步非阻塞模型
+ * 生产和消费两端实现了并行执行
+ * Created by XiaoDongLin.
+ * Date: 2025/2/25
+ */
+abstract class CacheRepository<T> {
+
+    companion object {
+        private const val TAG = "CacheRepository"
+    }
+
+
+    fun getData() = channelFlow<Rlt<T>> {
+        supervisorScope {
+            val dataFromLocalDeffer = async {
+                fetchDataFromLocal().also {
+                    Log.d(
+                        TAG,
+                        "fetchDataFromLocal result:$it , thread:${Thread.currentThread().name}"
+                    )
+
+                    if (it is Rlt.Success) {
+                        send(it)
+                    }
+                }
+            }
+
+            val dataFromNetDeffer = async {
+                fetchDataFromNetWork().also {
+                    Log.d(
+                        TAG,
+                        "fetchDataFromNetWork result:$it , thread:${Thread.currentThread().name}"
+                    )
+                    if (it is Rlt.Success) {
+                        send(it)
+                        //如果网络数据已加载,可以直接取消任务,就不需要处理本地数据了
+                        dataFromLocalDeffer.cancel()
+                    }
+                }
+            }
+
+            //本地数据和网络数据,都加载失败的情况
+            val localData = dataFromLocalDeffer.await()
+            val networkData = dataFromNetDeffer.await()
+            if (localData is Rlt.Failed && networkData is Rlt.Failed) {
+                send(Rlt.Failed(IError("load data error")))
+            }
+        }
+    }
+
+    protected abstract suspend fun fetchDataFromLocal(): Rlt<T>
+
+    protected abstract suspend fun fetchDataFromNetWork(): Rlt<T>
+
+}

+ 14 - 0
app/src/main/java/com/adealink/weparty/channel/AppChannel.kt

@@ -0,0 +1,14 @@
+package com.adealink.weparty.channel
+
+/**
+ * Created by sunxiaodong on 2021/9/8.
+ */
+enum class AppChannel(val channel: String) {
+    GP("gp"), //自然量
+    GOOGLE_PLAY("google-play"), //自然量
+    HUAWEI("huawei"),
+    LUOKAI("luokai"),
+    GOOGLE("google"), //google投放
+    FB("fb"), //fb投放
+    HUI_TENG("huiteng"), //辉腾
+}

+ 23 - 0
app/src/main/java/com/adealink/weparty/channel/ChannelUtil.kt

@@ -0,0 +1,23 @@
+package com.adealink.weparty.channel
+
+import com.adealink.frame.googleservice.CHANNEL_DEFAULT
+import com.adealink.frame.util.AppUtil
+import com.adealink.weparty.App
+import com.tencent.vasdolly.helper.ChannelReaderUtil
+
+/**
+ * Created by sunxiaodong on 2021/9/6.
+ */
+fun getChannel(): String {
+    var channel = ChannelReaderUtil.getChannel(AppUtil.appContext)
+    if (channel.isNullOrEmpty().not()) {
+        return channel
+    }
+
+    channel = App.instance.googleService.getGPReferrerSource()
+    if (channel.isEmpty().not()) {
+        return channel
+    }
+
+    return CHANNEL_DEFAULT
+}

+ 6 - 0
app/src/main/java/com/adealink/weparty/channel/Tags.kt

@@ -0,0 +1,6 @@
+package com.adealink.weparty.channel
+
+/**
+ * Created by sunxiaodong on 2021/9/6.
+ */
+const val TAG_CHANNEL = "tag_channel"

+ 201 - 0
app/src/main/java/com/adealink/weparty/cocosgame/BaseCocosWebGameFragment.kt

@@ -0,0 +1,201 @@
+package com.adealink.weparty.cocosgame
+
+import android.os.Bundle
+import android.view.View
+import androidx.annotation.LayoutRes
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.view.updateLayoutParams
+import androidx.fragment.app.DialogFragment
+import androidx.lifecycle.Observer
+import androidx.viewbinding.ViewBinding
+import com.adealink.frame.base.AppBaseInfo
+import com.adealink.frame.router.Router
+import com.adealink.weparty.cocosgame.chat.listener.IChatOperateListener
+import com.adealink.weparty.cocosgame.data.ClickAvatarOpData
+import com.adealink.weparty.cocosgame.data.ClickEmotionOpData
+import com.adealink.weparty.cocosgame.data.ClickMessageOpData
+import com.adealink.weparty.cocosgame.data.ClickMicOpData
+import com.adealink.weparty.cocosgame.data.CurrencyInsufficientDialogData
+import com.adealink.weparty.cocosgame.data.DialogData
+import com.adealink.weparty.cocosgame.data.ExitGameDialogData
+import com.adealink.weparty.cocosgame.data.Game
+import com.adealink.weparty.cocosgame.data.GameToNativeOpData
+import com.adealink.weparty.cocosgame.data.InitGameData
+import com.adealink.weparty.cocosgame.data.ShowGameRuleOpData
+import com.adealink.weparty.cocosgame.data.UnknownDialogData
+import com.adealink.weparty.cocosgame.viewmodel.BaseCocosWebGameViewModel
+import com.adealink.weparty.cocosgame.web.CocosGameWebView
+import com.adealink.weparty.commonui.BaseFragment
+import com.adealink.weparty.commonui.dialogfragment.BaseDialogFragment
+import com.adealink.weparty.commonui.ext.dp
+import com.adealink.weparty.commonui.toast.util.showToast
+import com.adealink.weparty.commonui.widget.BottomDialogFragment
+import com.adealink.weparty.module.room.Room
+import com.adealink.weparty.module.room.data.MemberInfoDialogSource
+import com.adealink.weparty.module.wallet.Wallet
+import com.adealink.weparty.module.wallet.data.Currency
+import com.qmuiteam.qmui.widget.util.QMUIStatusBarHelper
+
+/**
+ * Cocos Web 游戏基础
+ */
+abstract class BaseCocosWebGameFragment<VM : BaseCocosWebGameViewModel<*, *, *, *>, VB : ViewBinding>(@LayoutRes contentLayoutId: Int) :
+    BaseFragment(contentLayoutId),
+    IChatOperateListener {
+
+    abstract val cocosViewModel: VM
+
+    /**
+     * Cocos游戏码
+     */
+    abstract fun cocosGame(): Game
+
+    /**
+     * Cocos游戏启动Url
+     */
+    abstract fun gameUrl(): String
+
+    /**
+     * Activity content view
+     */
+    abstract fun contentView(): VB
+
+    /**
+     * 这个方法在 setContentView 之后调用,用于初始化一些视图控件
+     */
+    abstract fun onCocosGameViewCreated(viewBinding: VB)
+
+    /**
+     * Cocos游戏渲染安全区域(排除顶部状态栏, 底部导航栏等)
+     */
+    abstract fun safeView(): View
+
+    abstract fun inflateCocosWebView(viewBinding: VB)
+
+    abstract fun webView(): CocosGameWebView
+
+    override fun initViews() {
+        super.initViews()
+        val contentViewBinding = contentView()
+        fixSafeView()
+        cocosViewModel.onCreate()
+        cocosViewModel.initGame(
+            webView(),
+            safeView(),
+            InitGameData(cocosGame(), 58.dp())
+        )
+        loadUrlAfterViewCreated()
+        onCocosGameViewCreated(contentViewBinding)
+        if (!AppBaseInfo.isRelease) {
+            // DEBUG包打印游戏路径
+            showToast("DEBUG: gameCode:${cocosGame()}, gameUrl:${gameUrl()}")
+        }
+    }
+
+    open fun fixSafeView() {
+        safeView().updateLayoutParams<ConstraintLayout.LayoutParams> {
+            topMargin = QMUIStatusBarHelper.getStatusbarHeight(context) + 44.dp() //44dp是游戏顶部栏高度
+        }
+    }
+
+    open fun reload() {
+        webView().loadUrl(gameUrl())
+    }
+
+    open fun loadUrlAfterViewCreated(){
+        webView().loadUrl(gameUrl())
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        webView().onDestroy()
+        cocosViewModel.onDestroy()
+    }
+
+    override fun observeViewModel() {
+        super.observeViewModel()
+        cocosViewModel.apply {
+            gameToNativeOpLD.observeWithoutCache(viewLifecycleOwner,
+                object : Observer<GameToNativeOpData>{
+                    override fun onChanged(value: GameToNativeOpData) {
+                        onGameToNativeOpData(value)
+                    }
+                }
+            )
+            showDialogLD.observeWithoutCache(viewLifecycleOwner,
+                object: Observer<DialogData>{
+                    override fun onChanged(value: DialogData) {
+                        showDialog(value)
+                    }
+                }
+            )
+            dismissDialogLD.observeWithoutCache(viewLifecycleOwner,
+                object: Observer<Long>{
+                    override fun onChanged(value: Long) {
+                        dismissDialog(value)
+                    }
+                }
+            )
+        }
+    }
+
+    /*
+     * OpData
+     */
+    open fun onGameToNativeOpData(opData: GameToNativeOpData) {
+        when (opData) {
+            is ClickAvatarOpData -> onAvatarClick(opData.uid)
+            is ClickMicOpData -> onMicClick(opData.uid)
+            is ClickEmotionOpData -> onEmotionBtnClick(opData)
+            is ClickMessageOpData -> onMessageBtnClick(opData)
+            is ShowGameRuleOpData -> onGameRuleBtnClick(opData)
+            else -> {}
+        }
+    }
+
+    open fun onAvatarClick(uid: Long) {
+        Router.getRouterInstance<BottomDialogFragment>(Room.RoomMemberInfo.PATH)?.apply {
+            arguments = Bundle().apply {
+                putLong(Room.Common.EXTRA_UID, uid)
+                putSerializable(
+                    Room.Common.EXTRA_SOURCE,
+                    MemberInfoDialogSource.RoomGaming
+                )
+            }
+        }?.show(childFragmentManager)
+    }
+
+    open fun onMicClick(uid: Long) {}
+
+    open fun onEmotionBtnClick(data: ClickEmotionOpData) {}
+
+    open fun onMessageBtnClick(data: ClickMessageOpData) {}
+
+    open fun onGameRuleBtnClick(data: ShowGameRuleOpData) {}
+
+    /*
+     * DialogData
+     */
+    open fun showDialog(data: DialogData) {
+        when (data) {
+            is ExitGameDialogData -> showExitGameDialog(data)
+            is CurrencyInsufficientDialogData -> {
+                Router.getRouterInstance<BaseDialogFragment>(Wallet.WalletNotEnoughDialog.PATH)?.apply { //5
+                    arguments = Bundle().apply {
+                        putByte(Wallet.WalletNotEnoughDialog.EXTRA_CURRENCY_TYPE, Currency.getCurrencyByValue(data.type.toByte())?.value ?: Currency.Coin.value)
+                    }
+                }
+                    ?.show(childFragmentManager)
+            }
+
+            is UnknownDialogData -> {}
+        }
+    }
+
+    private fun dismissDialog(id: Long) {
+        (childFragmentManager.findFragmentByTag(id.toString()) as? DialogFragment)?.dismissAllowingStateLoss()
+    }
+
+    open fun showExitGameDialog(exitData: ExitGameDialogData) {}
+
+}

+ 5 - 0
app/src/main/java/com/adealink/weparty/cocosgame/CocosGame.kt

@@ -0,0 +1,5 @@
+package com.adealink.weparty.cocosgame
+
+interface CocosGame {
+
+}

+ 35 - 0
app/src/main/java/com/adealink/weparty/cocosgame/chat/adapter/QuickTextViewBinder.kt

@@ -0,0 +1,35 @@
+package com.adealink.weparty.cocosgame.chat.adapter
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import com.adealink.weparty.cocosgame.chat.listener.IChatOperateListener
+import com.adealink.weparty.cocosgame.data.QuickText
+import com.adealink.weparty.commonui.recycleview.adapter.BindingViewHolder
+import com.adealink.weparty.commonui.recycleview.adapter.multitype.ItemViewBinder
+import com.adealink.weparty.databinding.ItemCocosgameQuickTextBinding
+
+class QuickTextViewBinder(val l: IChatOperateListener) :
+    ItemViewBinder<QuickText, BindingViewHolder<ItemCocosgameQuickTextBinding>>() {
+
+    override fun onCreateViewHolder(
+        inflater: LayoutInflater,
+        parent: ViewGroup
+    ): BindingViewHolder<ItemCocosgameQuickTextBinding> {
+        return BindingViewHolder(ItemCocosgameQuickTextBinding.inflate(inflater, parent, false))
+    }
+
+    override fun onBindViewHolder(
+        holder: BindingViewHolder<ItemCocosgameQuickTextBinding>,
+        item: QuickText
+    ) {
+        holder.binding.root.text = item.getText()
+        holder.binding.root.setOnClickListener {
+            l.onQuickTextClick(item)
+        }
+    }
+
+    companion object {
+        const val SPAN_COUNT = 3
+    }
+
+}

+ 103 - 0
app/src/main/java/com/adealink/weparty/cocosgame/chat/component/ChatMessageComp.kt

@@ -0,0 +1,103 @@
+package com.adealink.weparty.cocosgame.chat.component
+
+import android.view.View
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.view.updateLayoutParams
+import androidx.lifecycle.LifecycleOwner
+import com.adealink.frame.base.fastLazy
+import com.adealink.frame.dot.Dot
+import com.adealink.frame.dot.DotView
+import com.adealink.frame.dot.NormalDot
+import com.adealink.frame.dot.NumDot
+import com.adealink.frame.mvvm.view.ViewComponent
+import com.adealink.weparty.cocosgame.data.ClickMessageOpData
+import com.adealink.weparty.cocosgame.data.CocosPosition
+import com.adealink.weparty.cocosgame.data.CocosViewSize
+import com.adealink.weparty.cocosgame.viewmodel.IBaseCocosWebGameViewModel
+import com.adealink.weparty.commonui.ext.dp
+import com.adealink.weparty.module.room.RoomModule
+import com.adealink.weparty.module.room.data.RoomChatMessagePanelStatus
+
+class ChatMessageComp(
+    lifecycleOwner: LifecycleOwner,
+    private val ivMessage: View,
+    private val dotView: DotView,
+    private val cocosViewModel: IBaseCocosWebGameViewModel<*, *, *, *>
+) : ViewComponent(lifecycleOwner) {
+
+    private val chatMessageViewModel by fastLazy { RoomModule.getChatMessageViewModel(requireActivity()) }
+    private val roomGameViewModel by fastLazy { RoomModule.getRoomGameViewModel(requireActivity()) }
+
+    private val messageDot = Dot(arrayListOf(), "MessageDot")
+
+    override fun onCreate() {
+        super.onCreate()
+        initViews()
+        observeViewModel()
+    }
+
+    private fun initViews() {
+        dotView.observeDot(viewLifecycleOwner, messageDot)
+    }
+
+    private fun observeViewModel() {
+        cocosViewModel.messageBtnNodeViewLD.observe(viewLifecycleOwner) {
+            initMessageBtn(it.position, it.size)
+        }
+        chatMessageViewModel?.messageAddLD?.observeWithoutCache(viewLifecycleOwner) {
+            showNewMessageDot(true)
+        }
+    }
+
+    private fun initMessageBtn(position: CocosPosition, size: CocosViewSize) {
+        ivMessage.updateLayoutParams<ConstraintLayout.LayoutParams> {
+            width = size.width.toInt()
+            height = size.height.toInt()
+            leftMargin = (position.x - size.width / 2).toInt()
+            bottomMargin = (position.y - size.height / 2).toInt()
+        }
+        dotView.updateLayoutParams<ConstraintLayout.LayoutParams> {
+            leftMargin = (size.width * 0.75f).toInt()
+            topMargin = 1.dp()
+        }
+    }
+
+    fun switchChatMessagePanel(data: ClickMessageOpData) {
+        if (isShow()) {
+            hideChatMessagePanel()
+        } else {
+            initMessageBtn(data.position, data.size)
+            showChatMessagePanel()
+            showNewMessageDot(false)
+        }
+    }
+
+    private fun showChatMessagePanel() {
+        roomGameViewModel?.updateRoomChatMessagePanelStatus(RoomChatMessagePanelStatus.Expanding)
+    }
+
+    fun isShow(): Boolean {
+        return roomGameViewModel?.isRoomChatMessageShow ?: false
+    }
+
+    fun hideChatMessagePanel() {
+        if (roomGameViewModel?.isIamPlayer() == true) {
+            roomGameViewModel?.updateRoomChatMessagePanelStatus(RoomChatMessagePanelStatus.Hide)
+            return
+        }
+        roomGameViewModel?.updateRoomChatMessagePanelStatus(RoomChatMessagePanelStatus.Shrinking)
+    }
+
+    fun clear() {
+        dotView.show(NumDot(0))
+    }
+
+    private fun showNewMessageDot(show: Boolean) {
+        if (roomGameViewModel?.isIamPlayer() == true) {
+            messageDot.show(NormalDot(show))
+        } else {
+            messageDot.hide()
+        }
+    }
+
+}

+ 638 - 0
app/src/main/java/com/adealink/weparty/cocosgame/chat/component/QuickChatComp.kt

@@ -0,0 +1,638 @@
+package com.adealink.weparty.cocosgame.chat.component
+
+import android.view.Gravity
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import android.widget.TextView
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams
+import androidx.core.view.isVisible
+import androidx.core.view.updateLayoutParams
+import androidx.lifecycle.LifecycleOwner
+import com.adealink.frame.aab.util.getCompatDrawable
+import com.adealink.frame.base.Rlt
+import com.adealink.frame.base.fastLazy
+import com.adealink.frame.effect.listener.IPlayListener
+import com.adealink.frame.effect.view.EffectView
+import com.adealink.frame.log.Log
+import com.adealink.frame.mvvm.view.ViewComponent
+import com.adealink.frame.util.DisplayUtil
+import com.adealink.frame.util.runOnUiThread
+import com.adealink.weparty.R
+import com.adealink.weparty.cocosgame.chat.data.EmotionBubble
+import com.adealink.weparty.cocosgame.chat.data.QuickChatBubble
+import com.adealink.weparty.cocosgame.chat.data.QuickTextBubble
+import com.adealink.weparty.cocosgame.chat.fragment.QuickChatFragment
+import com.adealink.weparty.cocosgame.data.ClickEmotionOpData
+import com.adealink.weparty.cocosgame.data.CocosPosition
+import com.adealink.weparty.cocosgame.data.CocosViewData
+import com.adealink.weparty.cocosgame.data.CocosViewLocation
+import com.adealink.weparty.cocosgame.data.CocosViewSize
+import com.adealink.weparty.cocosgame.data.Game
+import com.adealink.weparty.cocosgame.data.TAG_COCOS_GAME_FLOW
+import com.adealink.weparty.cocosgame.viewmodel.IBaseCocosWebGameViewModel
+import com.adealink.weparty.commonui.ext.gone
+import com.adealink.weparty.commonui.ext.show
+import com.adealink.weparty.commonui.toast.util.showFailedToast
+import com.adealink.weparty.databinding.LayoutCocosgameEmotionBubbleBottomBinding
+import com.adealink.weparty.databinding.LayoutCocosgameEmotionBubbleLeftBinding
+import com.adealink.weparty.databinding.LayoutCocosgameEmotionBubbleRightBinding
+import com.adealink.weparty.databinding.LayoutCocosgameEmotionBubbleTopBinding
+import com.adealink.weparty.databinding.LayoutCocosgameQuickTextBubbleBottomBinding
+import com.adealink.weparty.databinding.LayoutCocosgameQuickTextBubbleLeftBinding
+import com.adealink.weparty.databinding.LayoutCocosgameQuickTextBubbleRightBinding
+import com.adealink.weparty.databinding.LayoutCocosgameQuickTextBubbleTopBinding
+import com.adealink.weparty.module.emotion.EmotionModule
+import com.adealink.weparty.module.emotion.data.EmotionInfo
+import com.adealink.weparty.module.emotion.data.SendEmotionNotify
+import com.adealink.weparty.module.emotion.effect.EmotionEffectEntity
+import com.adealink.weparty.module.room.RoomModule
+import com.adealink.weparty.module.room.chat.data.EmotionMsgContent
+import com.adealink.weparty.module.room.chat.data.Message
+import com.adealink.weparty.module.room.chat.data.MessageType
+import com.adealink.weparty.module.room.chat.data.QuickMsgContent
+import com.adealink.weparty.module.room.data.SendEmotionScene
+import java.util.LinkedList
+import java.util.Queue
+import kotlin.collections.set
+
+class QuickChatComp(
+    lifecycleOwner: LifecycleOwner,
+    private val game: Game,
+    private val vEmotion: View, //表情按钮
+    private val panelContainer: FrameLayout, //表情面板
+    private val emotionContainer: ConstraintLayout, //表情气泡容器
+    private val cocosViewModel: IBaseCocosWebGameViewModel<*, *, *, *>?
+) : ViewComponent(lifecycleOwner) {
+
+    private val chatViewModel by fastLazy { RoomModule.getChatMessageViewModel(requireActivity()) }
+    private val emotionViewModel by fastLazy { EmotionModule.getEmotionViewModel(requireActivity()) }
+    private var quickChatFragment: QuickChatFragment? = null
+
+    //玩家头像位置辅助定位
+    private val player2EmotionBubble = HashMap<Long, Pair<CocosViewLocation, View>>(4)
+    private val player2QuickTextBubble = HashMap<Long, Pair<CocosViewLocation, View>>(4)
+    private val player2BubbleQueue = HashMap<Long, Queue<QuickChatBubble>>(4)
+
+    override fun onCreate() {
+        super.onCreate()
+        initViews()
+        observeViewModel()
+    }
+
+    private fun initViews() {
+
+    }
+
+    private fun observeViewModel() {
+        cocosViewModel?.emotionBtnNodeViewLD?.observe(viewLifecycleOwner) {
+            initEmotionBtn(it.position, it.size)
+        }
+        emotionViewModel?.apply {
+            clickedEmotionLD.observeWithoutCache(viewLifecycleOwner) {
+                this@QuickChatComp.onEmotionClick(it.first, it.second)
+            }
+            sendEmotionNotifyLD.observeWithoutCache(viewLifecycleOwner) {
+                onEmotionNotify(it)
+            }
+        }
+        chatViewModel?.messageAddLD?.observeWithoutCache(viewLifecycleOwner) {
+            it.onEach { msg ->
+                if (msg.getMessageType() == MessageType.QUICK_MESSAGE) {
+                    val quickText = (msg.content as? QuickMsgContent)?.quickText
+                    if (quickText != null) {
+                        quickText.senderUid = msg.sendUid
+                        addQuickChatBubble(QuickTextBubble(msg.sendUid, quickText))
+                    }
+                }
+            }
+        }
+    }
+
+    private fun onEmotionNotify(data: SendEmotionNotify) {
+        when (SendEmotionScene.map(data.scene)) {
+            SendEmotionScene.UNKNOWN -> {}
+            SendEmotionScene.MIC,
+            SendEmotionScene.ROOM_CHAT,
+            SendEmotionScene.SESSION_DETAIL -> {
+                addQuickChatBubble(EmotionBubble(data.fromUid, data.emotionInfo, data.resultIndex))
+            }
+
+            SendEmotionScene.COCOS_GAME -> {
+                chatViewModel?.addMessages(
+                    listOf(
+                        Message.crateMessage(
+                            MessageType.EMOTION,
+                            EmotionMsgContent(data),
+                            data.fromUid
+                        )
+                    )
+                )
+                addQuickChatBubble(EmotionBubble(data.fromUid, data.emotionInfo, data.resultIndex))
+            }
+
+            SendEmotionScene.GAME_PRANK -> {}
+        }
+    }
+
+    private fun onEmotionClick(emotionInfo: EmotionInfo, scene: SendEmotionScene) {
+        val roomId = RoomModule.getJoinedRoomId()
+        if (roomId == null || roomId <= 0) {
+            return
+        }
+        emotionViewModel?.sendEmotion(roomId, emotionInfo.id, scene)
+            ?.observe(viewLifecycleOwner) { rlt ->
+                when (rlt) {
+                    is Rlt.Success -> hideEmotionQuickTextPanel()
+                    else -> showFailedToast(rlt)
+                }
+            }
+    }
+
+    fun switchEmotionQuickTextPanel(data: ClickEmotionOpData) {
+        if (isShow()) {
+            hideEmotionQuickTextPanel()
+        } else {
+            initEmotionBtn(data.position, data.size)
+            showEmotionQuickTextPanel(data)
+        }
+    }
+
+    private fun initEmotionBtn(position: CocosPosition, size: CocosViewSize) {
+        vEmotion.updateLayoutParams<LayoutParams> {
+            width = size.width.toInt()
+            height = size.height.toInt()
+            leftMargin = (position.x - size.width / 2).toInt()
+            bottomMargin = (position.y - size.height / 2).toInt()
+        }
+    }
+
+    private fun showEmotionQuickTextPanel(data: ClickEmotionOpData) {
+        hideEmotionQuickTextPanel()
+        quickChatFragment =
+            QuickChatFragment.newInstance(data).also {
+                it.setHidePanelCallback {
+                    hideEmotionQuickTextPanel()
+                }
+            }
+        fragmentManager
+            .beginTransaction()
+            .replace(panelContainer.id, quickChatFragment!!)
+            .commitAllowingStateLoss()
+    }
+
+    fun isShow(): Boolean {
+        return quickChatFragment != null
+    }
+
+    fun hideEmotionQuickTextPanel() {
+        quickChatFragment?.let {
+            fragmentManager
+                .beginTransaction()
+                .remove(it)
+                .commitAllowingStateLoss()
+        }
+        quickChatFragment = null
+    }
+
+    private fun isQuickChatBubbleShow(uid: Long): Boolean {
+        val emotionBubbleBinding = player2EmotionBubble[uid]
+        if (emotionBubbleBinding != null && emotionBubbleBinding.second.isVisible) {
+            return true
+        }
+
+        val quickTextBubbleBinding = player2QuickTextBubble[uid]
+        return quickTextBubbleBinding != null && quickTextBubbleBinding.second.isVisible
+    }
+
+    private fun addQuickChatBubble(quickChatBubble: QuickChatBubble) {
+        val uid = quickChatBubble.uid
+        var queue = player2BubbleQueue[uid]
+        if (queue == null) {
+            queue = LinkedList()
+            player2BubbleQueue[uid] = queue
+        }
+        queue.offer(quickChatBubble)
+        if (isQuickChatBubbleShow(uid)) {
+            return
+        }
+
+        showNextQuickChatBubble(uid)
+    }
+
+    private fun showNextQuickChatBubble(uid: Long) {
+        val queue = player2BubbleQueue[uid]
+        val bubble = queue?.poll() ?: return
+        when (bubble) {
+            is EmotionBubble -> showEmotionBubble(bubble)
+            is QuickTextBubble -> showQuickTextBubble(bubble)
+        }
+    }
+
+    private fun showEmotionBubble(bubble: EmotionBubble) {
+        Log.d(TAG_COCOS_GAME_FLOW, "showEmotionBubble, $bubble")
+        val uid = bubble.uid
+        val cacheView = player2EmotionBubble[uid]
+        if (cacheView != null) {
+            playEmotionBubbleEffect(
+                getEmotionEffectView(cacheView.first, cacheView.second),
+                bubble,
+                onStart = {
+                    if (isValid) {
+                        cacheView.second.show()
+                    }
+                },
+                onComplete = {
+                    if (isValid) {
+                        cacheView.second.gone()
+                    }
+                },
+                onError = {
+                    if (isValid) {
+                        cacheView.second.gone()
+                    }
+                })
+            return
+        }
+
+        cocosViewModel?.getPlayerAvatarView(uid)?.observe(this.lifecycleOwner) {
+            Log.d(TAG_COCOS_GAME_FLOW, "getPlayerAvatarView, uid:$uid, data:${it}")
+            if (it is Rlt.Success) {
+                val position = it.data.position
+                val size = it.data.size
+                val location = getBubbleLocation(it.data)
+
+                //1.添加一个辅助View
+                var avatarGuideView = emotionContainer.findViewWithTag<View>("playerAvatar_$uid")
+                if (avatarGuideView == null) {
+                    avatarGuideView = View(context).apply {
+                        id = View.generateViewId()
+                        tag = "playerAvatar_$uid"
+                        background = getCompatDrawable(R.drawable.common_33000000_radius_12_bg)
+                    }
+                    emotionContainer.addView(avatarGuideView)
+                }
+                avatarGuideView.updateLayoutParams<LayoutParams> {
+                    width = size.width.toInt()
+                    height = size.height.toInt()
+                    leftToLeft = LayoutParams.PARENT_ID
+                    bottomToBottom = LayoutParams.PARENT_ID
+                    leftMargin = (position.x - size.width / 2).toInt()
+                    bottomMargin = (position.y - size.height / 2).toInt()
+                }
+
+                //2.添加气泡
+                val bubbleView = when (location) {
+                    CocosViewLocation.TOP,
+                    CocosViewLocation.TOP_LEFT,
+                    CocosViewLocation.TOP_RIGHT -> {
+                        LayoutCocosgameEmotionBubbleTopBinding.inflate(
+                            LayoutInflater.from(context),
+                            emotionContainer,
+                            true
+                        ).root.also { root ->
+                            root.updateLayoutParams<LayoutParams> {
+                                leftToLeft = avatarGuideView.id
+                                rightToRight = avatarGuideView.id
+                                topToBottom = avatarGuideView.id
+                            }
+                        }
+                    }
+
+                    CocosViewLocation.CENTER,
+                    CocosViewLocation.BOTTOM,
+                    CocosViewLocation.BOTTOM_LEFT,
+                    CocosViewLocation.BOTTOM_RIGHT -> {
+                        LayoutCocosgameEmotionBubbleBottomBinding.inflate(
+                            LayoutInflater.from(context),
+                            emotionContainer,
+                            true
+                        ).root.also { root ->
+                            root.updateLayoutParams<LayoutParams> {
+                                leftToLeft = avatarGuideView.id
+                                rightToRight = avatarGuideView.id
+                                bottomToTop = avatarGuideView.id
+                            }
+                        }
+                    }
+
+                    CocosViewLocation.LEFT -> {
+                        LayoutCocosgameEmotionBubbleLeftBinding.inflate(
+                            LayoutInflater.from(context),
+                            emotionContainer,
+                            true
+                        ).root.also { root ->
+                            root.updateLayoutParams<LayoutParams> {
+                                leftToRight = avatarGuideView.id
+                                topToTop = avatarGuideView.id
+                                bottomToBottom = avatarGuideView.id
+                            }
+                        }
+                    }
+
+                    CocosViewLocation.RIGHT -> {
+                        LayoutCocosgameEmotionBubbleRightBinding.inflate(
+                            LayoutInflater.from(context),
+                            emotionContainer,
+                            true
+                        ).root.also { root ->
+                            root.updateLayoutParams<LayoutParams> {
+                                rightToLeft = avatarGuideView.id
+                                topToTop = avatarGuideView.id
+                                bottomToBottom = avatarGuideView.id
+                            }
+                        }
+                    }
+                }
+                player2EmotionBubble[uid] = Pair(location, bubbleView)
+                playEmotionBubbleEffect(
+                    getEmotionEffectView(location, bubbleView),
+                    bubble,
+                    onStart = {
+                        if (isValid) {
+                            bubbleView.show()
+                        }
+                    },
+                    onComplete = {
+                        if (isValid) {
+                            bubbleView.gone()
+                        }
+                    },
+                    onError = {
+                        if (isValid) {
+                            bubbleView.gone()
+                        }
+                    }
+                )
+            } else {
+                showNextQuickChatBubble(uid)
+            }
+        }
+    }
+
+    private fun getEmotionEffectView(location: CocosViewLocation, view: View): EffectView {
+        return when (location) {
+            CocosViewLocation.TOP,
+            CocosViewLocation.TOP_LEFT,
+            CocosViewLocation.TOP_RIGHT -> {
+                LayoutCocosgameEmotionBubbleTopBinding.bind(view).evEmotion
+            }
+
+            CocosViewLocation.CENTER,
+            CocosViewLocation.BOTTOM,
+            CocosViewLocation.BOTTOM_LEFT,
+            CocosViewLocation.BOTTOM_RIGHT -> {
+                LayoutCocosgameEmotionBubbleTopBinding.bind(view).evEmotion
+            }
+
+            CocosViewLocation.LEFT -> {
+                LayoutCocosgameEmotionBubbleLeftBinding.bind(view).evEmotion
+            }
+
+            CocosViewLocation.RIGHT -> {
+                LayoutCocosgameEmotionBubbleRightBinding.bind(view).evEmotion
+            }
+        }
+    }
+
+    private fun playEmotionBubbleEffect(
+        effectView: EffectView,
+        bubble: EmotionBubble,
+        onStart: () -> Unit,
+        onComplete: () -> Unit,
+        onError: () -> Unit
+    ) {
+        Log.d(TAG_COCOS_GAME_FLOW, "playEmotionBubbleEffect, $bubble")
+        val uid = bubble.uid
+        val emotion = bubble.emotion
+        val effectPath = EmotionModule.getEmotionAnimationResPath(emotion)
+        if (effectPath.isNullOrEmpty()) {
+            showNextQuickChatBubble(uid)
+            return
+        }
+        onStart.invoke()
+        effectView.add(
+            EmotionEffectEntity(
+                path = effectPath,
+                emotionInfo = emotion,
+                loop = 0,
+                resultIndex = bubble.resultIndex,
+                showAvatarMask = false,
+                showResultOnly = false,
+                playListener = object : IPlayListener {
+
+                    override fun onComplete() {
+                        onComplete.invoke()
+                        showNextQuickChatBubble(uid)
+                    }
+
+                    override fun onError(errCode: Int) {
+                        onError.invoke()
+                        showNextQuickChatBubble(uid)
+                    }
+
+                }
+            )
+        )
+    }
+
+    private fun showQuickTextBubble(bubble: QuickTextBubble) {
+        val uid = bubble.uid
+        val cacheView = player2QuickTextBubble[uid]
+        if (cacheView != null) {
+            showQuickTextBubble(
+                getQuickChatTextView(cacheView.first, cacheView.second),
+                bubble,
+                onStart = {
+                    if (isValid) {
+                        cacheView.second.show()
+                    }
+                },
+                onComplete = {
+                    if (isValid) {
+                        cacheView.second.gone()
+                    }
+                }
+            )
+            return
+        }
+
+        cocosViewModel?.getPlayerAvatarView(uid)?.observe(this.lifecycleOwner) {
+            Log.d(TAG_COCOS_GAME_FLOW, "getPlayerAvatarView, uid:$uid, data:${it}")
+            if (it is Rlt.Success) {
+                val position = it.data.position
+                val size = it.data.size
+                val location = getBubbleLocation(it.data)
+
+                //1.添加一个辅助View
+                var avatarGuideView = emotionContainer.findViewWithTag<View>("playerAvatar_$uid")
+                if (avatarGuideView == null) {
+                    avatarGuideView = View(context).apply {
+                        id = View.generateViewId()
+                        tag = "playerAvatar_$uid"
+                    }
+                    emotionContainer.addView(avatarGuideView)
+                }
+                avatarGuideView.updateLayoutParams<LayoutParams> {
+                    width = size.width.toInt()
+                    height = size.height.toInt()
+                    leftToLeft = LayoutParams.PARENT_ID
+                    bottomToBottom = LayoutParams.PARENT_ID
+                    leftMargin = (position.x - size.width / 2).toInt()
+                    bottomMargin = (position.y - size.height / 2).toInt()
+                }
+
+                //2.添加气泡
+                val bubbleView = when (location) {
+                    CocosViewLocation.TOP,
+                    CocosViewLocation.TOP_LEFT,
+                    CocosViewLocation.TOP_RIGHT -> {
+                        LayoutCocosgameQuickTextBubbleTopBinding.inflate(
+                            LayoutInflater.from(context),
+                            emotionContainer,
+                            true
+                        ).root.also { root ->
+                            root.updateLayoutParams<LayoutParams> {
+                                topToBottom = avatarGuideView.id
+                                leftToLeft = avatarGuideView.id
+                                rightToRight = avatarGuideView.id
+                            }
+                        }
+                    }
+
+                    CocosViewLocation.BOTTOM,
+                    CocosViewLocation.CENTER,
+                    CocosViewLocation.BOTTOM_LEFT,
+                    CocosViewLocation.BOTTOM_RIGHT -> {
+                        LayoutCocosgameQuickTextBubbleBottomBinding.inflate(
+                            LayoutInflater.from(context),
+                            emotionContainer,
+                            true
+                        ).root.also { root ->
+                            root.updateLayoutParams<LayoutParams> {
+                                bottomToTop = avatarGuideView.id
+                                leftToLeft = avatarGuideView.id
+                                rightToRight = avatarGuideView.id
+                            }
+                        }
+                    }
+
+                    CocosViewLocation.LEFT -> {
+                        LayoutCocosgameQuickTextBubbleLeftBinding.inflate(
+                            LayoutInflater.from(context),
+                            emotionContainer,
+                            true
+                        ).root.also { root ->
+                            root.updateLayoutParams<LayoutParams> {
+                                leftToRight = avatarGuideView.id
+                                topToTop = avatarGuideView.id
+                                bottomToBottom = avatarGuideView.id
+                            }
+                        }
+                    }
+
+                    CocosViewLocation.RIGHT -> {
+                        LayoutCocosgameQuickTextBubbleRightBinding.inflate(
+                            LayoutInflater.from(context),
+                            emotionContainer,
+                            true
+                        ).root.also { root ->
+                            root.updateLayoutParams<LayoutParams> {
+                                rightToLeft = avatarGuideView.id
+                                topToTop = avatarGuideView.id
+                                bottomToBottom = avatarGuideView.id
+                            }
+                        }
+                    }
+                }
+                player2QuickTextBubble[uid] = Pair(location, bubbleView)
+                showQuickTextBubble(
+                    getQuickChatTextView(location, bubbleView),
+                    bubble,
+                    onStart = {
+                        if (isValid) {
+                            bubbleView.show()
+                        }
+                    },
+                    onComplete = {
+                        if (isValid) {
+                            bubbleView.gone()
+                        }
+                    }
+                )
+            } else {
+                showNextQuickChatBubble(uid)
+            }
+        }
+    }
+
+    private fun getQuickChatTextView(location: CocosViewLocation, view: View): TextView {
+        return when (location) {
+            CocosViewLocation.TOP,
+            CocosViewLocation.TOP_LEFT,
+            CocosViewLocation.TOP_RIGHT -> {
+                LayoutCocosgameQuickTextBubbleTopBinding.bind(view).tvText
+            }
+
+            CocosViewLocation.BOTTOM,
+            CocosViewLocation.CENTER,
+            CocosViewLocation.BOTTOM_LEFT,
+            CocosViewLocation.BOTTOM_RIGHT -> {
+                LayoutCocosgameQuickTextBubbleBottomBinding.bind(view).tvText
+            }
+
+            CocosViewLocation.LEFT -> {
+                LayoutCocosgameQuickTextBubbleLeftBinding.bind(view).tvText
+            }
+
+            CocosViewLocation.RIGHT -> {
+                LayoutCocosgameQuickTextBubbleRightBinding.bind(view).tvText
+            }
+        }
+    }
+
+    private fun showQuickTextBubble(
+        quickChatText: TextView,
+        bubble: QuickTextBubble,
+        onStart: () -> Unit,
+        onComplete: () -> Unit
+    ) {
+        val uid = bubble.uid
+        val text = bubble.quickText
+        onStart.invoke()
+        quickChatText.text = text.getText()
+        runOnUiThread({
+            onComplete.invoke()
+            showNextQuickChatBubble(uid)
+        }, 3000)
+    }
+
+    private fun getBubbleLocation(data: CocosViewData): CocosViewLocation {
+        val l = data.location
+        if (l != null) {
+            return CocosViewLocation.map(l) ?: CocosViewLocation.BOTTOM
+        }
+        val position = data.position
+        var location = 0
+        location = if (position.x > DisplayUtil.getScreenWidth() / 2) {
+            location or CocosViewLocation.RIGHT.location
+        } else {
+            location or CocosViewLocation.LEFT.location
+        }
+
+        location = if (position.y > DisplayUtil.getScreenHeight() / 2) {
+            location or Gravity.BOTTOM
+        } else {
+            location or Gravity.TOP
+        }
+        return CocosViewLocation.map(location) ?: CocosViewLocation.BOTTOM
+    }
+
+    fun clear() {
+        player2EmotionBubble.clear()
+        player2QuickTextBubble.clear()
+        player2BubbleQueue.clear()
+    }
+
+}

+ 20 - 0
app/src/main/java/com/adealink/weparty/cocosgame/chat/data/Data.kt

@@ -0,0 +1,20 @@
+package com.adealink.weparty.cocosgame.chat.data
+
+import com.adealink.weparty.cocosgame.data.QuickText
+import com.adealink.weparty.module.emotion.data.EmotionInfo
+import com.google.gson.annotations.SerializedName
+
+sealed class QuickChatBubble(open val uid: Long)
+
+data class QuickTextBubble(override val uid: Long, val quickText: QuickText) : QuickChatBubble(uid)
+
+data class EmotionBubble(override val uid: Long, val emotion: EmotionInfo, val resultIndex: Int) :
+    QuickChatBubble(uid)
+
+data class SendMessageReq(
+    @SerializedName("type") val type: String,
+    @SerializedName("roomId") val roomId: Long,
+    @SerializedName("ts") val ts: Long,
+    @SerializedName("content") val content: String,
+)
+

+ 16 - 0
app/src/main/java/com/adealink/weparty/cocosgame/chat/datasource/remote/ChatSocketService.kt

@@ -0,0 +1,16 @@
+package com.adealink.weparty.cocosgame.chat.datasource.remote
+
+import com.adealink.frame.base.Rlt
+import com.adealink.frame.network.data.Res
+import com.adealink.frame.network.socket.annotation.Uri
+import com.adealink.weparty.cocosgame.chat.data.SendMessageReq
+import retrofit2.http.Body
+import retrofit2.http.Core
+
+interface ChatSocketService {
+
+    @Core
+    @Uri("TEXT_REQUEST")
+    suspend fun sendMessage(@Body req: SendMessageReq): Rlt<Res<Any>>
+
+}

+ 119 - 0
app/src/main/java/com/adealink/weparty/cocosgame/chat/fragment/QuickChatFragment.kt

@@ -0,0 +1,119 @@
+package com.adealink.weparty.cocosgame.chat.fragment
+
+import android.os.Build
+import android.os.Bundle
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.view.updateLayoutParams
+import androidx.fragment.app.activityViewModels
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.adealink.frame.aab.util.getCompatDimension
+import com.adealink.frame.base.Rlt
+import com.adealink.frame.base.fastLazy
+import com.adealink.frame.mvvm.view.viewBinding
+import com.adealink.frame.router.Router
+import com.adealink.weparty.R
+import com.adealink.weparty.cocosgame.chat.adapter.QuickTextViewBinder
+import com.adealink.weparty.cocosgame.chat.listener.IChatOperateListener
+import com.adealink.weparty.cocosgame.chat.viewmodel.QuickChatViewModel
+import com.adealink.weparty.cocosgame.data.ClickEmotionOpData
+import com.adealink.weparty.cocosgame.data.CocosRoomType
+import com.adealink.weparty.cocosgame.data.QuickText
+import com.adealink.weparty.commonui.BaseFragment
+import com.adealink.weparty.commonui.dialogfragment.BaseDialogFragment
+import com.adealink.weparty.commonui.ext.dp
+import com.adealink.weparty.commonui.recycleview.adapter.MultiTypeListAdapter
+import com.adealink.weparty.commonui.recycleview.diffutil.BaseListDiffUtil
+import com.adealink.weparty.commonui.recycleview.itemdecoration.GridSpacingItemDecoration
+import com.adealink.weparty.commonui.toast.util.showFailedToast
+import com.adealink.weparty.databinding.FragmentCocosgameEmotionQuickTextBinding
+import com.adealink.weparty.module.emotion.Emotion
+import com.adealink.weparty.module.room.Room
+import com.adealink.weparty.module.room.RoomModule
+import com.adealink.weparty.module.room.chat.data.Message
+import com.adealink.weparty.module.room.chat.data.MessageType
+import com.adealink.weparty.module.room.chat.data.QuickMsgContent
+import com.adealink.weparty.module.room.data.SendEmotionScene
+
+class QuickChatFragment : BaseFragment(R.layout.fragment_cocosgame_emotion_quick_text), IChatOperateListener {
+
+    companion object {
+
+        const val EXTRA_COCOS_EMOTION_CLICK_OP_DATA = "extra_cocos_emotion_click_op_data"
+
+        fun newInstance(
+            data: ClickEmotionOpData
+        ): QuickChatFragment {
+            return QuickChatFragment().apply {
+                arguments = Bundle().apply {
+                    putParcelable(EXTRA_COCOS_EMOTION_CLICK_OP_DATA, data)
+                }
+            }
+        }
+
+    }
+
+    private val binding by viewBinding(FragmentCocosgameEmotionQuickTextBinding::bind)
+    private val chatMessageViewModel by fastLazy { RoomModule.getChatMessageViewModel(requireActivity()) }
+    private val quickChatViewModel by activityViewModels<QuickChatViewModel>()
+    private val quickTextAdapter by fastLazy { MultiTypeListAdapter(BaseListDiffUtil()) }
+
+    private var hidePanelCallback: (() -> Unit)? = null
+
+    fun setHidePanelCallback(callback: () -> Unit) {
+        this.hidePanelCallback = callback
+    }
+
+    override fun initViews() {
+        super.initViews()
+        val clickOpData = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+            arguments?.getParcelable(EXTRA_COCOS_EMOTION_CLICK_OP_DATA, ClickEmotionOpData::class.java)
+        } else {
+            arguments?.getParcelable(EXTRA_COCOS_EMOTION_CLICK_OP_DATA)
+        }
+        clickOpData?.let {
+            val leftMargin = (it.position.x - it.size.width / 2).toInt() - getCompatDimension(R.dimen.cocos_game_quick_chat_margin_left).toInt()
+            binding.ivBottomArrow.updateLayoutParams<ConstraintLayout.LayoutParams> {
+                this.leftMargin = leftMargin
+            }
+        }
+
+        quickTextAdapter.register(QuickTextViewBinder(this))
+        binding.rcQuickText.layoutManager = GridLayoutManager(
+            context,
+            QuickTextViewBinder.SPAN_COUNT,
+            RecyclerView.VERTICAL,
+            false
+        )
+        binding.rcQuickText.addItemDecoration(
+            GridSpacingItemDecoration(QuickTextViewBinder.SPAN_COUNT, 6.dp(), 6.dp(), false)
+        )
+        binding.rcQuickText.adapter = quickTextAdapter
+        if (childFragmentManager.findFragmentByTag(Room.RoomGameEmotionPanel.TAG) == null) {
+            val fragment =
+                Router.getRouterInstance<BaseDialogFragment>(Emotion.EmotionPanel.PATH) ?: return
+            childFragmentManager.beginTransaction()
+                .replace(R.id.fcv_emotion, fragment)
+                .commit()
+        }
+    }
+
+    override fun loadData() {
+        super.loadData()
+        quickChatViewModel.getQuickText().observe(viewLifecycleOwner) {
+            quickTextAdapter.submitList(it)
+        }
+    }
+
+    override fun onQuickTextClick(quickText: QuickText) {
+        chatMessageViewModel?.sendChatMessage(
+            Message.crateMessage(MessageType.QUICK_MESSAGE, QuickMsgContent(quickText))
+        )?.observe(viewLifecycleOwner) {
+            when (it) {
+                is Rlt.Success -> hidePanelCallback?.invoke()
+                is Rlt.Failed -> showFailedToast(it)
+            }
+        }
+    }
+
+}

+ 9 - 0
app/src/main/java/com/adealink/weparty/cocosgame/chat/listener/IChatOperateListener.kt

@@ -0,0 +1,9 @@
+package com.adealink.weparty.cocosgame.chat.listener
+
+import com.adealink.weparty.cocosgame.data.QuickText
+
+interface IChatOperateListener {
+
+    fun onQuickTextClick(quickText: QuickText) {}
+
+}

+ 24 - 0
app/src/main/java/com/adealink/weparty/cocosgame/chat/viewmodel/QuickChatViewModel.kt

@@ -0,0 +1,24 @@
+package com.adealink.weparty.cocosgame.chat.viewmodel
+
+import androidx.lifecycle.LiveData
+import com.adealink.frame.mvvm.livedata.OnceMutableLiveData
+import com.adealink.frame.mvvm.viewmodel.BaseViewModel
+import com.adealink.weparty.cocosgame.data.QuickText
+import com.adealink.weparty.config.GlobalConfigType
+import com.adealink.weparty.config.globalConfigManager
+import kotlinx.coroutines.launch
+
+class QuickChatViewModel:BaseViewModel() {
+
+    fun getQuickText(): LiveData<List<QuickText>> {
+        val liveData = OnceMutableLiveData<List<QuickText>>()
+        viewModelScope.launch {
+            val quickTextList = globalConfigManager.suspendGetConfigList(
+                GlobalConfigType.GLOBAL_GAME_QUICK_MESSAGE,
+                QuickText::class.java
+            )
+            liveData.send(quickTextList)
+        }
+        return liveData
+    }
+}

+ 181 - 0
app/src/main/java/com/adealink/weparty/cocosgame/comp/CoinsCollectAnimComp.kt

@@ -0,0 +1,181 @@
+package com.adealink.weparty.cocosgame.comp
+
+import android.content.Context
+import android.graphics.PointF
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import androidx.core.view.updateLayoutParams
+import androidx.lifecycle.LifecycleOwner
+import com.adealink.frame.log.Log
+import com.adealink.frame.mvvm.view.ViewComponent
+import com.adealink.frame.util.getCenterLocationInWindow
+import com.adealink.frame.util.runOnUiThread
+import com.adealink.weparty.App
+import com.adealink.weparty.commonui.ext.dp
+import com.adealink.weparty.commonui.ext.gone
+import com.adealink.weparty.commonui.ext.show
+import com.adealink.weparty.commonui.widget.CollectCoinView
+import com.adealink.weparty.commonui.widget.CollectPathAnimView
+import com.adealink.weparty.databinding.LayoutCoinsCollectAnimBinding
+import com.adealink.weparty.module.sound.data.Sound
+import com.opensource.svgaplayer.SVGACallback
+import kotlin.math.max
+
+class CoinsCollectAnimComp(
+    lifecycleOwner: LifecycleOwner,
+    private val container: FrameLayout
+) : ViewComponent(lifecycleOwner) {
+
+    private var binding: LayoutCoinsCollectAnimBinding = LayoutCoinsCollectAnimBinding.inflate(LayoutInflater.from(context), container, true)
+
+    private val animViewCreator = object : CollectPathAnimView.CollectViewCreator {
+        override fun createCollectView(context: Context): View {
+            return CollectCoinView(context)
+        }
+    }
+
+    override fun onCreate() {
+        super.onCreate()
+        initViews()
+    }
+
+    private fun initViews() {
+        binding.root.updateLayoutParams<FrameLayout.LayoutParams> {
+            width = FrameLayout.LayoutParams.MATCH_PARENT
+            height = FrameLayout.LayoutParams.MATCH_PARENT
+        }
+        binding.svgaCoins.updateLayoutParams<FrameLayout.LayoutParams> {
+            width = 190.dp()
+            height = 190.dp()
+        }
+        binding.svgaCoins.callback = object : SVGACallback {
+            override fun onFinished() {
+                runOnUiThread {
+                    binding.svgaCoins.gone()
+                }
+            }
+        }
+    }
+
+    fun playCollectAnim(onFinish: () -> Unit, endView: View, vararg startViews: View) {
+        val endPosition = endView.getCenterLocationInWindow()
+        val startPositions = startViews.map {
+            val startPosition = it.getCenterLocationInWindow()
+            PointF(
+                startPosition[0].toFloat(),
+                startPosition[1].toFloat()
+            )
+        }.toTypedArray()
+
+        reset()
+        playCollectAnimInner(
+            onFinish,
+            PointF(
+                endPosition[0].toFloat(),
+                endPosition[1].toFloat()
+            ),
+            *startPositions,
+        )
+    }
+
+    /**
+     * @param endPosition 终点坐标位置
+     * @param startPositions 起点坐标位置(可以设置多个起点)
+     */
+    private fun playCollectAnimInner(onFinish: () -> Unit, endPosition: PointF, vararg startPositions: PointF) {
+        Log.d(TAG, "playCollectAnim, endPosition:(${endPosition.x}, ${endPosition.y})")
+        startPositions.forEach { startPosition ->
+            val animView = generateAnimView(startPosition, endPosition)
+            binding.flAnim.addView(animView)
+            animView.updateLayoutParams<FrameLayout.LayoutParams> {
+                width = FrameLayout.LayoutParams.MATCH_PARENT
+                height = FrameLayout.LayoutParams.MATCH_PARENT
+            }
+        }
+        App.instance.soundPlayer.play(Sound.COINS_COLLECT.soundName)
+        for (i in 0 until binding.flAnim.childCount) {
+            val animView = binding.flAnim.getChildAt(i) as? CollectPathAnimView ?: continue
+            if (i == 0) {
+                //用第一个动画做监听
+                animView.setAnimListener(object : CollectPathAnimView.ICollectAnimListener {
+                    override fun onStart(position: Int) {
+                        Log.d(TAG, "playCollectAnim, onStart:$position")
+                        if (position == 0) {
+                            //第一个金币到达路径终点,开始显示金币撒花动画
+                            playCoinsCollectEffect(endPosition)
+                        }
+                    }
+
+                    override fun onUpdate(view: View, position: Int, percent: Float) {
+                        if (percent > 0.8f) {
+                            (view as? CollectCoinView)?.showFlashAnim()
+                        }
+                    }
+
+                    override fun onEnd(position: Int) {
+                        Log.d(TAG, "playCollectAnim, onEnd:$position")
+//                        if (position == 0) {
+//                            //第一个金币到达路径终点,开始显示金币撒花动画
+//                            playCoinsCollectEffect(endPosition)
+//                        }
+                    }
+
+                    override fun onFinish() {
+                        Log.d(TAG, "playCollectAnim, onFinish")
+                    }
+                })
+            } else {
+                animView.setAnimListener(null)
+            }
+            animView.startAnim()
+        }
+        //所有动画的时长
+        val allAnimDuration = max(
+            PATH_ANIM_DURATION + COINS_COLLECT_EFFECT_DURATION,
+            PATH_ANIM_DURATION + (COLLECT_COUNT - 1) * PATH_ANIM_OFFSET_DURATION
+        )
+        binding.root.postDelayed({
+            Log.d(TAG, "playCollectAnim, onFinish.invoke()")
+            onFinish.invoke()
+        }, allAnimDuration)
+    }
+
+    private fun generateAnimView(start: PointF, end: PointF): CollectPathAnimView {
+        val animView = CollectPathAnimView(binding.flAnim.context)
+        animView.setCollectCount(COLLECT_COUNT)
+        animView.setMaxAnimCount(6)
+        animView.setDuration(PATH_ANIM_DURATION)
+        animView.setOffsetDuration(PATH_ANIM_OFFSET_DURATION)
+        animView.setCollectViewCreator(animViewCreator, COLLECT_COIN_VIEW_WIDTH)
+        animView.setPath(start, end)
+        return animView
+    }
+
+    private fun playCoinsCollectEffect(endPosition: PointF) {
+        Log.d(TAG, "playCoinsCollectEffect, endPosition:(${endPosition.x}, ${endPosition.y})")
+        binding.svgaCoins.show()
+        binding.svgaCoins.x = endPosition.x - binding.svgaCoins.layoutParams.width / 2
+        binding.svgaCoins.y = endPosition.y - binding.svgaCoins.layoutParams.height / 2
+        binding.svgaCoins.setAsset("cocosgame_coins_collect_effect.svga")
+    }
+
+    private fun reset() {
+        binding.root.removeCallbacks(null)
+        binding.flAnim.removeAllViews()
+        binding.svgaCoins.stopAnimation()
+        binding.svgaCoins.gone()
+    }
+
+    companion object {
+        private const val TAG = "CoinsCollectAnimComp"
+
+        private val COLLECT_COIN_VIEW_WIDTH = 48.dp()
+
+        private const val COLLECT_COUNT = 10 //收集的金币数量
+        private const val PATH_ANIM_DURATION = 500L //(单个)金币路径动画时长
+        private const val PATH_ANIM_OFFSET_DURATION = 80L //每个金币动画时间间隔
+        private const val COINS_COLLECT_EFFECT_DURATION = 1900L //金币撒花动效时长
+    }
+
+}

+ 39 - 0
app/src/main/java/com/adealink/weparty/cocosgame/data/CocosDialogData.kt

@@ -0,0 +1,39 @@
+package com.adealink.weparty.cocosgame.data
+
+import com.google.gson.annotations.SerializedName
+
+/*
+ * Cocos游戏弹窗
+ */
+
+//@JsonAdapter(DialogParser::class)
+//class Dialog(@SerializedName("type") val type: DialogType) {
+//    @SerializedName("id")
+//    var id: Long = 0
+//
+//    @SerializedName("data")
+//    var data: DialogData? = null
+//}
+
+open class DialogData(var id: Long = 0, var players: Map<Long, GamePlayerInfo> = hashMapOf())
+
+/**
+ * 未知弹窗类型
+ */
+class UnknownDialogData : DialogData() {
+    override fun toString(): String {
+        return "UnknownDialogData()"
+    }
+}
+
+/**
+ * 退出游戏确认弹窗
+ */
+data class ExitGameDialogData(@SerializedName("game_record") val gameRecord: String) :
+    DialogData() {
+    override fun toString(): String {
+        return "ExitGameDialogData(gameRecord='$gameRecord')"
+    }
+}
+
+data class CurrencyInsufficientDialogData(@SerializedName("type") val type: Int) : DialogData()

+ 401 - 0
app/src/main/java/com/adealink/weparty/cocosgame/data/CocosGameData.kt

@@ -0,0 +1,401 @@
+package com.adealink.weparty.cocosgame.data
+
+import android.os.Parcelable
+import com.adealink.frame.aab.util.getCompatString
+import com.adealink.frame.base.AppBaseInfo
+import com.adealink.frame.locale.language.data.Language
+import com.adealink.frame.locale.language.languageManager
+import com.adealink.frame.util.PackageUtil
+import com.adealink.weparty.R
+import com.adealink.weparty.commonui.recycleview.diffutil.BaseListItemData
+import com.adealink.weparty.module.account.AccountModule
+import com.adealink.weparty.module.profile.data.UserInfo
+import com.adealink.weparty.module.rank.data.RankListItemData
+import com.google.gson.annotations.GsonNullable
+import com.google.gson.annotations.Must
+import com.google.gson.annotations.SerializedName
+import kotlinx.parcelize.Parcelize
+
+data class GameStartNotify(
+    @SerializedName("seqid") val seqId: Long,
+    @Must
+    @SerializedName("channel") val channel: String,
+    @Must
+    @SerializedName("gameId") val gameId: String,
+    @Must
+    @SerializedName("gamePlayers") val gamePlayers: List<GamePlayerInfo>,
+    @SerializedName("gameMeta") val gameMeta: String,
+    @SerializedName("gameType") val game: Int, //参考: Game
+    @SerializedName("gameMode") val gameMode: Int,
+    @SerializedName("gameFee") val gameFee: Int, //游戏参与费用,0表示免费游戏 >0表示加入游戏的花费
+    @SerializedName("magicMode") val magicMode: Int, //0:关闭道具模式,1:开启道具模式
+    @SerializedName("maxPlayerCount") val maxPlayerCount: Int, //最大玩家数量
+    @SerializedName("gameRoomType") val gameType: Int, //参考 LudoGameType, CarromGameType
+    @SerializedName("startRoomType") val cocosRoomType: Int,
+    @SerializedName("createTime") val createTime: Long, //游戏创建时间
+    @SerializedName("gameCreator") val gameCreator: Long, //游戏发起者ID
+    @SerializedName("gameState") val gameState: Int,//游戏状态,0:准备,1:进行中,2:游戏结束
+    @SerializedName("gameRewardInfoList") val gameRewardInfoList: List<GameRewardData> = listOf(),
+) {
+
+    fun toGameConfigInfo(): GameConfigInfo {
+        return GameConfigInfo(
+            game = game,
+            gameType = gameType,
+            gameMode = gameMode,
+            magicMode = magicMode,
+            maxPlayerCount = maxPlayerCount,
+            cocosRoomType = cocosRoomType,
+            gameFee = gameFee
+        )
+    }
+
+    fun toStartGameData(roomId: Long): StartGameData {
+        return StartGameData(
+            roomId,
+            channel,
+            gameId,
+            AccountModule.uid,
+            gameMeta,
+            gamePlayers,
+            cocosRoomType = cocosRoomType,
+            gameCreator = gameCreator,
+            gameConfig = toGameConfigInfo()
+        )
+    }
+
+}
+
+data class JoinCodeReq(
+    @SerializedName("teamCode") val teamCode: String,
+    @SerializedName("gameRoomType") val gameType: Int
+)
+
+data class ExitGameReq(
+    @SerializedName("roomId") val roomId: Long,
+
+    @SerializedName("gameId") val gameId: String? = null,
+    @SerializedName("teamCode") val teamCode: String? = null,
+    @SerializedName("gameType") val gameType: Int? = null,
+    @SerializedName("seqid") val seqId: Long = System.currentTimeMillis()
+)
+
+data class CreateGameRes(
+    @SerializedName("gameConfigInfo") val gameConfigInfo: GameConfigInfo,
+    @SerializedName("gameExpireTime") val gameExpireTime: Long,
+    @SerializedName("teamCode") val teamCode: String,
+    @SerializedName("channel") val channel: Long,
+)
+
+data class JoinRoomRes(
+    @SerializedName("token") val token: String,
+    @SerializedName("rtcType") val rtcType: Int = 0
+)
+
+enum class LeaveRoomReason(val reason: String) {
+    INITIATIVE("initiative"),
+}
+
+
+data class JoinRoomReq(
+    @SerializedName("roomId") val roomId: Long,
+    @SerializedName("versionCode") val versionCode: Int = PackageUtil.getVersionCode(),
+    @SerializedName("platform") val platform: String = AppBaseInfo.platform,
+    @SerializedName("packageName") val packageName: String = PackageUtil.getPackageName(),
+)
+
+data class LeaveRoomReq(
+    @SerializedName("roomId") val roomId: Long
+)
+
+data class GamePlayersInfo(
+    val players: List<GamePlayerInfo>,
+    val groupId: Int? = null
+)
+
+data class GameConfigReq(@SerializedName("gameConfigInfo") val gameConfigInfo: GameConfigInfo)
+
+@Parcelize
+data class GameInviteNotify(
+    @SerializedName("teamCode") val teamCode: String,
+    @Must
+    @SerializedName("inviterInfo") val inviterUser: UserInfo, //邀请方
+    @SerializedName("groupId") val groupId: Int,
+    @Must
+    @SerializedName("gameConfigInfo") val gameConfigInfo: GameConfigInfo,
+    @SerializedName("micSeat") val micSeat: Int,
+    @SerializedName("roomId") val roomId: Long,
+) : Parcelable
+
+@Parcelize
+data class PlayerChangedNotify(
+    @Must
+    @SerializedName("seqid") val seqId: Long,
+    @Must
+    @SerializedName("gamePlayerInfoList") val gamePlayerInfoList: List<GamePlayerInfo>,
+    @GsonNullable
+    @SerializedName("gameConfigInfo") val gameConfigInfo: GameConfigInfo?,
+    @SerializedName("code") val code: Int = 0,
+    @SerializedName("message") val message: String = "",
+) : Parcelable {
+
+    fun getSelfPlayerInfo(myUid: Long): GamePlayerInfo? {
+        return gamePlayerInfoList.find { it.playerId == myUid }
+    }
+
+    fun getConfigInfo(): GameConfigInfo {
+        return gameConfigInfo ?: GameConfigInfo(-1, -1, -1, -1, 0, -1, 0)
+    }
+
+}
+
+@Parcelize
+data class PlayerGameInfo(
+    val seqId: Long,
+    @Must
+    @SerializedName("gameConfigInfo") val configInfo: GameConfigInfo,
+    @Must
+    @SerializedName("gamePlayerInfoList") val players: List<GamePlayerInfo>,
+    @SerializedName("teamCreatorId") val creatorUid: Long,
+    @SerializedName("teamCode") val teamCode: String,
+    @SerializedName("gameExpireTime") val codeExpireTime: Long,
+    @Must
+    @SerializedName("channel") val channel: String,
+    @Must
+    @SerializedName("roomId") val roomId: Long,
+
+    @SerializedName("gameId") val gameId: String,
+    @SerializedName("gameMeta") val gameMeta: String,
+    @SerializedName("micSeat") val micSeat: Int = 0,
+) : Parcelable {
+
+    fun getPlayerChangedNotify(): PlayerChangedNotify {
+        return PlayerChangedNotify(
+            if (seqId > 0) seqId else System.currentTimeMillis(),
+            players,
+            configInfo
+        )
+    }
+
+}
+
+data class GameDashboardReq(
+    @SerializedName("gameType") val gameType: Int
+)
+
+data class GameDashboardRes(
+    @SerializedName("uid") val uid: Long,
+    @SerializedName("weekRank") val weekRank: Long,//周排名
+    @SerializedName("weekWinNum") val weekWinNum: Long,//周胜场次
+    @SerializedName("winNum") val winNum: Long,//胜场
+    @GsonNullable
+    @SerializedName("winRate") val winRate: Double? = null,//胜率
+    @GsonNullable
+    @SerializedName("weekTopUser") val weekTopUser: RankListItemData? = null,//周榜第一用户
+    @GsonNullable
+    @SerializedName("totalGainReward") val totalGainReward: Long? = null,    //赢得总奖金
+    @GsonNullable
+    @SerializedName("totalPlayCount") val totalPlayCount: Long? = null,    //总对局次数
+)
+
+
+data class QuickText(
+    @SerializedName("id") val id: Long,
+
+    @SerializedName("en")
+    val enStr: String = "",
+
+    @SerializedName("zh")
+    val zhStr: String = "",
+
+    @SerializedName("zh")
+    val zhTWStr: String = "",
+
+    @SerializedName("ar")
+    val arStr: String = "",
+
+    @SerializedName("tr")
+    val trStr: String = "",
+
+    @SerializedName("hi")
+    val hiStr: String = "",
+
+    @SerializedName("bn")
+    val bnStr: String = "",
+
+    @SerializedName("ur")
+    val urStr: String = "",
+
+    @SerializedName("in")
+    val idStr: String = "",
+
+    @SerializedName("th")
+    val thStr: String = "",
+
+    @SerializedName("vi")
+    val viStr: String = "",
+
+    @SerializedName("tl")
+    val tlStr: String = "",
+
+    @SerializedName("ms")
+    val msStr: String = "",
+
+    @SerializedName("pt")
+    val ptStr: String = "",
+
+    @SerializedName("es")
+    val esStr: String = "",
+
+    @SerializedName("kk")
+    val kkStr: String = "",
+
+    @SerializedName("ky")
+    val kyStr: String = "",
+
+    @SerializedName("ru")
+    val ruStr: String = "",
+
+    @SerializedName("tg")
+    val tgStr: String = "",
+
+    @SerializedName("tk")
+    val tkStr: String = "",
+
+    @SerializedName("uz")
+    val uzStr: String = "",
+) : BaseListItemData {
+
+    override fun areItemsTheSame(newItem: Any): Boolean {
+        return newItem is QuickText && newItem.id == id
+    }
+
+    var senderUid: Long = 0
+
+    fun getText(): String {
+        return when (languageManager?.getLanguage()) {
+            Language.AR -> arStr.ifEmpty { enStr }
+            Language.HI -> hiStr.ifEmpty { enStr }
+            Language.TR -> trStr.ifEmpty { enStr }
+            Language.TH -> thStr.ifEmpty { enStr }
+            Language.BN -> bnStr.ifEmpty { enStr }
+            Language.VI -> viStr.ifEmpty { enStr }
+            Language.UR -> urStr.ifEmpty { enStr }
+            Language.ID -> idStr.ifEmpty { enStr }
+            Language.TL -> tlStr.ifEmpty { enStr }
+            Language.MS -> msStr.ifEmpty { enStr }
+            Language.PT -> ptStr.ifEmpty { enStr }
+            Language.ES -> esStr.ifEmpty { enStr }
+            Language.KK -> kkStr.ifEmpty { enStr }
+            Language.KY -> kyStr.ifEmpty { enStr }
+            Language.RU -> ruStr.ifEmpty { enStr }
+            Language.TG -> tgStr.ifEmpty { enStr }
+            Language.TK -> tkStr.ifEmpty { enStr }
+            Language.ZH -> zhStr.ifEmpty { enStr }
+            Language.ZH_TW -> zhTWStr.ifEmpty { enStr }
+            Language.UZ -> uzStr.ifEmpty { enStr }
+            Language.EN -> enStr
+            else -> enStr
+        }
+    }
+
+}
+
+/**
+ * 游戏匹配请求数据
+ */
+data class SingleRoomMatchReq(
+    @SerializedName("seqid") val seqid: Long = System.currentTimeMillis(),
+    @SerializedName("lang") val lang: String = languageManager?.getLanguageCode() ?: "",
+    @SerializedName("gameConfigInfo") val gameConfigInfo: GuessGameConfigInfo
+)
+
+data class GuessGameConfigInfo(
+    @SerializedName("gameType") val gameType: Int, //游戏类型
+    @SerializedName("region") val region: String,  //大区
+    @SerializedName("maxPlayerCount") val maxPlayerCount: Int, //最大玩家数量
+    @SerializedName("gameMode") val gameMode: Int //游戏模式 0-默认
+)
+
+
+data class GameRecoverFailData(
+    @SerializedName("reason") val reason: Int, //失败原因, 1:网络重连
+    @SerializedName("msg") val msg: String, //失败原因描述
+) {
+    fun getReasonMsg(): String {
+        return msg.ifEmpty { GameRecoverFailReason.getReasonMessage(reason) }
+    }
+}
+
+data class OnNodeVisibleChangedData(
+    @SerializedName("type") val type: String, //按钮类型, emotion_btn, message_btn
+    @Must
+    @SerializedName("visible") val visible: Int, //控件可见, 0:不可见, 1:可见
+) {
+    fun isVisible(): Boolean {
+        return visible == 1
+    }
+}
+
+enum class GameRecoverFailReason(val reason: Int) {
+    GAME_OVER(1); //游戏已结束,重连失败
+
+    companion object {
+        @JvmStatic
+        fun getReasonMessage(reason: Int): String {
+            return when (GameRecoverFailReason.values().find { it.reason == reason }) {
+                GAME_OVER -> {
+                    getCompatString(R.string.common_game_recover_fail_for_game_over)
+                }
+
+                null -> {
+                    ""
+                }
+            }
+        }
+    }
+}
+
+/**
+ * 后端保存的用户默认麦位状态
+ */
+enum class MicSwitchStatus(val status: Int) {
+    OFF(0), //关闭
+    OPEN(1), //打开
+}
+
+/**
+ * 查询房间的用户麦位状态
+ */
+data class GetPlayerMicStatusReq(
+    @SerializedName("gameId") val gameId: String,
+)
+
+/**
+ * 查询房间的用户麦位状态返回
+ */
+data class GetPlayerMicStatusRes(
+    @SerializedName("gameId") val gameId: String,
+    @SerializedName("micInfos") val micInfos: List<GameMicInfo>?, // 麦位开关状态
+)
+
+data class GameMicInfo(
+    @SerializedName("micUid") val micUid: Long,
+    @SerializedName("micStatus") val micStatus: Int
+)
+
+/**
+ * 设置用户麦位状态
+ */
+data class OpPlayerMicStatusReq(
+    @SerializedName("gameId") val gameId: String,
+    @SerializedName("roomId") val roomId: Long,
+    @SerializedName("status") val status: Int
+)
+
+data class GameMicNotify(
+    @SerializedName("roomId") val roomId: Long,//房间id
+    @SerializedName("gameId") val gameId: String,//游戏id
+    @SerializedName("serverTs") val serverTs: Long = 0,//服务器时间戳
+    @SerializedName("micInfos") val micInfos: List<GameMicInfo>, // 麦位开关状态
+)

+ 189 - 0
app/src/main/java/com/adealink/weparty/cocosgame/data/CocosNativeData.kt

@@ -0,0 +1,189 @@
+package com.adealink.weparty.cocosgame.data
+
+import android.os.Parcelable
+import com.google.gson.annotations.GsonNullable
+import com.google.gson.annotations.SerializedName
+import kotlinx.parcelize.Parcelize
+
+
+data class DialogIdData(@SerializedName("id") val id: Long)
+
+data class InitGameData(
+    val game: Game,
+    val avatarSizePx: Int
+)
+
+data class StartGameData(
+    @SerializedName("room_id") val roomId: Long,
+    @SerializedName("channel") val channel: String,
+    @SerializedName("game_id") val gameId: String,
+    @SerializedName("uid") val uid: Long,
+    @SerializedName("game_meta_data") val gameMetaData: String,
+    @SerializedName("game_players") val gamePlayers: List<GamePlayerInfo>,
+    @SerializedName("game_config") val gameConfig: GameConfigInfo? = null,
+
+    @Deprecated("废弃")
+    @SerializedName("local") val local: Boolean = false,
+
+    @SerializedName("game_record") val gameRecord: String = "",
+    @SerializedName("game_skins") var gameSkins: List<GameSkin>? = null,
+    @SerializedName("other_config") var otherConfig: GameOtherConfig? = null,
+    @SerializedName("start_room_type") val cocosRoomType: Int,
+    @SerializedName("creator_uid") val gameCreator: Long,
+    @SerializedName("self_audience") var selfAudience: Boolean = false,
+) {
+    val channelLong: Long
+        get() = channel.toLongOrNull() ?: 0
+
+    // =========== 玩家信息 ===========
+    var gamePlayerMap: Map<Long, GamePlayerInfo>? = null
+}
+
+@Parcelize
+data class GameConfigInfo(
+    @SerializedName("gameType") val game: Int, //参考 Game
+    @SerializedName("gameRoomType") val gameType: Int, //参考 LudoGameType, CarromGameType
+    @SerializedName("gameMode") val gameMode: Int, //参考 LudoGameMode, CarromGameMode
+    @SerializedName("magicMode") val magicMode: Int, //参考 LudoMagicMode, CarromMagicMode
+    @SerializedName("maxPlayerCount") val maxPlayerCount: Int, //参考 LudoPlayerCount, CarromPlayerCount
+    @SerializedName("startRoomType") val cocosRoomType: Int, //参考 CocosRoomType
+    @SerializedName("gameFee") val gameFee: Int, //游戏参与费用,0表示免费游戏 >0表示加入游戏的花费
+    @SerializedName("roomId") val roomId: Long = 0,//关联的语聊房间ID
+
+    @SerializedName("gameId") val gameId: String = "",//游戏ID
+    @SerializedName("channel") val channel: Long = 0,//游戏房间ID,用于游戏指令/状态广播
+    @SerializedName("gameMeta") val gameMeta: String? = null,//游戏元数据信息
+    @SerializedName("createTime") val createTime: Long = 0, //游戏创建时间
+    @SerializedName("gameCreator") val gameCreator: Long = 0, //游戏发起者ID
+    @SerializedName("gameState") val gameState: Int = -1,//游戏状态,0:准备,1:进行中,2:游戏结束
+    @SerializedName("gamePlayers") val gamePlayers: List<GamePlayerInfo> = listOf(),//参与游戏的玩家信息
+    @SerializedName("gameRewardInfoList") val gameRewardInfoList: List<GameRewardData> = listOf(),
+    @SerializedName("playerCount2FirstCoinRewardMap")
+    @GsonNullable
+    val playerCount2FirstCoinRewardMap: Map<String, Int>? = null,
+) : Parcelable
+
+data class GameOtherConfig(
+    @SerializedName("canShowNewUserDiceGet") val canShowNewUserDiceGet: Boolean, //是否可展示获取色子
+    @SerializedName("newUserDiceId") val newUserDiceId: Long, //新客专属色子id
+    @SerializedName("canShowNewUserTask") val canShowNewUserTask: Boolean, //可以展示新手任务
+    @SerializedName("selfDefaultGameSkin") var selfDefaultGameSkin: GameSkin?, //自己默认皮肤
+)
+
+@Parcelize
+data class GameRewardData(
+    @SerializedName("rank") val rank: Int,
+    @SerializedName("reward") val reward: Int,
+) : Parcelable
+
+fun GameRewardData?.areContentsTheSame(other: GameRewardData?): Boolean {
+    if (this == null && other == null) {
+        return true
+    }
+    return this?.rank == other?.rank
+            && this?.reward == other?.reward
+}
+
+fun List<GameRewardData>?.areContentsTheSame(other: List<GameRewardData>?): Boolean {
+    if (this == null && other == null) return true
+
+    if (this == null || other == null) return false
+
+    if (this.size != other.size) return false
+
+    return this.indices.all { index ->
+        this[index].areContentsTheSame(other[index])
+    }
+}
+
+data class GameSkin(
+    @SerializedName("playerId")
+    var playerId: Long = 0,
+    @SerializedName("board")
+    var board: String = "",
+    @SerializedName("chess")
+    var chess: String = "",
+    @SerializedName("diceId")
+    var diceId: Long = 0,
+    @SerializedName("dice")
+    var dice: String = "",
+    @SerializedName("dicePreview")
+    var dicePreview: String = "",
+)
+
+data class ExitGameData(
+    @SerializedName("game_room_id") val roomId: Long,
+    @SerializedName("player_id") val uid: Long,
+)
+
+class RestGameData
+
+data class PlayerData(
+    @SerializedName("uid") val uid: Long,
+)
+
+class EmptyData
+
+data class GameRecordData(@SerializedName("game_record") val gameRecord: String)
+
+
+data class JoinCodeData(val code: String, val expiredTs: Long)
+
+data class NetworkStatusData(
+    @SerializedName("available") val available: Boolean,
+)
+
+data class RoomGameJoinReq(
+    @SerializedName("gameType") val gameType: Int,//游戏类型
+    @SerializedName("micSeat") val micSeat: Int,//游戏房间麦位
+    @SerializedName("roomId") val roomId: Long,//游戏房间ID
+    @SerializedName("seqid") val seqId: Long = System.currentTimeMillis()
+)
+
+data class RoomGameReq(
+    @SerializedName("gameType") val gameType: Int,//游戏类型
+    @SerializedName("roomId") val roomId: Long,//游戏房间ID
+)
+
+data class RoomGameKickReq(
+    @SerializedName("gameType") val gameType: Int,//游戏类型
+    @SerializedName("micSeat") val micSeat: Int,//游戏房间麦位
+    @SerializedName("micUid") val micUid: Long,//麦位Uid
+    @SerializedName("roomId") val roomId: Long,//游戏房间ID
+    @SerializedName("seqid") val seqId: Long = System.currentTimeMillis()
+)
+
+data class RoomGameInviteReq(
+    @SerializedName("gameType") val gameType: Int,//游戏类型
+    @SerializedName("micSeat") val micSeat: Int,//游戏房间麦位
+    @SerializedName("playerIds") val playerIds: List<Long>,//被邀请的玩家ID
+    @SerializedName("roomId") val roomId: Long,//游戏房间ID
+    @SerializedName("inviteScene") val inviteScene: Int,
+    @SerializedName("seqid") val seqId: Long = System.currentTimeMillis()
+)
+
+data class GameStartReq(
+    @SerializedName("gameConfigInfo") val gameConfigInfo: GameTypeInfo,
+    @SerializedName("roomId") val roomId: Long,//游戏房间ID
+)
+
+data class GameTypeInfo(
+    @SerializedName("gameType") val game: Int,//游戏
+)
+
+data class GameClosetReq(
+    @SerializedName("gameType") val game: Int,//游戏
+    @SerializedName("roomId") val roomId: Long,//游戏房间ID
+)
+
+data class AudienceJoinReq(
+    @SerializedName("gameId") val gameId: String,//游戏ID
+    @SerializedName("gameMeta") val gameMeta: String,
+    @SerializedName("seqid") val seqId: Long = System.currentTimeMillis()
+)
+
+data class AudienceExitReq(
+    @SerializedName("gameId") val gameId: String,//游戏ID
+    @SerializedName("gameMeta") val gameMeta: String,
+    @SerializedName("seqid") val seqId: Long = System.currentTimeMillis()
+)

Alguns arquivos não foram mostrados porque muitos arquivos mudaram nesse diff