Kaynağa Gözat

初始化项目

DoggyZhang 1 yıl önce
işleme
72297ce62b
100 değiştirilmiş dosya ile 13249 ekleme ve 0 silme
  1. 57 0
      .github/workflows/language_sync.yml
  2. 24 0
      .gitignore
  3. 23 0
      README.md
  4. 2 0
      app/.gitignore
  5. 60 0
      app/aab_res_guard.gradle
  6. 4586 0
      app/bt-proguard.txt
  7. 453 0
      app/build.gradle
  8. BIN
      app/libs/LiteAVSDK_Player_Mini.aar
  9. BIN
      app/libs/TCEffectPlayer_2.1.0.147.aar
  10. BIN
      app/libs/TCMediaX_2.1.0.147.aar
  11. BIN
      app/libs/wenext_jni-release.aar
  12. BIN
      app/libs/xcrash_lib-release.aar
  13. 0 0
      app/proguard-log-empty.pro
  14. 176 0
      app/proguard-log.gradle
  15. 9 0
      app/proguard-log.pro
  16. 103 0
      app/proguard-log.py
  17. 386 0
      app/proguard-rules.pro
  18. 2268 0
      app/proguard_log.txt
  19. 24 0
      app/src/androidTest/java/com/adealink/weparty/ExampleInstrumentedTest.kt
  20. 122 0
      app/src/lite/debug/google-services.json
  21. 122 0
      app/src/lite/release/google-services.json
  22. 4 0
      app/src/lite/res/values/strings.xml
  23. 154 0
      app/src/main/AndroidManifest.xml
  24. 0 0
      app/src/main/assets/TCMediaX.licence
  25. BIN
      app/src/main/assets/cocosgame_coins_collect_effect.svga
  26. BIN
      app/src/main/assets/common_svip_ic.svga
  27. BIN
      app/src/main/assets/couple_love_play.svga
  28. BIN
      app/src/main/assets/level/level_0_to_9_bg.svga
  29. BIN
      app/src/main/assets/level/level_100_to_109_bg.svga
  30. BIN
      app/src/main/assets/level/level_10_to_19_bg.svga
  31. BIN
      app/src/main/assets/level/level_110_to_119_bg.svga
  32. BIN
      app/src/main/assets/level/level_120_to_129_bg.svga
  33. BIN
      app/src/main/assets/level/level_130_to_139_bg.svga
  34. BIN
      app/src/main/assets/level/level_140_to_149_bg.svga
  35. BIN
      app/src/main/assets/level/level_150_bg.svga
  36. BIN
      app/src/main/assets/level/level_20_to_29_bg.svga
  37. BIN
      app/src/main/assets/level/level_30_to_39_bg.svga
  38. BIN
      app/src/main/assets/level/level_40_to_49_bg.svga
  39. BIN
      app/src/main/assets/level/level_50_to_59_bg.svga
  40. BIN
      app/src/main/assets/level/level_60_to_69_bg.svga
  41. BIN
      app/src/main/assets/level/level_70_to_79_bg.svga
  42. BIN
      app/src/main/assets/level/level_80_to_89_bg.svga
  43. BIN
      app/src/main/assets/level/level_90_to_99_bg.svga
  44. BIN
      app/src/main/assets/message_chat_cp.svga
  45. BIN
      app/src/main/assets/message_chat_guard.svga
  46. BIN
      app/src/main/assets/new_user_reward.svga
  47. BIN
      app/src/main/assets/profile_custom_id_bg.svga
  48. BIN
      app/src/main/assets/profile_sid_1_4.svga
  49. BIN
      app/src/main/assets/profile_sid_5_7.svga
  50. BIN
      app/src/main/assets/profile_sid_8_10.svga
  51. BIN
      app/src/main/assets/room_rocket_entrance_level1.svga
  52. BIN
      app/src/main/assets/room_rocket_entrance_level2.svga
  53. BIN
      app/src/main/assets/room_rocket_entrance_level3.svga
  54. BIN
      app/src/main/assets/room_rocket_entrance_level4.svga
  55. BIN
      app/src/main/assets/room_rocket_entrance_level5.svga
  56. BIN
      app/src/main/assets/skin.zip
  57. BIN
      app/src/main/assets/sound_wave_loading.svga
  58. 394 0
      app/src/main/java/com/adealink/weparty/App.kt
  59. 152 0
      app/src/main/java/com/adealink/weparty/MainActivity.kt
  60. 16 0
      app/src/main/java/com/adealink/weparty/Routers.kt
  61. 33 0
      app/src/main/java/com/adealink/weparty/aab/AABConfig.kt
  62. 13 0
      app/src/main/java/com/adealink/weparty/apm/APMConfig.kt
  63. 173 0
      app/src/main/java/com/adealink/weparty/apm/APMInit.kt
  64. 18 0
      app/src/main/java/com/adealink/weparty/apm/stat/APMStatEvent.kt
  65. 24 0
      app/src/main/java/com/adealink/weparty/base/AppBaseConfig.kt
  66. 14 0
      app/src/main/java/com/adealink/weparty/channel/AppChannel.kt
  67. 23 0
      app/src/main/java/com/adealink/weparty/channel/ChannelUtil.kt
  68. 6 0
      app/src/main/java/com/adealink/weparty/channel/Tags.kt
  69. 199 0
      app/src/main/java/com/adealink/weparty/cocosgame/BaseCocosWebGameFragment.kt
  70. 5 0
      app/src/main/java/com/adealink/weparty/cocosgame/CocosGame.kt
  71. 14 0
      app/src/main/java/com/adealink/weparty/cocosgame/chat/adapter/QuickTextItemListDiffUtil.kt
  72. 35 0
      app/src/main/java/com/adealink/weparty/cocosgame/chat/adapter/QuickTextViewBinder.kt
  73. 103 0
      app/src/main/java/com/adealink/weparty/cocosgame/chat/component/ChatMessageComp.kt
  74. 638 0
      app/src/main/java/com/adealink/weparty/cocosgame/chat/component/QuickChatComp.kt
  75. 20 0
      app/src/main/java/com/adealink/weparty/cocosgame/chat/data/Data.kt
  76. 16 0
      app/src/main/java/com/adealink/weparty/cocosgame/chat/datasource/remote/ChatSocketService.kt
  77. 116 0
      app/src/main/java/com/adealink/weparty/cocosgame/chat/fragment/QuickChatFragment.kt
  78. 9 0
      app/src/main/java/com/adealink/weparty/cocosgame/chat/listener/IChatOperateListener.kt
  79. 24 0
      app/src/main/java/com/adealink/weparty/cocosgame/chat/viewmodel/QuickChatViewModel.kt
  80. 181 0
      app/src/main/java/com/adealink/weparty/cocosgame/comp/CoinsCollectAnimComp.kt
  81. 43 0
      app/src/main/java/com/adealink/weparty/cocosgame/data/CocosDialogData.kt
  82. 409 0
      app/src/main/java/com/adealink/weparty/cocosgame/data/CocosGameData.kt
  83. 195 0
      app/src/main/java/com/adealink/weparty/cocosgame/data/CocosNativeData.kt
  84. 133 0
      app/src/main/java/com/adealink/weparty/cocosgame/data/CocosPlayerData.kt
  85. 12 0
      app/src/main/java/com/adealink/weparty/cocosgame/data/CocosRoomType.kt
  86. 183 0
      app/src/main/java/com/adealink/weparty/cocosgame/data/CocosWindowViewData.kt
  87. 27 0
      app/src/main/java/com/adealink/weparty/cocosgame/data/Error.kt
  88. 20 0
      app/src/main/java/com/adealink/weparty/cocosgame/data/GameConstants.kt
  89. 341 0
      app/src/main/java/com/adealink/weparty/cocosgame/data/GameData.kt
  90. 227 0
      app/src/main/java/com/adealink/weparty/cocosgame/data/GameToNativeOpData.kt
  91. 31 0
      app/src/main/java/com/adealink/weparty/cocosgame/data/NativeToGameOpData.kt
  92. 15 0
      app/src/main/java/com/adealink/weparty/cocosgame/data/Tags.kt
  93. 31 0
      app/src/main/java/com/adealink/weparty/cocosgame/datasource/local/CocosGameLocalService.kt
  94. 166 0
      app/src/main/java/com/adealink/weparty/cocosgame/datasource/remote/CommonGameHttpService.kt
  95. 81 0
      app/src/main/java/com/adealink/weparty/cocosgame/gift/adapter/SendGiftInfoRollViewBinder.kt
  96. 17 0
      app/src/main/java/com/adealink/weparty/cocosgame/gift/adapter/SendGiftItemDiffUtil.kt
  97. 124 0
      app/src/main/java/com/adealink/weparty/cocosgame/gift/comp/SendGiftComp.kt
  98. 20 0
      app/src/main/java/com/adealink/weparty/cocosgame/listener/ICocosGameOperateListener.kt
  99. 329 0
      app/src/main/java/com/adealink/weparty/cocosgame/manager/CocosWebGameManager.kt
  100. 46 0
      app/src/main/java/com/adealink/weparty/cocosgame/manager/ICocosWebGameManager.kt

+ 57 - 0
.github/workflows/language_sync.yml

@@ -0,0 +1,57 @@
+name: Sync Multi Language
+
+on:
+  workflow_dispatch:
+    inputs:
+      branchName:
+        description: 'Branch Name'
+        required: true
+      excelName:
+        description: 'Excel Name'
+        required: true
+      sheetName:
+        description: 'Sheet Name'
+        required: true
+
+jobs:
+  run-script:
+    runs-on: ubuntu-latest
+    permissions:
+      contents: write
+
+    steps:
+      - name: Check out code
+        uses: actions/checkout@v3
+        with:
+          ref: ${{ github.event.inputs.branchName }}
+
+      - name: Set up Python 3
+        uses: actions/setup-python@v4
+        with:
+          python-version: '3.x'
+
+      - name: Install dependencies
+        run: |
+          cd tool/translate 
+          pip3 install pygsheets
+          if [ ! -f requirements.txt ]; then
+            echo "No requirements.txt found, installing default dependencies"
+            pip install requests pandas 
+          else
+            pip install -r requirements.txt
+          fi
+
+      - name: Run Python script
+        run: |
+          cd tool/translate  
+          python3 synxml.py -f "${{ github.event.inputs.excelName }}" -t "${{ github.event.inputs.sheetName }}"
+
+      - name: Commit Language Change
+        run: |
+          git config --global user.email "450468291@qq.com"
+          git config --global user.name "GitHub Action"
+          git add .
+          git commit -m "Update Language" || echo "No changes to commit"
+          git push
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} #设置GitHub Token

+ 24 - 0
.gitignore

@@ -0,0 +1,24 @@
+*.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/wyak/release
+app/lite/release

+ 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"] // 关闭语言白名单
+}
+

+ 4586 - 0
app/bt-proguard.txt

@@ -0,0 +1,4586 @@
+0000O000000o
+000O00000Oo
+000O00000o0
+000O00000o
+000O00000oO
+000O00000oo
+000O0000O0o
+000O0000OOo
+000O0000Oo0
+000O0000Oo
+000O0000OoO
+00O0000Ooo
+00O0000o00
+00O0000o0
+00O0000o0O
+00O0000o0o
+00O0000o
+00O0000oO0
+00O0000oO
+00O0000oOO
+000O0000oOo
+00O0000oo0
+00O0000oo
+00O0000ooO
+00O0000ooo
+00O00oOooO
+00O00oOooo
+00O000O00o
+00O000O0OO
+00O000O0Oo
+000O00oOoOo
+00O000O0o0
+00O000O0o
+00O000O0oO
+00O000O0oo
+00O000OO00
+00O000OO0o
+00O000OO
+00O000OOOo
+00O000OOo0
+000O000OOo
+00O000OOoO
+00O000OOoo
+00O000Oo00
+00O000Oo0
+00O000Oo0O
+00O000Oo0o
+00O000OoO0
+00O00O0Oo
+00O000OoO
+000O000OoOO
+00O000OoOo
+00O000Ooo0
+00O000Ooo
+00O000OooO
+00O000Oooo
+00O000o000
+00O000o00
+00O000o00O
+00O000o00o
+000O000o0
+00O000o0O0
+00O000o0O
+00O000o0OO
+00O000o0Oo
+00O000o0o0
+00O000o0o
+00O000o0oo
+00O000o
+00O000oO00
+000O000oO0
+00O000oO0O
+00O000oO0o
+00O000oO
+00O000oOO0
+00O000oOO
+00O000oOOO
+00O000oOOo
+00O000oOo0
+00O000oOo
+000O000oOoO
+00O000oOoo
+00O000oo0
+00O000oo0O
+00O000oo0o
+00O000oo
+00O000ooO0
+00O000ooO
+00O000ooOO
+00O000ooOo
+000O000ooo0
+00O000ooo
+00O000oooO
+00O000oooo
+00oooOoO
+00O00oOOoo
+00O00O000o
+00O00O00Oo
+00O00O00o0
+00O00O00o
+000O00O00oO
+00O00O00oo
+00O00O0O0o
+00O00O0OO
+00O00O0OOo
+00O00O0Oo0
+00O00O0OoO
+00O00O0Ooo
+00O00O0o00
+00O00O0o0
+00O00O0o0O
+0O00O0o0o
+0O00O0o
+0O00O0oO0
+0O00O0oOO
+0O00O0oOo
+0O00O0oo0
+0O00O0oo
+0O00O0ooO
+0O00O0ooo
+00O00OO0O
+0O00OO0o
+0O00OOOo
+0O00OOo0
+0O00OOo
+0O00OOoO
+0O00OOoo
+0O00Oo00
+0O00Oo00o
+0O00Oo0
+00O00Oo0OO
+0O00Oo0Oo
+0O00Oo0o0
+0O00Oo0o
+0O00Oo0oO
+0O00OooOO
+0O00Oo0oo
+0O00Oo
+0O00OoO0
+0O00OoO0o
+00O00OoO0O
+0O00Ooo
+0O00OoO
+0O00OoOO0
+0O00OoOO
+0O00OoOo0
+0O00OoOo
+0O00OoOoO
+0O00OoOoo
+0O00Ooo00
+00O00Ooo0
+0O00Ooo0O
+0O00Ooo0o
+0O00OooO0
+0O00OooO
+0O00OooOo
+0O00Oooo0
+0O00Oooo
+0O00Ooooo
+0O00OoooO
+00O00o0000
+0O00o000
+0O00o000O
+0O00o000o
+0O00o00
+0O00o00O0
+0O00o00O
+0O00o00OO
+0O00o00Oo
+0O00o00o0
+00O00o00o
+0O00o00oO
+0O00o00oo
+0O00o0
+0O00o0O00
+0O00o0O0
+0O00oo000
+0O00o0O0O
+0O00o0O0o
+0O00o0O
+00O00o0OO0
+0O00o0OO
+0O00o0OOO
+0O00o0OOo
+0O00o0Oo0
+0O00o0Oo
+0O00o0OoO
+0O00o0Ooo
+0O00o0o00
+0O00o0o0
+00O00o0o0O
+0O00o0o0o
+0O00o0o
+0O00o0oO0
+0O00o0oO
+0O00o0oOO
+0O00o0oOo
+0O00o0oo0
+0O00o0oo
+0O00o0ooO
+000O00o0ooo
+00O00o
+00O00oO0O0
+00O00oO000
+00O00oO00
+00O00oO00O
+00O00oO0OO
+00O00oO00o
+00O00oO0
+00O00oO0O
+00O00ooO00
+0O00oO0Oo
+0O00oO0o0
+0O00oO0o
+0O00oO0oO
+0O00oO0oo
+0O00oO
+0O00oOO0o
+0O00oOO00
+0O00oOO0
+00O00oOO0O
+0O00oOo
+0O00oOO
+0O00oOOO0
+0O00oOOO
+0O00oOOOO
+0O00oOOOo
+0O00oOOo0
+0O00oOOo
+0O00oOOoO
+00O00oOo00
+0O00oOo0
+0O00oOo0O
+0O00oOo0o
+0O00oOoO
+0O00oOoOO
+0O00oOoo0
+0O00oOoo
+0O00oo00
+0O00oo00O
+00O00oo00o
+0O00oo0
+0O00oo0O0
+0O00oo0O
+0O00oo0OO
+0O00oo0Oo
+0O00oo0o0
+0O00oo0o
+0O00oo0oo
+0O00oo
+00O00ooO0
+0O00ooO0O
+0O00ooO0o
+0O00ooO
+0O00ooOo0
+0O00ooOO0
+0O00ooOO
+0O00ooOOO
+0O00ooOOo
+0O00ooOo
+00O00ooOoO
+0O00ooOoo
+0O00ooo00
+0O00ooo0
+0O00ooo0o
+0O00ooo
+0O00oooO0
+0O00oooO
+0O00oooOO
+0O00oooOo
+00O00oooo0
+0O00oooo
+0O00ooooo
+0ooooooo
+0O0O00oO
+0O0O000o
+0O0O00OO
+0O0O00Oo
+0O0O00o0
+0O0O00o
+00O0O0O0o
+0O0O0O
+0O0O0OO0
+0O0O0OO
+0O0O0OOO
+0O0O0OOo
+0O0O0Oo0
+0O0O0OoO
+0O0O0Ooo
+0O0O0o00
+00O0O0o0
+0O0O0o0O
+0O0O0o0o
+0O0O0o
+0O0O0oO0
+0O0O0oO
+0O0O0oOO
+0O0O0oOo
+0O0O0oo0
+0O0O0oo
+000O0O0ooO
+00O0O0ooo
+00O0OoOo
+00O0OoOO
+00O0OO00O
+00O0OO00o
+00O0OO0O
+00O0Oo0o0
+00O0OO0Oo
+00O0OO0o0
+00O0OO0oO
+0O0OO0oo
+0O0OOO00
+0O0OOoo
+0O0OOoO
+0O0OOO0
+0O0OOO0O
+0O0OOO0o
+0OO0oO
+0O0OOOO
+00O0OOOOO
+0O0OOOOo
+0O0OOOo0
+0O0OOOo
+0O0OOOoO
+0O0OOOoo
+0O0OOo00
+0O0OOo0O
+0O0OOo0o
+0O0OOo
+00O0OOoO0
+0O0OOoOO
+0O0OOoOo
+0O0OOoo0
+0O0OOooO
+0O0OOooo
+0O0Oo000
+0O0Oo00
+0O0Oo00O
+0O0Oo00o
+00O0Oo0
+0O0Oo0O0
+0O0Oooo
+0O0Oo0O
+0O0Oo0OO
+0O0Oo0Oo
+0O0Oo0o
+0O0Oo0oO
+0O0Oo0oo
+0o00o00O
+00O0Oo
+0O0OoO00
+0O0OoO0
+0O0OoO0O
+0OooOO
+0OoO0o
+0O0OoO
+0O0OoOO0
+0O0OoOOO
+0O0OoOOo
+00O0OoOo0
+0O0OoOoO
+0O0OoOoo
+0O0Ooo00
+0O0Ooo0
+0O0Ooo0O
+0O0Ooo0o
+0O0Ooo
+0O0OooO0
+0O0OooO
+00O0OooOO
+0O0OooOo
+0O0Oooo0
+0O0OoooO
+0O0Ooooo
+0O0o0000
+0O0o000
+0O0o000O
+0O0o000o
+0O0o00
+00O0o00O0
+0O0o00O
+0O0o00OO
+0O0o00Oo
+0O0o00o0
+0O0o00o
+0O0o00oO
+0O0o00oo
+0O0o0
+0O0o0O00
+00O0o0O0
+0O0o0O0O
+0O0o0O0o
+0O0o0O
+0O0o0OO0
+0O0oo0o
+0O0o0OO
+0O0o0OOO
+0O0o0OOo
+0O0o0Oo0
+000O0o0Oo
+00O0o0OoO
+00O0o0Ooo
+00O0o0o00
+00O0o0o0
+00O0o0o0O
+00O0o0o0o
+00O0o0oO0
+00O0o0oO
+00O0o0oOO
+00O0o0oOo
+0O0o0oo
+0O0o0ooO
+0oOOoOO
+0O0o
+0O0oO000
+0O0oO00
+0O0oO0oO
+0O0oO00O
+0O0oO00o
+00O0oO0
+0O0oO0O0
+0O0oO0O
+0O0oO0OO
+0O0oO0Oo
+0O0oO0o0
+0O0oO0o
+0O0oO0oo
+0o0Oo0o0
+0O0oO
+00O0oOO00
+0O0oOO0
+0o00O00O0
+0O0oOO0O
+0O0oOO0o
+0O0oOO
+0O0oOOO0
+0O0oOOOo
+0O0oOOo0
+0Oo0OOo
+00O0oOOo
+0O0oOOoO
+0O0oOOoo
+0O0oOo00
+0O0oOo0
+0ooO0Ooo
+0O0oOo0O
+0O0oOo0o
+0O0oOo
+0O0oOoO0
+00O0oOoO
+0O0oOoOO
+0O0oOoOo
+0OOoOoo
+0O0oOoo0
+0O0oOoo
+0O0oOooO
+0O0oOooo
+0O0oo000
+0O0oo00
+00O0oo00O
+0O0oo00o
+0O0oo0
+0O0oo0O0
+0O0oo0O
+0O0oo0OO
+0O0oo0Oo
+0O0oo0o0
+0O0oo0oO
+0O0oo0oo
+00O0oo
+0O0ooO00
+0O0ooOo
+0O0oooo
+0O0oooO
+0O0ooO0
+0O0oooOo
+0O0ooO0O
+0O0ooO0o
+0O0ooO
+00O0ooOO0
+0O0ooOO
+0O0ooOOO
+0O0ooOOo
+0O0ooOo0
+0O0ooOoO
+0O0ooOoo
+0O0ooo00
+0O0ooo0
+0O0ooo0O
+00O0ooo0o
+0O0ooo
+0O0oooO0
+0O0oooOO
+0O0oooo0
+0O0ooooO
+0O0ooooo
+0OO0000
+0OO0000o
+0OoOOO
+000OO000OO
+00OO000Oo
+00OO000o0
+00OO000o
+00OO000oO
+00OO000oo
+00OO00OO
+00OO00O0
+00OO00O0o
+00OO00O
+00OO00OOO
+0OO00OOo
+0OO00Oo0
+0OO0ooO
+0OO00Oo
+0OO00OoO
+0OO00Ooo
+0OO00o00
+0OO00o0O
+0OO00o0o
+00OO00oO0
+0OO00oO
+0OO00oOO
+0OO00oOo
+0OO00oo0
+0OO00oo
+0OO00ooO
+0OO00ooo
+0OO0O00o
+0OO0O0O
+00OO0O0OO
+0OO0O0Oo
+0OO0O0o0
+0OO0oOo
+0OO0oOO
+0OO0O0o
+0OO0O0oO
+0OO0O0oo
+0oOo00
+0OO0OO0o
+00OOo00
+0OO0OO
+0OO0OOO
+0OO0OOOO
+0OO0OOOo
+0OO0OOo0
+0OO0OOo
+0OO0OOoO
+0OO0OOoo
+0OO0Oo00
+00OO0Oo0
+0OO0Oo0O
+0OO0Oo0o
+0Oo0Oo0O
+0OO0OoO0
+0OO0OoO
+0OO0OoOO
+0Oo0Ooo0
+0OO0OoOo
+0OO0Ooo0
+00OO0Ooo
+0OO0OooO
+0OO0Oooo
+0OO0o000
+0OO0o00
+0OO0o00O
+0OO0o00o
+0OO0o0
+0OO0o0O0
+0OO0o0OO
+00OO0o0Oo
+0OO0o0o0
+0OO0o0o
+0OO0o0oO
+0OO0o0oo
+0OO0o
+0OO0oO00
+0OO0oO0
+0OO0oO0O
+0OO0oO0o
+00OO0oOO0
+0OoOOoOo
+0OO0oOOO
+0OO0oOOo
+0OO0oOo0
+0OO0oOoO
+0OO0oOoo
+0OO0oo00
+0OO0oo0
+0OO0oo0O
+00OO0oo0o
+0OO0oo
+0OO0ooO0
+0OO0ooOO
+0OO0ooOo
+0OO0ooo0
+0OO0ooo
+0OO0oooO
+0OO0oooo
+0OOO000o
+000OOO00
+00OOO00O0
+00OOO00Oo
+00OOO00o0
+00OOO00o
+00OOO00oO
+00OOO00oo
+00OOO0O0O
+00OOO0O0o
+00OOO0O
+00o00OOOOO
+0OOO0OO0
+0OOO0OOO
+0OOO0OOo
+0OOO0Oo0
+0OOO0oO
+0OOO0Oo
+0OOO0OoO
+0OOO0Ooo
+0OOO0o00
+00OOO0o0
+0OOO0o0O
+0OOO0o0o
+0OOO0o
+0OOO0oO0
+0OOO0oOO
+0OOO0oOo
+0OOO0oo0
+0OOO0oo
+0OOO0ooO
+00OOO0ooo
+0OOO
+0OOOO00O
+0OOOO00o
+0OOOO0
+0OOOOoO
+0OOOOo0
+0OOOOoo
+0OOOO0O
+0OOOO0OO
+00OOOO0Oo
+0OOOO0o0
+0OOOO0oO
+0OOOO0oo
+0OOOOO00
+0OOOOO0
+0OOOOO0o
+0OOOOO
+0OOOOOO
+0OOOOOOO
+00OOOOOOo
+0OOOOOo0
+0OOooOo
+0OOoooo
+0OOOOOo
+0OOOOOoO
+0OOOOOoo
+0OOOOo00
+0OOOOo0O
+0OOOOo0o
+00OOOOo
+0OOOOoO0
+0OOOOoOO
+0OOOOoOo
+0OOOOoo0
+0OOOOooO
+0OOOOooo
+0OOOo000
+0OOo000
+0OOOo00
+00OOOo00O
+0OOOo00o
+0OOOo0
+0ooOOo00
+0OOOo0O0
+0OOOo0O
+0OOOo0OO
+0OOOo0Oo
+0OOOo0o0
+0OOOo0o
+00OOOo0oO
+0OOOo0oo
+0OOOo
+0OOOoO00
+0OOOoO0
+0OOOoO0O
+0o0ooo0OO
+0OOOoO0o
+0OOOoO
+0OOOoOO0
+00OOOoOO
+0OOOoOOO
+0OOOoOOo
+0OOOoOo0
+0OOOoOo
+0OOOoOoO
+0OOOoOoo
+0ooO00O0
+0OOOoo00
+0OOOoo0
+000o00O0oO0
+00OOOoo0O
+00OOOoo0o
+00OOOoo
+00OOOooO0
+00OOOooO
+00o0OOoOoo
+00OOOooOO
+00OOOooOo
+00OOOooo0
+00OOOooo
+0OOOoooO
+0OOOoooo
+0OOo0000
+0OOo000O
+0OOo000o
+0OOo00O0
+0OOo00O
+0OOo00OO
+0OOo00Oo
+00OOo00o0
+0OOo00o
+0OOo00oO
+0OOo00oo
+0ooOo
+0OOo0
+0OOo0O00
+0OOo0OO
+0OOo0Oo
+0OOoo00
+00OOo0O0
+0OOo0O0O
+0OOo0O0o
+0OOoOOoO
+0OOo0OO0
+0OOo0OOO
+0OOo0OOo
+0OOo0Oo0
+0OOo0OoO
+0OOo0Ooo
+00OOo0o00
+0OOo0o0
+0OOo0o0O
+0OOo0o0o
+0OOo0o
+0OOo0oO0
+0OOo0oO
+0OOo0oOO
+0OOo0oOo
+0OOo0oo0
+00OOo0oo
+0OOo0ooO
+0OOo0ooo
+0OOoO000
+0OOoO00
+0OOoO00O
+0OOoO00o
+0OOoO0
+0OOoO0O0
+0OOoO0o
+00OOoO0O
+0OOoO0OO
+0OOoO0Oo
+0OOoO0o0
+0OOoO0oO
+0OOoO0oo
+0OOoO
+0OOoOO00
+0OOoOO0O
+0OOoOO0o
+00OOoOO
+0OOoOOO0
+0OOoOOO
+0OOoOOOO
+0OOoOOOo
+0OOoOOo0
+0OOoOOo
+0OOoOOoo
+0OOoOo00
+0OOoOo0
+00OOoOo0O
+0OOoOo0o
+0OOoOo
+0OOoOoO0
+0oO0OO0
+0OOoOoO
+0OOoOoOO
+0OOoOoOo
+0OOoOoo0
+0OOoOooO
+00OOoOooo
+0OOoo000
+0OOoo00O
+0OOoo00o
+0OOoo0
+0OOoo0O0
+0OOoo0O
+0OOoo0OO
+0OOoo0Oo
+0OOoo0o0
+000OOoo0o
+00OOoo0oO
+00OOoo0oo
+00OOoo
+00OOooO00
+00OOooO0
+00OOooO0O
+00OOooO0o
+00OOooO
+00OOooOO0
+00OOooOO
+0OOooOOO
+0OOooOOo
+0OOooOo0
+0OOooOoO
+0OOooOoo
+0OOooo00
+0OOooo0
+0OOooo0O
+0OOooo0o
+00OOooo
+0OOoooO0
+0OOoooO
+0OOoooOO
+0OOoooOo
+0OOoooo0
+0OOooooO
+0OOooooo
+0Oo00000
+0Oo0000O
+00Oo0000o
+0Oo000
+0Oo000O0
+0Oo000O
+0Oo000OO
+0Oo000Oo
+0Oo000o0
+0Oo000o
+0Oo000oO
+0Oo000oo
+00Oo00
+0Oo00O00
+0Oo00O0
+0Oo0o0Oo
+0Oo00O0O
+0Oo00O0o
+0Oo00O
+0Oo00OO0
+0Oo0o0o
+0Oo00Oo
+00Oo00OO
+0Oo00OOO
+0Oo00OOo
+0Oo00Oo0
+0Oo0o0O0
+0Oo00OoO
+0Oo00Ooo
+0Oo00o00
+0Oo00o0
+0Oo00o0O
+00Oo00o0o
+0Oo00o
+0Oo00oO0
+0Oo00oOO
+0Oo00oo0
+0Oo00oo
+0Oo00ooO
+0Oo00ooo
+0Oo0
+0Oo0O000
+00Oo0O00
+0Oo0O00O
+0Oo0O00o
+0Oo0O0
+0Oo0O0O0
+0Oo0O0O
+0Oo0O0OO
+0Oo0O0Oo
+0Oo0O0o0
+0Oo0O0o
+00Oo0O0oO
+0Oo0O0oo
+0o00oO00
+0o00OO00
+0Oo0O
+0Oo0OO00
+0Oo0OO0
+0Oo0OO0O
+0Oo0OO0o
+0Oo0OO
+00Oo0OOO0
+0Oo0OOOO
+0Oo0OOOo
+0Oo0OOo0
+0Oo0OOoO
+0Oo0OOoo
+0Oo0Oo00
+0Oo0Oo0
+0Oo0Oo0o
+0Oo0Oo
+000Oo0OoO0
+00Oo0OoO
+00Oo0OoOo
+00Oo0Ooo
+00Oo0OooO
+00Oo0Oooo
+00Oo0o000
+00Oo0o00
+00Oo0o00O
+00Oo0o00o
+00Oo0o0
+0Oo0o0O
+0Oo0o0o0
+0Oo0o0oO
+0Oo0o0oo
+0Oo0o
+0Oo0oO00
+0Oo0ooO
+0Oo0oOO
+0Oo0oo0
+00Oo0ooo
+0Oo0oO0
+0Oo0oO0O
+0Oo0oO0o
+0Oo0oO
+0Oo0oOO0
+0Oo0oOOO
+0Oo0oOOo
+0Oo0oOo0
+0Oo0oOo
+00Oo0oOoO
+0Oo0oOoo
+0Oo0oo00
+0Oo0oo0O
+0Oo0oo0o
+0Oo0oo
+0Oo0ooO0
+0Oo0ooOO
+0Oo0ooOo
+0Oo0ooo0
+00Oo0oooO
+0Oo0oooo
+0Oo
+0OoO0000
+0OoO000
+0OoO000O
+0OoO000o
+0OoO00O0
+0OoO00O
+0OoO00OO
+00OoO00Oo
+0OoO00o0
+0OoO00o
+0OoO00oO
+0OoO00oo
+0OoO0
+0OoO0O00
+0OoO0Oo
+0OoO0O0
+0OoO0O0O
+00OoO0O0o
+0OoO0O
+0OoO0OO0
+0OoO0OO
+0OoO0OOO
+0OoOo00O
+0OoO0OOo
+0OoO0Oo0
+0OoO0OoO
+0OoO0Ooo
+00OoO0o00
+0OoO0o0
+0OoO0o0O
+0OoO0o0o
+0OoO0oO0
+0OoO0oo
+0OoO0oO
+0OoO0oOO
+0OoO0oOo
+0OoO0oo0
+00OoO0ooO
+0OoO0ooo
+0OoO
+0OoOO000
+0OoOOo0
+0OoOOOO
+0OoOO0O
+0OoOOoo
+0OoOOoO
+0OoOOO0
+00OoOOOo
+0OoOoOo
+0OoOoOO
+0OoOO00
+0OoOO00O
+0OoOO00o
+0OoOO0
+0OoOO0O0
+0OoOO0OO
+0OoOO0Oo
+000OoOO0o0
+00OoOO0o
+00OoOO0oO
+00OoOO0oo
+00OoOo
+00OoOO
+00OoOOO0O
+00OoOOO0o
+00OoOOOO0
+00OoOOOOO
+00OoOOOOo
+0OoOOOo0
+0OoOOOoO
+0OoOOOoo
+0OoOOo00
+0OoOOo0O
+0OoOOo0o
+0OoOOo
+0OoOOoO0
+0OoOOoOO
+00OoOOoo0
+0OoOOooO
+0OoOOooo
+0OoOo000
+0OoOooo
+0OoOoo0
+0OoOo00
+0OoOo00o
+0OoOo0
+0OoOo0O0
+00OoOo0o
+0OoOo0O
+0OoOo0OO
+0OoOo0Oo
+0OoOo0o0
+0OoOo0oO
+0OoOo0oo
+0OoOoO00
+0OoOoO0
+0OoOoO0O
+00OoOoO0o
+0OoOoO
+0OoOoOO0
+0OoOoOOO
+0OoOoOOo
+0OoOoOo0
+0OoOoOoO
+0OoOoOoo
+0OoOoo00
+0OoOoo0O
+00OoOoo0o
+0OoOoo
+0OoOooO0
+0OoOooO
+0OoOooOO
+0OoOooOo
+0OoOooo0
+0OoOoooO
+0OoOoooo
+0Ooo0000
+00Ooo000
+0Ooo000O
+0Ooo000o
+0Ooo00
+0Ooo00O0
+0Ooo00O
+0Ooo00OO
+0Ooo00Oo
+0Ooo00o0
+0Ooo00o
+00Ooo00oO
+0o00oo00o
+0Ooo00oo
+0Ooo0
+0Ooo0O00
+0Ooo0O0
+0Ooo0O0O
+0Ooo0O0o
+0Ooo0O
+0Ooo0OO0
+00Oooo00
+0Ooo0oO
+0Ooo0Oo
+0Ooo0OO
+0o000O0OO
+0o000oO0O
+0Ooo0OOO
+0Ooo0OOo
+0Ooo0OoO
+0Ooo0Ooo
+00Ooo0o00
+0Ooo0o0
+0Ooo0o0o
+0Ooo0o
+0Ooo0oO0
+0Ooo0oOO
+0Ooo0oOo
+0Ooo0oo0
+0Ooo0oo
+0Ooo0ooO
+00Ooo0ooo
+0o0oo
+0Ooo
+0OooO000
+0OooO0O
+0Ooooo0
+0OooooO
+0OoooOO
+0OoooOo
+0OooO00
+0OooO00O
+OooO00o
+OooO0O0
+OooO0OO
+OooO0Oo
+OooO0o0
+OooO0o
+OooO0oO
+OooO0oo
+OooO
+0OooOO00
+OooOO0
+OooOO0O
+OooOO0o
+OooOOO0
+OooOOO
+OooOOOO
+OooOOOo
+OooOOo0
+OooOOo
+0OooOOoO
+OooOOoo
+OooOo00
+OooOo0
+OooOo0O
+OooOo0o
+OooOo
+OooOoO0
+OooOoO
+OooOoOO
+0OooOoOo
+OooOoo0
+OooOoo
+OooOooO
+OooOooo
+Oooo000
+Oooo00O
+Oooo00o
+Oooo0
+Oooo0O0
+0Oooo0O
+Oooo0OO
+Oooo0o0
+Oooo0o
+Oooo0oO
+Oooo0oo
+Oooo
+OoooO00
+OoooO0
+OoooO0O
+0OoooO0o
+OoooO
+OoooOO0
+o000oOoO
+OoooOOO
+OoooOOo
+OoooOo0
+OoooOoO
+OoooOoo
+Ooooo00
+0Ooooo0O
+Ooooo0o
+OooooO0
+OooooOO
+OooooOo
+Oooooo0
+Oooooo
+OoooooO
+Ooooooo
+o0OoOo0
+0o0OoO0O
+ooOO
+o00O0O
+o00Oo0
+o00Ooo
+o00o0O
+o00ooo
+oo000o
+o00oO0o
+o00oO0O
+0o0oo000
+o0ooOO0
+o0ooOOo
+o0ooOoO
+o0OOO0o
+o0Oo0oo
+o0OO00O
+oo0o0Oo
+o0O0O00
+o000OOo
+00oo0oOoO
+0ooo0Ooo
+0ooOo00O
+0o0OoOO0
+0o0O00oo
+0o0ooo00
+0o000OOoo
+0o000OOOo
+0o000OOOO
+0o000OOO0
+0o0000000
+o000000
+o000000O
+o000000o
+o00000
+o00000O0
+o00000O
+o00000OO
+o00000Oo
+o00000o0
+0o00000o
+o0000Ooo
+o00000oO
+o00000oo
+o0000
+o0000O00
+o0000oo
+o0000oO
+o0000O0
+o0000O0O
+0o0000O0o
+o000OO
+o0000O
+o0000OO0
+o0000OO
+o0000OOO
+o0000OOo
+o0000Oo0
+o0000Oo
+o0000OoO
+0o0000o00
+o0000o0
+o0000o0O
+o0000o0o
+o0000o
+o0000oO0
+o0000oOO
+o0000oOo
+o0000oo0
+o0000ooO
+0o0000ooo
+o000
+o000O000
+o000OoO
+o000O0o
+o000Ooo
+o000O0O
+o000Oo0
+o000O00
+o000O00O
+0o000O00o
+o000O0
+o000O0Oo
+o000OO0O
+o000O0O0
+o000O0o0
+o000O0oO
+o000O0oo
+o000O
+o000OO00
+0o000OO0
+o0OoO0o
+o000OO0o
+o000OOO
+o000OOo0
+o000OOoO
+o000Oo00
+o000Oo0O
+o000Oo0o
+o000Oo
+0o000OoO0
+o000OoOO
+o000OoOo
+o000Ooo0
+o000OooO
+o000Oooo
+o000o000
+o000o00
+o000o00O
+o000o00o
+0o000o0
+oooo00o
+o000o0O0
+o000o0O
+o000o0OO
+o000o0Oo
+o000o0o0
+o000o0o
+o000o0oO
+o000o0oo
+00o000o
+0o000oO00
+0o000oO0
+0o000oO0o
+0o000oO
+0o000oOO0
+0o000oOO
+0o000oOOO
+0o000oOOo
+0o000oOo0
+0o000oOo
+o000oOoo
+o000oo00
+o000oo0
+o000oo0O
+o000oo0o
+o000oo
+o000ooO0
+o000ooO
+o000ooOO
+0o000ooOo
+o000ooo0
+o000ooo
+o000oooO
+o000oooo
+o00
+o00O0000
+o0O0ooO
+o00oOoo
+o00O000
+0o00O000O
+o00O000o
+o00O00
+o00O00O
+oOO00O
+o00O00OO
+o00O00Oo
+o00O00o0
+o00O00o
+o00O00oO
+0o00O00oo
+oo00o
+o00O0
+o00O0O00
+o00O0O0
+o00O0O0O
+o00O0O0o
+o00O0OO0
+oo0o0O0
+o00O0OO
+0o00Oo00O
+o00O0OOO
+o00O0OOo
+o00O0Oo0
+oo0oOO0
+o00O0Oo
+o00O0OoO
+o00O0Ooo
+o00O0o00
+o00oOOo
+0o00oOOO
+o00O0o0
+o00O0o0O
+o00O0o0o
+o00O0o
+o00O0oO
+o00O0oOO
+o00O0oOo
+o00O0oo0
+o00O0oo
+0o00O0ooO
+o00O0ooo
+o00O
+o00OO000
+o00OO00O
+o00OO00o
+o00OO0
+o00OO0O0
+o00OO0O
+o00OO0OO
+0o00OO0Oo
+o00OO0o0
+o00OO0o
+o00OO0oO
+o00OO0oo
+oo0O
+o00OO
+o00OOO00
+o00OOO0
+o00OOO0O
+0o00OOO0o
+o0o0Oo
+o00OOO
+o00OOOO0
+o00OOOO
+o00OOOOo
+oOooo0o
+o00OOOo0
+o00OOOo
+o00OOOoO
+00o00OOOoo
+0o00OOo00
+0o00OOo0
+0o00OOo0O
+0o00OOo0o
+0o00OOo
+0o00OOoO0
+0o00OOoO
+0o00OOoOO
+0o00OOoOo
+0o00OOoo0
+o00OOoo
+o00OOooO
+o00OOooo
+o00OoOoO
+o00Oo000
+o00Oo00
+o00Oo00o
+o00Oo0O0
+o00Oo0O
+0o00Oo0OO
+o00Oo0Oo
+o00Oo0o0
+o00Oo0o
+o00Oo0oO
+o00Oo0oo
+o0oOO
+o00Oo
+o00OoO00
+o00OoO0
+0o00OoO0O
+o00OoO0o
+o00OoO
+o00OoOO0
+o00OoOO
+o00OoOOO
+o00OoOOo
+o00OoOo0
+o00OoOo
+o00OoOoo
+0o00Oooo0
+o00Ooo00
+o00Ooo0
+o00Ooo0O
+o00Ooo0o
+o00OooO0
+o00OooO
+o00OooOO
+o00OooOo
+o0O00o0
+0o00Oooo
+o00OoooO
+o00Ooooo
+o00o0000
+o00o000
+o00o000O
+o00o000o
+oo00oO
+o00o00
+o00o00O0
+0o00o00OO
+o00o00Oo
+o00o00o0
+o00o00o
+o00o00oO
+o00o00oo
+o00o0
+o00o0O00
+o00o0O0
+o00o0O0O
+0o00o0O0o
+o00o0OO0
+o00o0OO
+o00o0OOO
+o00o0OOo
+o00o0Oo0
+o00o0Oo
+o00o0OoO
+o00o0Ooo
+o00o0o00
+0o00o0o0
+o00o0o0O
+o00o0o0o
+o00o0o
+o00o0oO0
+o00o0oO
+o00o0oOO
+o00o0oOo
+o00o0oo0
+o00o0oo
+0o00o0ooO
+o00o0ooo
+o00o
+o00oo000
+o00oO000
+o00oO00O
+o00oO00o
+o00oOo
+o00oO0
+o00oO0O0
+00o00oO0OO
+0o00oO0Oo
+0o00oO0o0
+0o00oO0oO
+0o00oO0oo
+0o00oO
+0o00oOOoo
+0o00oOO00
+0o00oOO0
+0o00oOO0O
+0o00oOO0o
+o0oOOo
+o0ooOO
+o00oOO
+o00oOOO0
+o00oOOOO
+o00oOOOo
+o00oOOo0
+o00oOOoO
+o00oOo00
+0o00oOo0
+o00oOooO
+o00oOo0O
+o00oOo0o
+o00oOoO0
+o00oOoO
+o00oOoOO
+o00oOoOo
+o00oOoo0
+o00oOooo
+0o00oo00
+o00oo00O
+o00oo0
+o00oo0OO
+o00oo0O0
+o00oo0O
+o00oo0Oo
+o00oo0o0
+o00oo0o
+o00oo0oO
+0o00oo0oo
+o0O0o
+o00oo
+o00ooO00
+o00ooO0
+o00ooO0O
+o00ooO0o
+o00ooO
+o00ooOO0
+o00ooOO
+0o00ooOOO
+o00ooOOo
+o00ooOo0
+o00ooOo
+o00ooOoO
+o00ooOoo
+o00ooo00
+o00ooo0
+o00ooo0O
+o00ooo0o
+0o00oooO0
+o00oooO
+o00oooOO
+o00oooOo
+o00oooo0
+o00oooo
+o00ooooO
+o00ooooo
+o0
+o0O00000
+0o0O0000
+o0O0000O
+o0O0000o
+o0O000
+o0O000O
+o0OoOoOo
+o0O000Oo
+o0OoOoOO
+o0O000o0
+o0O000o
+0o0O000oO
+o0ooOoOO
+o0O000oo
+o0O00
+o0O00O0
+o0OoO00O
+o0O00O0o
+o0O00O
+o0O00OO
+o0O00OOO
+0o0O00Oo0
+o0O00Oo
+o0oO0Ooo
+o0O00OoO
+o0O00Ooo
+o0O00o00
+o0O00o0O
+o0O00o0o
+o0O00o
+o0O00oO0
+00o0O00oO
+0o0O00oOO
+0o0O00oOo
+0o0O00oo0
+0o0Oo0oOO
+0o0O00ooO
+0o0O00ooo
+0o0O0
+0o0ooOOOo
+0o0O0O00O
+0o0O0O0
+o0O0O0O
+o0oO0O0o
+o0O0oo0o
+o0O0O0Oo
+o0O0O0o0
+o0O0O0o
+o0O0O0oO
+o0O0O0oo
+o0ooO
+0o0O0O
+o0O0OO0
+o0O0OO0O
+o0O0OO
+o0O0OOO0
+o0O0OOO
+o0O0OOOo
+o0O0OOo
+o0O0OOoO
+o0O0OOoo
+0o0O0Oo00
+o0OooO0
+o0O0Oo0
+o0O0Oo0O
+o0O0Oo0o
+o0O0Oo
+o0O0Oooo
+o0O0OoO0
+oo0OOoo
+o0O0OoO
+0o0O0OoOO
+o0O0OoOo
+o0O0Ooo0
+o0O0Ooo
+o0O0OooO
+o0O0o000
+o0O0o00
+o0O0o00O
+o0O0o00o
+o0O0o0
+0o0O0o0O0
+o0O0o0O
+o0O0o0OO
+o0O0o0Oo
+o0O0o0o0
+o0O0o0o
+o0oOo0O0
+o0O0o0oO
+o0O0o0oo
+o0O0oo0O
+0o0O0oO00
+oooOO0
+o0O0oO0
+o0O0oO0O
+o0O0oO0o
+o0O0oO
+o0O0oOO0
+o0O0oOO
+o0O0oo00
+o0O0oOOO
+0o0O0oOOo
+o0O0oOo0
+oo0oOOo
+o0O0oOo
+o0O0oOoO
+o0O0oOoo
+o0O0oo0
+o0O0oo
+o0O0ooO0
+o0O0ooOO
+0o0O0ooOo
+o0O0ooo0
+o0O0ooo
+o0O0oooO
+o0O0oooo
+o0O
+o0OO000
+o0OO000o
+oo0oO0
+oo0ooO
+0o0OO00
+o0OO00OO
+o0OO00oo
+o0OO00Oo
+o0OO00o0
+o0OoOoO
+o0OO00o
+o0OO0
+o0OO0O0
+o0OO0O0O
+00o0OO0O0o
+0o0OO0O
+0o0OOoOOo
+0o0OOoOoO
+0o0OO0OO0
+0o0OO0OO
+0o0OOoOO0
+0o0OO0OOO
+0o0OO0OOo
+0o0OO0Oo0
+0o0OO0Oo
+o0OO0OoO
+o0OOooO0
+o0OO0Ooo
+o0OO0o00
+o0OO0o0
+o0OO0o0O
+o0OO0o0o
+o0OO0o
+o0OO0oO0
+0o0OO0oo
+o0OO0oO
+o0OO0oOO
+o0OOoooO
+o0OO0oOo
+o0OO0oo0
+o0OO0ooO
+o0OO0ooo
+o0OO
+o0OOO00
+0o0OOO00O
+o0OOO00o
+o0OOO0
+o0OOO0O0
+o0OOO0O
+o0OOO0OO
+o0OOO0Oo
+o0OOO0o0
+o0OOO0oO
+o0OOO0oo
+0o0OOO
+o0OOOOoO
+o0OOOO00
+o0OOOO0
+o0OOOO0o
+o0OOOO
+o0OOOOO0
+o0OOOOO
+o0OOOOOO
+o0OOOOOo
+0o0OOOOo0
+o0OOOOo
+o0OOOOoo
+o0OOOo00
+o0OOOo0
+o0OOOo0O
+o0OOOo0o
+o0OOOo
+o0OOOoO0
+o0OOOoO
+0o0OOOoOO
+o0OOOoOo
+o0OOOoo0
+o0OOOoo
+o0OOOooO
+o0OOOooo
+o0OOo000
+o0OOo00
+o0OOo00O
+o0OOo00o
+0o0OOo0
+o0OOo0O0
+o0OooOo
+o0OOo0O
+o0OOo0OO
+o0OOo0Oo
+o0OOo0o0
+o0OOo0o
+o0OOo0oO
+o0OOo0oo
+0o0OOo
+o0OOoO00
+o0OOoO0
+o0OOoO0O
+o0OOoO0o
+o0OOoO
+o0OOoOO
+o0OOoOOO
+o0OOoOo0
+o0OOoOo
+0o0OOoo00
+o0OOoo0
+o0OOoo0O
+o0OOoo0o
+o0OOoo
+o0OOooO
+o0OOooOO
+o0OOooOo
+o0OOooo0
+o0OOooo
+00o0OOoooo
+0o0Oo0000
+0o0Oo000
+0o0Oooo0O
+0o0Oo00O0
+0o0Oo000O
+0o0Oo000o
+0o0Oo00
+0o0Oo00OO
+0o0Oo00Oo
+0o0oooOoo
+o0Oo00o0
+o0Oo00o
+o0Oo00oO
+o0Oo00oo
+o0Oo0
+o0ooO0O0
+o0Oo0O00
+o0Oo0O0
+o0Oo0O0O
+0o0Oo0O0o
+o0Oo0O
+o0Oo0OO0
+o0Oo0OO
+o0oOooO0
+o0ooOOOO
+o0oOo000
+o0Oo0OOO
+o0oOo0o0
+o0oOoo00
+0o0oO0OOo
+o0Oo0OOo
+o0Oo0Oo0
+o0Oo0Oo
+o0Oo0OoO
+o0Oo0oOo
+o0Oo0Ooo
+o0Oo0o00
+o0Oo0o0O
+o0Oo0o0o
+0o0Oo0o
+o0Oo0oO0
+o0Oo0oO
+o0Oo0oo0
+o0Oo0ooO
+o0Oo0ooo
+o0Oo
+o0OooOoo
+o0OoO000
+o0OoO0
+0o0OoO0O0
+o0oO0O00
+o0OoO0OO
+o0ooOOO0
+o0OoO0Oo
+o0OoO0o0
+o0OoO0oO
+o0OoO0oo
+o0OoO
+o0OoOO00
+0o0OoOO0O
+o0OoOO0o
+o0OoOO
+o0OoOOoO
+o0OoOOO0
+o0OoOOO
+o0OoOOOO
+o0OoOOOo
+o0OoOOo0
+o0OoOOo
+0o0OoOOoo
+o0OoOo00
+o0OoOo0O
+o0ooOOoo
+o0OoOo0o
+o0OoOo
+o0OoOoO0
+o0OoOoo0
+o0OoOoo
+o0OoOooO
+0o0OoOooo
+o0Ooo000
+o0Ooo00
+o0Ooo00O
+o0oOO0Oo
+o0oOoOoo
+o0Ooo00o
+o0Ooo0
+o0Ooo0O0
+o0Ooo0O
+0o0Ooo0OO
+o0oOOOoo
+o0OoooO0
+o0oOOO0o
+o0Ooo0Oo
+o0Ooo0o0
+o0Ooo0o
+o0Ooo0oO
+o0Ooo0oo
+o0Ooo
+00o0OooO00
+0o0OooO0O
+0o0OooO0o
+0o0OooO
+0o0OooOO0
+0o0OooOO
+0o0OooOOO
+0o0OooOOo
+0o0OooOo0
+0o0OooOoO
+0o0Oooo00
+o0Oooo0
+o0Oooo0o
+o0Oooo
+o0OoooO
+o0OoooOO
+o0OoooOo
+o0Ooooo0
+o0Ooooo
+o0OooooO
+0o0Oooooo
+o0o0000
+o0o0o00O
+o0o0000o
+o0o000
+o0o000O0
+o0o000O
+o0o000OO
+o0o000Oo
+o0o000o0
+0o0o000o
+o0o000oO
+o0o000oo
+o0o00
+o0o00O00
+o0o00O0
+o0o00O0O
+o0o00O0o
+o0o00O
+o0o00OO0
+0o0o00OO
+o0o00OOO
+o0o00OOo
+o0o00Oo0
+o0o00Oo
+o0o00OoO
+o0o00Ooo
+o0o00o00
+o0o00o0
+o0o00o0O
+0o0o00o0o
+o0o00o
+o0o00oO0
+o0o00oO
+o0o00oOO
+o0o00oOo
+o0o00oo0
+o0o00oo
+o0o00ooO
+o0o00ooo
+0o0o0
+o0o0O000
+o0o0O00
+o0o0O00O
+o0o0O00o
+o0o0O0
+o0o0O0O0
+o0o0O0O
+o0o0Oo00
+o0o0O0OO
+0o0o0O0Oo
+o0o0O0o0
+o0o0O0o
+o0o0O0oO
+o0o0O0oo
+oO0Oo
+oO0OO
+o0ooo
+o0o0O
+o0o0OoO0
+0o0o0OO00
+o0o0OO0
+o0o0OO0O
+o0o0OO0o
+o0o0OO
+o0o0OOO0
+o0o0OOO
+o0o0OOOO
+o0o0OOOo
+o0o0OOo0
+0o0o0OOo
+o0o0OOoO
+o0o0OOoo
+o0o0Oo0
+o0o0Oo0O
+o0o0Oo0o
+o0o0OoO
+o0o0OoOO
+o0o0OoOo
+o0o0Ooo0
+000o0o0Ooo
+00o0o0OooO
+00o0o0Oooo
+00o0o0o000
+00o0o0o00
+00o0o0o00o
+00o0o0o0
+00o0o0o0O0
+00o0o0o0O
+00o0o0o0OO
+00o0o0o0Oo
+0o0o0o0o0
+0o0o0o0o
+0o0o0o0oO
+0o0o0o0oo
+0o0o0o
+0o0o0oO00
+0o0o0oO0
+0o0o0oO0O
+0o0o0oO0o
+00o0oo00
+0o0o0oO
+0o0o0oOO0
+0o0o0oOO
+0o0o0oOOO
+0o0o0oOOo
+0o0o0oOo0
+0o0o0oOo
+0o0o0oOoO
+0o0o0oOoo
+00o0o0oo00
+0o0o0oo0
+0o0o0oo0O
+0o0o0oo0o
+0o0o0oo
+0o0o0ooO0
+0o0o0ooO
+0o0o0ooOO
+0o0o0ooOo
+0o0o0ooo0
+00o0o0ooo
+0o0o0oooO
+0o0o0oooo
+0o0o
+0o0oO0000
+0ooooooO
+0o0oO000
+0o0oO000O
+0o0oO000o
+0o0oO00
+00o0oO00O0
+0o0oO00O
+0o0oO00Oo
+0o0oO00OO
+0o0oO00o0
+0o0oO00o
+0o0oO00oO
+0o0oO00oo
+0o0oO0
+0o0oO0O0
+00o0oO0O
+0o0oO0OO0
+0o0oO0OO
+0o0oO0OOO
+0o0oO0Oo
+0o0oO0OoO
+0o0oO0o00
+0o0oO0o0
+0o0oO0o0o
+0o0oO0o0O
+00o0oO0o
+0o0oO0oO0
+0o0oO0oO
+0o0oO0oOO
+0o0oO0oo0
+0o0oO0oo
+0o0oO0ooO
+0o0oO0ooo
+0o0oO
+0o0oOO000
+00o0oOO00
+0o0oOO00O
+0o0oOO00o
+0o0oOO0
+0o0oOO0O0
+0o0oOO0O
+0o0oOo0oO
+0o0oOo0oo
+0o0oOO0OO
+0o0oOO0o0
+00o0oOO0o
+0o0oOO0oO
+0o0oOO0oo
+0o0oOOO00
+0o0oOOO0
+0o0oOOO0O
+0o0oOOO
+0o0oOOOO0
+0o0oOOOO
+0o0oOOOOO
+00o0oOOOOo
+0o0oOOOo0
+0o0oOOOo
+0o0oOOOoO
+0o0oOOo00
+0o0oOOo0
+0o0oOOo0O
+0o0oOOo0o
+0o0oOOoO0
+0o0oOOoO
+0o0oOoO00
+o0oOOoOO
+o0oOooOO
+o0oOOoOo
+o0oOOoo0
+o0oOOoo
+o0oOOooO
+o0oOOooo
+o0oOo00
+o0oOo00O
+0o0oOo00o
+o0oOo0
+o0oOo0O
+o0oOo0OO
+o0oOo0Oo
+o0oOo0o
+o0oOo
+o0oOoO0
+o0oOoO0o
+o0oOoOo0
+0o0oOoO0O
+o0oOoO
+o0oOoOO0
+o0oOoOO
+o0oOoOOO
+o0oOoOOo
+o0oOoOo
+o0oOoOoO
+o0oOoo0
+o0oOoo0O
+0o0oOoo0o
+o0oOoo
+o0oOooO
+o0oOooOo
+o0oOooo0
+o0oOooo
+o0oOoooO
+o0oOoooo
+o0oo0000
+o0oo000O
+0o0oo000o
+o0oo00O0
+o0oo00O
+o0oo00OO
+o0oo00Oo
+o0oo00o0
+o0oo00o
+o0oo00oO
+o0oo00oo
+o0oo0
+0o0oo0O00
+o0oo0O0
+o0oo0O0O
+o0oo0O0o
+o0oo0o
+o0oo0O
+o0oo0OO0
+o0oo0OO
+o0oo0OOO
+o0oo0OOo
+0o0oo0Oo0
+o0oo0Oo
+o0oo0OoO
+o0oo0Ooo
+o0oo0o00
+o0oo0o0
+o0oo0o0O
+o0oo0o0o
+o0oo0oO0
+o0oo0oO
+0o0oo0oOO
+o0oo0oOo
+o0oo0oo0
+o0oo0oo
+o0oo0ooO
+o0oo0ooo
+o0ooO000
+o0ooO00
+o0ooO00O
+o0ooO00o
+0o0ooO0
+o0ooO0O
+o0ooO0OO
+o0ooO0Oo
+o0ooO0o0
+o0ooO0o
+o0ooO0oO
+o0ooO0oo
+o0ooOO0O
+o0ooOO0o
+00o0ooOOO
+0o0ooOOoO
+0o0ooOo00
+0o0ooOo0
+0o0ooOo0o
+0o0ooOo0O
+0o0ooOo
+0o0ooOooO
+0o0ooOoO0
+0o0ooOoOo
+0o0ooOoo0
+o0ooOoo
+o0ooOooo
+o0ooo000
+o0ooo00O
+o0ooo00o
+o0ooo0
+o0ooo0O
+o0ooo0o0
+o0ooo0o
+0o0ooo0oO
+o0ooo0oo
+o0oooO00
+o0oooO0
+o0oooO0o
+o0oooO0O
+o0oooO
+o0oooOO0
+o0oooOO
+o0oooOOo
+0o0oooOOO
+o0oooOo0
+o0oooOo
+o0oooOoO
+o0oooo00
+o0oooo0
+o0oooo0O
+o0oooo0o
+o0oooo
+o0ooooO0
+0o0ooooO
+o0ooooOo
+o0ooooo0
+ooo0Oo0
+o0ooooo
+o0oooooO
+o0oooooo
+o
+oO00000
+oO00000o
+0oO0000
+oO0000O
+oO0000Oo
+oO0000o0
+oO0000o
+oO0000oO
+oO0000oo
+oO000
+oO000O0
+oO000O0O
+0oO0Ooo00
+oO0Ooooo
+oO000O0o
+oO0o0o
+oO000O
+oO0OoOO0
+oO0OOooo
+oO0OoOOO
+oO0OOoO0
+oO0Oo0oo
+0oO000OOO
+oO0OO0oo
+oO0OOooO
+oO000OOo
+oO0OoOoO
+ooOOOOoo
+oO0OOo0o
+oO000Oo0
+oO000Oo
+oO0OO0OO
+0oO0OoOoo
+oO0OOoo0
+oO000OoO
+oO0OOo0O
+oO000Ooo
+oO000o00
+oO000o0
+ooOOOOOo
+oO000o0o
+oO000o
+0oO0Oo0OO
+oO000oO0
+oO000oO
+oO0Ooo0O
+oO000oOO
+oO0Ooo0o
+oO000oOo
+oO000oo0
+oO000oo
+oO0Oo0Oo
+00oO000ooO
+0oO0Oo0O0
+0oO000ooo
+0oO00
+0oO00O00
+0oO0O0o0o
+0ooOOoooO
+0oO00O00o
+0oO00O0
+0oO00O0O
+0oO00O0Oo
+oO0O0OoO
+ooOOooOo
+oO00O0o0
+oO00O0o
+oOo00OO0
+oO00O0oO
+oO0O0OOo
+oO00O0oo
+oO00O
+0oO00OO0
+ooOOoOoO
+oO0O0OOO
+oO00OO0O
+oO00OO
+oO00OOO
+oO00OOOo
+oOo00Oo0
+ooOOoOOo
+oO00OOo0
+0oO00OOo
+oO0oOOO0
+oO00OOoO
+oOOoOoOO
+oO00OOoo
+oO00Oo00
+oO00Oo0
+oO00Oo0O
+oO00Oo0o
+oO00Oo
+0oO00oOOO
+oO00OoO0
+oO00OoO
+oOOoOOO0
+oO00OoOO
+oO0oOOOo
+oO00OoOo
+oO00Ooo0
+oO00Ooo
+oO00OooO
+0oO00Oooo
+oO0OOoOo
+oO00o000
+oO00o00
+oO0OooOO
+oO0OoOo0
+oO0OOO00
+oO00o00O
+oO0OOoOO
+oO00o00o
+0ooOO0O
+ooOOo0
+oO00o0
+oO0OOOoo
+oO00o0O0
+oO00o0O
+oO0OOOOo
+oO00o0OO
+oO0OOOoO
+oO00o0Oo
+0oO0OoO0O
+oO0OOO0o
+oO00o0o0
+oO00o0o
+oO0OOOo0
+oO00o0oO
+oO0OOO0O
+oO00o0oo
+oO00o
+oO0OO0oO
+0oO0OO000
+oOo00o0o
+oO00oO00
+oO00oO0
+oO0OoooO
+oOo00ooO
+oO00oO0O
+oO0OooOo
+oOo00ooo
+oO00oO0o
+0oO00oO
+oO00oOO0
+oO00oOO
+oO0OO00o
+oO00oOOo
+oOo00oO0
+oO00oOo0
+oO00oOo
+oO0OO00O
+ooOOOOoO
+00oOo00oOO
+0oO00oOoO
+0oOo00oOo
+0oO00oOoo
+0oO0OOOOO
+0oO00oo00
+0oO00oo0
+0oO0OO0o0
+0oO00oo0O
+0oO00oo0o
+0oO00oo
+oO00ooO0
+oO00ooO
+oOo000Oo
+oO00ooOO
+oO00ooOo
+oOo0000O
+oO00ooo0
+oO00ooo
+oOo000oo
+0oO00oooO
+oOo000o0
+oO00oooo
+oO0
+oO0O000
+oO0Oo00O
+ooOOoOo0
+oO0O000o
+oO0O00
+oO0O00O
+0oO0O00Oo
+oO0O00o0
+oO0O00o
+oO0O00oO
+oO0O00oo
+oOoo0
+oO0O0
+oO0O0O00
+oO0O0O0
+ooOOOoO0
+0oO0O0O0O
+ooOOOoo0
+oO0O0O0o
+oO0O0o
+oO0O0O
+oO0O0OO
+oOo0oooO
+ooOOOoOo
+oO0O0Oo0
+oO0O0Oo
+0oO0O0o0
+oO0O0o0O
+oO0O0oO0
+oO0O0oO
+oOo0o0oO
+ooOOO0Oo
+oO0O0oOO
+oO0O0oOo
+oOo00OOo
+oO0O0oo0
+0oO0O0oo
+oOo00OOO
+ooOOO00O
+oO0O0ooO
+oO0O0ooo
+ooo0o
+oO0O
+oO0OO00
+oO0OO0O
+oO0OO0Oo
+0oO0OO0o
+oO0OOO0
+oO0OOO
+oO0OooO0
+oO0OOOO0
+oO0OOOO
+oO0OOOo
+oO0OOo0
+oO0OOo
+oO0OOoO
+0oO0OOoo
+oO0Oo00
+oO0Oo0
+oO0Oo0O
+oO0Oo0o0
+oO0Oo0o
+oO0OoO00
+oO0OoO0
+oO0OoO
+oO0OoOO
+0oO0OoOo
+oO0Ooo0
+oO0Ooo
+oO0OooO
+oO0Oooo
+oO0o0000
+oO0o000
+oO0o000O
+ooOOoOoo
+oO0o000o
+00oO0o00
+0oO0o0oo0
+0oO0o00O0
+0oO0o00O
+0oO0o00OO
+0oO0o00Oo
+0oO0o00o0
+0oO0o00o
+0oO0o00oO
+0oO0o00oo
+0oO0o0
+oO0o0O00
+oO0o0O0
+oO0o0O0O
+oO0o0O0o
+oO0o0O
+oO0o0OO0
+oO0o0OO
+oO0o0OOO
+oO0o0OOo
+0oO0o0Oo0
+oO0o0Oo
+oO0o0OoO
+oO0o0Ooo
+oO0o0o00
+oO0o0o0
+oO0o0o0O
+oO0o0o0o
+oO0o0oO0
+oO0o0oO
+0oO0o0oOO
+oO0o0oOo
+oO0o0oo
+oO0o0ooO
+oO0o0ooo
+oO0o
+oO0oO000
+oO0oO00
+oOo00O0O
+ooOOoOO0
+0oO0oO00O
+oO0oO00o
+oO0oO0
+oO0oO0O0
+oO0oO0O
+oO0oO0OO
+oOo00OoO
+ooOOoOOO
+oO0oO0Oo
+oO0oO0o0
+0oO0oO0o
+oO0oO0oO
+oO0oO0oo
+oOooo
+oO0oO
+oO0oOoOO
+oO0oOO00
+oO0oOO0
+oO0oOO0O
+oO0oOO0o
+0oO0oOO
+oO0oOOO
+oO0oOOOO
+oO0oOOo0
+oO0oOOo
+oO0oOOoO
+oO0oOOoo
+oO0oOo00
+oO0oOo0
+oO0oOo0O
+0oO0oOo0o
+oO0oOo
+oO0oOoO0
+oO0oOoO
+oO0oOooo
+oO0oOoOo
+oO0oOoo0
+oO0oOoo
+oO0oOooO
+oO0oo000
+0oO0oo00
+oO0oo00O
+oO0oo00o
+oO0oo0
+oO0oo0O0
+oO0oo0O
+oO0oo0OO
+oO0oo0Oo
+oO0oo0o0
+oO0oo0o
+0oO0oo0oO
+oO0oo0oo
+oO0oo
+oO0ooO00
+oO0ooO0
+oO0ooO0O
+oO0ooO0o
+oO0ooO
+oO0ooOO0
+oO0ooOO
+00oO0ooOOO
+0oO0ooOOo
+0oO0ooOo0
+0oO0ooOo
+0oO0ooOoO
+0oO0ooOoo
+0oO0ooo00
+0oO0ooo0
+0oO0ooo0O
+0oO0ooo0o
+0oO0ooo
+oOo00o0O
+oO0oooO0
+oO0oooO
+oO0oooOO
+oOo00oo0
+oO0oooOo
+oOo000OO
+oO0oooo0
+oO0oooo
+0oO0ooooO
+oOo00o00
+oO0ooooo
+oO
+oOO00000
+oOO0000
+oOO0000O
+oOO0000o
+oOO000
+oOO000O0
+0oOO000O
+oOO000OO
+oOO000Oo
+oOO000o0
+oOO000o
+oOO000oO
+oOO000oo
+oOO00
+oOO00O00
+oOO00O0
+0ooooOO00
+oOO00O0O
+oOO00O0o
+oOO00OO0
+oOO00OO
+oOO00OOO
+oOO00OOo
+oOO00Oo0
+oOO00Oo
+ooooO000
+0oOO00OoO
+ooooOoOo
+oOO00Ooo
+oOO00o00
+oOO00o0
+oOO00ooo
+oOO00o0O
+oOO00o0o
+oOO00o
+oOO00oO0
+0oOO00oO
+ooooOOo0
+ooooO0oo
+oOO00oOO
+oOO00oOo
+oOO00oo
+oOO00ooO
+oOO0
+ooooOoOO
+oOO0O000
+0oOO0O00
+oOO0O00O
+oOO0O00o
+oOO0O0
+oOO0O0O0
+oOO0O0O
+oOO0O0OO
+oOO0O0Oo
+oOO0O0o0
+oOO0O0o
+0oOO0O0oO
+oOO0O0oo
+oOO0O
+oOO0OO0
+oOO0OO0O
+oOO0OO0o
+oOO0OO
+oOO0OOO
+oOO0OOOO
+oOO0OOOo
+0oOO0OOo0
+oOO0OOo
+oOO0OOoO
+oOO0OOoo
+oOO0Oo00
+oOO0Oo0
+oOO0Oo0O
+oOO0Oo0o
+oOO0Oo
+oOO0OoO0
+00oOO0OoO
+0oOO0OoOO
+0oOO0OoOo
+0oOO0Ooo0
+0oOO0Ooo
+0oOO0OooO
+0oOO0Oooo
+0oOO0o00
+0ooooOOoo
+0oOO0o00o
+0oOO0o0
+oOO0o0O0
+oOO0o0O
+oOO0o0OO
+oOO0o0Oo
+ooooO0O0
+oOO0o0o0
+oOO0o0o
+oOO0o0oO
+oOO0o0oo
+0oOO0o
+oOO0oO00
+oOO0oO0
+oOO0oO0O
+oOO0oO0o
+oOO0oO
+oOO0oOO0
+oOO0oOO
+oOO0oOOO
+oOO0oOOo
+0oOO0oOo0
+oOO0oOo
+oOO0oOoO
+oOO0oOoo
+oOO0oo00
+oOO0oo0
+oOO0oo0O
+oOO0oo0o
+oOO0oo
+oOO0ooO0
+0oOO0ooO
+oOO0ooOO
+oOO0ooOo
+oOO0ooo0
+oOO0ooo
+oOO0oooO
+oOO0oooo
+oOO
+oOOO000
+oOOO000o
+0oOOO0O
+oOOOoO
+oOOO00
+oOOO00O
+oOOO00Oo
+oOOO00o0
+oOOO00o
+oOOO00oO
+oOOO00oo
+oOOO0
+0oOOO0oOO
+oOOO0O0
+oOOO0O0o
+oOOO0OO0
+oOOO0OO
+oOOO0OOO
+oOOO0OOo
+oOOO0Oo
+oOOO0OoO
+oOOO0Ooo
+0oOOO0o00
+oOOO0o0
+oOOO0o0O
+oOOO0o0o
+oOOOOO
+oOOO0o
+oOOO0oO0
+oOOO0oO
+oOOO0ooo
+oOOO0ooO
+0oOOO0oOo
+oOOO0oo0
+oOOO0oo
+oOOO
+oOOOO0OO
+oOOOO000
+oOOOO00
+oOOOoo00
+oOOOO00O
+oOOOO00o
+0oOOOO0
+oOOOO0O0
+oOOOO0O
+oOOOO0Oo
+oOOOO0o0
+oOOOO0o
+oOOOO0oO
+oOOOO0oo
+oOo0o
+oOOOO
+00oOOOOO00
+0oOOOOO0
+0oOOOOO0O
+0oOOOOO0o
+0oOOOOOO0
+0oOOOOOO
+0oOOOOOOO
+0oOOOOOOo
+0oOOOOOo0
+0oOOOOOo
+0oOOOOOoO
+oOOOOOoo
+oOOOOo00
+oOOOOo0
+oOOOOo0O
+oOOOOo0o
+oOOOOo
+oOOOOoO0
+oOOOOoO
+oOOOOoOO
+0oOOOOoOo
+oOOOOoo0
+oOOOOoo
+oOOOOooO
+oOOOOooo
+oOOOo000
+oOOOo00
+oOOOo00O
+oOOOo00o
+oOOOo0
+0oOOOo0O0
+oOOOo0O
+oOOOo0OO
+oOOOo0Oo
+oOOOo0o0
+oOOOo0o
+oOOOo0oO
+oOOOo0oo
+oOOOo
+oOOOoO00
+0oOOOoO0
+oOOOoO0O
+oOOOoO0o
+oOOOoOO0
+oOOOoOO
+oOOOoOOO
+oOOOoOOo
+oOOOoOo0
+oOOOoOo
+oOOOoOoO
+0oOOOoOoo
+oOOOoo0
+oOOOoo0O
+oOOOoo0o
+oOOOoo
+oOOOooO0
+oOOOooO
+oOOOooOO
+oOOOooOo
+oOOOooo0
+0oOOOooo
+oOOOoooO
+oOOOoooo
+oOOo0000
+oOOo000
+oOOo000O
+oOOo000o
+oOOo00
+oOOo00O0
+oOOo00O
+0oOOo00OO
+oOOo00Oo
+oOOo00o0
+oOOo00o
+oOOo00oO
+oOOo00oo
+oOOo0
+oOOo0O00
+oOOo0O0
+oOOo0O0O
+0oOOo0O0o
+oOOo0O
+oOOo0OO0
+oOOo0OO
+oOOo0OOO
+oOOo0OOo
+oOOo0Oo0
+oOOo0Oo
+oOOo0OoO
+oOOo0Ooo
+0oOOo0o00
+oOOo0o0
+oOOo0o0O
+oOOo0o0o
+oOOo0o
+oOOo0oO0
+oOOo0oO
+oOOo0oOO
+oOOo0oOo
+oOOo0oo0
+00oOOo0oo
+0oOOo0ooO
+0oOOo0ooo
+0oOOo
+0oOOoO000
+0oOOoO00
+0oOOoO00O
+0oOOoO00o
+0oOOoO0
+0oooO0oOO
+0oOOoO0O0
+oOOoO0O
+oOOoO0OO
+oOOoO0Oo
+oOOoO0o
+oOOoO0oO
+oOOoO0oo
+oOOoO
+oOOoOO0o
+oOOoOO00
+0oOOoOO0
+oOOoOO0O
+oOOoOOO
+oOOoOOOO
+oOOoOOOo
+oOOoOOo0
+oOOoOOo
+oOOoOOoO
+oOOoOOoo
+oOOoOo00
+0oOOoOo0
+oOOoOo0O
+oOOoOo0o
+oOOoOo
+oOOoOoO0
+oOOoOoO
+oOOoOoOo
+oOOoOoo0
+oOOoOoo
+oOOoOooO
+0oOOoOooo
+oOOoo000
+oOOoo00
+oOOoo00O
+oOOoo00o
+oOOoo0
+oOOoo0O0
+oOOoo0O
+oOOoo0OO
+oOOoo0Oo
+0oOOoo0o0
+oOOoo0o
+oOOoo0oO
+oOOoo0oo
+oOOoo
+oOOooO00
+oOOooO0
+oOOooO0O
+oOOooO0o
+oOOooO
+0oOOooOO0
+oOOooOO
+oOOooOOO
+oOOooOOo
+oOOooOo0
+oOOooOo
+oOOooOoO
+oOOooOoo
+oOOooo00
+oOOooo0
+0oOOooo0O
+oOOooo0o
+oOOooo
+oOOoooO0
+oOOoooO
+oOOoooOO
+oOOoooOo
+oOOoooo0
+oOOoooo
+oOOooooO
+0oOOooooo
+oOo0000
+oOo000
+oOo000O
+oOo000o
+oOo00O0
+oOo00O
+oOo00OO
+oOo0o00
+oOo00Oo
+0oOo00o0
+oOo00o
+oOo00oO
+oOo00oo
+oOo0
+oOo0O000
+oOo0O00
+oOo0O00O
+oOo0oO0o
+oOo0O00o
+000oOo0oo
+00oOo0O0
+00oOo0O0O0
+00oOo0O0O
+00oOo0O0OO
+00oOo0O0Oo
+00oOo0O0o0
+00oOo0O0o
+00oOo0O0oO
+00oOo0O0oo
+00oOo0O
+0oOo0OO00
+0oOoo00o
+0oOo0OO0
+0oOo0OO0O
+0oOo0OO0o
+0oOo0OO
+0oOo0OOO0
+0oOo0OOO
+0oOo0OOOO
+00oOo0OOOo
+0oOo0OOo0
+0oOo0OOo
+0oOo0OOoO
+0oOo0OOoo
+0oOo0Oo00
+0oOo0Oo0
+0oOo0Oo0O
+0oOo0Oo0o
+0oOo0Oo
+00oOo0OoO0
+0oOo0OoO
+0oOo0OoOO
+0oOo0OoOo
+0oOo0Ooo0
+0oOo0Ooo
+0oOo0OooO
+0oOo0Oooo
+0oOo0o000
+0oOo0o00O
+00oOo0o00o
+0oOo0o0
+0oOo0o0O
+0oOo0o0OO
+0oOo0o0Oo
+0oOo0o0o0
+0oOo0o0o
+0oOo0oO00
+0oOo0oO0
+0oOo0oO0O
+00oOo0oO
+0oOo0oOO0
+0oOo0oOO
+0oOo0oOOO
+0oOo0oOOo
+0oOo0oOo0
+0oOo0oOo
+0oOo0oOoO
+0oOo0oOoo
+0oOo0oo00
+00oOo0oo0
+0oOo0oo0O
+0oOo0ooO0
+0oOo0ooO
+0oOo0ooOO
+0oOo0ooOo
+0oOo0ooo0
+0oOo0ooo
+0oOo
+0oOooOOoO
+00oOoO0000
+0oOoO000
+0oOoO000O
+0oOoO000o
+0oOoO00
+0oOoO00O0
+0oOoO00O
+0oOoO00OO
+0oOoO00Oo
+0oOoO00o0
+00oOoO00o
+0oOoO00oO
+0oOoO00oo
+0oOoO0
+0oOooOo0O
+0oOoO0O00
+0oOoO0O0
+0oOoO0O0O
+0oOoO0O0o
+0oOoO0o
+00oOoO0O
+0oOoO0OO0
+0oOoO0OO
+0oOoO0OOO
+0oOoO0OOo
+0oOoO0Oo0
+0oOoO0Oo
+0oOoO0OoO
+0oOoO0Ooo
+0oOooOo00
+00oOoO0o00
+0oOoO0o0
+0oOoO0o0O
+0oOoOoOOO
+0oOoO0o0o
+0oOoO0oO0
+0oOoO0oO
+0oOoO0oOO
+0oOoO0oOo
+0oOooOo0o
+0oOoO0oo0
+oOoO0oo
+oOoO0ooO
+oOoO0ooo
+oOoO
+oOoOO000
+oOoOO00
+oOoOO00O
+oOoOO00o
+oOoOO0
+0oOoOO0O0
+oOoOO0O
+oOoOOo0O
+oOoOO0OO
+oOoOO0Oo
+oOoOO0o0
+oOoOO0o
+oOoOO0oO
+oOooOOOO
+oOoOO0oo
+0oOoOO
+oOoOOO00
+oOoOOO0
+oOoOOO0O
+oOoOOO0o
+oOoOOO
+oOoOOOO0
+oOoOOOO
+oOoOOOOO
+oOoOOOOo
+0oOoOOOo0
+oOoOOOo
+oOoOOOoO
+oOooOooO
+oOoOOOoo
+oOoOOo00
+oOoOOo0
+oOoOOo0o
+oOoOOo
+oOoOOoO0
+0oOoOOoO
+oOoOOoOO
+oOoOOoOo
+oOoOOoo0
+oOoOOoo
+oOoOOooO
+oOoOOooo
+oOoOo000
+oOoOo00
+oOoOo00O
+0oOoOo00o
+oOoOo0
+oOoOoo0O
+oOoOo0O0
+oOoOo0O
+ooOOO0oo
+oOooo0Oo
+oOooo0oo
+oOoOo0OO
+ooOOO0o0
+0oOoOo0Oo
+oOoOo0o0
+oOoOo0o
+oOoOo0oO
+oOoOo0oo
+oOoOo
+ooOOOooo
+oOoOoO00
+oOoOoO0
+oOoOoO0O
+0ooOOOooO
+oOooooOO
+oOoOoO0o
+oOoOoO
+oOoOoOO0
+oOoOoOO
+oOoOoOOo
+oOoOoOo0
+oOoOoOo
+oOooOOoo
+0oOoOoOoO
+oOoOoOoo
+ooOOO0oO
+oOooo0oO
+oOoOoo00
+oOoOoo0
+ooOOO0O0
+oOooo0O0
+oOoOoo0o
+oOoOoo
+00ooOOOo0o
+0oOoooooo
+0oOoOooO0
+0oOoOooO
+0oOoOooOO
+0ooOOOo0O
+0oOoooooO
+0oOoOooOo
+0oOoOooo0
+0oOoOooo
+0ooOOOo00
+oOooooo0
+oOoOoooO
+oOoOoooo
+oOoo0000
+oOoo000
+oOoo000O
+oOoo000o
+oOoo00
+oOoo00O0
+0oOoo00O
+oOoo00OO
+oOoo00Oo
+oOoo00o0
+oOoo00oO
+oOoo00oo
+oOoo0Oo0
+oOoo0O00
+oOoo0O0
+oOoo0O0O
+0oOoo0O0o
+oOoo0O
+oOoo0OO0
+oOoo0oO
+oOooo00
+oOoo0OO
+oOoo0OOO
+oOoo0OOo
+oOoo0Oo
+oOoo0OoO
+0oOoo0Ooo
+oOoo0o00
+oOoo0o0
+oOoo0o0O
+oOoo0o0o
+oOoo0o
+oOoo0oO0
+oOoo0oOO
+oOoo0oOo
+oOoo0oo0
+0oOoo0oo
+oOoo0ooO
+oOoo0ooo
+oOoo
+oOooO000
+oOooO00
+oOooO00O
+oOooO00o
+oOooO0
+oOooO0O0
+0oOooO0O
+oOooO0OO
+oOooO0Oo
+oOooO0o0
+oOooO0o
+oOooO0oO
+oOooO0oo
+oOooO
+oOooOO00
+oOooOO0
+0oOooOOo0
+oOooOO0O
+oOooOO0o
+oOooOO
+oOooOOO0
+oOooOOO
+oOooOOOo
+oOooOOo
+oOooOo0
+oOooOo
+0oOooOoO0
+oOooOoO
+oOooOoOO
+oOooOoOo
+oOooOoo0
+oOooOoo
+oOooOooo
+oOooo000
+oOooo00O
+oOooo00o
+0oOoooO
+oOooo0
+oOooo0O
+oOooo0OO
+oOooo0o0
+oOoooO00
+oOoooO0
+oOoooO0O
+oOoooO0o
+oOoooOO0
+00oOoooOO
+0oOoooOOO
+0oOoooOOo
+0oOoooOo0
+0oOoooOo
+0oOoooOoO
+0oOoooOoo
+0oOoooo00
+0oOoooo0
+0oOoooo0O
+0oOoooo0o
+oOoooo
+oOooooO0
+oOooooO
+oOooooOo
+oOooooo
+oo000000
+oo00000
+oo00000O
+oo00000o
+0oo0000
+oo0000O0
+oo0000O
+oo0000OO
+oo0000Oo
+oo0000o0
+oo0000o
+oo0000oO
+oo0000oo
+oo000
+0oo000O00
+oo000O0
+oo000O0O
+oo000O0o
+oo000O
+oo000OO0
+oo0OOOO
+oo000OO
+oo000OOO
+oo000OOo
+0oo000Oo0
+oo000Oo
+oo000OoO
+oo000Ooo
+oo000o00
+oo000o0
+oo000o0O
+oo000o0o
+oo000oO0
+oo000oO
+0oo000oOO
+oo000oOo
+oo000oo0
+oo000oo
+oo000ooO
+oo00
+oo00O00o
+oo00O000
+oo00O00
+oo00O00O
+0oo00O0
+oo00O0O0
+oo00O0O
+oo00Oo0O
+oo00O0OO
+oo00O0Oo
+oo00O0o0
+oo00O0o
+oo00O0oO
+oo00O0oo
+0oo00O
+oo00OO00
+oo00OO0
+oo00OO0O
+oo00OO0o
+oo00OO
+oo00OOO0
+oo00OOO
+oo00OOOO
+oo00OOOo
+0oo00OOo0
+oo00OOo
+oo00OOoO
+oo00OOoo
+oo00Oo00
+oo00Oo0
+oo00Oo0o
+oo00Oo
+oo00OoO0
+oo00OoO
+0oo00OoOO
+oo00OoOo
+oo00Ooo0
+oo00Ooo
+oo00OooO
+oo00Oooo
+oo00o000
+oo00o00
+oo00o0o0
+oo00o00O
+00oo00o00o
+0oo00o0
+0oo00o0oo
+0oo00ooOO
+0oo00o0O0
+0oo00o0O
+0oo00o0OO
+0oo00o0Oo
+0oo00o0o
+0oo00o0oO
+0oo00oO00
+oo00oO0
+oo00oO0O
+oo00oO0o
+oo00oOoO
+oo00oOO0
+oo00oOo
+oo00oOO
+oo00oOOO
+oo00oOOo
+0oo00oOo0
+oo00oOoo
+oo00oo00
+oo00oo0
+oo00oo0O
+oo00oo0o
+oo00oo
+oo00ooO0
+oo00ooO
+oo00ooOo
+0oo00ooo0
+oo00ooo
+oo00oooO
+oo00oooo
+oo0
+oo0O0000
+oo0O000
+oo0O000O
+oo0O000o
+oo0O00
+0oo0oOOOO
+oo0O00O0
+oo0O00O
+oo0O00OO
+oo0O00Oo
+oo0O00o0
+oo0O00o
+oo0O00oO
+oo0O00oo
+oo0O0
+0oo0O0O00
+oo0O0O0
+oo0O0O0O
+oo0O0O0o
+oo0O0O
+oo0O0OO0
+oo0O0OO
+oo0O0OOO
+oo0O0OOo
+oo0O0Oo0
+0oo0O0Oo
+oo0O0OoO
+oo0O0Ooo
+oo0O0o00
+oo0O0o0
+oo0O0o0O
+oo0O0o0o
+oo0O0o
+oo0O0oO0
+ooo0OoO
+0oo0O0oO
+oo0oOOoO
+oo0O0oOO
+oo0O0oOo
+oo0O0oo0
+oo0O0oo
+oo0O0ooO
+oo0O0ooo
+oo0OoOO0
+oo0OO00o
+0oo0OO00O
+oo0OO000
+oo0OO00
+oo0Oo0
+oo0Ooo
+oo0OoO
+oo0OO0
+oo0OOoOO
+oo0OO0Oo
+oo0OOo0O
+0oo0OOooO
+oo0OO0O0
+oo0OO0O
+oo0OO0OO
+oo0OO0o0
+oo0OO0o
+oo0OO0oO
+oo0OO0oo
+oo0OO
+oo0OOO00
+00oo0oOoo
+0oo0OOO0
+0oo0OOO0O
+0ooo000O0
+0oo0OOO0o
+0oo0OOO
+0ooo000o0
+0oo0OOOO0
+0oo0OOOOO
+0oo0OOOOo
+0oo0OOOo0
+oo0OOOo
+oo0OOOoO
+ooo0000O
+ooo00000
+oo0OOOoo
+oo0OOo00
+oo0OOo0
+oo0OOo0o
+oo0OOo
+0oo0OOoO0
+oo0OOoO
+oo0OOoOo
+oo0OOoo0
+oo0OOooo
+oo0Oo000
+oo0Oo00
+oo0Oo00O
+oo0Oo00o
+oo0Oo0O0
+0oo0Oo0O
+oo0Oo0OO
+oo0Oo0Oo
+oo0Oo0o0
+oo0Oo0o
+oo0Oo0oO
+oo0Oo0oo
+oo0Oo
+oo0OoO00
+oo0OoO0
+0oo0OoO0O
+oo0OoO0o
+oo0OoOO
+oo0OoOOO
+oo0OoOOo
+oo0OoOo0
+oo0OoOo
+oo0OoOoO
+oo0OoOoo
+oo0Ooo00
+0oo0Ooo0
+oo0Ooo0O
+oo0Ooo0o
+oo0OooO
+oo0OooOO
+oo0OooOo
+oo0Oooo0
+oo0Oooo
+oo0OoooO
+0oo0Ooooo
+oo0o0000
+oo0o000
+oo0o000O
+oo0o000o
+oo0o00
+oo0o00O0
+oo0o00o
+oo0o00O
+oo0o00Oo
+0oo0o00oO
+oo0o00OO
+oo0o00o0
+oo0o00oo
+oo0o0
+oo0o0O00
+oo0o0O0o
+oo0o0o
+oo0o0O
+oo0o0OO0
+0oo0o0OO
+oo0o0OOO
+oo0o0OOo
+oo0o0Oo0
+oo0o0OoO
+oo0o0Ooo
+oo0o0o00
+oo0o0o0
+oo0o0o0O
+oo0o0o0o
+0oo0o0oO0
+oo0o0oO
+oo0o0oOO
+oo0o0oOo
+oo0o0oo0
+oo0o0oo
+oo0o0ooO
+oo0o0ooo
+oo0o
+oo0oO000
+00oo0oO00
+0oo0oO0oO
+0oo0oO00O
+0oo0oO00o
+0oo0oO0O0
+0oo0oO0O
+0oo0oO0OO
+0oo0oO0Oo
+0oo0oO0o0
+0oo0oO0o
+0oo0oO0oo
+oo0oO
+oo0oOO00
+oo0oOO0O
+oo0oOO0o
+oo0oOO
+oo0oOOO0
+oo0oOOO
+oo0oOOOo
+oo0oOOo0
+0oo0oOOoo
+oo0oOo00
+oo0oOo0
+oo0oOo0O
+oo0oOo0o
+oo0oOo
+oo0oOoO0
+oo0oOoOO
+oo0oOoOo
+oo0oOoo0
+0oo0oOooO
+oo0oOooo
+oo0oo000
+ooo0oOO
+oooOooO
+oo0oo00
+oo0oo00O
+oo0oo00o
+oo0oo0
+oo0oo0O0
+0oo0oo0O
+oo0oo0OO
+oo0oo0Oo
+oo0oo0o0
+oo0oo0o
+oo0oo0oO
+oo0oo0oo
+oo0oo
+oo0ooO00
+oo0ooO0
+0oo0ooO0O
+oo0ooO0o
+oo0ooOO0
+oo0ooOO
+ooo0O0oo
+ooo0oooO
+oo0ooOOO
+oo0ooOOo
+oo0ooOo0
+oo0ooOo
+0oo0ooOoO
+oo0ooOoo
+oo0ooo00
+oo0ooo0
+oo0ooo0o
+oo0ooo0O
+oo0ooo
+oo0oooOO
+oo0oooO0
+oo0oooO
+0oo0oooOo
+oo0oooo0
+oo0oooo
+oo0ooooO
+oo0ooooo
+oo
+ooO00000
+oooooOo
+ooO0000
+ooO0000O
+0ooOooOOO
+ooO0000o
+ooO0oo
+ooO000
+ooO000O0
+ooO000O
+ooO000OO
+ooO000Oo
+ooO000o0
+ooO000o
+0ooO000oO
+ooO000oo
+ooO00
+ooO0OOoo
+ooOo000o
+ooOo00O0
+ooO00O00
+ooO00O0O
+ooO0OOoO
+ooO00O0o
+00ooO00O
+0ooOo00o0
+0ooO00OO0
+0ooO00OO
+0ooO0OOOO
+0ooO00OOO
+0ooO00OOo
+0ooO00Oo0
+0ooO00Oo
+0ooO00OoO
+0ooOo00OO
+ooO00Ooo
+ooO00o00
+ooO00o0
+ooO00o0O
+ooO00o0o
+ooO00o
+ooO00oO0
+ooO00oO
+ooO00oOO
+0ooO00oOo
+ooO00oo0
+ooO00oo
+ooO00ooO
+ooO00ooo
+ooO0
+ooO0O000
+oooo000
+ooO0O00
+ooO0O00O
+0ooO0O00o
+ooO0O0
+ooO0O0O0
+ooO0O0O
+ooO0O0OO
+ooO0O0Oo
+ooO0O0o0
+ooO0OOO
+ooO0O0o
+ooO0O0oO
+0ooO0O0oo
+ooO0O
+ooOo000O
+ooO0OO00
+ooO0OO0
+ooO0OO0O
+ooO0OO0o
+ooO0OO
+ooO0OOO0
+ooO0OOOo
+0ooO0OOo0
+ooO0OOo
+ooO0Oo00
+ooo0oOo
+ooO0Oo0
+ooO0Oo0O
+ooO0Oo0o
+ooO0Oo
+ooO0OoO0
+ooO0OoO
+0ooO0OoOO
+ooO0OoOo
+ooO0Ooo0
+ooO0OooO
+ooO0Oooo
+ooO0o000
+ooO0o00
+ooO0o00O
+ooO0o00o
+ooO0o0
+0ooO0o0O0
+ooO0o0O
+ooO0o0OO
+ooO0o0Oo
+ooO0o0o0
+ooO0o0o
+ooO0o0oO
+ooO0o0oo
+ooO0o
+ooO0oO00
+0ooO0oO0
+ooO0oO0O
+ooOo00Oo
+ooO0oO0o
+ooO0oO
+ooO0oOO0
+ooO0oOO
+ooO0oOOO
+ooO0oOOo
+ooO0oOo0
+0ooo0OOo
+ooO0oOo
+ooO0oOoO
+ooO0oOoo
+ooO0oo00
+ooO0oo0
+ooO0oo0O
+ooO0oo0o
+ooO0ooO0
+ooO0ooO
+00ooO0ooOO
+0ooO0ooOo
+0ooO0ooo0
+0ooO0ooo
+0ooO0oooO
+0ooO0oooo
+0ooo
+0ooO
+0ooOO0000
+0ooOO000
+0ooOO000O
+ooOO000o
+ooOO00
+ooOO00O0
+ooOO00O
+ooOO00OO
+ooOO00Oo
+ooOO00o0
+ooOO00o
+ooOO00oO
+0ooOO00oo
+ooOO0
+ooOO0O00
+ooOO0O0
+ooOO0O0O
+ooOO0O0o
+ooOO0OO0
+ooOO0OO
+ooOO0OOO
+ooOOo0OO
+0ooOO0OOo
+ooOO0Oo0
+ooOO0Oo
+ooOO0OoO
+ooOO0Ooo
+ooOO0o00
+ooOO0o0
+ooOO0o0O
+ooOO0o0o
+ooOO0o
+0ooOO0oO0
+ooOO0oO
+ooOO0oOO
+ooOO0oOo
+ooOO0oo0
+ooOO0oo
+ooOO0ooO
+ooOO0ooo
+ooOOO00
+ooOOoO
+0ooOOO0
+ooOOO0O
+ooOOO0o
+ooOOO
+ooOOOO00
+ooOOOO0
+ooOOOO0O
+ooOOOO0o
+ooOOOO
+ooOOOOO0
+0ooOOOOO
+ooOOOOOO
+ooOOOOo
+ooOOOo0
+ooOOOo
+ooOOOoO
+ooOOOoo
+ooOOo000
+ooOOo00O
+ooOOo00o
+0ooOOo0O0
+ooOOo0O
+ooOOo0Oo
+ooOOo0o0
+ooOOo0o
+ooOOo0oO
+ooOOo0oo
+ooOOo
+ooOOoO0
+ooOOoO0O
+0ooOOoO0o
+ooOOoOo
+ooOOoo00
+ooOOoo0
+ooOOoo0O
+ooOOoo0o
+ooOOoo
+ooOOooO0
+ooOOooO
+ooOOooOO
+0ooOOooo0
+ooOOooo
+ooOOoooo
+ooOo0000
+ooOo000
+ooOo00
+ooOo00oO
+ooOo00oo
+ooOo0
+ooOo0O00
+00ooOo0o0
+0ooOo0O0
+0ooOo0oOo
+0ooOo0O0O
+0ooOo0O0o
+0ooOo0O
+0ooOo0OO0
+0ooOo0OO
+0ooOoOo0o
+0ooOooOOo
+0ooOo0OOO
+ooOo0OOo
+ooOo0Oo0
+ooOo0Oo
+ooOo0OoO
+ooOo0Ooo
+ooOo0o00
+ooOo0o0O
+ooOo0o0o
+ooOo0o
+0ooOo0oO0
+ooOo0oO
+ooOo0oOO
+ooOo0oo0
+ooOo0oo
+ooOo0ooO
+ooOo0ooo
+ooOoO000
+ooOoO00
+ooOoO00O
+0ooOoO00o
+ooOoO0
+ooOoO0O0
+ooOoO0O
+ooOoO0OO
+ooOoO0Oo
+ooOoO0o0
+ooOoO0o
+ooOoO0oO
+ooOoO0oo
+0ooOoO
+ooOoOoOO
+ooOoOO00
+ooOoOO0
+ooOoOOOo
+ooOoOO0O
+ooOoOO0o
+ooOoOO
+ooOoOOO0
+ooOoOOO
+0ooOoOOOO
+ooOoOOo0
+ooOoOOo
+ooOoOOoO
+ooOoOOoo
+ooOoOo00
+ooOoOo0
+ooOoOo0O
+ooOoOo
+ooOoOoO0
+0ooOoOoO
+ooOoOoOo
+ooOoOoo0
+ooOoOoo
+ooOoOooO
+ooOoOooo
+ooOoo000
+ooOoo00
+ooOooo0o
+ooOoo00O
+0ooOoo00o
+ooOoo0
+ooOoo0O0
+ooOoo0O
+ooOoo0OO
+ooOoo0Oo
+ooOoo0o0
+ooOoo0o
+ooOoo0oo
+ooOoo0oO
+0ooooO
+ooOoo
+ooOooO00
+ooOooO0
+ooOooO0O
+ooOooO0o
+ooOooo
+ooOooO
+ooOooOO0
+ooOooOO
+0ooOooOo0
+ooOooOo
+ooOooOoO
+ooOooOoo
+ooOooo00
+ooOooo0
+ooOooo0O
+ooOoooO0
+ooOoooO
+ooOoooOO
+000ooOoooOo
+00ooOoooo0
+00ooOoooo
+00ooOooooO
+00ooOooooo
+00ooo0000
+00ooo000oo
+00ooo0000o
+00ooo000
+00ooo000O
+00ooo000OO
+0ooo000Oo
+0ooo000o
+0ooo000oO
+0ooo00
+0ooo00O00
+0ooo00oO0
+0ooo00O0O
+0ooo00O0o
+0ooo00O
+00ooo00Oo0
+0ooo00OO0
+0ooo00OO
+0ooo00OOO
+0ooo00OOo
+0ooo00Oo
+0ooo00OoO
+0ooo00Ooo
+0ooo00o00
+0ooo00o0
+00ooo00o0O
+0ooo00o0o
+0ooo00o
+0ooo00oO
+0ooo00oOO
+0ooo00oOo
+0ooo00oo0
+0ooo00oo
+0ooo00ooO
+0ooo00ooo
+00ooo0
+0ooo0O000
+0ooo0O00
+0ooo0O00O
+0ooo0O00o
+0ooo0oo
+0ooo0o0
+0ooo0oO
+0ooo0O0
+0ooo0O0O0
+00ooo0O0O
+0ooo0O0OO
+0ooo0O0Oo
+0ooo0O0o0
+0ooo0O0o
+0ooo0O0oO
+0ooo0O
+0ooo0OO00
+0ooo0OO0
+0ooo0OO0O
+00ooo0OO0o
+0ooo0Oo
+0ooo0OO
+0ooo0OOO0
+0ooo0OOO
+0oooo00oo
+0ooo0OOoO
+0ooo0OOOO
+0ooo0OOOo
+0ooo0OOo0
+00ooo0OOoo
+0ooo0Oo00
+0ooo0Oo0O
+0ooo0Oo0o
+0ooo0OoO0
+0ooo0OoOO
+0ooo0OoOo
+0ooo0Ooo0
+0ooo0OooO
+0ooo0Oooo
+00ooo0o000
+0ooo0o00
+0ooo0o00O
+0ooo0o00o
+0ooo0o0OO
+0ooo0o0O0
+0ooo0o0O
+0ooo0o0Oo
+0ooo0o0o0
+0ooo0o0o
+00ooo0o0oO
+0ooo0o0oo
+0ooo0oO00
+0ooo0oO0
+0ooo0oO0O
+0ooo0oO0o
+0ooo0oOO0
+0ooo0oOOO
+0ooo0oOOo
+0ooo0oOo0
+00ooo0oOoO
+0ooo0oOoo
+0ooo0oo00
+0ooo0oo0
+0ooo0oo0O
+0ooo0oo0o
+0ooo0ooO0
+0ooo0ooO
+0ooo0ooOO
+0ooo0ooOo
+0ooo0ooo0
+ooo0oooo
+oooO0oo0
+oooO0000
+oooO000
+oooO00o0
+oooO000O
+oooO0ooo
+oooO000o
+ooooo0
+0oooooo
+ooooOo
+oooO00
+oooO00O0
+oooO00O
+oooO00Oo
+oooO00OO
+oooO00o
+ooooOOOo
+ooooOOOO
+0ooooOo00
+oooO0oO0
+oooO00oO
+oooO00oo
+oooO0
+oooO0O00
+oooO0O0
+oooO0O0O
+oooO0O0o
+oooO0O
+0oooOOo0O
+oooO0OO0
+oooO0OO
+oooO0OOO
+oooO0OOo
+oooO0Oo0
+oooO0Oo
+oooO0OoO
+oooO0Ooo
+oooO0o00
+0oooO0o0
+oooO0o0O
+oooO0o0o
+oooooO
+oooO0o
+oooO0oO
+oooO0oo
+oooO0ooO
+oooO
+oooOooOO
+0oooOO000
+oooOO00
+oooOO0o0
+oooOO00O
+oooOO00o
+oooOO0O0
+oooOO0O
+oooOO0OO
+oooOO0Oo
+oooOO0o
+0oooOO0oO
+oooOO0oo
+oooOO
+oooOOO00
+oooOOO0
+oooOOO0O
+oooOOO0o
+oooOOO
+oooOOOO0
+oooOOOO
+0oooOOOOO
+oooOOOOo
+oooOOOo0
+oooOOOo
+oooOOOoO
+oooOOOoo
+oooOOo00
+oooOOo0
+oooOOo0o
+oooOOo
+0oooOOoO0
+oooOOoO
+oooOOoOO
+oooOOoOo
+oooOOoo0
+oooOOoo
+oooOOooO
+oooOOooo
+oooOo000
+oooOo00
+00oooOo00O
+0oooOo00o
+0oooOo0
+0oooOo0oo
+0oooOo0O0
+0oooOo0O
+0oooOo0OO
+0oooOo0Oo
+0oooOo0o0
+0oooOo0o
+0oooOo0oO
+oooOo
+oooOoO00
+oooOoO0
+oooOoO0O
+oooOoO0o
+oooOoOOO
+oooOoOO0
+oooOoOO
+oooOoOOo
+0oooOoOo0
+oooOoOo
+oooOoOoO
+oooOoOoo
+oooOoo00
+oooOoo0
+oooOoo0O
+oooOoo0o
+oooOooO0
+oooOooOo
+0oooOooo0
+oooOooo
+oooOoooO
+oooOoooo
+oooo0000
+oooo000O
+oooo000o
+oooo00
+oooo00o0
+oooo00O0
+0oooo00O
+oooo00OO
+oooo00Oo
+oooo00oO
+oooo0
+oooo0O00
+oooo0O0
+oooo0O0O
+oooo0O0o
+oooo0O
+0oooo0OO0
+oooo0OO
+oooo0OOO
+oooo0OOo
+oooo0Oo0
+oooo0Oo
+oooo0OoO
+oooo0Ooo
+oooo0o00
+oooo0o0
+0oooo0o0O
+oooo0o0o
+oooo0o
+oooo0oO0
+oooo0oO
+oooo0oOO
+oooo0oOo
+oooo0oo0
+oooo0oo
+oooo0ooO
+0oooo0ooo
+oooo
+ooooO00
+ooooOOoO
+ooooO00O
+ooooO00o
+ooooO0
+ooooO0O
+ooooO0OO
+ooooO0Oo
+0ooooO0o0
+ooooO0o
+ooooO0oO
+ooooOO0
+ooooOO0o
+ooooOO
+ooooOoo0
+ooooOOO0
+ooooOOO
+ooooOOo
+0ooooOo0
+ooooOo0O
+ooooOo0o
+ooooOoO0
+ooooOoO
+ooooOoo
+ooooOooO
+ooooOooo
+ooooo000
+ooooo00
+00ooooo00O
+0ooooo00o
+0ooooo0O0
+0ooooo0O
+0ooooo0OO
+0ooooo0Oo
+0ooooo0o0
+0ooooo0o
+0ooooo0oO
+0ooooo0oo
+0ooooo
+oooooO00
+oooooO0
+oooooOo0
+oooooO0O
+oooooO0o
+oooooOO0
+oooooOO
+oooooOOO
+oooooOOo
+0oooooOoO
+oooooOoo
+oooooo00
+oooooo0
+oooooo0O
+oooooo0o
+ooooooO0
+ooooooOO
+ooooooOo
+ooooooo0
+0oooooooO
+oooooooo
+O
+O0
+O00
+O000
+O0000
+O00000
+O000000
+O0000000
+O000000O
+O00000O
+O00000O0
+O00000OO
+O0000O
+O0000O0
+O0000O00
+O0000O0O
+O0000OO
+O0000OO0
+O0000OOO
+O000O
+O000O0
+O000O00
+O000O000
+O000O00O
+O000O0O
+O000O0O0
+O000o0oO
+O000Oo
+O000OO0
+O000oo00
+O000OO0O
+O000OOO
+O000OOO0
+O000OOOO
+O00O
+O00O0
+O00O00
+O00O000
+O00O0000
+O00O000O
+O00O00O
+O00O00O0
+O00O00OO
+O00O0O
+O00O0O0
+O00O0O00
+O00O0O0O
+O00O0oO
+O00O0OO0
+O00O0OOO
+O00OO
+O00OO0
+O00OO00
+O00Oo000
+O00Oo00O
+O00Oo0O
+O00Oo0O0
+O00oo0oO
+O00OOO
+O00OOO0
+O00OoO00
+O00ooo0O
+O00OOOO
+O00oOoO0
+O00ooooO
+O0O
+O0O0
+O0O00
+O0O000
+O0O0000
+o0o00000
+o0o0000O
+O0O000O
+o0O000O0
+o0O000OO
+O0O00O
+O0O00O0
+o0O00O00
+o0O00O0O
+O0O00oo
+o0O00OO0
+o0O00OOo
+O0o0o
+O0O0O0
+O0O0O00
+o0O0O000
+o0O0O00o
+O0O0O0O
+o0O0O0O0
+o0O0O0OO
+O0O0Oo
+O0o0oo0
+o0O0OO00
+o0O0OO0o
+O0o0ooo
+o0O0OOo0
+o0O0OOOO
+O0OO
+O0OO0
+O0OO00
+O0OO000
+o0OO0000
+o0OO000O
+o0Oo00O
+o0OO00O0
+o0OO00oO
+O0OO0o
+O0OO0O0
+o0OO0O00
+o0oO0O0O
+O0OO0OO
+o0oO0Oo0
+o0oO0oOo
+O0OOO
+O0OOo0
+o0OoO00
+o0OOO000
+o0OoO00o
+O0OoO0o
+o0ooo0O0
+o0ooo0Oo
+O0oOOO
+O0OOOO0
+o0ooOO00
+o0OOOO0O
+O0oOOOO
+o0ooOOo0
+o0ooooOO
+OO
+OO0
+OO00
+OO000
+Oo0000
+OO00000
+oO000000
+oO00000O
+OO0000O
+oO0000O0
+oO0000OO
+OO000O
+OO000O0
+oO000O00
+oO000o0O
+oO000OO
+oO000OO0
+oo000ooo
+OO00o
+OO00o0
+OO00O00
+oO00O000
+oO00O00O
+OO00O0O
+oO00O0O0
+oO00O0OO
+Oo00oO
+OO00OO0
+oO00OO00
+oO00OO0o
+Oo00oOo
+oO00OOO0
+oO00OOOO
+OO0O
+OO0O0
+OO0O00
+OO0O000
+oO0O0000
+oO0O000O
+OO0O00O
+oO0O00O0
+oO0O00OO
+OO0o0O
+OO0O0O0
+oO0O0o00
+oo0o0O0O
+Oo0o0OO
+oO0O0OO0
+oO0O0Ooo
+OO0Oo
+OO0OO0
+OO0OO00
+oO0Oo000
+oO0Oo00o
+OO0OO0O
+oO0OO0O0
+oO0Oo0oO
+Oo0OOO
+OO0OOO0
+oO0OOo00
+oO0OoO0o
+Oo0OoOO
+oO0Oooo0
+oO0OoOOo
+OOo
+OOO0
+OoO00
+OOO000
+OOO0000
+oOo00000
+oOo0000o
+OOO000O
+oOo000O0
+oOo000oO
+OOO00O
+ooo00O0
+oOo00O00
+oOo00O0o
+OOO00OO
+oOO00oo0
+oOo00Ooo
+OOo0O
+OOO0O0
+OOO0O00
+oOO0o000
+oOO0o00O
+Ooo0o0O
+oOo0o0O0
+oOo0o0oo
+OOO0OO
+Ooo0Oo0
+oOO0OO00
+oOo0oo0o
+ooo0ooo
+oOO0OOO0
+oOo0oooo
+OOOO
+OooO0
+OOOO00
+OOOO000
+oOOO0000
+oOOO000O
+ooOo00o
+oOOO00O0
+oOOO00OO
+OOOO0o
+OOOO0O0
+oOOO0O00
+oOOO0O0O
+Oooo0Oo
+oOOO0Oo0
+oooO0oOo
+Ooooo
+OOoOO0
+OoOOO00
+ooOOO000
+ooOOO00o
+OOOOO0O
+oOOoO0o0
+ooOOO0OO
+oooOoo
+OOOOOO0
+ooOOoO00
+ooooOO0O
+ooOOoOO
+ooOOOOo0
+ooOOOoOO

+ 453 - 0
app/build.gradle

@@ -0,0 +1,453 @@
+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'
+}
+
+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"
+
+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.wenext.wayak"
+        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"
+        buildConfigField("boolean", "OFFICIAL", project.OFFICIAL)
+        buildConfigField("boolean", "IS_RELEASE", hookConfigByLocalProperties("IS_RELEASE", IS_RELEASE))
+        buildConfigField("String", "HTTPS_WEB_HOST", '"https://web.wenext.chat"')
+        buildConfigField("String", "AUTH_APPLE_PATH", '"/web/wyak-auth/apple"')
+        ndk {
+            abiFilters "armeabi-v7a"
+            abiFilters "arm64-v8a"
+            debugSymbolLevel 'FULL'
+        }
+        //指定room.schemaLocation生成的文件路径  处理Room 警告 Schema export Error
+        javaCompileOptions {
+            annotationProcessorOptions {
+                arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
+            }
+        }
+    }
+
+    signingConfigs {
+        debug {
+            storeFile file('../keystore/debug/debug.keystore')
+        }
+        release {
+            keyAlias "wayak-upload"
+            keyPassword "wayak654321"
+            storeFile file("../keystore/wayak/upload-keystore.jks")
+            storePassword "wayak654321"
+        }
+    }
+
+    flavorDimensions "product"
+
+    productFlavors {
+        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
+        def jvcl = project.hasProperty('JKS_VERSION_CODE_LITE') ? project.JKS_VERSION_CODE_LITE : ""
+        def jvnl = project.hasProperty('JKS_VERSION_NAME_LITE') ? project.JKS_VERSION_NAME_LITE : ""
+        def localVCL = hookConfigByLocalProperties("VERSION_CODE_LITE", VERSION_CODE_LITE)
+        def localVNL = hookConfigByLocalProperties("VERSION_NAME_LITE", VERSION_NAME_LITE)
+        def vcl = jvcl != "" ? project.JKS_VERSION_CODE_LITE.toInteger() : localVCL.toInteger()
+        def vnl = jvnl != "" ? project.JKS_VERSION_NAME_LITE : localVNL
+        println("version_code = " + vc)
+        println("version_name = " + vn)
+        println("version_code_lite = " + vcl)
+        println("version_name_lite = " + vnl)
+
+        lite {
+            dimension "product"
+            applicationId "com.wenext.wyaklite"
+            versionCode vcl
+            versionName vnl
+            manifestPlaceholders = [
+                    fbAppId             : "262361153408130",
+                    fbClientToken       : "ad9cf0683a5d9f56b765f6687e013adf",
+                    deepLinkHost        : "wayak",
+                    httpDeepLinkHost    : "wayak.wenext.chat",
+                    httpWebPathAuthApple: "web/wyak-auth/apple"
+            ]
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro', (isOfficial ? 'proguard-log-empty.pro' : 'proguard-log.pro')
+            signingConfig signingConfigs.release
+
+            buildConfigField("String", "AGORA_APP_ID", '"59ac267560ad4b14b8b3d5b109050d9c"')
+            buildConfigField("String", "QTT_APP_KEY", '"baa9f1307e2e8ee02742d960828b5c50"')
+            buildConfigField("String", "HTTP_DEEP_LINK_HOST", '"wayak.wenext.chat"')
+            buildConfigField("Integer", "TRTC_APP_ID", "1400748842")
+            buildConfigField("String", "DEEP_LINK_HOST", '"wayak"')
+            buildConfigField("String", "UTM_FACEBOOK_KEY", '"33475e59ef700534f8f23685d7699eceb89a707fbdb91ca23c2e7f6f5b32a54f"')
+        }
+
+        wyak {
+            dimension "product"
+            applicationId "com.wenext.wayak"
+            versionCode vc
+            versionName vn
+            manifestPlaceholders = [
+                    fbAppId         : "857800288719942",
+                    fbClientToken   : "79b45901eb91e5ebafd12a17fd257fd4",
+                    deepLinkHost    : "wayak",
+                    httpDeepLinkHost: "wayak.wenext.chat",
+                    httpWebPathAuthApple: "web/wyak-auth/apple"
+            ]
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro', (isOfficial ? 'proguard-log-empty.pro' : 'proguard-log.pro')
+            signingConfig signingConfigs.release
+
+            buildConfigField("String", "AGORA_APP_ID", '"59ac267560ad4b14b8b3d5b109050d9c"')
+            buildConfigField("String", "QTT_APP_KEY", '"baa9f1307e2e8ee02742d960828b5c50"')
+            buildConfigField("String", "HTTP_DEEP_LINK_HOST", '"wayak.wenext.chat"')
+            buildConfigField("Integer", "TRTC_APP_ID", "1400748842")
+            buildConfigField("String", "DEEP_LINK_HOST", '"wayak"')
+            buildConfigField("String", "UTM_FACEBOOK_KEY", '"c4ea5534613d8b3cdcd5b80097989f9eb2414138aa8e49ddfbc458cd5e50f1a6"')
+        }
+    }
+
+    buildTypes {
+        debug {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+            signingConfig signingConfigs.debug
+            firebaseCrashlytics {
+                mappingFileUploadEnabled false
+            }
+        }
+        release {
+            debuggable false
+            minifyEnabled true
+            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_*'
+    }
+    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:roomtask',
+                       ':module:userprotect',
+                       ':module:medal',
+                       ':module:guardtreasure',
+                       ':module:bettingpk',
+                       ':module:micgrab',
+                       ':module:moment',
+                       ':module:youtube',
+                       ':module:visitors',
+                       ':module:youtube',
+                       ':module:gamehub:uno',
+                       ':module:gamehub:carrom',
+                       ':module:gamehub:domino',
+                       ':module:gamehub:ludo',
+    ]
+    buildFeatures {
+        viewBinding true
+        buildConfig true
+    }
+    configurations.configureEach {
+        exclude group: 'com.facebook.fresco', module: 'drawee'
+    }
+}
+
+gradle.taskGraph.addTaskExecutionListener(new TaskExecutionListener() {
+    @Override
+    void beforeExecute(Task task) {
+
+    }
+
+    @Override
+    void afterExecute(Task task, TaskState taskState) {
+        if (task.name.equalsIgnoreCase("bundleWyak")
+                || task.name.equalsIgnoreCase("bundleLite")
+                || task.name.equalsIgnoreCase("bundleRelease")
+                || task.name.equalsIgnoreCase("liteRelease")
+                || task.name.equalsIgnoreCase("wyakRelease")
+                || task.name.equalsIgnoreCase("bundleDebug")
+                || task.name.equalsIgnoreCase("bundleLiteDebug")
+                || task.name.equalsIgnoreCase("bundleWyakDebug")) {
+
+            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
+    kapt libs.androidx.room.compiler
+
+    //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
+
+    //reflect
+    implementation libs.free.reflection
+
+    //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
+
+    //frame
+    api platform(libs.frame.bom)
+    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 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
+
+//    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
+
+}

BIN
app/libs/LiteAVSDK_Player_Mini.aar


BIN
app/libs/TCEffectPlayer_2.1.0.147.aar


BIN
app/libs/TCMediaX_2.1.0.147.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)

+ 386 - 0
app/proguard-rules.pro

@@ -0,0 +1,386 @@
+# 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 bt-proguard.txt
+-classobfuscationdictionary bt-proguard.txt
+-packageobfuscationdictionary bt-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.** { *;}
+
+# 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 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.** {*;}

+ 2268 - 0
app/proguard_log.txt

@@ -0,0 +1,2268 @@
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\app\src\main\java\com\adealink\weparty\apm\APMInit.kt
+ -                 Log.d(tag, msg)
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\app\src\main\java\com\adealink\weparty\config\GlobalConfigManager.kt
+ -             Log.d(
+ -                 TAG_GLOBAL_CONFIG,
+ -                 "run schedule get all config, currentTime:${System.currentTimeMillis()}"
+ -             )
+ + 
+ -         Log.d(TAG_GLOBAL_CONFIG, "notifyConfigs:$configsMap")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\app\src\main\java\com\adealink\weparty\json\JsonConfig.kt
+ -         Log.d(tag, msg)
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\app\src\main\java\com\adealink\weparty\location\viewmodel\LocationViewModel.kt
+ -             Log.d(TAG_LOCATION_REPORT, "reportLocation")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\app\src\main\java\com\adealink\weparty\MainActivity.kt
+ -             Log.d(TAG, "dispatchPath:$dispatchPath")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\app\src\main\java\com\adealink\weparty\network\NetworkConfig.kt
+ -         Log.d("tag_serve_report", "reportWebSocketEvent, event:${event}")
+ + 
+ -         Log.d(tag, msg)
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\app\src\main\java\com\adealink\weparty\network\NetworkManager.kt
+ -         Log.d(TAG_NETWORK, "handleLogin")
+ + 
+ -         Log.d(TAG_NETWORK, "onLogout")
+ + 
+ -             Log.d(
+ -                 TAG_DISCONNECT_TIP,
+ -                 "addDisconnectFloatView, disconnectFloatView:${disconnectFloatView}"
+ -             )
+ + 
+ -             Log.d(
+ -                 TAG_DISCONNECT_TIP,
+ -                 "removeDisconnectFloatView, disconnectFloatView:${disconnectFloatView}"
+ -             )
+ + 
+ -             Log.d(TAG_PING, "ping")
+ + 
+ -         Log.d(TAG_PING, "startPing, pingInterval:$pingInterval")
+ + 
+ -         Log.d(TAG_PING, "stopPing")
+ + 
+ -         Log.d(TAG_NETWORK, "onNetChanged, available:$available")
+ + 
+ -         Log.d(TAG_NETWORK, "onEnterForeGround")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\app\src\main\java\com\adealink\weparty\push\PushServiceConfig.kt
+ -         Log.d(TAG_PUSH, "PushServiceConfig, onReceivedPushMessage:$pushMessage")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\app\src\main\java\com\adealink\weparty\stat\manager\ServeReportManager.kt
+ -                 Log.d(TAG_ATTRIBUTION, "reportFirebaseAppInstanceId, deviceId empty")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\app\src\main\java\com\adealink\weparty\stat\standard\FacebookStandardStatEvent.kt
+ -             Log.d("FacebookStandardStatEvent", "amount:${BigDecimal.valueOf(amount)}, currency: ${Currency.getInstance(currency)}")
+ + 
+ -             Log.d("FacebookStandardStatEvent", "amount:${BigDecimal.valueOf(amount)}, currency: ${Currency.getInstance(currency)}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\app\src\main\java\com\adealink\weparty\storage\config\StorageConfig.kt
+ -         Log.d(tag, msg)
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\app\src\main\java\com\adealink\weparty\ui\home\BaseHomeFragment.kt
+ -                 Log.d(TAG_LEVEL, "handleAnchorMessage: $message")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\app\src\main\java\com\adealink\weparty\update\UpdateManager.kt
+ -             Log.d(TAG_UPDATE, "checkUpdate, updateConfigNoReady")
+ + 
+ -                     Log.d(TAG_UPDATE, "immediate update checked")
+ + 
+ -                     Log.d(TAG_UPDATE, "flex update checked")
+ + 
+ -                     Log.d(TAG_UPDATE, "start flex update")
+ + 
+ -             Log.d(TAG_UPDATE, "onConfigGet")
+ + 
+ -         Log.d(TAG_UPDATE, "doOnConfigGet: ${config.joinToString(",")}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\app\src\main\java\com\adealink\weparty\widget\skin\SkinObserveData.kt
+ -         Log.d("[skin]${resourceType.name}", "updateSkin, $entity")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\app\src\main\java\com\adealink\weparty\widget\skin\SkinStyleObserveData.kt
+ -         Log.d("[skinStyle]", "updateSkinStyle, $entity")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\app\src\main\java\com\adealink\weparty\widget\skin\SkinView.kt
+ -         Log.d(TAG, "setSkinResource, $skinResourceUri")
+ + 
+ -         Log.d(TAG, "setSkinStyle, $style")
+ + 
+ -             Log.d(TAG, "setSvgaView fail, for Uri.parse($svgaUri) fail, ${e.message}")
+ + 
+ -                     Log.d(TAG, "setSvgaView return, for autoPlay is false.")
+ + 
+ -             Log.d(TAG, "setVapView fail, for Uri.parse($vapUri) fail, ${e.message}")
+ + 
+ -                     Log.d(TAG, "setVapView return, for autoPlay is false")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\aab\src\main\java\com\adealink\frame\aab\AABDownloadActivity.kt
+ -         Log.d(
+ -             TAG_AAB_DOWNLOAD,
+ -             "AABDownloadActivity, featureName:$featureName, onProgress, progressBytes:$progressBytes, totalBytes:$totalBytes"
+ -         )
+ + 
+ -         Log.d(
+ -             TAG_AAB_DOWNLOAD,
+ -             "AABDownloadActivity, featureName:$featureName, onFailure, errorCode:$errorCode"
+ -         )
+ + 
+ -         Log.d(TAG_AAB_DOWNLOAD, "AABDownloadActivity, featureName:$featureName, onInstalled")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\apm\src\main\java\com\adealink\frame\apm\plugins\largebitmap\HookRequestLoggingListener.java
+ -         Log.d(TAG, "Fresco onRequestFailure url = " + request.getSourceUri().toString());
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\apm\src\main\java\com\adealink\frame\apm\plugins\memory\memoryleak\MemoryLeakPlugin.java
+ -             Log.d(TAG, "remove " + ref.name);
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\apm\src\main\java\com\adealink\frame\apm\plugins\uiblock\UIBlockMonitor.kt
+ -                     Log.d(TAG, "stop collect ui block")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\commonui\src\main\java\com\adealink\frame\commonui\dialogfragment\BaseDialogFragment.kt
+ -             Log.d(TAG, "onStart exception, exception message = ${e.message}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\commonui\src\main\java\com\adealink\frame\commonui\drag\BaseDragView.kt
+ -         Log.d(TAG_DRAG_VIEW, "fixLocation(), resetPoint:$resetPoint")
+ + 
+ -         Log.d(TAG_DRAG_VIEW, "fixLocation(), x:$x, y:$y, fixedX:$fixedX, fixedY:$fixedY")
+ + 
+ -         Log.d(TAG_DRAG_VIEW, "setLocation() called with: x = [$x], y = [$y]")
+ + 
+ -         Log.d(TAG_DRAG_VIEW, "load last drag location, $lastLocationStr")
+ + 
+ -                 Log.d(TAG_DRAG_VIEW, "save last drag location, ${lastX}_${lastY}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\commonui\src\main\java\com\adealink\frame\commonui\drag\DragTouchEventDelegate.kt
+ -                 Log.d(TAG_DRAG_VIEW_DELEGATE, "onLongPress, ${MotionEvent.actionToString(e.action)}")
+ + 
+ -                 Log.d(TAG_DRAG_VIEW_DELEGATE, "onScroll, ${e1?.let { MotionEvent.actionToString(it.action) }}, ${e2?.let { MotionEvent.actionToString(it.action) }}")
+ + 
+ -         Log.d(TAG_DRAG_VIEW_DELEGATE, "onTouchEvent, ${MotionEvent.actionToString(event.action)}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\commonui\src\main\java\com\adealink\frame\commonui\drag\DragViewContainer.kt
+ -             Log.d(TAG_DRAG_VIEW_CONTAINER, "doOnLayout")
+ + 
+ -         Log.d(TAG_DRAG_VIEW_CONTAINER, "makeChildrenFloat invoked..")
+ + 
+ -         Log.d(TAG_DRAG_VIEW_CONTAINER, "setChildAvailableArea")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\commonui\src\main\java\com\adealink\frame\commonui\drag\DragViewMaker.kt
+ -         Log.d(TAG_DRAG_VIEW_MAKER, "(makeViewFloat): " + view.hashCode())
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\commonui\src\main\java\com\adealink\frame\commonui\drawabletoolbox\FluidColorfulFrameDrawable.kt
+ -         Log.d(TAG, "startFluid")
+ + 
+ -         Log.d(TAG, "cancelFluid")
+ + 
+ -         Log.d(TAG, "resumeFluid")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\commonui\src\main\java\com\adealink\frame\commonui\fresco\NetworkImageView.kt
+ -             Log.d(TAG, "setViewListener, url:$url, listener is null")
+ + 
+ -             Log.d(TAG, "setViewListener, url:$url, addOnPreDrawListener")
+ + 
+ -                         Log.d(TAG, "OnPreDrawListener(width:${measuredWidth}, height:${measuredHeight}), url:$url")
+ + 
+ -                             Log.d(TAG, "setViewListener, url:$url, removeOnPreDrawListener")
+ + 
+ -                                 Log.d(TAG, "setViewListener, url:$url, viewTreeObserver2.removeOnPreDrawListener")
+ + 
+ -         Log.d(TAG, "setImageURIWithResizeOpt,uri:$uri,options:$options")
+ + 
+ -                         Log.d(TAG, "getResizeImgUrl, url:$url, optUrl is null")
+ + 
+ -                         Log.d(TAG, "getResizeImgUrl, optUrl:$optUrl, has loaded")
+ + 
+ -                     Log.d(TAG, "getResizeImgUrl, url:$url, optUrl:$optUrl")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\commonui\src\main\java\com\adealink\frame\commonui\fresco\stat\ImageRequestStaticListener.kt
+ -         Log.d(TAG, "onRequestStart: requestId:$requestId, uri:${request?.sourceUri}")
+ + 
+ -         Log.d(TAG, "onRequestSuccess: requestId:$requestId, uri:${request?.sourceUri}")
+ + 
+ -         Log.d(TAG, "onRequestCancellation: requestId:$requestId")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\commonui\src\main\java\com\adealink\frame\commonui\keyboard\KeyboardChangeListener.kt
+ -         Log.d(TAG, "visibleHeight:${visibleHeight}, rootViewVisibleHeight:${rootViewVisibleHeight}")
+ + 
+ -             Log.d(TAG, "rootViewVisibleHeight == visibleHeight")
+ + 
+ -             Log.d(TAG, "rootViewVisibleHeight - visibleHeight > 200")
+ + 
+ -             Log.d(TAG, "visibleHeight - rootViewVisibleHeight > 200")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\commonui\src\main\java\com\adealink\frame\commonui\text\DataBindingSpanEditText.kt
+ -         Log.d(TAG, "onTextChanged() called with: text = $text, start = $start, lengthBefore = $lengthBefore, lengthAfter = $lengthAfter")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\commonui\src\main\java\com\adealink\frame\commonui\text\watcher\InputTextWatcher.kt
+ -         Log.d(TAG, "beforeTextChanged() called with: s = $s, start = $start, count = $count, after = $after")
+ + 
+ -         Log.d(TAG, "onTextChanged() called with: s = $s, start = $start, before = $before, count = $count")
+ + 
+ -         Log.d(TAG, "afterTextChanged() called with: s = $s")
+ + 
+ -             Log.d(TAG, "checkInputInWatch, $text")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\commonui\src\main\java\com\adealink\frame\commonui\widget\banner\util\LogUtils.java
+ -             Log.d(TAG, msg);
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\commonui\src\main\java\com\adealink\frame\commonui\widget\floatview\mode\ApplicationModeWindowManager.kt
+ -         Log.d(TAG_FLOAT_VIEW, "${SUB_TAG}, addView: $view")
+ + 
+ -                         Log.d(TAG_FLOAT_VIEW, "${SUB_TAG}, addView, viewAddMap.add($view)")
+ + 
+ -         Log.d(TAG_FLOAT_VIEW, "${SUB_TAG}, removeView: $view, reason:$reason")
+ + 
+ -             Log.d(TAG_FLOAT_VIEW, "${SUB_TAG}, removeView, viewAddMap.remove($view)")
+ + 
+ -         Log.d(TAG_FLOAT_VIEW, "${SUB_TAG}, onActivityChange")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\commonui\src\main\java\com\adealink\frame\commonui\widget\floatview\ModeWindowManagerProxy.kt
+ -         Log.d(TAG_FLOAT_VIEW, "[${getActivityName(activity)}]: onCreate")
+ + 
+ -         Log.d(TAG_FLOAT_VIEW, "[${getActivityName(activity)}]: onStart")
+ + 
+ -         Log.d(TAG_FLOAT_VIEW, "[${getActivityName(activity)}]: onResume")
+ + 
+ -         Log.d(TAG_FLOAT_VIEW, "[${getActivityName(activity)}]: onPause")
+ + 
+ -         Log.d(TAG_FLOAT_VIEW, "[${getActivityName(activity)}]: onStop")
+ + 
+ -         Log.d(TAG_FLOAT_VIEW, "[${getActivityName(activity)}]: onDestroy")
+ + 
+ -         Log.d(TAG_FLOAT_VIEW, "[${getActivityName(activity)}]: onSaveInstanceState")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\coroutine\src\main\java\com\adealink\frame\coroutine\dispatcher\Dispatcher.kt
+ -         Log.d(
+ -             "Dispatcher",
+ -             "AVAILABLE_PROCESSORS:${AVAILABLE_PROCESSORS}, IO_CORE_POOL_SIZE:${IO_CORE_POOL_SIZE}, CPU_CORE_POOL_SIZE:${CPU_CORE_POOL_SIZE}"
+ -         )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\crash\src\main\java\com\adealink\frame\crash\install\FinalizeInstaller.kt
+ -                     Log.d(
+ -                         TAG_CRASH_FINALIZE,
+ -                         "Thread[" + t.name + "] set priority to " + t.priority
+ -                     )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\crash\src\main\java\com\adealink\frame\crash\report\XCrashInit.java
+ -                 Log.d(TAG, "log path: " + (logPath != null ? logPath : "(null)") + ", emergency: " + (emergency != null ? emergency : "(null)"));
+ + 
+ -         Log.d(TAG, "xCrash SDK init: start");
+ + 
+ -         Log.d(TAG, "xCrash SDK init: end");
+ + 
+ -             Log.d(TAG, "onCrash callback finished");
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\deviceid\src\main\java\com\adealink\frame\deviceid\DeviceIdService.kt
+ -         Log.d(TAG_DEV_ID, "getDeviceId:$cachedDeviceId")
+ + 
+ -         Log.d(TAG_DEV_ID, "getAndroidId:$cachedAndroidId")
+ + 
+ -         Log.d(TAG_DEV_ID, "getSN:$cachedSN")
+ + 
+ -         Log.d(TAG_DEV_ID, "getSerialNumber:$serialNumber")
+ + 
+ -         Log.d(TAG_DEV_ID, "getNormalDeviceInfoHash, totalInfo:${totalInfo}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\dot\src\main\java\com\adealink\frame\dot\Dot.kt
+ -         Log.d(tag, "addChildren, dot:${dot.dotEntity}")
+ + 
+ -         Log.d(tag, "removeChildren, dot:${dot.dotEntity}")
+ + 
+ -         Log.d(tag, "show, dot:${entity}")
+ + 
+ -         Log.d(tag, "hide, dot:${dotEntity}")
+ + 
+ -             Log.d(tag, "collectChildren, children is empty, dot:$dotEntity")
+ + 
+ -         Log.d(tag, "collectChildren, num:${num}, text:$text, normalShow:$normalShow")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\download\src\main\java\com\adealink\frame\download\DownloadService.kt
+ -                 Log.d(TAG_DOWNLOAD, "addTask, add task, task:$task")
+ + 
+ -         Log.d(
+ -             TAG_DOWNLOAD,
+ -             "downloadNext, start, downloadingTaskNum:$downloadingTaskNum"
+ -         )
+ + 
+ -         Log.d(
+ -             TAG_DOWNLOAD,
+ -             "downloadNext, finished, downloadingTaskNum:$downloadingTaskNum"
+ -         )
+ + 
+ -             Log.d(TAG_DOWNLOAD, "removeTask, task:$task")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\download\src\main\java\com\adealink\frame\download\manager\DownloadManager.kt
+ -         Log.d(
+ -             TAG_DOWNLOAD_RESOURCE,
+ -             "addDownloadResourceTask, start download, url:$url, task:$task"
+ -         )
+ + 
+ -                 Log.d(
+ -                     TAG_DOWNLOAD_RESOURCE,
+ -                     "addDownloadResourceTask, download success, task:$task"
+ -                 )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\download\src\main\java\com\adealink\frame\download\task\Task.kt
+ -         Log.d(TAG_DOWNLOAD_STATUS, "notifyStart, task:$this")
+ + 
+ -         Log.d(TAG_DOWNLOAD_STATUS, "notifyFinished, task:$this, size:${size}Byte")
+ + 
+ -         Log.d(TAG_DOWNLOAD_STATUS, "notifyError, task:$this, error:$error")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\effect\src\main\java\com\adealink\frame\effect\exoplayer\ExoPlayerHelper.kt
+ -         Log.d(TAG, "initializePlayer")
+ + 
+ -         Log.d(TAG, "releasePlayer")
+ + 
+ -         Log.d(TAG, "onStart")
+ + 
+ -         Log.d(TAG, "onResume")
+ + 
+ -         Log.d(TAG, "onPause")
+ + 
+ -         Log.d(TAG, "onStop")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\effect\src\main\java\com\adealink\frame\effect\video\VideoEffectView.kt
+ -                 Log.d(TAG_EFFECT_VIEW_VIDEO, "onVideoComplete")
+ + 
+ -                 Log.d(TAG_EFFECT_VIEW_VIDEO, "onVideoDestroy")
+ + 
+ -                 Log.d(TAG_EFFECT_VIEW_VIDEO, "onVideoStart")
+ + 
+ -                     Log.d(TAG_EFFECT_VIEW_VIDEO, "onVideoComplete(lastVideo)")
+ + 
+ -                     Log.d(TAG_EFFECT_VIEW_VIDEO, "onVideoDestroy(lastVideo)")
+ + 
+ -         Log.d(TAG_EFFECT_VIEW_VIDEO, "stop")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\effect\src\main\java\com\adealink\frame\effect\view\EffectView.kt
+ -             Log.d(TAG_EFFECT_VIEW, "timeoutRunnable run")
+ + 
+ -         Log.d(TAG_EFFECT_VIEW, "add, entity:$entity")
+ + 
+ -             Log.d(TAG_EFFECT_VIEW, "add, stop, entity:$entity")
+ + 
+ -                 Log.d(TAG_EFFECT_VIEW, "add return, for queueLimit($queueLimit), currentQueue size($currentQueueSize)")
+ + 
+ -         Log.d(TAG_EFFECT_VIEW, "add effect, currentQueue size(${entityPriorityQueue.size})")
+ + 
+ -             Log.d(
+ -                 TAG_EFFECT_VIEW,
+ -                 "add, create view, entityClassName:$entityClassName, entity:$entity"
+ -             )
+ + 
+ -         Log.d(TAG_EFFECT_VIEW, "playNext, currentQueue size(${entityPriorityQueue.size})")
+ + 
+ -             Log.d(TAG_EFFECT_VIEW, "playNext, not play status")
+ + 
+ -             Log.d(TAG_EFFECT_VIEW, "playNext, playing")
+ + 
+ -             Log.d(TAG_EFFECT_VIEW, "playNext, effect entity is empty")
+ + 
+ -             Log.d(TAG_EFFECT_VIEW, "playNext, effect view null, entity:${firstEffectEntity}")
+ + 
+ -         Log.d(TAG_EFFECT_VIEW, "playNext, play, entity:${firstEffectEntity}")
+ + 
+ -                 Log.d(TAG_EFFECT_VIEW, "playNext, play complete, entity:${firstEffectEntity}")
+ + 
+ -         Log.d(TAG_EFFECT_VIEW, "play, entity:${entity}")
+ + 
+ -         Log.d(TAG_EFFECT_VIEW, "pause")
+ + 
+ -             Log.d(TAG_EFFECT_VIEW, "pause, current play status is not play")
+ + 
+ -         Log.d(TAG_EFFECT_VIEW, "stop")
+ + 
+ -             Log.d(TAG_EFFECT_VIEW, "stop, current play status is stop")
+ + 
+ -         Log.d(TAG_EFFECT_VIEW, "stop entity: ${entity.priority}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\googleservice\src\main\java\com\adealink\frame\googleservice\GoogleService.kt
+ -                 Log.d(
+ -                     TAG_GOOGLE_SERVICE,
+ -                     "retryGetGPReferrerSourceFromService, over retry count:$gpReferrerSourceRetryCount"
+ -                 )
+ + 
+ -             Log.d(
+ -                 TAG_GOOGLE_SERVICE,
+ -                 "retryGetGPReferrerSourceFromService, retry count:$gpReferrerSourceRetryCount"
+ -             )
+ + 
+ -         Log.d(TAG_GOOGLE_SERVICE, "parseReferrerUrl, url: $referrerUrl")
+ + 
+ -         Log.d(TAG_GOOGLE_SERVICE, "parseReferrerUrl, referrer: $referrer")
+ + 
+ -         Log.d(TAG_GOOGLE_SERVICE, "getReferrerValue, key:$key")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\guide\src\main\java\com\adealink\frame\guide\BaseGuide.kt
+ -             Log.d(TAG, "register return, already register with $activity")
+ + 
+ -             Log.d(TAG, "register return, already register with $dialog")
+ + 
+ -             Log.d(TAG, "unregister return, for register with other $curActivity")
+ + 
+ -             Log.d(TAG, "unregister return, for register with other $curDialog")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\guide\src\main\java\com\adealink\frame\guide\core\GuideController.kt
+ -         Log.d(TAG, "refresh")
+ + 
+ -             Log.d(TAG, "showGuidePageInner, return for guidePage isInvalid or Intercept")
+ + 
+ -         Log.d(TAG, "showGuidePageInner")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\guide\src\main\java\com\adealink\frame\guide\GuideCenter.kt
+ -         Log.d(TAG, "registerGuide: type:$guideType")
+ + 
+ -         Log.d(TAG, "unregisterGuide: type:$guideType")
+ + 
+ -             Log.d(TAG, "activeGuide: type:$guideType")
+ + 
+ -             Log.d(TAG, "refreshGuide: type:$guideType")
+ + 
+ -             Log.d(TAG, "dismissGuide: type:$guideType, label:$guideLabel")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\imageselect\src\main\java\com\adealink\frame\imageselect\util\CompressorUtil.kt
+ -             Log.d(
+ -                 TAG_COMPRESS, "compressImage, originPath:${path}, " +
+ -                         "compressPath:${compressFilePath}, " +
+ -                         "originSize:${getFileSizeKB(File(path))}KB, " +
+ -                         "compressSize:${getFileSizeKB(File(compressFilePath))}KB"
+ -             )
+ + 
+ -                 Log.d(
+ -                     TAG_COMPRESS,
+ -                     "compressVideo, originSize:${originSize}KB less 400KB, path:${path}"
+ -                 )
+ + 
+ -                 Log.d(
+ -                     TAG_COMPRESS, "compressVideo, originPath:${path}, " +
+ -                             "compressPath:${filePath}, " +
+ -                             "originSize:${originSize}KB, " +
+ -                             "compressSize:${getFileSizeKB(File(filePath))}KB"
+ -                 )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\locale\src\main\java\com\adealink\frame\locale\language\LanguageManager.kt
+ -             Log.d(TAG, "init, saved locale is null")
+ + 
+ -             Log.d(TAG, "init, app locale is null")
+ + 
+ -             Log.d(TAG, "init, default locale is null")
+ + 
+ -         Log.d(TAG, "init, locale:${locale}")
+ + 
+ -         Log.d(TAG, "updateLocale, locale:$locale")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\media\src\main\java\com\adealink\frame\media\MediaService.kt
+ -             Log.d(
+ -                 TAG_MEDIA,
+ -                 "rtcType:$rtcType, currentChannel:$currentChannel, selfUid:$selfUid, $msg"
+ -             )
+ + 
+ -         Log.d(
+ -             TAG_MEDIA_MUSIC,
+ -             "currentChannel:$currentChannel, selfUid:$selfUid, playState:$musicPlayState, toPlayMusicFilePath:$toPlayMusicFilePath, playMusicFilePath:$playMusicFilePath, $msg"
+ -         )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\media\src\main\java\com\adealink\frame\media\trtc\TRtcEngine.kt
+ -                 Log.d(
+ -                     TAG_MEDIA_TRTC,
+ -                     "onEnterRoom, roomId:$roomId, elapsed:$elapsed, result:$result"
+ -                 )
+ + 
+ -                 Log.d(TAG_MEDIA_TRTC, "onExitRoom, roomId:$roomId, reason:$reason")
+ + 
+ -                 Log.d(
+ -                     TAG_MEDIA_TRTC,
+ -                     "onError, roomId:$roomId, errCode:$errCode, errMsg:$errMsg, extraInfo:$extraInfo"
+ -                 )
+ + 
+ -         Log.d(TAG_MEDIA_TRTC, "switchRole, roomId:$roomId, role:$tRtcRole")
+ + 
+ -         Log.d(TAG_MEDIA_TRTC, "switchRole, roomId:$roomId, role:$tRtcRole")
+ + 
+ -         Log.d(TAG_MEDIA_TRTC, "enterRoom, param:$param")
+ + 
+ -         Log.d(TAG_MEDIA_TRTC, "enterRoom, param:$param")
+ + 
+ -         Log.d(TAG_MEDIA_TRTC, "exitRoom, roomId:$roomId")
+ + 
+ -         Log.d(
+ -             TAG_MEDIA_TRTC,
+ -             "enableAudioVolumeEvaluation, interval:$interval, report_vad:$report_vad"
+ -         )
+ + 
+ -             Log.d(TAG_MEDIA_TRTC, "startLocalAudio, roomId:$roomId")
+ + 
+ -             Log.d(TAG_MEDIA_TRTC, "stopLocalAudio, roomId:$roomId")
+ + 
+ -         Log.d(TAG_MEDIA_TRTC, "muteLocalAudio, roomId:$roomId, muted:$muted")
+ + 
+ -         Log.d(TAG_MEDIA_TRTC, "muteAllRemoteAudio, roomId:$roomId, muted:$muted")
+ + 
+ -             Log.d(TAG_MEDIA_TRTC, "setAudioRoute Speakerphone, roomId:$roomId")
+ + 
+ -             Log.d(TAG_MEDIA_TRTC, "setAudioRoute Earpiece, roomId:$roomId")
+ + 
+ -             Log.d(
+ -                 TAG_MEDIA_TRTC,
+ -                 "startAudioMixing same music, musicId:$musicId, filePath:$filePath, roomId:$roomId"
+ -             )
+ + 
+ -             Log.d(
+ -                 TAG_MEDIA_TRTC,
+ -                 "startAudioMixing playing other music, playMusicId:$playMusicId, filePath:$filePath, roomId:$roomId"
+ -             )
+ + 
+ -                     Log.d(TAG_MEDIA_TRTC, "onStart, roomId:$roomId, id:$id")
+ + 
+ -                     Log.d(TAG_MEDIA_TRTC, "onComplete, roomId:$roomId, id:$id")
+ + 
+ -         Log.d(TAG_MEDIA_TRTC, "stopPlayMusic, roomId:$roomId, playMusicId:$playMusicId")
+ + 
+ -         Log.d(TAG_MEDIA_TRTC, "pausePlayMusic, roomId:$roomId, playMusicId:$playMusicId")
+ + 
+ -         Log.d(TAG_MEDIA_TRTC, "resumePlayMusic, roomId:$roomId, playMusicId:$playMusicId")
+ + 
+ -         Log.d(
+ -             TAG_MEDIA_TRTC,
+ -             "seekMusicToPosInMS, roomId:$roomId, playMusicId:$playMusicId, pos:$pos"
+ -         )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\network\src\main\java\com\adealink\frame\network\http\interceptor\HttpLoggingInterceptor.kt
+ -             Log.d(
+ -                 TAG_HTTP,
+ -                 "HttpLoggingInterceptor, request:$request, requestBody:${bodyString}, response:$response, responseBody:${responseBodyString}"
+ -             )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\network\src\main\java\com\adealink\frame\network\http\interceptor\HttpStatInterceptor.kt
+ -                     Log.d(
+ -                         TAG_HTTP,
+ -                         "HttpStatInterceptor, path:$path, code:$code, serverCode:$serverCode, duration:$duration, needStat:$needStat, request:$request"
+ -                     )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\network\src\main\java\com\adealink\frame\network\NetworkService.kt
+ -             Log.d(TAG_SOCKET_CONNECT, "disConnect, connectState:${connectState}")
+ + 
+ -                     Log.d(
+ -                         TAG_SOCKET_MSG,
+ -                         "send msg, seqId:${reqInfo.seqId}, uri:${reqInfo.uri}, code:$code, serverCode:${serverCode}, duration:$duration"
+ -                     )
+ + 
+ -             Log.d(TAG_SOCKET_MSG, "send, msg:$newMsg")
+ + 
+ -             Log.d(TAG_SOCKET_MSG, "onMessage, encode:$encode, text:$text")
+ + 
+ -             Log.d(TAG_SOCKET_MSG, "onMessage, text:$text")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\permission\src\main\java\com\adealink\frame\permission\PermissionUtils.kt
+ -                     Log.d(TAG_PERMISSION, "$permissionName is granted")
+ + 
+ -                                         Log.d(
+ -                                             TAG_PERMISSION,
+ -                                             "retry $permissionName is granted"
+ -                                         )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\permission\src\main\java\com\adealink\frame\permission\RxPermissionsFragment.java
+ -             Log.d(RxPermissions.TAG, message);
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\push\src\main\java\com\adealink\frame\push\fcm\WeNextFirebaseMessagingService.kt
+ -         Log.d(TAG_PUSH, "onNewToken:$token")
+ + 
+ -         Log.d(
+ -             TAG_PUSH, "onMessageReceived," +
+ -                     "data:${remoteMessage.data},messageId:${remoteMessage.messageId}" +
+ -                     "messageType:${remoteMessage.messageType}"
+ -         )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\push\src\main\java\com\adealink\frame\push\manager\PushService.kt
+ -             Log.d(TAG_PUSH, msg)
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\qmui\src\main\java\com\qmuiteam\qmui\link\QMUILinkTouchDecorHelper.java
+ -                 Log.d(this.toString(), "getPressedSpan", e);
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\qmui\src\main\java\com\qmuiteam\qmui\recyclerView\QMUIRVItemSwipeAction.java
+ -                 Log.d(TAG, "intercept: x:" + event.getX() + ",y:" + event.getY() + ", " + event);
+ + 
+ -                     Log.d(TAG, "pointer index " + index);
+ + 
+ -                 Log.d(TAG,
+ -                         "on touch: x:" + mInitialTouchX + ",y:" + mInitialTouchY + ", :" + event);
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\qmui\src\main\java\com\qmuiteam\qmui\widget\pullRefreshLayout\QMUIPullRefreshLayout.java
+ -             Log.d(TAG, "onMeasure: mTargetView == null");
+ + 
+ -             Log.d(TAG, "onLayout: mTargetView == null");
+ + 
+ -                 Log.d(TAG, "fast end onIntercept: isEnabled = " + isEnabled() + "; canChildScrollUp = "
+ -                         + canChildScrollUp() + " ; mNestedScrollInProgress = " + mNestedScrollInProgress);
+ + 
+ -             Log.d(TAG, "fast end onTouchEvent: isEnabled = " + isEnabled() + "; canChildScrollUp = "
+ -                     + canChildScrollUp() + " ; mNestedScrollInProgress = " + mNestedScrollInProgress);
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\qmui\src\main\java\com\qmuiteam\qmui\widget\textview\QMUILinkTextView.java
+ -             Log.d(TAG, "handleMessage: " + msg.obj);
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\room\src\main\java\com\adealink\frame\room\controller\impl\BaseDeviceController.kt
+ -         Log.d(TAG_ROOM_DEVICE, "userMuteMic, mute:$mute, reason:$reason")
+ + 
+ -         Log.d(TAG_ROOM_DEVICE, "ownerMuteMic, mute:$mute")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\room\src\main\java\com\adealink\frame\room\controller\impl\BaseJoinController.kt
+ -         Log.d(
+ -             TAG_ROOM_FLOW,
+ -             "changeToState, currState:${currState}, toState:${toState}, flowStateInfo:${flowStateInfo}"
+ -         )
+ + 
+ -         Log.d(TAG_ROOM_FLOW, "joinRoom: $req")
+ + 
+ -             Log.d(TAG_ROOM_FLOW, "joinRoom: $joinedRoomInfo")
+ + 
+ -             Log.d(
+ -                 TAG_ROOM_FLOW,
+ -                 "joinChannel, reason:$reason, channelToken:${channelToken}, channel:${channel}"
+ -             )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\router\router-api\src\main\java\com\adealink\frame\router\RouterDeepLinkActivity.kt
+ -         Log.d(TAG_DEEPLINK_ACTIVITY, "onCreate, data:${intent.data}")
+ + 
+ -                 Log.d(TAG_DEEPLINK_ACTIVITY,
+ -                     "genParametersFromUri, uri: $uri, encodedSchemeSpecificPart: $parameter")
+ + 
+ -                             Log.d(TAG_DEEPLINK_ACTIVITY,
+ -                                 "genParametersFromUri, query: ${p[0]}=${
+ -                                     URLDecoder.decode(p[1],
+ -                                         "UTF-8")
+ -                                 }")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\security\src\main\java\com\adealink\frame\security\virtualapk\VirtualApkChecker.kt
+ -         Log.d(TAG_SECURITY, "Have right to access dir of other application: " + dir.exists())
+ + 
+ -         Log.d(TAG_SECURITY, "checkByPrivateFilePath, privateFilePath:$path")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\statistics\src\main\java\com\adealink\frame\statistics\BaseStatEvent.kt
+ -             Log.d(TAG_STAT, "eventId:$eventId, bundle:$bundle")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\storage\src\main\java\com\adealink\frame\storage\assets\AssetsUtil.kt
+ -     Log.d(
+ -         TAG_STORAGE_ASSETS,
+ -         "copyVersionZipFromAssetsToFile, copy zip source cost time: ${System.currentTimeMillis() - startTime}ms"
+ -     )
+ + 
+ -     Log.d(
+ -         TAG_STORAGE_ASSETS,
+ -         "copyVersionZipFromAssetsToFile, calculate zip md5 cost time: ${(System.currentTimeMillis() - startTime)}ms"
+ -     )
+ + 
+ -             Log.d(
+ -                 TAG_STORAGE_ASSETS,
+ -                 "needToUnzipPkg, originMd5:$originMd5, compareMd5:$compareMd5"
+ -             )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\storage\src\main\java\com\adealink\frame\storage\config\IStorageConfig.kt
+ -         Log.d(tag, msg)
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\storage\src\main\java\com\adealink\frame\storage\sp\SpProxy.kt
+ -         Log.d(TAG, "getString, key:$key, typeKey:$typeKey")
+ + 
+ -         Log.d(TAG, "getStringSet, key:$key, typeKey:$typeKey")
+ + 
+ -         Log.d(TAG, "getInt, key:$key, typeKey:$typeKey")
+ + 
+ -         Log.d(TAG, "getLong, key:$key, typeKey:$typeKey")
+ + 
+ -         Log.d(TAG, "getFloat, key:$key, typeKey:$typeKey")
+ + 
+ -         Log.d(TAG, "getBoolean, key:$key, typeKey:$typeKey")
+ + 
+ -         Log.d(TAG, "contains, key:$key, realKey:$realKey")
+ + 
+ -         Log.d(TAG, "putString, key:$key, typeKey:$typeKey")
+ + 
+ -         Log.d(TAG, "putStringSet, key:$key, typeKey:$typeKey")
+ + 
+ -         Log.d(TAG, "putInt, key:$key, typeKey:$typeKey")
+ + 
+ -         Log.d(TAG, "putLong, key:$key, typeKey:$typeKey")
+ + 
+ -         Log.d(TAG, "putFloat, key:$key, typeKey:$typeKey")
+ + 
+ -         Log.d(TAG, "putBoolean, key:$key, typeKey:$typeKey")
+ + 
+ -         Log.d(TAG, "remove, key:$key, realKey:$realKey")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\util\src\main\java\com\adealink\frame\ext\ImageExt.kt
+ -             Log.v(TAG, "save file: $imagePath")
+ + 
+ -         Log.v(TAG, "query: path: $imagePath exists")
+ + 
+ -             Log.v(TAG, "query: path: $imagePath exists uri: $existsUri")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\util\src\main\java\com\adealink\frame\util\AppSignatureHelper.java
+ -             Log.d(TAG, String.format("pkg: %s -- hash: %s", packageName, base64Hash));
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\util\src\main\java\com\adealink\frame\util\ViewShotUtil.kt
+ -             Log.d(TAG, "saveBitmap fail, for path is null")
+ + 
+ -             Log.d(TAG, "saveBitmap fail, for ${e.message}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\webview\src\main\java\com\adealink\frame\webview\BaseWebView.kt
+ -         Log.d(
+ -             TAG_WEB_VIEW,
+ -             "initJSBridge, jsBridge:$jsBridge, interfaceName:${jsBridge.interfaceName()}"
+ -         )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\webview\src\main\java\com\adealink\frame\webview\CommonWebViewClient.kt
+ -         Log.d(TAG_WEB_VIEW_LOAD_FLOW, "page load back, url:$url")
+ + 
+ -         Log.d(TAG_WEB_VIEW_LOAD_FLOW, "page load start, url:$url")
+ + 
+ -             Log.d(TAG_WEB_VIEW_LOAD_FLOW, "page load finished, url:$url")
+ + 
+ -         Log.d(
+ -             TAG_WEB_VIEW_LOAD_FLOW,
+ -             "page load error, webUrl:$webUrl, requestUrl:${requestUrl}, code:$code, error:$error"
+ -         )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\webview\src\main\java\com\adealink\frame\webview\jsbridge\JSBridgeImpl.kt
+ -         Log.d(TAG_WEB_VIEW_JS_BRIDGE, "handleJSMessage, json:${json}")
+ + 
+ -         Log.d(TAG_WEB_VIEW_JS_BRIDGE, "isDenied, url:${url}, originalUrl:${originalUrl}")
+ + 
+ -             Log.d(TAG_WEB_VIEW_JS_BRIDGE,
+ -                 "resolve, methodName:${methodName}, callbackId:${callbackId}, data:${data}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\webview\src\main\java\com\adealink\frame\webview\jsbridge\method\LogJSNativeMethod.kt
+ -             Log.d(tag, msg)
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\webview\src\main\java\com\adealink\frame\webview\loader\OkHttpResourceLoader.kt
+ -             Log.d(
+ -                 TAG_WEB_VIEW_RES_LOAD,
+ -                 "okhttp load, url:$url, method:${method}, redirect:${request.isRedirect}, forMainFrame:${request.isForMainFrame}, requestHeaders:${requestHeaders.toMap()}, protocol:${res.protocol}, responseCode:${resCode}, cacheType:${cacheType.cache}, responseHeaders:${responseHeaders}, duration:$duration"
+ -             )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\frame\webview\src\main\java\com\adealink\frame\webview\loader\WebResourceLoader.kt
+ -                 Log.d(
+ -                     TAG_WEB_VIEW_RES_LOAD,
+ -                     "normal load, url:${request.url}, method:${request.method}, redirect:${request.isRedirect}, forMainFrame:${request.isForMainFrame}, requestHeaders:${request.headers}"
+ -                 )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\account\src\main\java\com\adealink\weparty\account\login\auth\FacebookAuth.kt
+ -                 Log.d(TAG_FB_AUTH, "Facebook auth onSuccess")
+ + 
+ -                 Log.d(TAG_FB_AUTH, "Facebook auth onCancel")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\account\src\main\java\com\adealink\weparty\account\login\auth\GoogleAuth.kt
+ -             Log.d(TAG_GOOGLE_AUTH, "handleSignInResult, account.id:${account.id}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\account\src\main\java\com\adealink\weparty\account\login\LoginActivity.kt
+ -         Log.d(TAG_ACCOUNT_LOGIN, "handleIntent, inviteUidCode:$inviteUidCode, inviteRoomIdCode:$inviteRoomIdCode, shareScene:$shareScene")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\account\src\main\java\com\adealink\weparty\account\login\manager\LoginManager.kt
+ -                 Log.d(TAG_ACCOUNT_LOGIN, "refresh token(${i})")
+ + 
+ -                     Log.d(TAG_ACCOUNT_LOGIN, "refresh token success, new Token:${newToken}")
+ + 
+ -                     Log.d(TAG_ACCOUNT_LOGIN_PHONE, "sms retrieved, message:$message")
+ + 
+ -                         Log.d(TAG_ACCOUNT_LOGIN_PHONE, "sms retrieved, verifyCode:$verifyCode")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\anchor\src\main\java\com\adealink\weparty\anchor\viewmodel\AnchorViewModel.kt
+ -                 Log.d(TAG_ANCHOR_HIGH_POTENTIAL, "onNotify, data:$data")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\attribution\src\main\java\com\adealink\weparty\attribution\manager\AttributionManager.kt
+ -                     Log.d(TAG_ATTRIBUTION, "onAppOpen_attribute: ${it.key} = ${it.value}")
+ + 
+ -         Log.d(
+ -             TAG_ATTRIBUTION,
+ -             "reportPurchase, eventName:${AFInAppEventType.PURCHASE}, eventValues:${map}"
+ -         )
+ + 
+ -         Log.d(
+ -             TAG_ATTRIBUTION,
+ -             "reportPurchase, eventName:${AFInAppEventType.PURCHASE}, eventValues:${map}"
+ -         )
+ + 
+ -                 Log.d(TAG_ATTRIBUTION, "reportAttributionData, deviceId empty")
+ + 
+ -                 Log.d(TAG_ATTRIBUTION, "reportAttributionData, attributionData null")
+ + 
+ -             Log.d(TAG_ATTRIBUTION, "doReportFirstOpen return, for gpReferrer get fail")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\attribution\src\main\java\com\adealink\weparty\attribution\utm\FacebookUtm.kt
+ -     Log.d(TAG_ATTRIBUTION, "decodeFacebookUtm, content:$content")
+ + 
+ -     Log.d(TAG_ATTRIBUTION, "decodeFacebookUtm, utm:$facebookUtm, originContent:$decodeContent")
+ + 
+ -         Log.d(TAG_ATTRIBUTION, "decodeFacebookUtm, decrypt fail for ${e.message}", e)
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\attribution\src\main\java\com\adealink\weparty\attribution\utm\TiktokUtm.kt
+ -     Log.d(TAG_ATTRIBUTION, "decodeTiktokUtm, content:$content")
+ + 
+ -     Log.d(TAG_ATTRIBUTION, "decodeTiktokUtm, utm:$tiktokUtm, originContent:$content")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\bettingpk\src\main\java\com\adealink\weparty\bettingpk\manager\BettingPKManager.kt
+ -         Log.d(TAG_PK, "registerNotify")
+ + 
+ -         Log.d(TAG_PK, "unRegisterNotify")
+ + 
+ -         Log.d(TAG_PK, "onRoomIn: $roomId")
+ + 
+ -         Log.d(TAG_PK, "onRoomLeaved: $roomId")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\bettingpk\src\main\java\com\adealink\weparty\bettingpk\MyBettingPKDialog.kt
+ -                 Log.d(TAG_PK, "search invite info is invalid, uid:$uid, roomOwnerUid:$roomOwnerUid")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\bettingpk\src\main\java\com\adealink\weparty\bettingpk\widget\RoomBettingPkEntranceView.kt
+ -                     Log.d(TAG_DRAG_VIEW, "onDown(RoomBettingPkEntranceView.clCollapse), ${MotionEvent.actionToString(e.action)}")
+ + 
+ -                     Log.d(TAG_DRAG_VIEW, "onSingleTapUp(RoomBettingPkEntranceView.clCollapse), ${MotionEvent.actionToString(e.action)}")
+ + 
+ -                     Log.d(TAG_DRAG_VIEW, "onLongPress(RoomBettingPkEntranceView.clCollapse), ${MotionEvent.actionToString(e.action)}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\call\src\main\java\com\adealink\weparty\call\manager\CallManager.kt
+ -         Log.d(TAG_CALL_FLOW, "onRoomIn, flowStateInfo:$flowStateInfo")
+ + 
+ -         Log.d(TAG_CALL_FLOW, "onChannelIn, flowStateInfo:$flowStateInfo")
+ + 
+ -         Log.d(TAG_CALL_FLOW, "onChannelLeavel, flowStateInfo:$flowStateInfo")
+ + 
+ -         Log.d(TAG_CALL_FLOW, "onRoomLeaved, flowStateInfo:$flowStateInfo")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\couple\src\main\java\com\adealink\weparty\couple\fragment\CoupleFragment.kt
+ -         Log.d(TAG_COUPLE, "tryInviteIntimacy: ${CoupleLocalService.showInviteCoupleRechargeTip}")
+ + 
+ -                 Log.d(TAG_COUPLE, "realInviteIntimacy when show confirm dialog: ")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\couple\src\main\java\com\adealink\weparty\couple\manager\CoupleManager.kt
+ -                                 Log.d(TAG_COUPLE, "send cp invited: $selectedNotPrompt")
+ + 
+ -                             Log.d(TAG_COUPLE, "not tip dialog send cp invited")
+ + 
+ -                         Log.d(TAG_COUPLE, "sendInviteAndTip: $inviteIntimacyRlt")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\emotion\src\main\java\com\adealink\weparty\emotion\manager\EmotionManager.kt
+ -                         Log.d(TAG_EMOTION, "getRemoteEmotionPackages, size:${res.size}")
+ + 
+ -             Log.d(TAG_EMOTION, "loadLocalEmotionPackages,size:${localEmotionPackages.size}")
+ + 
+ -             Log.d(TAG_EMOTION, "handleSendEmotionNotify, sendEmotionNotify:${sendEmotionNotify}")
+ + 
+ -             Log.d(TAG_EMOTION, "notifyEmotionPackages,size:${packages.size}")
+ + 
+ -         Log.d(
+ -             TAG_EMOTION_DOWNLOAD,
+ -             "addDownloadEmotionResTask, start download, resUrl:${emotionInfo.packageUrl}, task:$task"
+ -         )
+ + 
+ -                 Log.d(
+ -                     TAG_EMOTION_DOWNLOAD,
+ -                     "addDownloadEmotionResTask, download success, task:$task"
+ -                 )
+ + 
+ -             Log.d(
+ -                 TAG_EMOTION_UNZIP,
+ -                 "unzipEmotion success, emotionId:${emotionInfo.id}"
+ -             )
+ + 
+ -             Log.d(TAG_EMOTION, "getEmotionAnimationResPath, exist, filePath: $filePath")
+ + 
+ -             Log.d(TAG_EMOTION, "getEmotionAnimationResultPath, exist, filePath: $filePath")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\entereffect\src\main\java\com\adealink\weparty\entereffect\manager\EnterEffectManager.kt
+ -             Log.d(TAG_ENTER_EFFECT, "onNotify,$data")
+ + 
+ -                     Log.d(TAG_ENTER_EFFECT, "listeners is empty")
+ + 
+ -         Log.d(TAG_ENTER_EFFECT, "subscribeNotify")
+ + 
+ -         Log.d(TAG_ENTER_EFFECT, "getUnhandledUserEnterNotifyMap:$map")
+ + 
+ -         Log.d(
+ -             TAG_ENTER_EFFECT_DOWNLOAD,
+ -             "addDownloadResourceTask, start download, url:$url, task:$task"
+ -         )
+ + 
+ -                 Log.d(
+ -                     TAG_ENTER_EFFECT_DOWNLOAD,
+ -                     "addDownloadGiftEffectTask, download success, task:$task"
+ -                 )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\entereffect-export\src\main\java\com\adealink\weparty\effect\CarEffectView.kt
+ -         Log.d(TAG, "play, entity:$entity, listener:$l")
+ + 
+ -             Log.d(TAG, "play, updateBanner")
+ + 
+ -             Log.d(TAG, "play, updateBanner, onlyVip")
+ + 
+ -                 Log.d(TAG, "play, onComplete")
+ + 
+ -                 Log.d(TAG, "play, onError:$errCode")
+ + 
+ -                     Log.d(TAG, "play, showBanner, onComplete")
+ + 
+ -                     Log.d(TAG, "play, showBanner, onError:$errCode")
+ + 
+ -         Log.d(TAG, "stop")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\game\src\main\java\com\adealink\weparty\game\miccharmpk\manager\MicCharmPKManager.kt
+ -             Log.d(
+ -                 TAG_MIC_CHARM_PK,
+ -                 "handleMicCharmPKInfo, currentPKInfo:${currentPKInfo}, info:${info}"
+ -             )
+ + 
+ -         Log.d(TAG_MIC_CHARM_PK, "clearMicCharmPK, currentPKInfo:${currentPKInfo}")
+ + 
+ -         Log.d(TAG_MIC_CHARM_PK, "notifyMicCharmPKStart, info:${info}")
+ + 
+ -         Log.d(TAG_MIC_CHARM_PK, "notifyMicCharmPKChanged, userDiamonds:${userDiamonds}")
+ + 
+ -         Log.d(TAG_MIC_CHARM_PK, "notifyMicCharmPKFinished, results:${results}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\game\src\main\java\com\adealink\weparty\game\redpacket\manager\RedPacketManager.kt
+ -         Log.d(TAG_RED_PACKET, "handleRedPacketQueueNotify:$notify")
+ + 
+ -         Log.d(TAG_RED_PACKET, "handleRedPacketGrabNotify:$notify")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\game\src\main\java\com\adealink\weparty\game\rocket\dialog\RocketPanelDialog.kt
+ -                     Log.d(TAG_ROCKET_EFFECT, "playRocketVapVideo, onVideoDestroy")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\game\src\main\java\com\adealink\weparty\game\rocket\manager\IRocketManager.kt
+ -                 Log.d(TAG_ROOM_GLOBAL_BROADCAST, "canShowRocketNotify level1 should not be displayed")
+ + 
+ -                 Log.d(TAG_ROOM_GLOBAL_BROADCAST, "canShowRocketNotify level2 should not be displayed")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\game\src\main\java\com\adealink\weparty\game\rocket\manager\RocketManager.kt
+ -         Log.d(TAG_ROCKET, "handleAddRoomCoinsNotify, $notify")
+ + 
+ -         Log.d(TAG_ROCKET, "handleRocketUpgradeNotify, $notify")
+ + 
+ -         Log.d(TAG_ROCKET, "handleRocketRewardNotify, $notify")
+ + 
+ -         Log.d(TAG_ROCKET, "addRocketUpgradeNotify, $notify")
+ + 
+ -                 Log.d(TAG_ROCKET, "addRocketUpgradeNotify return, for can't show")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\game\src\main\java\com\adealink\weparty\game\rocket\viewmodel\RocketViewModel.kt
+ -             Log.d(TAG_ROOM_GLOBAL_BROADCAST, "onLevelUpgrade.canShowRocketNotify")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\game\src\main\java\com\adealink\weparty\game\roulette\dialog\RouletteDialog.kt
+ -         Log.d(TAG_ROULETTE_DIALOG, "pollGameInfo")
+ + 
+ -                 Log.d(TAG_ROULETTE_DIALOG,
+ -                     "startJoinCountDown, onTick: $millisUntilFinished, roundMillis:${roundSecond}, gameInfo.leftSecond:${gameInfo?.leftSecond}")
+ + 
+ -                 Log.d(TAG_ROULETTE_DIALOG,
+ -                     "startStartGameCountDown, onTick: $millisUntilFinished, roundMillis:${roundSecond}, gameInfo.leftSecond:${gameInfo?.leftSecond},")
+ + 
+ -                 Log.d(TAG_ROULETTE_DIALOG,
+ -                     "startBettingCountDown, onTick: $millisUntilFinished, roundMillis:${roundSecond}, gameInfo.leftSecond:${gameInfo?.leftSecond},")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\gift\src\main\java\com\adealink\weparty\gift\GiftPanelFragment.kt
+ -             Log.d(TAG_GIFT, "getAvailableGiftTypes(): ${it.joinToString(",")} ")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\gift\src\main\java\com\adealink\weparty\gift\manager\GiftManager.kt
+ -         Log.d(TAG_GIFT_GET, "getGifts, forceNet:$forceNet")
+ + 
+ -         Log.d(TAG_GIFT_GET, "getCustomGiftConfig")
+ + 
+ -         Log.d(TAG_GIFT_GET, "getFreeGiftConfig")
+ + 
+ -         Log.d(
+ -             TAG_GIFT_DOWNLOAD,
+ -             "addDownloadGiftEffectTask, start download, giftInfo:$giftInfo, task:$task"
+ -         )
+ + 
+ -                 Log.d(
+ -                     TAG_GIFT_DOWNLOAD,
+ -                     "addDownloadGiftEffectTask, download success, task:$task"
+ -                 )
+ + 
+ -         Log.d(
+ -             TAG_GIFT_DOWNLOAD,
+ -             "addDownloadFlashEffectTask, start download, url:$url, task:$task"
+ -         )
+ + 
+ -                 Log.d(
+ -                     TAG_GIFT_DOWNLOAD,
+ -                     "addDownloadFlashEffectTask, download success, task:$task"
+ -                 )
+ + 
+ -             Log.d(TAG_GIFT_SEND, "handleSendGiftNotify, sendGiftNotify:${sendGiftNotify}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\gift\src\main\java\com\adealink\weparty\gift\treasure\TreasureGiftManager.kt
+ -         Log.d(TAG_GIFT_TREASURE, "handlePacketQueueNotify:$notify")
+ + 
+ -         Log.d(TAG_GIFT_TREASURE, "handlePacketGrabNotify:$notify")
+ + 
+ -         Log.d(TAG_GIFT_TREASURE, "handleTreasureGiftGlobalNotify:$notify")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\gift\src\main\java\com\adealink\weparty\gift\treasure\view\TreasureGiftFragment.kt
+ -                     Log.d(TAG_GIFT_TREASURE, "playPacketVapVideo, onVideoDestroy")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\gift\src\main\java\com\adealink\weparty\gift\viewmodel\GiftViewModel.kt
+ -         Log.d(TAG_GIFT, "selectGift, id:${giftInfo.id}, name:${giftInfo.name}")
+ + 
+ -         Log.d(
+ -             TAG_GIFT,
+ -             "selectBackPackItem, id:${userPackageInfo.id}, name:${userPackageInfo.goodsType}"
+ -         )
+ + 
+ -             Log.d(TAG_GIFT, "onFreeGiftInfoUpdated return ,for gift isn't free")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\guardtreasure\src\main\java\com\adealink\weparty\guardtreasure\manager\GuardTreasureManager.kt
+ -                 Log.d(TAG, "loadGuardTreasureConfig return, for config is null")
+ + 
+ -         Log.d(TAG, "checkGuardTreasureResource")
+ + 
+ -                 Log.d(TAG, "downloadNecessaryResource, path:$savePath")
+ + 
+ -                     Log.d(TAG, "downloadNecessaryResource return, file exists")
+ + 
+ -         Log.d(TAG, "handleInviteNotify, $notify")
+ + 
+ -             Log.d(TAG, "goClubRoom return, for already in club room.")
+ + 
+ -             Log.d(TAG, "showGuardInviteDialog return, for boxId / roomId is 0")
+ + 
+ -         Log.d(TAG, "handleInviteConfirmNotify, $notify")
+ + 
+ -             Log.d(TAG, "goClubRoom return, for already in club room.")
+ + 
+ -         Log.d(TAG, "handleGuardSuccessNotify, $notify")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\guardtreasure\src\main\java\com\adealink\weparty\guardtreasure\viewmodel\GuardTreasureViewModel.kt
+ -             Log.d(TAG, "sendInvite: boxId:$boxId, inviteUid:$inviteUid")
+ + 
+ -             Log.d(TAG, "openGuardTreasure")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\headline\src\main\java\com\adealink\weparty\headline\fragment\HeadlineFragment.kt
+ -         Log.d(TAG_HEADLINE, "onComplete, showNextHeadLine")
+ + 
+ -         Log.d(TAG_HEADLINE, "onError($errCode), showNextHeadLine")
+ + 
+ -             Log.d(TAG_HEADLINE, "findIdleEffectView, headlineEffectView1")
+ + 
+ -             Log.d(TAG_HEADLINE, "findIdleEffectView, headlineEffectView2")
+ + 
+ -         Log.d(TAG_HEADLINE, "showNextHeadLine")
+ + 
+ -             Log.d(TAG_HEADLINE, "showNextHeadLine return, for no idle effectView")
+ + 
+ -             Log.d(TAG_HEADLINE, "showNextHeadLine return, effect entity is empty")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\headline\src\main\java\com\adealink\weparty\headline\manager\HeadlineManager.kt
+ -         Log.d(TAG_HEADLINE, "handleGlobalRoomBroadcastNotify: $notify")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\headline\src\main\java\com\adealink\weparty\headline\viewmodel\HeadlineViewModel.kt
+ -         Log.d(TAG_ROOM_GLOBAL_BROADCAST, "HeadlineViewModel init: $this")
+ + 
+ -             Log.d(TAG_ROOM_GLOBAL_BROADCAST, "onHeadlineNotify send gift should not be displayed")
+ + 
+ -             Log.d(TAG_ROOM_GLOBAL_BROADCAST, "onHeadlineNotify lucky fruit should not be displayed")
+ + 
+ -             Log.d(TAG_ROOM_GLOBAL_BROADCAST, "onHeadlineNotify slot should not be displayed")
+ + 
+ -             Log.d(TAG_ROOM_GLOBAL_BROADCAST, "onHeadlineNotify level1 treasure should not be displayed")
+ + 
+ -             Log.d(TAG_ROOM_GLOBAL_BROADCAST, "onHeadlineNotify level2 treasure should not be displayed")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\image\src\main\java\com\adealink\weparty\image\viewmodel\VideoViewModel.kt
+ -         Log.d(
+ -             TAG_VIDEO_DOWNLOAD,
+ -             "addDownloadTask, start download, url:$url, task:$task"
+ -         )
+ + 
+ -                 Log.d(
+ -                     TAG_VIDEO_DOWNLOAD,
+ -                     "addDownloadTask, download success, task:$task"
+ -                 )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\level\src\main\java\com\adealink\weparty\level\adapter\LevelBagListViewBinder.kt
+ -                     Log.d(TAG_LEVEL, "scroll :$initIndex ")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\level\src\main\java\com\adealink\weparty\level\data\LevelData.kt
+ -             Log.d(TAG_LEVEL, "LevelBirthdayBagListItemData same: $same")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\level\src\main\java\com\adealink\weparty\level\LevelDetailActivity.kt
+ -             Log.d(TAG_LEVEL, "refresh list..")
+ + 
+ -         Log.d(TAG_LEVEL, "targetLevel: $targetLevel")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\level\src\main\java\com\adealink\weparty\level\LevelRewardReceiveDialog.kt
+ -         Log.d(TAG_LEVEL, "sendLevelReward $requestLevel")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\level\src\main\java\com\adealink\weparty\level\manager\LevelManager.kt
+ -         Log.d(TAG_LEVEL, "handleLevelRewardNotify: $notify")
+ + 
+ -         Log.d(TAG_LEVEL, "handleLevelUpgradeNotify, $notify")
+ + 
+ -         Log.d(TAG_LEVEL, "showLevelUpgradeDialog")
+ + 
+ -                     Log.d(TAG_LEVEL, "checkBirthdayDay return, for birthday bag msg is empty.")
+ + 
+ -                         Log.d(TAG_LEVEL, "checkBirthdayDay return, for birthday bag reward parse fail.")
+ + 
+ -         Log.d(TAG_LEVEL, "handleReceiveBirthdayBagMsg")
+ + 
+ -                     Log.d(TAG_LEVEL, "handleReceiveBirthdayBagMsg return, for download birthday effect fail.")
+ + 
+ -         Log.d(
+ -             TAG_LEVEL,
+ -             "addDownloadTask, start download, url:$url, task:$task"
+ -         )
+ + 
+ -                 Log.d(
+ -                     TAG_LEVEL,
+ -                     "addDownloadTask, download success, task:$task"
+ -                 )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\level\src\main\java\com\adealink\weparty\level\viewmodel\LevelViewModel.kt
+ -             Log.d(TAG, "loadUserLevelInfoList, uid:$uid")
+ + 
+ -                     Log.d(TAG_LEVEL, "shouldShowIndex $shouldShowIndex")
+ + 
+ -             Log.d(TAG_LEVEL, "send levelListLiveData")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\ludo\src\main\java\com\adealink\weparty\ludo\manager\LudoManager.kt
+ -         Log.d(TAG_GAME_LUDO, "handleEntranceChanged: GLOBAL_LUDO_ENTRANCE, $json")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\ludo\src\main\java\com\adealink\weparty\ludo\method\OnGameOp.kt
+ -         Log.d(TAG_GAME_LUDO_ON_GAME_OP, "data:${data}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\medal\src\main\java\com\adealink\weparty\medal\layout\MyMedalItemTouchHelperCallback.kt
+ -         Log.d(TAG, "getMovementFlags")
+ + 
+ -         Log.d(
+ -             TAG,
+ -             "onMove: originPosition: ${viewHolder.bindingAdapterPosition}, targetPosition:${target.bindingAdapterPosition}"
+ -         )
+ + 
+ -         Log.d(TAG, "onSwiped: direction:${direction}")
+ + 
+ -         Log.d(TAG, "onSelectedChanged:$viewHolder, $actionState")
+ + 
+ -             Log.d(TAG, "start drag position:$startDragPosition")
+ + 
+ -         Log.d(TAG, "clearView")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\medal\src\main\java\com\adealink\weparty\medal\manager\MedalManager.kt
+ -         Log.d(TAG, "handleMedalAchieveNotify: $notify")
+ + 
+ -         Log.d(TAG, "prepareInner,  pullHistoryMedalAchieve: $pullHistoryMedalAchieve")
+ + 
+ -         Log.d(TAG, "refreshMedalCache")
+ + 
+ -         Log.d(TAG, "getHistoryMedalAchieveList")
+ + 
+ -             Log.d(TAG, "checkCacheIfNeedUpdate return false, don't need to update")
+ + 
+ -         Log.d(TAG, "checkCacheIfNeedUpdate return true, cache is expired")
+ + 
+ -         Log.d(TAG, "getMedalByMedalId, medalIds:${medalIds.toLongArray()}")
+ + 
+ -         Log.d(TAG, "showNextMedalAchieveRecord, alreadyRead:$alreadyRead, medalId:$medalId")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\medal\src\main\java\com\adealink\weparty\medal\view\MedalView.kt
+ -                     Log.d(TAG, "setSvgaView return, for autoPlay is false.")
+ + 
+ -                         Log.d(TAG, "setVapView return, for autoPlay is false")
+ + 
+ -         Log.d(
+ -             TAG,
+ -             "addDownloadTask, start download, url:$url, task:$task"
+ -         )
+ + 
+ -                 Log.d(
+ -                     TAG,
+ -                     "addDownloadTask, download success, task:$task"
+ -                 )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\medal\src\main\java\com\adealink\weparty\medal\viewbinder\MyEquippedMedalItemViewBinder.kt
+ -         Log.d(TAG, "onViewAttachedToWindow, position:${holder.layoutPosition}")
+ + 
+ -         Log.d(TAG, "onViewDetachedFromWindow, position:${holder.layoutPosition}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\medal\src\main\java\com\adealink\weparty\medal\viewmodel\MedalViewModel.kt
+ -         Log.d(TAG, "adjustEquippedMedalPosition, from:$fromPosition, to:$toPosition")
+ + 
+ -                 Log.d(TAG, "equipMedal return, for already equipped.")
+ + 
+ -                 Log.d(TAG, "equipMedal return, equip too many medal, can not equip more.")
+ + 
+ -                 Log.d(TAG, "unEquipMedal return, for already unEquipped.")
+ + 
+ -                 Log.d(TAG, "unEquipMedal return, for no medal can be unEquipped.")
+ + 
+ -                 Log.d(TAG, "saveImage fail, for shotView fail.")
+ + 
+ -                 Log.d(TAG, "saveImage fail, for can not find imgName.")
+ + 
+ -             Log.d(TAG, "saveImgAndUpload: ---> shot View, path:$shotPath")
+ + 
+ -             Log.d(TAG, "saveImgAndUpload: ---> upload img, res: $uploadRtl")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\message\src\main\java\com\adealink\weparty\message\manager\MessageManager.kt
+ -         Log.d(TAG_MESSAGE_LIST, "handleNewMessage: $sessionInfo")
+ + 
+ -         Log.d(TAG_MESSAGE_LIST, "refreshSessionList")
+ + 
+ -                 Log.d(TAG_MESSAGE_LIST, "getCacheUserInfos, uids:${uids} failed, rlt:$rlt")
+ + 
+ -         Log.d(TAG_MESSAGE_LIST, "getSessionList")
+ + 
+ -         Log.d(TAG_MESSAGE_LIST, "updateSession: $sessionInfo")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\message\src\main\java\com\adealink\weparty\message\MessageListFragment.kt
+ -             Log.d(TAG_MESSAGE_LIST, "officialRewardData: $it")
+ + 
+ -             Log.d(TAG_MESSAGE_LIST, "sortList: $temp")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\message\src\main\java\com\adealink\weparty\message\sessiondetail\MessageItemOfficialRewardViewBinder.kt
+ -         Log.d(TAG_MESSAGE_DETAIL, "bindRewardList")
+ + 
+ -         Log.d(TAG_MESSAGE_DETAIL, "reward: $data")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\message\src\main\java\com\adealink\weparty\message\sessiondetail\viewmodel\SessionDetailViewModel.kt
+ -             Log.d(TAG_MESSAGE_DETAIL, "local and new loadNewMessagesFromRemote: $sessionInfo")
+ + 
+ -             Log.d(TAG_MESSAGE_DETAIL, "loadNewMessages: $sessionInfo")
+ + 
+ -         Log.d(TAG_MESSAGE_DETAIL, "loadMessagesFromLocal, sessionId:$sessionId, messageId:$messageId, official1V1MessageId:$official1V1MessageId")
+ + 
+ -         Log.d(TAG_MESSAGE_DETAIL, "loadMessagesFromLocal, localMessages:$localMessages")
+ + 
+ -         Log.d(TAG_MESSAGE_DETAIL, "loadMessagesFromRemote: sessionId:$sessionId, sessionMessageReadOffset:$sessionMessageReadOffset, sessionOfficial1V1ReadOffset:$sessionOfficial1V1ReadOffset")
+ + 
+ -         Log.d(TAG_MESSAGE_DETAIL, "loadHistoryMessagesFromRemote: sessionId:$sessionId, sessionMessageReadOffset:$sessionMessageReadOffset, sessionOfficial1V1ReadOffset:$sessionOfficial1V1ReadOffset")
+ + 
+ -                 Log.d(TAG_MESSAGE_COMPETITIVE_WORD, "send message_competitive_keywords error")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\music\src\main\java\com\adealink\wepary\music\manager\MusicManager.kt
+ -             Log.d(TAG_MUSIC_LIST, "getMyMusic, validMusicList:$validMusicList, insertMusicList:$insertMusicList, deleteMusicList:$deleteMusicList")
+ + 
+ -             Log.d(TAG_MUSIC_PLAY, "play, currPlayMusic:$currPlayMusic")
+ + 
+ -             Log.d(TAG_MUSIC_PLAY, "playNext")
+ + 
+ -             Log.d(TAG_MUSIC_PLAY, "pause, currPlayMusic:$currPlayMusic")
+ + 
+ -             Log.d(TAG_MUSIC_PLAY, "resume, currPlayMusic:$currPlayMusic")
+ + 
+ -             Log.d(TAG_MUSIC_PLAY, "stop, currPlayMusic:$currPlayMusic, reason:${reason}")
+ + 
+ -                 Log.d(TAG_MUSIC_PLAY, "setPlayMode, no change, playMode:$playMode")
+ + 
+ -             Log.d(TAG_MUSIC_PLAY, "setPlayMode, currPlayMode:$playMode, newPlayMode:$mode")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\operation\src\main\java\com\adealink\weparty\operation\invite\comp\BigSupportInviteShareComp.kt
+ -             Log.d(TAG, "onShareSuccess, channel:$channel")
+ + 
+ -             Log.d(TAG, "onShareFailed, channel:$channel, code:$code")
+ + 
+ -             Log.d(TAG, "onShareCancel")
+ + 
+ -             Log.d(TAG, "doShare return, for shareLink is null")
+ + 
+ -         Log.d(TAG, "doShare, shareLink:${linkData.shareLink}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\operation\src\main\java\com\adealink\weparty\operation\invite\viewmodel\BigSupportInviteViewModel.kt
+ -             Log.d(TAG, "getShareLink: 1.generate image and upload to get image link")
+ + 
+ -             Log.d(TAG, "            | imageLink:$imageLink")
+ + 
+ -             Log.d(TAG, "getShareLink, 2.generate share link: $sharePageLink")
+ + 
+ -         Log.d(TAG, "saveImgAndUpload: ---> shot View, path:$shotPath")
+ + 
+ -         Log.d(TAG, "saveImgAndUpload: ---> upload img, res: $uploadRtl")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\pk\src\main\java\com\adealink\weparty\pk\manager\PKManager.kt
+ -         Log.d(TAG_PK, "handleRoomPkAddScoreNotify:$data")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\profile\src\main\java\com\adealink\weparty\profile\country\manager\CountryManager.kt
+ -         Log.d(TAG_COUNTRY, "getCountryConfig, forceNet:$forceNet")
+ + 
+ -             Log.d(TAG_COUNTRY, "onCountrySelect,code:${countryCode}")
+ + 
+ -                         Log.d(TAG_COUNTRY, "getRemoteCountryConfig,size:${res.countryList.size}")
+ + 
+ -                 Log.d(TAG_COUNTRY, "notifyCountries,size:${countries.size}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\profile\src\main\java\com\adealink\weparty\profile\manager\ProfileManager.kt
+ -             Log.d(TAG_PROFILE, "handleLadyPrivilegeNotify:$notify")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\profile\src\main\java\com\adealink\weparty\profile\userprofile\ProfileCoverPreviewActivity.kt
+ -             Log.d(TAG_PROFILE, "$it")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\profile\src\main\java\com\adealink\weparty\profile\viewmodel\ProfileViewModel.kt
+ -             Log.d(TAG_PROFILE, "updateProfileCover, coverUrl:${coverUrl}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\activity\RoomActivityDragView.kt
+ -                 Log.d(TAG_DRAG_VIEW, "onLongPress(RoomActivityDragView)")
+ + 
+ -         Log.d(TAG_DRAG_VIEW, "onInterceptTouchEvent(RoomActivityDragView), isDragging: $isDragging, ${ev?.let { MotionEvent.actionToString(it.action) }}")
+ + 
+ -         Log.d(TAG_DRAG_VIEW, "dispatchTouchEvent(RoomActivityDragView), $ev")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\attr\info\RoomInfoFragment.kt
+ -         Log.d(
+ -             TAG_ROOM,
+ -             "handleChangeMicModeResult: micMode:${micMode.name}, changeResult:$changeResult"
+ -         )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\attr\setting\RoomSettingFragment.kt
+ -                         Log.d(TAG_ROOM_GLOBAL_BROADCAST, "isChecked: $isChecked")
+ + 
+ -             Log.d(TAG_ROOM_GLOBAL_BROADCAST, "change state: $it")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\attr\widget\RoomMicModeSwitcher.kt
+ -         Log.d(TAG, "setDefaultType: ${micMode.name}")
+ + 
+ -         Log.d(TAG, "switchTo: ${micMode.name}, switchResult:$switchResult")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\bettingpk\widget\RoomBettingPkDragView.kt
+ -         Log.d(TAG_DRAG_VIEW, "onInterceptTouchEvent(RoomBettingPkDragView), isDragging: $isDragging, ${ev?.let { MotionEvent.actionToString(it.action) }}")
+ + 
+ -         Log.d(TAG_DRAG_VIEW, "dispatchTouchEvent(RoomBettingPkDragView), ${ev?.let { MotionEvent.actionToString(it.action) }}")
+ + 
+ -                     Log.d(TAG_DRAG_VIEW, "onDown(RoomBettingPkDragView.ivWaiting), ${MotionEvent.actionToString(e.action)}")
+ + 
+ -                     Log.d(TAG_DRAG_VIEW, "onSingleTapUp(RoomBettingPkDragView.ivWaiting), ${MotionEvent.actionToString(e.action)}")
+ + 
+ -                     Log.d(TAG_DRAG_VIEW, "onLongPress(RoomBettingPkDragView.ivWaiting), ${MotionEvent.actionToString(e.action)}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\car\LudoRoomCarComp.kt
+ -             Log.d(TAG_LUDO_CAR_COMP, "addWelcomeMsg return, for msg is null/empty")
+ + 
+ -             Log.d(TAG_LUDO_CAR_COMP, "addWelcomeMsg return, for owner uid is null")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\car\RoomCarComp.kt
+ -         Log.d(TAG_CAR_COMP, "observeViewModels")
+ + 
+ -         Log.d(TAG_CAR_COMP, "fetchUnhandledUserEnterNotify:$unhandledNotifyList")
+ + 
+ -         Log.d(TAG_CAR_COMP, "handleEnterRoomNotify: $notify")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\car\viewmodel\RoomCarViewModel.kt
+ -                 Log.d(TAG_CAR_COMP, "addWelcomeMsg return, for msg is null/empty")
+ + 
+ -                     Log.d(TAG_CAR_COMP, "addWelcomeMsg return, for owner uid is null")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\chat\ChatMessageFragment.kt
+ -         Log.d(TAG, "clickUnreadText")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\chat\livemessage\LiveMessageRecyclerHelper.kt
+ -         Log.d(TAG, "scrollToBottom")
+ + 
+ -         Log.d(TAG, "scrollToUnreadPosition")
+ + 
+ -         Log.d(TAG, "scrollToPosition, position:$position")
+ + 
+ -         Log.d(TAG, "scrollToCurrentReadPosition")
+ + 
+ -         Log.d(TAG, "notifyAndScrollToBottom: newCount:$newCount")
+ + 
+ -             Log.d(TAG, "notifyAndScrollToBottom: scroll")
+ + 
+ -             Log.d(TAG, "notifyAndScrollToBottom: scrolling")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\chat\manager\ChatMessageManager.kt
+ -             Log.d(TAG, "onNotify, data:${data}")
+ + 
+ -         Log.d(TAG, "addMessage:${msg}")
+ + 
+ -         Log.d(TAG, "addMessages:${msgList.size}")
+ + 
+ -         Log.d(TAG, "addMessageInner:${msgList.size}")
+ + 
+ -                 Log.d(TAG, "addMessageInner, size achieve limit")
+ + 
+ -         Log.d(TAG, "clearMessage")
+ + 
+ -         Log.d(TAG, "addNoticeMessage(roomType:$roomType)")
+ + 
+ -         Log.d(TAG, "updateLastAtMeMessage, $msg")
+ + 
+ -         Log.d(TAG, "updateInputHistory, input:${input}")
+ + 
+ -         Log.d(TAG, "clearHistoryInput")
+ + 
+ -         Log.d(TAG, "onRoomStateChanged: fromState:${fromState.name}, toState:${toState.name}")
+ + 
+ -         Log.d(TAG, "onRoomIn:$roomId, curRoomId:$joinedRoomId")
+ + 
+ -         Log.d(TAG, "onRoomLeaved")
+ + 
+ -         Log.d(TAG, "notifyMessageChanged")
+ + 
+ -         Log.d(TAG, "notifyMessageUpdate, index:$index")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\chat\viewmodel\ChatMessageViewModel.kt
+ -             Log.d(TAG_ROOM_CHAT, "chatStatusChangedNotify, data:$data")
+ + 
+ -                         Log.d(TAG_BELL_INVITED, "sendChatMessage error MessageContentCompetitiveWordsError")
+ + 
+ -             Log.d(TAG_ROOM_CHAT, "sayHi return, for toUid is 0")
+ + 
+ -                 Log.d(TAG_ROOM_CHAT, "sayHi return, for toUserInfo is null")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\chatroom\ChatRoomFragment.kt
+ -         Log.d(TAG_ROOM, "chat fragment initComponents")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\couple\CoupleComp.kt
+ -             Log.d(TAG_ROOM_COUPLE, "coupleOnMicNotify:$notify")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\FlashEffectController.kt
+ -         Log.d(TAG, "effect flashanimSet cancel: $flashId")
+ + 
+ -             Log.d(TAG, "remove view: $flashId")
+ + 
+ -                         Log.d(TAG, "need to play anim again: $view flashid: $flashId")
+ + 
+ -                         Log.d(TAG, "clear data : $flashId")
+ + 
+ -         Log.d(TAG, " anim start : $flashId")
+ + 
+ -         Log.d(TAG, " bottom : $topRy")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\game\redpacket\RedPacketComp.kt
+ -                 Log.d(TAG_RED_PACKET_COMP,
+ -                     "pullRoomRedPacketQueue: ids:${it.data.map { redPacketInfo -> redPacketInfo.redPacketId }}"
+ -                 )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\game\rocket\RoomRocketComp.kt
+ -                             Log.d(TAG_ROOM_ROCKET, "onRocketUpgradeEffectComplete")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\game\RoomGameDragView.kt
+ -                 Log.d(TAG_DRAG_VIEW, "onLongPress(RoomGameDragView)")
+ + 
+ -         Log.d(TAG_DRAG_VIEW, "onInterceptTouchEvent(RoomGameDragView), isDragging: $isDragging")
+ + 
+ -         Log.d(TAG_DRAG_VIEW, "dispatchTouchEvent(RoomGameDragView), $ev")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\gift\view\RoomGiftComp.kt
+ -                             Log.d(TAG_ROOM_GIFT, "CommonGiftEffectEntity play onComplete")
+ + 
+ -                                     Log.d(TAG_ROOM_GIFT, "CommonGiftEffectEntity play onComplete")
+ + 
+ -                                     Log.d(TAG_ROOM_GIFT, "CommonGiftEffectEntity play onComplete")
+ + 
+ -                                     Log.d(TAG_ROOM_GIFT, "VideoEffectEntity play onComplete")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\gift\widget\GiftFallingLayout.kt
+ -         Log.d(TAG, "setEndPosition: x:$x, y:$y")
+ + 
+ -         Log.d(TAG, "startFalling")
+ + 
+ -             Log.d(TAG, "startFalling return, for fallingPath is null")
+ + 
+ -             Log.d(TAG, "startFalling return, for path.length <= 0.")
+ + 
+ -         Log.d(TAG, "moveToEndPosition: endX:$endX, endY:$endY")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\gift\widget\SendGiftNoticeLayout.kt
+ -         Log.d(TAG, "onLayout, measuredHeight:${measuredHeight}")
+ + 
+ -         Log.d(TAG, "findAvailableNoticeViewCount: availableHeight:$availableHeight, availableCount:$availableCount")
+ + 
+ -             Log.d(TAG, "addNotice return, for noticeView is not ready")
+ + 
+ -             Log.d(TAG, "addNotice return, for channelView is 0")
+ + 
+ -         Log.d(TAG, "addNotice: $giftIcon (x$giftCount)")
+ + 
+ -             Log.d(TAG, "playNext return, for no available notice view.")
+ + 
+ -             Log.d(TAG, "playNext return, for noticeQueue is empty.")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\guardtreasure\GuardTreasureComp.kt
+ -             Log.d(TAG, "guardSuccessNotifyLD, $it")
+ + 
+ -             Log.d(TAG, "scheduleExitRoomByGuardSuccess, delay:$roomCloseSeconds seconds")
+ + 
+ -         Log.d(TAG, "checkGuardStatus: $status")
+ + 
+ -             Log.d(TAG, "checkGuardStatus return, for unStart")
+ + 
+ -             Log.d(TAG, "checkGuardStatus return, for guarding")
+ + 
+ -         Log.d(TAG, "checkGuardStatus, exitRoom for guard fail")
+ + 
+ -         Log.d(TAG, "schedulePlayTreasureReadyAnim")
+ + 
+ -         Log.d(TAG, "playTreasureReadyAnim")
+ + 
+ -             Log.d(TAG, "checkPartnerLeaveOrNot return")
+ + 
+ -             Log.d(TAG, "checkPartnerMicOnOrNot return, for I am not partner")
+ + 
+ -             Log.d(TAG, "checkPartnerMicOnOrNot return, for already on mic")
+ + 
+ -             Log.d(TAG, "checkPartnerMicOnOrNot, owner auto on mic")
+ + 
+ -             Log.d(TAG, "checkPartnerMicOnOrNot, partner on mic")
+ + 
+ -         Log.d(TAG, "onRoomLeaved")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\interceptor\EnterRoomUriInterceptor.kt
+ -         Log.d(TAG_ROOM_ENTER_ROOM, "EnterRoomUriInterceptor, getEnterRoomInfo from enterRoomInfo, $enterRoomInfo")
+ + 
+ -             Log.d(TAG_ROOM_ENTER_ROOM, "EnterRoomUriInterceptor, getEnterRoomInfo from BigSupportInvite, $enterRoomInfo")
+ + 
+ -             Log.d(TAG_ROOM_ENTER_ROOM, "EnterRoomUriInterceptor, getEnterRoomInfo from roomId, $enterRoomInfo")
+ + 
+ -             Log.d(TAG_ROOM_ENTER_ROOM, "EnterRoomUriInterceptor, already joined room, proceed()")
+ + 
+ -         Log.d(TAG_ROOM_ENTER_ROOM, "getEnterRoomInfoByInvite, from Intent, inviteUidCode:$inviteUidCode, inviteRoomIdCode:$inviteRoomIdCode, shareScene:$shareScene")
+ + 
+ -             Log.d(TAG_ROOM_ENTER_ROOM, "getEnterRoomInfoByInvite, from Local, inviteUidCode:$inviteUidCode, inviteRoomIdCode:$inviteRoomIdCode")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\invite\manager\CountDownTimerManager.kt
+ -                 Log.d(TAG_ROOM_GLOBAL_BROADCAST, "on Tick: $millisUntilFinished")
+ + 
+ -                 Log.d(TAG_ROOM_GLOBAL_BROADCAST, "on onFinish")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\invite\manager\InvitedBellManager.kt
+ -             Log.d(TAG_BELL_INVITED, "InviteBellHeadlineData: $data")
+ + 
+ -             Log.d(TAG_ROOM_GLOBAL_BROADCAST, "cacheLastNotify: ${data.remainTime}")
+ + 
+ -         Log.d(TAG_BELL_INVITED, "register receiver invited enter room listener")
+ + 
+ -         Log.d(TAG_BELL_INVITED, "onLogout")
+ + 
+ -         Log.d(TAG_ROOM_GLOBAL_BROADCAST, "resetCacheNotify")
+ + 
+ -         Log.d(TAG_ROOM_GLOBAL_BROADCAST, "canShowNextInvitedBellLayout: ${hasLastInvitedBellRemainTime()}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\invite\manager\RoomInvitedBellManager.kt
+ -         Log.d(TAG_BELL_INVITED, "RoomInvitedBellManager init: ")
+ + 
+ -         Log.d(TAG_BELL_INVITED, "register room enter listener: ")
+ + 
+ -         Log.d(
+ -             TAG_BELL_INVITED,
+ -             "onRoomIn: $roomId , RoomModule.isIAmRoomOwner(): ${RoomModule.isIAmRoomOwner()}"
+ -         )
+ + 
+ -         Log.d(TAG_BELL_INVITED, "onRoomLeaved: ")
+ + 
+ -             Log.d(TAG_BELL_INVITED, "uri_new_room_invite_confirm: $data");
+ + 
+ -             Log.d(TAG_BELL_INVITED, "uri_new_room_invite_build onNotifyShowBell: $data");
+ + 
+ -         Log.d(TAG_BELL_INVITED, "RoomInviteBellUserNotify: $data")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\invite\RoomInvitedBellComp.kt
+ -         Log.d(TAG_BELL_INVITED, "I am is room owner RoomInvitedBellComp initiate")
+ + 
+ -                 Log.d(TAG_BELL_INVITED, "isNewRoom : $isNewRoom")
+ + 
+ -         Log.d(TAG_BELL_INVITED, "sendConfirmJoinRoomIfNeed.from: $from")
+ + 
+ -         Log.d(TAG_BELL_INVITED, "sendConfirmJoinRoomIfNeed.invitedBell: $fromInvitedBell")
+ + 
+ -             Log.d(TAG_BELL_INVITED, "sendConfirmJoinRoomIfNeed: ");
+ + 
+ -         Log.d(TAG_BELL_INVITED, "RoomInvitedBellComp onNewIntent: $fromInvitedBell")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\invite\stat\InvitedBellStatEvent.kt
+ -             Log.d(TAG_BELL_INVITED, "reportShow: ")
+ + 
+ -             Log.d(TAG_BELL_INVITED, "reportClick: ")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\invite\view\InvitedBellFloatView.kt
+ -         Log.d(TAG_BELL_INVITED, "startAnimation: ${hashCode()}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\invite\view\RoomInvitedBellTimeLayout.kt
+ -         Log.d(TAG_BELL_INVITED, "onDetachedFromWindow")
+ + 
+ -         Log.d(TAG_BELL_INVITED, "reset")
+ + 
+ -         Log.d(TAG_BELL_INVITED, "startCountDownTime: $millisInFuture")
+ + 
+ -             Log.d(TAG_BELL_INVITED, "playSvga return, for !isVisible or !isAttachedToWindow")
+ + 
+ -             Log.d(TAG_BELL_INVITED, "playSvga, using cache.")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\invite\viewmodel\RoomInvitedBellViewModel.kt
+ -             Log.d(TAG_BELL_INVITED, "getInviteBellInfo: $value")
+ + 
+ -                 Log.d(TAG_BELL_INVITED, "initInviteBellState: $msg")
+ + 
+ -             Log.d(TAG_BELL_INVITED, "confirmJoinRoom: $roomId")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\ludo\micseat\LudoMicSeatFragment.kt
+ -         Log.d(TAG, "switchMicMode, mode:${mode.name}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\micseat\BaseSeatView.kt
+ -         Log.d(
+ -             TAG_ROOM_SEAT,
+ -             "handleMicSeatInfo, index:${index.index}, newMicSeatInfo:$micSeatInfo, oldMicSeatInfo:${this.micSeatInfo}"
+ -         )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\micseat\club\ClubSeatsTemplate.kt
+ -         Log.d(TAG, "switchMicMode, mode:${mode.name}, layout:${layout.name}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\micseat\ClubMicSeatFragment.kt
+ -         Log.d(TAG, "switchMicMode, mode:${mode.name}, layout:${layout.name}")
+ + 
+ -         Log.d(TAG, "switchMicSeatTemplate, template:${template.name}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\micseat\defaults\DefaultSeatsTemplate.kt
+ -         Log.d(TAG, "switchMicMode, mode:${mode.name}, layout:${layout.name}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\micseat\game\BaseGameMatchingSeatView.kt
+ -         Log.d(
+ -             TAG_ROOM_SEAT,
+ -             "handleGameMicSeatInfo, index:${index.index}, newMicSeatInfo:$micSeatInfo, oldMicSeatInfo:${this.micSeatInfo}"
+ -         )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\micseat\globalbroadcast\manager\RoomGlobalBroadcastManager.kt
+ -             Log.d(TAG_ROOM_GLOBAL_BROADCAST, "initRoomGlobalBroadcastData cacheJson: $cacheJson")
+ + 
+ -                 Log.d(TAG_ROOM_GLOBAL_BROADCAST, "initRoomGlobalBroadcastData userGlobalBroadcastCacheJson empty map")
+ + 
+ -             Log.d(TAG_ROOM_GLOBAL_BROADCAST, "initRoomGlobalBroadcastData cacheLastMap: $cacheLastMap")
+ + 
+ -             Log.d(TAG_ROOM_GLOBAL_BROADCAST, "initRoomGlobalBroadcastData userGlobalBroadcastCacheJson: $roomGlobalBroadcastData")
+ + 
+ -         Log.d(TAG_ROOM_GLOBAL_BROADCAST, "clear data ")
+ + 
+ -             Log.d(TAG_ROOM_GLOBAL_BROADCAST, "getRoomGlobalBroadcastData is null bean")
+ + 
+ -                 Log.d(TAG_ROOM_GLOBAL_BROADCAST, "saveData sendGiftBroadcast: convert json $newJSon")
+ + 
+ -             Log.d(TAG_ROOM_GLOBAL_BROADCAST, "saveData userGlobalBroadcastCacheJson: ${RoomGlobalBroadcastLocalService.userGlobalBroadcastCacheJson}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\micseat\globalbroadcast\viewbinder\RoomGlobalBroadcastTitleViewBinder.kt
+ -                         Log.d(TAG_ROOM_GLOBAL_BROADCAST, "switch: ${data.enable}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\micseat\MicSeatFragment.kt
+ -         Log.d(TAG, "switchMicMode, mode:${mode.name}, layout:${layout.name}")
+ + 
+ -         Log.d(TAG, "switchMicSeatTemplate, template:${template.name}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\micseat\pk\PKSeatsTemplate.kt
+ -         Log.d(TAG, "switchMicMode, mode:${mode.name}, layout:${layout.name}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\micseat\view\select\adapter\MemberSelectItemViewBinder.kt
+ -             Log.d("MemberSelectItemViewBinder", "selectItem:$selectItem")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\micseat\viewmodel\RoomSeatViewModel.kt
+ -                 Log.d(
+ -                     TAG_ROOM_SEAT,
+ -                     "changeRoomMicMode return, for current is ${micMode.name} now."
+ -                 )
+ + 
+ -                 Log.d(
+ -                     TAG_ROOM_SEAT,
+ -                     "changeRoomMicLayout return, for current is ${micLayout.name} now."
+ -                 )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\minimize\dialog\RoomCloseTipDialog.kt
+ -         Log.d(TAG_ROOM_GLOBAL_BROADCAST, "canShowNextInvitedBellLayout: $canShowNextInvitedBellLayout")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\minimize\manager\MinimizeRoomManager.kt
+ -         Log.d(TAG, "onActivityResumed")
+ + 
+ -         Log.d(TAG, "onRoomIn")
+ + 
+ -         Log.d(TAG, "onRoomLeaved")
+ + 
+ -         Log.d(TAG, "checkShowOrNot, roomInfo:$roomInfo, joinRoomState:$joinRoomState, activity:$activity")
+ + 
+ -         Log.d(TAG, "showMinimizeRoomIfNeed")
+ + 
+ -         Log.d(TAG, "addMinimizedRoomView")
+ + 
+ -         Log.d(TAG, "removeMinimizedRoomView")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\RoomActivity.kt
+ -             Log.d(TAG, "getRoomTypeFrom intent, ${intentRoomType.name}")
+ + 
+ -             Log.d(TAG, "getRoomTypeFrom roomInfo, ${roomInfoRoomType.name}")
+ + 
+ -             Log.d(TAG, "current roomType: $it")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\roomlist\recently\manager\RecentRoomManager.kt
+ -             Log.d(TAG, "enterRoom, roomId:$roomId")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\roomtask\RoomTaskComp.kt
+ -             Log.d(TAG, "showTaskEntrance, entrance:$entrance ")
+ + 
+ -             Log.d(TAG, "aladdinEffectLiveData, effect: $it")
+ + 
+ -         Log.d(TAG, "updateAladdinTaskInfo, $aladdinInfo")
+ + 
+ -         Log.d(TAG, "showExchangeCoinBonusDialog, coin:$coin")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\roomtask\taskmonitor\monitor\NewUserInMicMonitor.kt
+ -         Log.d(TAG_NEWER_IN_MIC_MONITOR, "startMonitor")
+ + 
+ -             Log.d(TAG_NEWER_IN_MIC_MONITOR, "startMonitorInner return for already running")
+ + 
+ -         Log.d(TAG_NEWER_IN_MIC_MONITOR, "startMonitorInner")
+ + 
+ -         Log.d(TAG_NEWER_IN_MIC_MONITOR, "stopMonitor")
+ + 
+ -         Log.d(TAG_NEWER_IN_MIC_MONITOR, "stopMonitorInner")
+ + 
+ -             Log.d(
+ -                 TAG_NEWER_IN_MIC_MONITOR,
+ -                 "run schedule check task monitor, currentTime:${System.currentTimeMillis()}"
+ -             )
+ + 
+ -         Log.d(TAG_NEWER_IN_MIC_MONITOR, "doAction")
+ + 
+ -                 Log.d(TAG_NEWER_IN_MIC_MONITOR, "doAction return for can't get roomId")
+ + 
+ -         Log.d(TAG_NEWER_IN_MIC_MONITOR, "doSkip")
+ + 
+ -         Log.d(TAG_NEWER_IN_MIC_MONITOR, "onMicSeatsChanged")
+ + 
+ -                 Log.d(TAG_NEWER_IN_MIC_MONITOR, "onMicSeatsChanged, ${usersInfoByUid.data.values.joinToString(",") { "(uid:${it.uid},isNormal:${it.isNormalAccount()})" }}")
+ + 
+ -                 Log.d(TAG_NEWER_IN_MIC_MONITOR, "onMicSeatsChanged return,for user not changed,")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\roomtask\taskmonitor\monitor\NormalUserInMicMonitor.kt
+ -         Log.d(TAG_USER_IN_MIC_MONITOR, "startMonitor")
+ + 
+ -             Log.d(TAG_USER_IN_MIC_MONITOR, "startMonitorInner, return for already running")
+ + 
+ -         Log.d(TAG_USER_IN_MIC_MONITOR, "startMonitorInner")
+ + 
+ -         Log.d(TAG_USER_IN_MIC_MONITOR, "stopMonitor")
+ + 
+ -         Log.d(TAG_USER_IN_MIC_MONITOR, "stopMonitorInner")
+ + 
+ -             Log.d(
+ -                 TAG_USER_IN_MIC_MONITOR,
+ -                 "run schedule check task monitor, currentTime:${System.currentTimeMillis()}"
+ -             )
+ + 
+ -         Log.d(TAG_USER_IN_MIC_MONITOR, "doAction")
+ + 
+ -                 Log.d(TAG_USER_IN_MIC_MONITOR, "doAction return for can't get roomId")
+ + 
+ -         Log.d(TAG_USER_IN_MIC_MONITOR, "doSkip: ")
+ + 
+ -         Log.d(TAG_USER_IN_MIC_MONITOR, "onMicSeatsChanged")
+ + 
+ -                 Log.d(TAG_USER_IN_MIC_MONITOR, "onMicSeatsChanged, ${usersInfoByUid.data.values.joinToString(",") { "(uid:${it.uid},isNormal:${it.isNormalAccount()})" }}")
+ + 
+ -                 Log.d(TAG_USER_IN_MIC_MONITOR, "onMicSeatsChanged return,for user not changed,")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\roomtask\taskmonitor\RoomTaskMonitor.kt
+ -         Log.d(TAG_ROOM_TASK_MONITOR, "registerMonitor, monitorId:${monitor.monitorId}")
+ + 
+ -         Log.d(TAG_ROOM_TASK_MONITOR, "unRegisterMonitor, monitorId:${monitorId}")
+ + 
+ -         Log.d(TAG_ROOM_TASK_MONITOR, "startMonitor")
+ + 
+ -         Log.d(TAG_ROOM_TASK_MONITOR, "stopMonitor")
+ + 
+ -         Log.d(TAG_ROOM_TASK_MONITOR, "onRoomLeaved, stopAllMonitor and clear.")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\sdk\controller\impl\WPSeatController.kt
+ -             Log.d(TAG_ROOM_SEAT, "micSeatListenNotify, data:${data}")
+ + 
+ -                 Log.d(TAG_ROOM_SEAT, "upMicRequestQueueChangedNotify, data:${data}")
+ + 
+ -             Log.d(
+ -                 TAG_ROOM_SEAT,
+ -                 "handleMicSeatsInfo, roomId:$roomId, micSeatsInfo:$micSeatsInfo, micUserInfo:$micUserInfo, reqDecorInfo: $reqDecorInfo"
+ -             )
+ + 
+ -                 Log.d(
+ -                     TAG_ROOM_SEAT,
+ -                     "handleMicSeatsInfo, reqDecorInfo:${reqDecorInfo}"
+ -                 )
+ + 
+ -         Log.d(TAG_ROOM_SEAT, "onOwnerInRoom")
+ + 
+ -         Log.d(TAG_ROOM_SEAT, "onOwnerLeaveRoom")
+ + 
+ -             Log.d(TAG_ROOM_SEAT_SPEAKING, "onMicMute")
+ + 
+ -             Log.d(TAG_ROOM_SEAT_SPEAKING, "onAudioPlayerMute")
+ + 
+ -         Log.d(TAG_ROOM_SEAT_SPEAKING, "correctMicSeatSpeaking, micSeats:${micSeats}")
+ + 
+ -         Log.d(TAG_ROOM_SEAT_SPEAKING, "recoverMicSeatSpeaking")
+ + 
+ -         Log.d(TAG_ROOM_SEAT_SPEAKING, "clearMicSeatSpeaking")
+ + 
+ -             Log.d(TAG_ROOM_SEAT_SPEAKING, "onUsersSpeaking, uidSet:${uidSet}")
+ + 
+ -         Log.d(TAG_ROOM_SEAT, "handleRoomUserInfoMap, micUserInfo:$micUserInfo")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\share\RoomShareComp.kt
+ -             Log.d(TAG_SHARE, "checkBigSupportInvite return, for is not invitee")
+ + 
+ -             Log.d(TAG, "checkBigSupportShareGuide return, for guide is finished.")
+ + 
+ -         Log.d(TAG, "onRoomStateChanged: fromState:${fromState.name}, toState:${toState.name}")
+ + 
+ -         Log.d(TAG, "onRoomIn:$roomId")
+ + 
+ -         Log.d(TAG, "onRoomLeaved")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\stat\RoomGlobalBroadcastSwitchStatEvent.kt
+ -             Log.d(TAG_BELL_INVITED, "reportShow: ")
+ + 
+ -             Log.d(TAG_BELL_INVITED, "reportClick: ")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\theme\ThemeComp.kt
+ -                     Log.d(TAG_ROOM_THEME, "theme video onPrepared")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\room\src\main\java\com\adealink\weparty\room\userprotect\UserProtectComp.kt
+ -                 Log.d(TAG, "addUserBeBanMsg return, for already shown.")
+ + 
+ -                 Log.d(TAG, "addUserBeBanMsg return, for already shown.")
+ + 
+ -         Log.d(TAG, "addUserReqHelpWindow")
+ + 
+ -             Log.d(TAG, "addUserReqHelpWindow return, for userReqHelpWindow already added.")
+ + 
+ -         Log.d(TAG, "removeUserReqHelpView")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\roomtask\src\main\java\com\adealink\weparty\roomtask\dialog\AladdinTakeCoinDialog.kt
+ -             Log.d(TAG, "resourceReady, effectPath:${it?.remoteUrl}, effectDuration:${it?.duration}")
+ + 
+ -                         Log.d(TAG, "onIncreaseStart")
+ + 
+ -                         Log.d(TAG, "onIncreaseComplete, ${System.currentTimeMillis() - start}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\roomtask\src\main\java\com\adealink\weparty\roomtask\dialog\RoomTaskFinishDialog.kt
+ -             Log.d(TAG, "dialog dismiss for taskFinishBean is null")
+ + 
+ -             Log.d(TAG, "resourceReady, effectPath:${it?.remoteUrl}, effectDuration:${it?.duration}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\roomtask\src\main\java\com\adealink\weparty\roomtask\dialog\viewmodel\TaskFinishTakeRewardViewModel.kt
+ -             Log.d("TaskFinishTakeRewardViewModel", "checkEffectResource, needDownload:$needDownload ")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\roomtask\src\main\java\com\adealink\weparty\roomtask\home\viewmodel\RoomTaskViewModel.kt
+ -         Log.d(TAG, "refreshTaskList")
+ + 
+ -         Log.d(TAG, "takeReward, activityId:$activityId, taskId:$taskId")
+ + 
+ -         Log.d(TAG, "refreshCurrentRoomTask")
+ + 
+ -         Log.d(TAG, "exchangeCoinBonus, roomId:$roomId")
+ + 
+ -         Log.d(TAG, "refreshCoinBonus")
+ + 
+ -         Log.d(TAG, "onTaskFinish, onTaskFinish, taskId:${task.taskId}")
+ + 
+ -         Log.d(TAG, "onCoinBonusChanged, coinBonus:$coinBonusCanTake, totalCoin:$totalCoinBonus, status:$aladdinStatus")
+ + 
+ -         Log.d(TAG, "onAllTaskComplete, taskType:$taskType")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\roomtask\src\main\java\com\adealink\weparty\roomtask\manager\RoomTaskManager.kt
+ -             Log.d(TAG, "roomTaskFinishNotify, task(activityId:${data.activityId}, taskId:${data.taskId})")
+ + 
+ -             Log.d(TAG, "aladdinAddCoinNotify, addCoin:${data.addValue}, coinCanTake:${data.currencyValue}")
+ + 
+ -         Log.d(TAG, "handleAladdinAddCoinNotify: currencyValue:$currencyValue, addCoin:$addCoin")
+ + 
+ -         Log.d(TAG, "handleRoomFinishNotify: $notify")
+ + 
+ -         Log.d(TAG, "prepareTaskInfo")
+ + 
+ -         Log.d(TAG, "downloadNecessaryResource")
+ + 
+ -                 Log.d(TAG, "downloadNecessaryResource, path:$savePath")
+ + 
+ -                     Log.d(TAG, "downloadNecessaryResource return, file exists")
+ + 
+ -         Log.d(TAG, "notifyAladdinBonusChanged, coinBonusCanTake:$coinBonusCanTake, coinBonusTotal:$coinBonusTotal")
+ + 
+ -         Log.d(TAG, "notifyRoomTaskFinish, taskInfo:$taskInfo")
+ + 
+ -         Log.d(TAG, "notifyAllTaskFinish")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\roomtask-export\src\main\java\com\adealink\weparty\roomtask\viewmodel\VapcEffectViewModel.kt
+ -         Log.d(TAG, "addDownloadTask, url: $url")
+ + 
+ -         Log.d(TAG, "                 savePath: $savePath")
+ + 
+ -             Log.d(TAG, "addDownloadTask, fileExist, return")
+ + 
+ -         Log.d(
+ -             TAG,
+ -             "addDownloadTask, start download, url:$url, task:$task"
+ -         )
+ + 
+ -                 Log.d(
+ -                     TAG,
+ -                     "addDownloadTask, download success, task:$task"
+ -                 )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\search\src\main\java\com\adealink\weparty\search\RoomSupportAddUserConfirmDialog.kt
+ -                 Log.d(TAG, "show return, for SearchLocalService.roomSupportAddUserShowConfirm is false")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\share\src\main\java\com\adealink\weparty\share\manager\ShareManager.kt
+ -             Log.d(TAG_SHARE, "onConfigGet, dynamicLinkPrefix: $dynamicLinkPrefix")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\skin\src\main\java\com\adealink\weparty\skin\manager\SkinManager.kt
+ -             Log.d(TAG_SKIN, "loadCurrentSkinFromLocal: ${SkinLocalService.currentSkinInfo}")
+ + 
+ -             Log.d(TAG_SKIN, "useSkin, $skin")
+ + 
+ -         Log.d(
+ -             TAG_SKIN_DOWNLOAD,
+ -             "addDownloadSkinTask, start download, resUrl:${skinInfo.resourceUrl}, task:$task"
+ -         )
+ + 
+ -                     Log.d(
+ -                         TAG_SKIN_DOWNLOAD,
+ -                         "addDownloadSkinTask, download success, task:$task"
+ -                     )
+ + 
+ -             Log.d(
+ -                 TAG_SKIN_UNZIP,
+ -                 "unzipSkin, success, skinInfo:$skinInfo"
+ -             )
+ + 
+ -             Log.d(
+ -                 TAG_SKIN_UNZIP,
+ -                 "unzipSkin, fail, skinInfo:$skinInfo"
+ -             )
+ + 
+ -         Log.d(TAG_SKIN, "notifySkinResourceReady: $skinInfo")
+ + 
+ -                 Log.d(TAG_SKIN, "notifySkinResourceReady return, download skin isn't currentSkin.")
+ + 
+ -                     Log.d(TAG_SKIN, "notifySkinResourceReady, resource fold not exist, $skinResourceFolder")
+ + 
+ -         Log.d(TAG_SKIN, "resetCurrentSkin")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\userprotect\src\main\java\com\adealink\weparty\userprotect\HelpListActivity.kt
+ -         Log.d(TAG, "showPenalizeActionDialog, id:${item.id}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\userprotect\src\main\java\com\adealink\weparty\userprotect\manager\ReportManager.kt
+ -         Log.d(TAG_REPORT, "showReportUserDialog, reportedUid: $reportedUid, reportFrom:$reportFrom, reportType:$reportType")
+ + 
+ -         Log.d(TAG_REPORT, "showReportUserChatDialog, reportedUid: $reportedUid, reportFrom:$reportFrom, reportType:$reportType, contents:${contents.joinToString(",")}")
+ + 
+ -         Log.d(TAG_REPORT, "showReportRoomDialog, reportedRoomId: $reportedRoomId, reportFrom:$reportFrom")
+ + 
+ -         Log.d(TAG_REPORT, "showReportImSessionDialog, reportedUid: $reportedUid, showReportAction:$showReportAction")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\userprotect\src\main\java\com\adealink\weparty\userprotect\manager\UserProtectManager.kt
+ -         Log.d(TAG, "notifyUserReqHelp")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\userprotect\src\main\java\com\adealink\weparty\userprotect\viewmodel\HelpListViewModel.kt
+ -         Log.d(TAG, "loadHelpList")
+ + 
+ -         Log.d(TAG, "loadMore, curPage:$curPage")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\userprotect\src\main\java\com\adealink\weparty\userprotect\viewmodel\ReportNewViewModel.kt
+ -         Log.d(TAG, "chooseReason, type:$type")
+ + 
+ -         Log.d(TAG, "chooseScene, type:$type ")
+ + 
+ -                 Log.d(TAG, "reportUser return, for reportType isn't selected")
+ + 
+ -                 Log.d(TAG, "reportUser return, for reasonType isn't selected")
+ + 
+ -                 Log.d(TAG, "reportRoom return, for reportType isn't selected")
+ + 
+ -                 Log.d(TAG, "reportRoom return, for reasonType isn't selected")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\wallet\src\main\java\com\adealink\weparty\wallet\manager\WalletManager.kt
+ -             Log.d(TAG_GLOBAL_CONFIG, "walletManager isFirstRechargeEnable: $isFirstRechargeEnable")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\wallet\src\main\java\com\adealink\weparty\wallet\pay\GoogleBilling.kt
+ -                 Log.d(
+ -                     TAG_GOOGLE_BILLING,
+ -                     "purchaseUpdate, resultCode: ${billingResult.responseCode}, debugMessage:${billingResult.debugMessage}"
+ -                 )
+ + 
+ -                         Log.d(
+ -                             TAG_GOOGLE_BILLING,
+ -                             "onBillingSetupFinished, resultCode: ${billingResult.responseCode}, debugMessage:${billingResult.debugMessage}"
+ -                         )
+ + 
+ -                         Log.d(TAG_GOOGLE_BILLING, "onBillingServiceDisconnected")
+ + 
+ -                         Log.d(
+ -                             TAG_GOOGLE_BILLING,
+ -                             "queryProductDetailsAsync, resultCode: ${billingResult.responseCode}, debugMessage:${billingResult.debugMessage}, skuDetailSize: ${productDetails.size}"
+ -                         )
+ + 
+ -                         Log.d(
+ -                             TAG_GOOGLE_BILLING,
+ -                             "querySkuDetailsAsync, resultCode: ${billingResult.responseCode}, debugMessage:${billingResult.debugMessage}, skuDetailSize: ${skuDetailsList?.size}"
+ -                         )
+ + 
+ -                     Log.d(
+ -                         TAG_GOOGLE_BILLING,
+ -                         "consumeAsync, resultCode: ${billingResult.responseCode}, debugMessage:${billingResult.debugMessage}"
+ -                     )
+ + 
+ -                     Log.d(
+ -                         TAG_GOOGLE_BILLING,
+ -                         "queryPurchases, resultCode: ${purchasesResult.responseCode}"
+ -                     )
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\webview\src\main\java\com\adealink\weparty\webview\game\BSJSBridge.kt
+ -         Log.d(TAG_WEB_BS, "游戏调⽤getConfig")
+ + 
+ -         Log.d(TAG_WEB_BS, "游戏调⽤destroy")
+ + 
+ -         Log.d(TAG_WEB_BS, "游戏调⽤gameRecharge")
+ + 
+ -         Log.d(TAG_WEB_BS, "游戏调⽤gameLoaded")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\webview\src\main\java\com\adealink\weparty\webview\jsnativemethod\CopyToClipBoardJSNativeMethod.kt
+ -         Log.d(TAG, "handleMethodCall(copyToClipBoard), data:$data")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\webview\src\main\java\com\adealink\weparty\webview\jsnativemethod\GetInviteShareLinkJSNativeMethod.kt
+ -         Log.d(TAG, "handleMethodCall, channel: ${data.channel}, link: $link")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\webview\src\main\java\com\adealink\weparty\webview\jsnativemethod\GetShareLinkJSNativeMethod.kt
+ -         Log.d(TAG, "handleMethodCall(shareLink), data:$data")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\webview\src\main\java\com\adealink\weparty\webview\jsnativemethod\ReportAfBackEvent.kt
+ -         Log.d(TAG_WEB_VIEW_JS_BRIDGE, "reportAfBackEvent, data:${data}")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\webview\src\main\java\com\adealink\weparty\webview\jsnativemethod\ShareLinkJSNativeMethod.kt
+ -         Log.d(TAG, "handleMethodCall(shareLink), data:$data")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\module\webview\src\main\java\com\adealink\weparty\webview\jsnativemethod\UpdateInviteShareLinkJSNativeMethod.kt
+ -         Log.d(TAG, "handleMethodCall(shareLink), data:$data")
+ + 
+--------------------------------------------------------------------
+F:\Android\wenext\wyak\wyak3\app\..\third-party-libs\xcrash_lib\src\main\java\xcrash\DefaultLogger.java
+ -         Log.v(tag, msg);
+ + 
+ -         Log.v(tag, msg, tr);
+ + 
+ -         Log.d(tag, msg);
+ + 
+ -         Log.d(tag, msg, tr);
+ + 

+ 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)
+    }
+}

+ 122 - 0
app/src/lite/debug/google-services.json

@@ -0,0 +1,122 @@
+{
+  "project_info": {
+    "project_number": "166092120979",
+    "project_id": "wayak-1665567402509",
+    "storage_bucket": "wayak-1665567402509.appspot.com"
+  },
+  "client": [
+    {
+      "client_info": {
+        "mobilesdk_app_id": "1:166092120979:android:d910fd1f80b73a6ff17e66",
+        "android_client_info": {
+          "package_name": "com.wenext.wayak"
+        }
+      },
+      "oauth_client": [
+        {
+          "client_id": "166092120979-6v36jd33ub39ota31j4o52cl2021qqvg.apps.googleusercontent.com",
+          "client_type": 1,
+          "android_info": {
+            "package_name": "com.wenext.wayak",
+            "certificate_hash": "c6f6ffb12878edaa4aede7cc9ed2461b8391e7e8"
+          }
+        },
+        {
+          "client_id": "166092120979-hqmqhh4cmg61elpcu56op5ghmqlicdb3.apps.googleusercontent.com",
+          "client_type": 1,
+          "android_info": {
+            "package_name": "com.wenext.wayak",
+            "certificate_hash": "7c700108b1a6c384c58fb7d22871849fab3dc785"
+          }
+        },
+        {
+          "client_id": "166092120979-a3j4juaql4qnsd6c23p4ep1ua3rv8hvd.apps.googleusercontent.com",
+          "client_type": 3
+        }
+      ],
+      "api_key": [
+        {
+          "current_key": "AIzaSyADGfrduCZBhPiEfq0fmf9PV-JqZYxHDmo"
+        }
+      ],
+      "services": {
+        "appinvite_service": {
+          "other_platform_oauth_client": [
+            {
+              "client_id": "166092120979-a3j4juaql4qnsd6c23p4ep1ua3rv8hvd.apps.googleusercontent.com",
+              "client_type": 3
+            },
+            {
+              "client_id": "166092120979-uifm2tvth9n12cmoe0h5i1c7h9rd7q14.apps.googleusercontent.com",
+              "client_type": 2,
+              "ios_info": {
+                "bundle_id": "com.courage.wyak"
+              }
+            }
+          ]
+        }
+      }
+    },
+    {
+      "client_info": {
+        "mobilesdk_app_id": "1:166092120979:android:5f461d0f70cf4745f17e66",
+        "android_client_info": {
+          "package_name": "com.wenext.wyaklite"
+        }
+      },
+      "oauth_client": [
+        {
+          "client_id": "166092120979-2ugrvjtcrfv1n9e3jvknu6c26cevauel.apps.googleusercontent.com",
+          "client_type": 1,
+          "android_info": {
+            "package_name": "com.wenext.wyaklite",
+            "certificate_hash": "7c700108b1a6c384c58fb7d22871849fab3dc785"
+          }
+        },
+        {
+          "client_id": "166092120979-cekc7jipssmb9v608fnmosa1v0a8h8ki.apps.googleusercontent.com",
+          "client_type": 1,
+          "android_info": {
+            "package_name": "com.wenext.wyaklite",
+            "certificate_hash": "c6f6ffb12878edaa4aede7cc9ed2461b8391e7e8"
+          }
+        },
+        {
+          "client_id": "166092120979-rctdit0ffim03r52s8odgj3k72a4t35l.apps.googleusercontent.com",
+          "client_type": 1,
+          "android_info": {
+            "package_name": "com.wenext.wyaklite",
+            "certificate_hash": "7955fe852c88e5988f3d65e2b7a1444683a9757e"
+          }
+        },
+        {
+          "client_id": "166092120979-a3j4juaql4qnsd6c23p4ep1ua3rv8hvd.apps.googleusercontent.com",
+          "client_type": 3
+        }
+      ],
+      "api_key": [
+        {
+          "current_key": "AIzaSyADGfrduCZBhPiEfq0fmf9PV-JqZYxHDmo"
+        }
+      ],
+      "services": {
+        "appinvite_service": {
+          "other_platform_oauth_client": [
+            {
+              "client_id": "166092120979-a3j4juaql4qnsd6c23p4ep1ua3rv8hvd.apps.googleusercontent.com",
+              "client_type": 3
+            },
+            {
+              "client_id": "166092120979-uifm2tvth9n12cmoe0h5i1c7h9rd7q14.apps.googleusercontent.com",
+              "client_type": 2,
+              "ios_info": {
+                "bundle_id": "com.courage.wyak"
+              }
+            }
+          ]
+        }
+      }
+    }
+  ],
+  "configuration_version": "1"
+}

+ 122 - 0
app/src/lite/release/google-services.json

@@ -0,0 +1,122 @@
+{
+  "project_info": {
+    "project_number": "166092120979",
+    "project_id": "wayak-1665567402509",
+    "storage_bucket": "wayak-1665567402509.appspot.com"
+  },
+  "client": [
+    {
+      "client_info": {
+        "mobilesdk_app_id": "1:166092120979:android:d910fd1f80b73a6ff17e66",
+        "android_client_info": {
+          "package_name": "com.wenext.wayak"
+        }
+      },
+      "oauth_client": [
+        {
+          "client_id": "166092120979-6v36jd33ub39ota31j4o52cl2021qqvg.apps.googleusercontent.com",
+          "client_type": 1,
+          "android_info": {
+            "package_name": "com.wenext.wayak",
+            "certificate_hash": "c6f6ffb12878edaa4aede7cc9ed2461b8391e7e8"
+          }
+        },
+        {
+          "client_id": "166092120979-hqmqhh4cmg61elpcu56op5ghmqlicdb3.apps.googleusercontent.com",
+          "client_type": 1,
+          "android_info": {
+            "package_name": "com.wenext.wayak",
+            "certificate_hash": "7c700108b1a6c384c58fb7d22871849fab3dc785"
+          }
+        },
+        {
+          "client_id": "166092120979-a3j4juaql4qnsd6c23p4ep1ua3rv8hvd.apps.googleusercontent.com",
+          "client_type": 3
+        }
+      ],
+      "api_key": [
+        {
+          "current_key": "AIzaSyADGfrduCZBhPiEfq0fmf9PV-JqZYxHDmo"
+        }
+      ],
+      "services": {
+        "appinvite_service": {
+          "other_platform_oauth_client": [
+            {
+              "client_id": "166092120979-a3j4juaql4qnsd6c23p4ep1ua3rv8hvd.apps.googleusercontent.com",
+              "client_type": 3
+            },
+            {
+              "client_id": "166092120979-uifm2tvth9n12cmoe0h5i1c7h9rd7q14.apps.googleusercontent.com",
+              "client_type": 2,
+              "ios_info": {
+                "bundle_id": "com.courage.wyak"
+              }
+            }
+          ]
+        }
+      }
+    },
+    {
+      "client_info": {
+        "mobilesdk_app_id": "1:166092120979:android:5f461d0f70cf4745f17e66",
+        "android_client_info": {
+          "package_name": "com.wenext.wyaklite"
+        }
+      },
+      "oauth_client": [
+        {
+          "client_id": "166092120979-2ugrvjtcrfv1n9e3jvknu6c26cevauel.apps.googleusercontent.com",
+          "client_type": 1,
+          "android_info": {
+            "package_name": "com.wenext.wyaklite",
+            "certificate_hash": "7c700108b1a6c384c58fb7d22871849fab3dc785"
+          }
+        },
+        {
+          "client_id": "166092120979-cekc7jipssmb9v608fnmosa1v0a8h8ki.apps.googleusercontent.com",
+          "client_type": 1,
+          "android_info": {
+            "package_name": "com.wenext.wyaklite",
+            "certificate_hash": "c6f6ffb12878edaa4aede7cc9ed2461b8391e7e8"
+          }
+        },
+        {
+          "client_id": "166092120979-rctdit0ffim03r52s8odgj3k72a4t35l.apps.googleusercontent.com",
+          "client_type": 1,
+          "android_info": {
+            "package_name": "com.wenext.wyaklite",
+            "certificate_hash": "7955fe852c88e5988f3d65e2b7a1444683a9757e"
+          }
+        },
+        {
+          "client_id": "166092120979-a3j4juaql4qnsd6c23p4ep1ua3rv8hvd.apps.googleusercontent.com",
+          "client_type": 3
+        }
+      ],
+      "api_key": [
+        {
+          "current_key": "AIzaSyADGfrduCZBhPiEfq0fmf9PV-JqZYxHDmo"
+        }
+      ],
+      "services": {
+        "appinvite_service": {
+          "other_platform_oauth_client": [
+            {
+              "client_id": "166092120979-a3j4juaql4qnsd6c23p4ep1ua3rv8hvd.apps.googleusercontent.com",
+              "client_type": 3
+            },
+            {
+              "client_id": "166092120979-uifm2tvth9n12cmoe0h5i1c7h9rd7q14.apps.googleusercontent.com",
+              "client_type": 2,
+              "ios_info": {
+                "bundle_id": "com.courage.wyak"
+              }
+            }
+          ]
+        }
+      }
+    }
+  ],
+  "configuration_version": "1"
+}

+ 4 - 0
app/src/lite/res/values/strings.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name" translatable="false">Wyak Lite</string>
+</resources>

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

@@ -0,0 +1,154 @@
+<?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.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.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" />
+
+    <queries>
+        <package android:name="com.google.android.gms" />
+        <package android:name="com.facebook.katana" />
+        <package android:name="com.whatsapp" />
+    </queries>
+
+    <uses-sdk
+        android:minSdkVersion="21"
+        tools:overrideLibrary="com.hw.videoprocessor,me.weishu.freereflection" />
+
+    <application
+        android:name=".App"
+        android:allowBackup="false"
+        android:icon="@drawable/ic_launcher"
+        android:label="@string/app_name"
+        android:largeHeap="true"
+        android:requestLegacyExternalStorage="true"
+        android:supportsRtl="true"
+        android:theme="@style/Theme.Weparty"
+        android:networkSecurityConfig="@xml/network_security_config"
+        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=".splash.SplashActivity"
+            android:exported="true"
+            android:theme="@style/SplashActivityTheme">
+            <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>
+        </activity>
+
+        <activity
+            android:name=".MainActivity"
+            android:exported="true"
+            android:launchMode="singleTop"
+            android:screenOrientation="portrait"
+            android:theme="@style/MainActivityTheme" />
+
+        <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="wenext" />
+                <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>
+        </activity>
+        <activity android:name=".debug.DebugActivity" />
+
+        <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.videoselect.VideoSelectActivity"
+            android:screenOrientation="portrait" />
+
+    </application>
+
+</manifest>

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 0
app/src/main/assets/TCMediaX.licence


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


BIN
app/src/main/assets/message_chat_guard.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


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

@@ -0,0 +1,394 @@
+package com.adealink.weparty
+
+import android.content.Context
+import android.content.SharedPreferences
+import android.content.res.Configuration
+import android.graphics.Bitmap
+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.coroutine.dispatcher.Dispatcher
+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.image.listener.IImageLoadResultListener
+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.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.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.ProductFlavor
+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.download.DownloadConfig
+import com.adealink.weparty.effect.EffectConfig
+import com.adealink.weparty.effect.TCEffectConfig
+import com.adealink.weparty.googleservice.GoogleServiceConfig
+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.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.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.opensource.svgaplayer.control.SvgaConfig
+import com.opensource.svgaplayer.disk.DiskCacheParamsSupplier
+import com.opensource.svgaplayer.disk.DiskTrimStrategy
+import com.opensource.svgaplayer.executor.ExecutorsSupplier
+import com.opensource.svgaplayer.remote.ImageFetchCallback
+import com.opensource.svgaplayer.remote.ImageFetcher
+import com.opensource.svgaplayer.utils.ByteConstants
+import com.tencent.mars.xlog.Xlog
+import me.weishu.reflection.Reflection
+import java.io.File
+import java.util.concurrent.Executor
+import java.util.concurrent.TimeUnit
+
+/**
+ * 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()
+    }
+
+    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)
+        try {
+            Reflection.unseal(base)
+        } catch (e: Throwable) {
+            Log.e("App", "Reflection", e)
+        }
+        appStartTime = SystemClock.elapsedRealtime()
+        AppUtil.registerActivityLifecycleCallbacks(this)
+        logTime(TAG_TIME_APP_START, "app attachBaseContext end")
+    }
+
+    override fun onCreate() {
+        super.onCreate()
+        logTime(TAG_TIME_APP_START, "app onCreate start")
+        initAPMService(APMConfig())
+        securityService.init()
+        initXLog()
+        initStorageService(StorageConfig())
+        installCrashProtector(CrashConfig())
+        initJsonConfig(JsonConfig())
+        initShareManager(ShareConfig())
+        AAB.init(AABConfig())
+        initStat { StatConfig() }
+        initCrash(this) //依赖xlog,stat,crash protector
+        googleService.init()
+        initRouter()
+        initLanguage()
+        initNetwork()
+        registerNetworkReceiver()
+        imageService.init(ImageConfig())
+        initOssService { ossService }
+        initJSBridgeManager { jsBridgeManager }
+        initWebResourceLoader(WebResourceConfig())
+        initDownloadService { downloadService }
+        initEffect { effect }
+        WEUI.init(this)
+        WindowManagerProxy.init()
+        initTCEffect()
+        initSVGA()
+        initFirebaseAnalytics()
+        initDeeplinkConfig()
+        initPush()
+        initFB()
+        initPayerMax()
+        initAPM(this)
+        networkService.fetchNetAntiBanConfig()
+        deviceIdService.updateDeviceId()
+        initAttribution()
+        initAutoSize()
+        initDebugKit()
+        soundPlayer.prepare()
+        initTooLargeTool()
+        logTime(TAG_TIME_APP_START, "app onCreate end")
+    }
+
+    private fun initTooLargeTool() {
+        TooLargeTool.startLogging(this, 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)
+            }
+        })
+    }
+
+    private fun initRouter() {
+        Router.config = RouterConfig()
+    }
+
+    private fun initNetwork() {
+        networkManager.init(this)
+    }
+
+    private fun initLanguage() {
+        languageManager.init()
+    }
+
+    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())
+    }
+
+    private fun initTCEffect() {
+        if (ProductFlavor.isCurrProductLite()) {
+            //Lite目前不支持
+            return
+        }
+        TCEffectManager.init(TCEffectConfig())
+    }
+
+    private fun initSVGA() {
+        val config = SvgaConfig(
+            diskCacheParamsSupplier = object : DiskCacheParamsSupplier {
+                override fun getPath(): File = File(applicationContext.cacheDir, "svga")
+
+                override fun getDiskTrimStrategy(): DiskTrimStrategy {
+                    return object : DiskTrimStrategy {
+                        override fun maxSize(): Long = ByteConstants.MB * 100L
+                        override fun timeExpired(): Long = TimeUnit.DAYS.toMillis(10)
+                    }
+                }
+            },
+            executorsSupplier = object : ExecutorsSupplier {
+
+                override fun getBackgroundExecutor(): Executor {
+                    return Dispatcher.wenextThreadPoolExecutor
+                }
+
+                override fun getIOExecutor(): Executor {
+                    return Dispatcher.wenextThreadPoolExecutor
+                }
+
+                override fun getNetWorkExecutor(): Executor {
+                    return Dispatcher.wenextThreadPoolExecutor
+                }
+
+            },
+            debuggable = !AppBaseInfo.isRelease,
+            imageFetcher = object : ImageFetcher {
+
+                override fun fetchDynamicImage(
+                    url: String,
+                    forKey: String,
+                    callback: ImageFetchCallback?
+                ) {
+                    imageService.fetchImage(url, object : IImageLoadResultListener {
+                        override fun onSuccess(bitmap: Bitmap) {
+                            super.onSuccess(bitmap)
+                            callback?.onFetched(bitmap, forKey)
+                        }
+
+                        override fun onFailed() {
+                            super.onFailed()
+                            callback?.onFailure(Exception("load image failed"), forKey)
+                        }
+
+                        override fun onCancel() {
+                            super.onCancel()
+                            callback?.onFailure(Exception("load image canceled"), forKey)
+                        }
+                    })
+                }
+
+            },
+            designWidthPx = 750f,
+            enableSvgaChecker = !AppBaseInfo.isRelease && DebugPrefs.enableSvgaChecker,
+            enableShowSvgaCheckResult = !AppBaseInfo.isRelease && DebugPrefs.enableSvgaChecker && DebugPrefs.enableShowSvgaCheckResult
+        )
+        SVGAManager.init(applicationContext, config)
+    }
+
+    private fun initFirebaseAnalytics() {
+        val analytics = FirebaseAnalytics.getInstance(applicationContext)
+        analytics.setUserProperty(ALLOW_AD_PERSONALIZATION_SIGNALS, "true")
+        analytics.setAnalyticsCollectionEnabled(true)
+    }
+
+    private fun initDeeplinkConfig() {
+        deeplinkRouterManager.init(DeepLinkConfig())
+    }
+
+    private fun initAttribution() {
+        AttributionModule.initialize()
+        AttributionModule.start()
+        AttributionModule.reportFirstOpen()
+    }
+
+    private fun initFB() {
+        if (!AppBaseInfo.isRelease) {
+            FacebookSdk.setIsDebugEnabled(AppBaseInfo.isRelease)
+            FacebookSdk.addLoggingBehavior(LoggingBehavior.APP_EVENTS)
+        }
+    }
+
+    private fun initPush() {
+        pushService.init(PushServiceConfig())
+        NotificationUtil.createNotificationChannel(getCompatString(R.string.channel_event))
+    }
+
+    private fun initPayerMax() {
+        PayerMaxHelper.init(this)
+    }
+
+    private fun initAutoSize() {
+        ScreenAutoSizeUtil.init(this, !AppBaseInfo.isRelease)
+    }
+
+    private fun initDebugKit() {
+        if (BuildConfig.IS_RELEASE.not() && DebugPrefs.showPerformanceFloatView) {
+            floatKitManager.install(this, true)
+            floatKitManager.onMainIconDoubleClick = {
+                AppUtil.currentActivity?.let {
+                    Router.build(it, Debug.Debug.PATH).start()
+                }
+            }
+            performanceDataController.start()
+        }
+    }
+
+    override fun onTerminate() {
+        super.onTerminate()
+        Log.appenderClose()
+    }
+
+    override fun onConfigurationChanged(newConfig: Configuration) {
+        super.onConfigurationChanged(newConfig)
+        languageManager.onConfigurationChanged(newConfig)
+    }
+
+    override fun onEnterForeGround() {
+        super.onEnterForeGround()
+        CallModule.stopKeepCallForeground()
+    }
+
+    override fun onEnterBackGround() {
+        super.onEnterBackGround()
+        CallModule.startKeepCallForeground()
+    }
+
+    override fun getSharedPreferences(name: String, mode: Int): SharedPreferences {
+        return storageService.hookSharedPreferences(
+            name,
+            mode,
+            super.getSharedPreferences(name, mode)
+        )
+    }
+
+    override fun onTrimMemory(level: Int) {
+        super.onTrimMemory(level)
+        imageService.onTrimMemory(level)
+    }
+
+}

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

@@ -0,0 +1,152 @@
+package com.adealink.weparty
+
+import android.content.Intent
+import android.graphics.drawable.ColorDrawable
+import android.os.Bundle
+import android.view.WindowManager
+import com.adealink.frame.aab.util.getCompatColor
+import com.adealink.frame.log.Log
+import com.adealink.frame.push.manager.pushService
+import com.adealink.frame.router.Router
+import com.adealink.frame.router.annotation.RouterUri
+import com.adealink.frame.router.manager.deeplinkRouterManager
+import com.adealink.frame.statistics.BaseStatEvent
+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.attribution.AttributionModule
+import com.adealink.weparty.module.profile.ProfileModule
+import com.adealink.weparty.module.share.ShareModule
+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.home.HomeFragment
+import com.adealink.weparty.update.updateManager
+import com.google.firebase.crashlytics.ktx.crashlytics
+import com.google.firebase.crashlytics.ktx.setCustomKeys
+import com.google.firebase.ktx.Firebase
+import com.qmuiteam.qmui.widget.util.QMUIStatusBarHelper
+
+@RouterUri(path = [AppModule.Main.PATH], desc = "首页")
+class MainActivity : BaseActivity() {
+
+    companion object {
+        private const val TAG = "MainActivity"
+    }
+
+    private var dispatchRouter = false
+    override val routeSubPage: Boolean
+        get() = !dispatchRouter
+
+    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(R.layout.activity_main)
+        dispatch(intent)
+    }
+
+    override fun initOthers() {
+        super.initOthers()
+        reportAppOpenIfNeed(false)
+        serveReportManager.reportFirebaseAppInstanceId()
+        if (AccountModule.isLogin()) {
+            AccountModule.checkRemoteVirtualAppConfig()
+            AttributionModule.reportAttributionData()
+            BaseStatEvent.setUserId(ProfileModule.getMyUid())
+            ShareModule.reportUserType()
+            pushService.getPushTokenAndReport()
+            serveReportManager.getNeedReportOrdersAndReport()
+
+            Firebase.crashlytics.setCustomKeys {
+                key("uid", AccountModule.uid)
+                key("BuildType", BuildConfig.BUILD_TYPE)
+                key("IS_RELEASE", BuildConfig.IS_RELEASE)
+            }
+        }
+    }
+
+    override fun onResume() {
+        super.onResume()
+        updateManager.checkUpdate(this)
+    }
+
+    override fun handleNewIntent(intent: Intent?) {
+        super.handleNewIntent(intent)
+
+        dispatch(intent)
+    }
+
+    private fun dispatch(intent: Intent?) {
+        val dispatchPath = intent?.getStringExtra(deeplinkRouterManager.getDispatchPathKey())
+        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
+            }
+        }
+        initView()
+        intent ?: return
+        if (!dispatchPath.isNullOrEmpty()) {
+            Log.d(TAG, "dispatchPath:$dispatchPath")
+            if (dispatchPath.startsWith(Router.getDeepLink(AppModule.Main.PATH))) {
+                return
+            }
+
+            dispatchRouter = true
+            Router.build(this, dispatchPath).putExtras(intent).start()
+        }
+    }
+
+    private fun initView() {
+        inflateHomeFragment()
+    }
+
+    private fun needGoLoginActivity(): Boolean {
+        return !AccountModule.isLogin()
+    }
+
+    private fun needGoRegisterActivity(): Boolean {
+        return AppPref.needRegister
+    }
+
+    private fun inflateHomeFragment() {
+        var homeFragment =
+            supportFragmentManager.findFragmentByTag(HomeFragment.TAG) as? HomeFragment
+        if (homeFragment?.isDetached == true) {
+            return
+        }
+
+        homeFragment = homeFragment ?: HomeFragment.newInstance()
+        supportFragmentManager.beginTransaction()
+            .replace(R.id.fl_content, homeFragment, HomeFragment.TAG)
+            .commit()
+        reportEnterHome()
+    }
+
+}

+ 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"
+        }
+    }
+
+    interface Splash {
+        companion object {
+            const val PATH = "/splash"
+        }
+    }
+}

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

@@ -0,0 +1,33 @@
+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.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) {
+
+            }
+
+            override fun logI(tag: String, msg: String) {
+
+            }
+
+            override fun logE(tag: String, msg: String, e: Exception?) {
+
+            }
+
+        }
+
+
+}

+ 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)
+    }
+}

+ 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() = "wenext"
+    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() = "wyak"
+}

+ 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"

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

@@ -0,0 +1,199 @@
+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 webView(): CocosGameWebView
+
+    override fun initViews() {
+        super.initViews()
+        val contentViewBinding = contentView()
+        onCocosGameViewCreated(contentViewBinding)
+        if (!AppBaseInfo.isRelease) {
+            // DEBUG包打印游戏路径
+            showToast("DEBUG: gameCode:${cocosGame()}, gameUrl:${gameUrl()}")
+        }
+        fixSafeView()
+        cocosViewModel.onCreate()
+        cocosViewModel.initGame(
+            webView(),
+            safeView(),
+            InitGameData(cocosGame(), 58.dp())
+        )
+        loadUrlAfterViewCreated()
+    }
+
+    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 {
+
+}

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

@@ -0,0 +1,14 @@
+package com.adealink.weparty.cocosgame.chat.adapter
+
+import androidx.recyclerview.widget.DiffUtil
+import com.adealink.weparty.cocosgame.data.QuickText
+
+class QuickTextItemListDiffUtil: DiffUtil.ItemCallback<QuickText>() {
+    override fun areItemsTheSame(oldItem: QuickText, newItem: QuickText): Boolean {
+        return oldItem == newItem
+    }
+
+    override fun areContentsTheSame(oldItem: QuickText, newItem: QuickText): Boolean {
+        return oldItem.id == newItem.id
+    }
+}

+ 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>>
+
+}

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

@@ -0,0 +1,116 @@
+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.QuickTextItemListDiffUtil
+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.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.itemdecoration.GridSpacingItemDecoration
+import com.adealink.weparty.commonui.toast.util.showFailedToast
+import com.adealink.weparty.databinding.FragmentCocosgameEmotionQuickTextBinding
+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
+
+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(QuickTextItemListDiffUtil()) }
+
+    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>(Room.RoomGameEmotionPanel.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 //金币撒花动效时长
+    }
+
+}

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

@@ -0,0 +1,43 @@
+package com.adealink.weparty.cocosgame.data
+
+import com.google.gson.annotations.JsonAdapter
+import com.google.gson.annotations.SerializedName
+import com.google.gson.internal.bind.ExtReflectiveTypeAdapterFactory
+
+/*
+ * 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()"
+    }
+}
+
+/**
+ * 退出游戏确认弹窗
+ */
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class ExitGameDialogData(@SerializedName("game_record") val gameRecord: String) :
+    DialogData() {
+    override fun toString(): String {
+        return "ExitGameDialogData(gameRecord='$gameRecord')"
+    }
+}
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class CurrencyInsufficientDialogData(@SerializedName("type") val type: Int) : DialogData()

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

@@ -0,0 +1,409 @@
+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.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.JsonAdapter
+import com.google.gson.annotations.Must
+import com.google.gson.annotations.SerializedName
+import com.google.gson.internal.bind.ExtReflectiveTypeAdapterFactory
+import kotlinx.parcelize.Parcelize
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+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()
+)
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+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
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+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
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+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
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+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
+)
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+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,    //总对局次数
+)
+
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+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 = "",
+) {
+
+    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-默认
+)
+
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class GameRecoverFailData(
+    @SerializedName("reason") val reason: Int, //失败原因, 1:网络重连
+    @SerializedName("msg") val msg: String, //失败原因描述
+) {
+    fun getReasonMsg(): String {
+        return msg.ifEmpty { GameRecoverFailReason.getReasonMessage(reason) }
+    }
+}
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+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,
+)
+
+/**
+ * 查询房间的用户麦位状态返回
+ */
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+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
+)
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+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>, // 麦位开关状态
+)

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

@@ -0,0 +1,195 @@
+package com.adealink.weparty.cocosgame.data
+
+import android.os.Parcelable
+import com.google.gson.annotations.GsonNullable
+import com.google.gson.annotations.JsonAdapter
+import com.google.gson.annotations.SerializedName
+import com.google.gson.internal.bind.ExtReflectiveTypeAdapterFactory
+import kotlinx.parcelize.Parcelize
+
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+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
+}
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+@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
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+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
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+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()
+)

+ 133 - 0
app/src/main/java/com/adealink/weparty/cocosgame/data/CocosPlayerData.kt

@@ -0,0 +1,133 @@
+package com.adealink.weparty.cocosgame.data
+
+import android.os.Parcelable
+import com.google.gson.annotations.GsonNullable
+import com.google.gson.annotations.JsonAdapter
+import com.google.gson.annotations.SerializedName
+import com.google.gson.internal.bind.ExtReflectiveTypeAdapterFactory
+import kotlinx.parcelize.Parcelize
+
+enum class PlayerStatus(val status: Int) {
+    GAME_IN(1),  //游戏中
+    GAME_EXIT(0); //退出游戏
+
+    companion object {
+        fun map(status: Int): PlayerStatus {
+            return PlayerStatus.values().firstOrNull { it.status == status } ?: GAME_EXIT
+        }
+    }
+
+}
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class PlayerStatusData(@SerializedName("status") val status: Int)
+
+@Parcelize
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class GamePlayerInfo(
+    @SerializedName("feeOrder") val feeOrder: String = "",//如果是付费场,则该字段记录用户的付费订单ID
+    @SerializedName("playerId") var playerId: Long = 0,//参与游戏的玩家ID
+    @SerializedName("playerAvatarUrl") var playerAvatarUrl: String = "",
+    @SerializedName("avatarDynamicUrl") var avatarDynamicUrl: String? = "",
+    @SerializedName("playerName") var playerName: String = "",
+    @SerializedName("rank") val rank: Int = 0,//排名
+    @SerializedName("teamCode") val teamCode: String = "",
+    @SerializedName("gameExpireTime") val codeExpireTime: Long = 0,
+    @SerializedName("channel") val channel: Long = 0,
+    @SerializedName("groupId") val groupId: Int = 0, //团队id,只有1和2取值
+    @SerializedName("teamCreatorId") var creatorUid: Long = 0, //创建者uid
+    @SerializedName("playerAvatarPath") var playerAvatarPath: String = "",//玩家头像本地路径
+    @GsonNullable
+    @SerializedName("useGameGoodsMap") var userGameGoodsMap: Map<Int, PlayerCommonConfigData>? = null,//用户佩戴的游戏商品
+    @SerializedName("score") var score: Int = 0, //玩家得分
+    @SerializedName("reward") var reward: Int = 0, //获得金币数
+    @SerializedName("exp") var exp: Int = 0, //经验值
+    @SerializedName("micSeat") val micSeat: Int = 0, //对应的麦位
+    @SerializedName("playerGameState") val playerGameState: Int = 0, //用户游戏状态
+
+    @GsonNullable
+    @SerializedName("extraInfo") val extraInfo: PlayerExtraInfoData? = null, //用户信息扩展字段
+
+) : Parcelable {
+
+    //客户端使用
+    var index: Int = 0
+    var selfTeam: Boolean = false //是否是队友
+    var myInvitor: Boolean = false //我的邀请者
+    var liked: Boolean = false //被点赞
+
+    val avatarUrl: String?
+        get() = if (this.avatarDynamicUrl?.isNotEmpty() == true) {
+            this.avatarDynamicUrl
+        } else {
+            this.playerAvatarUrl
+        }
+
+    fun isEmpty(): Boolean {
+        return playerId <= 0
+    }
+
+    fun isSelf(myUid: Long): Boolean {
+        return playerId == myUid
+    }
+
+    fun isCreator(): Boolean = creatorUid == playerId
+
+    fun isWin(): Boolean {
+        return rank <= 3
+    }
+
+    companion object {
+        @JvmStatic
+        fun emptyPlayer(): GamePlayerInfo {
+            return GamePlayerInfo()
+        }
+
+        @JvmStatic
+        fun gamePlayerInfo(playerId: Long, playerName: String): GamePlayerInfo {
+            return GamePlayerInfo().apply {
+                this.playerId = playerId
+                this.playerName = playerName
+            }
+        }
+    }
+
+}
+
+@Parcelize
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class PlayerCommonConfigData(
+    @SerializedName("type") val type: Int, //配置类型
+    @SerializedName("intDataValue1") val intDataValue1: Long, //整型配置值1
+    @SerializedName("intDataValue2") val intDataValue2: Long, //整型配置值2
+    @SerializedName("intDataValue3") val intDataValue3: Long, //整型配置值3
+    @SerializedName("intDataValue4") val intDataValue4: Long, //整型配置值4
+    @SerializedName("intDataValue5") val intDataValue5: Long, //整型配置值5
+    @SerializedName("intDataValue6") val intDataValue6: Long, //整型配置值6
+    @SerializedName("intDataValue7") val intDataValue7: Long, //整型配置值7
+    @SerializedName("intDataValue8") val intDataValue8: Long, //整型配置值8
+    @SerializedName("intDataValue9") val intDataValue9: Long, //整型配置值9
+    @SerializedName("strDataValue1") var stringDataValue1: String, //字符串配置值1
+    @SerializedName("strDataValue2") val stringDataValue2: String, //字符串配置值2
+    @SerializedName("strDataValue3") val stringDataValue3: String, //字符串配置值3
+    @SerializedName("floatDataValue") val floatDataValue: Float, //浮点数配置值
+    @SerializedName("configValue") val configValue: String, //配置值--用于房间配置/客户端配置
+    @SerializedName("expireTime") val expireTime: Long, //配置过期时间, 传0或者null代表不过期, 否则传秒级过期时间戳
+    @SerializedName("ts") val ts: Long, //配置更新时间
+) : Parcelable
+
+
+@Parcelize
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class PlayerExtraInfoData(
+    @SerializedName("undoItemList") val ludoItemList: List<UndoItemData>, //重摇道具
+    @SerializedName("undoItemCountLimit") val undoItemCountLimit: Int, //每回合使用重摇道具次数
+    @SerializedName("isUndoNewPlay") val isUndoNewPlay: Boolean, //免费使用1次重摇道具新用户
+) : Parcelable
+
+
+@Parcelize
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class UndoItemData(
+    @SerializedName("diamond") val diamond: Int, //配置类型
+) : Parcelable

+ 12 - 0
app/src/main/java/com/adealink/weparty/cocosgame/data/CocosRoomType.kt

@@ -0,0 +1,12 @@
+package com.adealink.weparty.cocosgame.data
+
+enum class CocosRoomType(val type: Int) {
+    //GAME_ROOM(0), //游戏房( 暂无此功能 )
+    CHAT_ROOM(1); //语音房
+
+    companion object {
+        fun map(type: Int): CocosRoomType? {
+            return entries.firstOrNull { it.type == type }
+        }
+    }
+}

+ 183 - 0
app/src/main/java/com/adealink/weparty/cocosgame/data/CocosWindowViewData.kt

@@ -0,0 +1,183 @@
+package com.adealink.weparty.cocosgame.data
+
+import android.os.Parcelable
+import android.util.Size
+import com.adealink.frame.base.AppBaseInfo
+import com.adealink.frame.data.json.froJsonErrorNull
+import com.adealink.frame.data.json.toJsonErrorNull
+import com.adealink.frame.router.Router
+import com.google.gson.JsonDeserializationContext
+import com.google.gson.JsonDeserializer
+import com.google.gson.JsonElement
+import com.google.gson.JsonPrimitive
+import com.google.gson.JsonSerializationContext
+import com.google.gson.JsonSerializer
+import com.google.gson.annotations.GsonNullable
+import com.google.gson.annotations.JsonAdapter
+import com.google.gson.annotations.SerializedName
+import com.google.gson.internal.bind.ExtReflectiveTypeAdapterFactory
+import kotlinx.parcelize.Parcelize
+import java.lang.reflect.Type
+
+/*
+ * Cocos窗口视图相关数据
+ */
+
+/**
+ * 游戏视图节点类型
+ */
+enum class NodeViewType(val type: String) {
+    UNKNOWN("unknown"),
+    EMOTION_BTN("emotion_btn"),
+    MESSAGE_BTN("message_btn"),
+    RANK_BTN("rank_btn"),
+    SETTING_BTN("setting_btn"),
+
+    BOTTOM_BTNS_AREA("bottom_btns_area"), //底部按钮区
+    TOP_BAR_AREA("top_bar_area"), //顶部留白区域
+    BOTTOM_BAR_AREA("bottom_bar_area"); //底部留白区域
+
+    companion object {
+
+        fun getNodeViewType(type: String): NodeViewType {
+            for (entry in NodeViewType.values()) {
+                if (entry.type == type) {
+                    return entry
+                }
+            }
+            return UNKNOWN
+        }
+
+    }
+}
+
+data class NodeViewData(
+    @SerializedName("type") val type: String,
+)
+
+@JsonAdapter(InitNodeViewDataParser::class)
+data class InitNodeViewData(
+    @SerializedName("type") val type: NodeViewType,
+    @SerializedName("data") var data: CocosViewData?
+)
+
+object InitNodeViewDataParser : JsonDeserializer<InitNodeViewData>,
+    JsonSerializer<InitNodeViewData> {
+
+    override fun deserialize(
+        json: JsonElement?,
+        typeOfT: Type?,
+        context: JsonDeserializationContext?,
+    ): InitNodeViewData? {
+        val obj = json?.asJsonObject ?: return null
+        val typeString = obj.get("type")?.asString ?: return null
+        val type = NodeViewType.getNodeViewType(typeString)
+        return InitNodeViewData(
+            type,
+            froJsonErrorNull(obj.get("data")?.asJsonObject?.toString(), CocosViewData::class.java)
+        )
+    }
+
+    override fun serialize(
+        src: InitNodeViewData?,
+        typeOfSrc: Type?,
+        context: JsonSerializationContext?,
+    ): JsonElement? {
+        return if (src == null) null else JsonPrimitive(toJsonErrorNull(src))
+    }
+
+}
+
+@Parcelize
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class CocosPosition(
+    @SerializedName("x") val x: Float,
+    @SerializedName("y") val y: Float,
+) : Parcelable {
+
+    companion object {
+        /**
+         * 转化为 Cocos坐标
+         * 坐标原点(左下角)
+         * ↑ Y轴
+         * → X轴
+         */
+        @JvmStatic
+        fun convertToCocosPosition(
+            x: Float, y: Float, //控件坐标(相对于游戏容器)
+            viewSize: Size,  //控件大小
+            gameContainerSize: Size //游戏容器大小
+        ): CocosPosition {
+            return CocosPosition(
+                x + viewSize.width / 2,
+                gameContainerSize.height - (y + viewSize.height / 2)
+            )
+        }
+    }
+
+}
+
+
+@Parcelize
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class CocosViewSize(
+    @SerializedName("width") val width: Float,
+    @SerializedName("height") val height: Float,
+) : Parcelable
+
+
+/**
+ * Cocos视图方位
+ */
+enum class CocosViewLocation(val location: Int) {
+    CENTER(0b0000), //居中
+    TOP(0b0001), //上
+    BOTTOM(0b0010), //下
+    LEFT(0b0100), //左
+    RIGHT(0b1000), //右
+    TOP_LEFT(0b0101), //左上
+    TOP_RIGHT(0b1001), //右上
+    BOTTOM_LEFT(0b0110), //左下
+    BOTTOM_RIGHT(0b1010); //右下
+
+    companion object {
+        @JvmStatic
+        fun map(location: Int?): CocosViewLocation? {
+            location ?: return null
+            return CocosViewLocation.values().find { it.location == location }
+        }
+    }
+}
+
+/**
+ * Cocos视图控件位置大小
+ */
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class CocosViewData(
+    @SerializedName("position") val position: CocosPosition,
+    @SerializedName("size") val size: CocosViewSize,
+    @GsonNullable
+    @SerializedName("location") val location: Int? = null
+)
+
+/**
+ * Cocos游戏视图位置大小数据
+ */
+data class CocosWindowViewData(
+    @SerializedName("sound_open") val soundOpen: Boolean,
+    @SerializedName("vibrate_open") val vibrateOpen: Boolean,
+    @SerializedName("lang_code") val langCode: String,
+    @SerializedName("view_size") val viewSize: CocosViewSize,
+    @SerializedName("safe_area") val safeArea: CocosWindowSafeArea,
+    @SerializedName("token") val token: String = "",
+    @SerializedName("baseUrl") val baseUrl: String = "",
+    @SerializedName("room_id") val roomId: Long = 0,
+    @SerializedName("scheme_host") val schemeHost: String = Router.getDeepLink(""),
+    @SerializedName("ws_url") val wsUrl: String = "",
+    @SerializedName("app_name") val appName: String = AppBaseInfo.appName
+)
+
+data class CocosWindowSafeArea(
+    @SerializedName("position") val position: CocosPosition,
+    @SerializedName("size") val size: CocosViewSize,
+)

+ 27 - 0
app/src/main/java/com/adealink/weparty/cocosgame/data/Error.kt

@@ -0,0 +1,27 @@
+package com.adealink.weparty.cocosgame.data
+
+import com.adealink.frame.aab.util.getCompatString
+import com.adealink.frame.base.IError
+import com.adealink.weparty.R
+import com.adealink.weparty.module.network.data.ServerCode
+import com.adealink.weparty.module.wallet.data.CurrencyNotSufficientError
+import com.adealink.weparty.module.wallet.data.GameCoinNotSufficientError
+
+class CocosGameUnSupportError :
+    IError(msg = getCompatString(R.string.cocosgame_un_support_tip))
+
+class CocosGamingNotAcceptInvitationError :
+    IError(msg = getCompatString(R.string.cocosgame_gaming_not_accept_invitation))
+
+class CocosInGamingError(data: Any? = null) : IError(data = data)
+
+class GameTypeUnSupportError : IError()
+
+fun getErrorByServerError(error: IError): IError {
+    return when (error.serverCode) {
+        ServerCode.CURRENCY_NOT_ENOUGH.code -> CurrencyNotSufficientError()
+        ServerCode.GAME_IN_PROGRESS.code -> CocosInGamingError(error.data)
+        ServerCode.GAME_COIN_NOT_ENOUGH.code -> GameCoinNotSufficientError()
+        else -> error
+    }
+}

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

@@ -0,0 +1,20 @@
+package com.adealink.weparty.cocosgame.data
+
+sealed class PlayerGameState(val state: Int, val desc: String) {
+    data class Error(val s: Int) : PlayerGameState(s, "异常,类型不正确[$s]")
+    object Normal : PlayerGameState(1, "正常进行")
+    object Quit : PlayerGameState(2, "中通退出")
+    object OverTime : PlayerGameState(3, "超时托管")
+
+    companion object {
+        fun map(state: Int?): PlayerGameState {
+            return when (state) {
+                Normal.state -> Normal
+                Quit.state -> Quit
+                OverTime.state -> OverTime
+                else -> Error(state ?: -1)
+            }
+        }
+    }
+
+}

+ 341 - 0
app/src/main/java/com/adealink/weparty/cocosgame/data/GameData.kt

@@ -0,0 +1,341 @@
+package com.adealink.weparty.cocosgame.data
+
+import android.os.Parcelable
+import androidx.annotation.StringRes
+import com.adealink.frame.aab.util.getCompatString
+import com.adealink.frame.base.AppBaseInfo
+import com.adealink.frame.util.PackageUtil
+import com.adealink.weparty.R
+import com.google.gson.annotations.GsonNullable
+import com.google.gson.annotations.JsonAdapter
+import com.google.gson.annotations.SerializedName
+import com.google.gson.internal.bind.ExtReflectiveTypeAdapterFactory
+import kotlinx.parcelize.Parcelize
+import org.json.JSONObject
+
+/**
+ * @param type 游戏类型(和后端对齐)
+ * @param code Cocos游戏码(唯一)
+ */
+enum class Game(val type: Int, val code: String, val startMode: GameStartMode, val needDownloadResource: Boolean) {
+    LUDO(1, "ludo", GameStartMode.COCOS, true),
+    CARROM(2, "carrom", GameStartMode.WEB, true),
+    UNO(4, "uno", GameStartMode.COCOS, true);
+//    DOMINO(7, "domino", GameStartMode.WEB, true);
+
+    companion object {
+        fun map(type: Int?): Game? {
+            type ?: return null
+            return entries.firstOrNull { it.type == type }
+        }
+
+        fun getNameStrBy(game: Game): String {
+            return when (game) {
+                LUDO -> {
+                    getCompatString(R.string.game_hub_ludo)
+                }
+
+                CARROM -> {
+                    getCompatString(R.string.game_hub_carrom)
+                }
+
+//                SPY -> {
+//                    getCompatString(R.string.game_hub_spy)
+//                }
+
+                UNO -> {
+                    getCompatString(R.string.game_hub_uno)
+                }
+
+//                BRAIN_STORM -> {
+//                    getCompatString(R.string.game_hub_brainstorm)
+//                }
+
+//                GUESS_ANSWER -> {
+//                    getCompatString(R.string.game_hub_guess)
+//                }
+
+//                DOMINO -> {
+//                    getCompatString(R.string.game_hub_domino)
+//                }
+
+//                OKEY101 -> {
+//                    getCompatString(R.string.game_hub_101_okey)
+//                }
+
+//                JACKAROO -> {
+//                    getCompatString(R.string.game_hub_jackaroo)
+//                }
+            }
+        }
+
+        @JvmStatic
+        fun getGameHubGames(): List<Game> {
+            return Game.entries.toList()
+        }
+    }
+}
+
+enum class GameStartMode(val mode: Int) {
+    NATIVE(0), //原生APP开发的游戏
+    COCOS(1), //Cocos引擎启动的游戏
+    WEB(2) //Web手机浏览器启动的游戏
+}
+
+data class LuckyFruitShowRes(@SerializedName("show") val show: Boolean)
+
+enum class GameType(@StringRes val resId: Int) {
+    TEAM_PK(R.string.game_team_pk), //团战pk
+    MIC_PK(R.string.game_mic_pk)//麦位pk
+}
+
+data class GameShowConfigReq(
+    @SerializedName("configTypes") val configTypes: List<Int>,
+    @SerializedName("versionCode") val versionCode: Int,
+    @SerializedName("channel") val channel: String,
+    @SerializedName("platform") val platform: String = AppBaseInfo.platform,
+    @SerializedName("packageName") val packageName: String = PackageUtil.getPackageName(),
+)
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class GameShowConfig(
+    @SerializedName("show") val show: Boolean,
+    @GsonNullable
+    @SerializedName("loadingUrl") val loadingUrl: String?,
+    @GsonNullable
+    @SerializedName("offlineUrl") val offlineUrl: String?,
+    @GsonNullable
+    @SerializedName("showNew") val showNew: Boolean?,
+    @GsonNullable
+    @SerializedName("showRed") val showRed: Boolean?,
+    @GsonNullable
+    @SerializedName("updateTs") val updateTs: Long?,
+)
+
+
+/**
+ * @param gameType eg: [Game]
+ * @param gameRoomType
+ */
+data class GameBetCoinsReq(
+    @SerializedName("gameType")
+    val gameType: Int,
+
+    @GsonNullable
+    @SerializedName("gameRoomType")
+    val gameRoomType: Int? = null,
+)
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class GameBetCoinsRes(
+    /**
+     * key: eg: [CarromGameType]
+     */
+    @SerializedName("gameRoomTypeMap")
+    val gameTypeConfig: Map<Int, GameModeBetConfigData>
+)
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class GameModeBetConfigData(
+    /**
+     * key: eg: [CarromGameMode]
+     * value: 下注值
+     */
+    @SerializedName("configMap")
+    val gameModeConfig: Map<Int, List<Int>>,
+
+    /**
+     * key: eg: [CarromGameMode]
+     * value: 奖励值
+     */
+    @SerializedName("rewardConfigMap")
+    val rewardConfigMap: Map<Int, List<Int>>
+)
+
+data class ReceiveRewardReq(
+    @SerializedName("requestType") val requestType: Int = 0
+)
+
+data class CommonActivityRewardInfoReq(
+    @SerializedName("activityId") val activityId: Long = 0,
+    @SerializedName("requestType") val requestType: Int = 0,
+)
+
+data class GetUserLevelInfoReq(
+    @SerializedName("seqid") val seqid: Long = System.currentTimeMillis(),
+    @SerializedName("uids") val uids: List<Long>
+)
+
+interface RoomCommonOperatorData {
+
+    fun areItemsTheSame(newItem: RoomCommonOperatorData): Boolean = this == newItem
+
+    fun areContentsTheSame(newItem: RoomCommonOperatorData): Boolean
+
+    fun changeReceiveStatus(status: Int)
+}
+
+@Parcelize
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class GameActivityRewardInfo(
+    @GsonNullable
+    @SerializedName("rewardTypeName") val rewardTypeName: String? = null,
+    @SerializedName("detailList") val detailList: List<RewardDetailSource>,
+    @SerializedName("rewardSourceContent") val rewardSourceContent: String
+) : RoomCommonOperatorData, Parcelable {
+    companion object {
+        //未领取
+        const val STATUS_UNRECEIVED = 0
+
+        //已领取
+        const val STATUS_RECEIVED = 1
+
+        //未达领取资格
+        const val STATUS_INSUFFICIENT = 2
+    }
+
+    override fun areContentsTheSame(newItem: RoomCommonOperatorData): Boolean {
+        if (newItem !is GameActivityRewardInfo) {
+            return false
+        }
+        return rewardTypeName == newItem.rewardTypeName && rewardSourceContent == newItem.rewardSourceContent
+    }
+
+    fun getLevel(): Int {
+        return try {
+            rewardSourceContent.toInt()
+        } catch (exception: Exception) {
+            0
+        }
+    }
+
+    fun hasRewardToReceived(): Boolean {
+        if (detailList.isEmpty()) {
+            return false
+        }
+        val rewardData = detailList[0]
+        return rewardData.receiveStatus == STATUS_UNRECEIVED && rewardData.rewardItemList?.isNotEmpty() == true
+    }
+
+    override fun changeReceiveStatus(status: Int) {
+        if (detailList.isEmpty()) {
+            return
+        }
+        detailList[0].receiveStatus = status
+    }
+
+    fun translateToRewardDetail(): RewardDetailData? {
+        if (detailList.isEmpty()) {
+            return null
+        }
+        val rewardList = mutableListOf<RewardItemData>()
+        detailList[0].rewardItemList?.forEach { rewardItem ->
+            rewardList.add(
+                RewardItemData(
+                    rewardItem.rewardResourceUrl,
+                    rewardItem.num,
+                    rewardItem.rewardResourceType,
+                    rewardItem.rewardResourceName,
+                    rewardItem.rewardValue
+                )
+            )
+        }
+        return RewardDetailData(
+            RoomPlayCenterOperatorType.LUCKY_GAME_REWARD,
+            rewardList
+        )
+    }
+}
+
+enum class RoomPlayCenterOperatorType {
+    //操作类型
+    LUCKY_GAME_REWARD,
+    LUCKY_GAME_RANK,
+}
+
+@Parcelize
+data class RewardDetailData(
+    val type: RoomPlayCenterOperatorType,
+    val rewardList: MutableList<RewardItemData>? = null
+) : Parcelable
+
+@Parcelize
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class RewardDetailSource(
+    @SerializedName("date") val date: Long,
+    @GsonNullable
+    @SerializedName("rewardItemList") val rewardItemList: ArrayList<RewardItem>? = null,
+    @SerializedName("rewardSourceContent") val rewardSourceContent: String,
+    @SerializedName("receiveStatus") var receiveStatus: Int,
+) : Parcelable
+
+@Parcelize
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class RewardItem(
+    @SerializedName("carveUpNum") val carveUpNum: Int,
+    @SerializedName("dynamicResourceUrl") val dynamicResourceUrl: String,
+    @SerializedName("goodIdLengthType") val goodIdLengthType: Int,
+    @SerializedName("id") val id: Int,
+    @SerializedName("issueToPackage") val issueToPackage: Int,
+    @SerializedName("jumpId") val jumpId: Int,
+    @SerializedName("num") val num: Int,
+    @SerializedName("packageId") val packageId: Int,
+    @SerializedName("probability") val probability: Float,
+    @SerializedName("rewardDuration") val rewardDuration: Int,
+    @SerializedName("rewardEffectTime") val rewardEffectTime: Int,
+    @SerializedName("rewardNum") val rewardNum: Int,
+    @SerializedName("rewardNumScale") val rewardNumScale: Float,
+    @SerializedName("rewardRate") val rewardRate: Float,
+    @SerializedName("rewardResourceId") val rewardResourceId: Int,
+    @GsonNullable
+    @SerializedName("rewardResourceName") val rewardResourceName: String? = null,
+    @SerializedName("rewardResourceType") val rewardResourceType: Int,
+    @GsonNullable
+    @SerializedName("rewardResourceUrl") val rewardResourceUrl: String? = null,
+    @SerializedName("rewardValue") val rewardValue: Int,
+    @SerializedName("source") val source: Int,
+    @SerializedName("status") val status: Int,
+) : Parcelable
+
+@Parcelize
+data class RewardItemData(
+    val rewardUrl: String? = null,
+    var rewardCount: Int = 0,
+    var rewardType: Int,
+    var rewardName: String? = null,
+    var rewardValue: Int = 0
+) : Parcelable {
+
+    fun getRewardName(language: String): String? {
+        try {
+            val json = this.rewardName?.let { JSONObject(it) }
+            return json?.optString(language) ?: ""
+        } catch (e: Exception) {
+            //doNothing
+        }
+        return this.rewardName
+    }
+}
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class UserGameLevelInfoResult(
+    @SerializedName("seqid") val seqid: Long,
+    @GsonNullable
+    @SerializedName("userGameLevelInfoMap") val userGameLevelInfoMap: MutableMap<String, UserGameLevelInfo>? = null,
+)
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class UserGameLevelInfo(
+    @SerializedName("uid") val uid: Long,
+    @SerializedName("gameLevel") val gameLevel: Int,
+    @SerializedName("expire") val expire: Long,
+    @SerializedName("score") val score: Long,
+    @SerializedName("starNum") val starNum: Int,
+    @SerializedName("lastGameLevel") val lastGameLevel: Int,
+    @SerializedName("lastScore") val lastScore: Long,
+    @SerializedName("nextGameLevel") val nextGameLevel: Int,
+    @SerializedName("nextScore") val nextScore: Long,
+    @SerializedName("levelTime") val levelTime: Long,
+    @SerializedName("createTime") val createTime: Long,
+
+    )

+ 227 - 0
app/src/main/java/com/adealink/weparty/cocosgame/data/GameToNativeOpData.kt

@@ -0,0 +1,227 @@
+package com.adealink.weparty.cocosgame.data
+
+import android.os.Parcelable
+import com.adealink.frame.data.json.froJsonErrorNull
+import com.adealink.frame.data.json.toJsonErrorNull
+import com.google.gson.JsonDeserializationContext
+import com.google.gson.JsonDeserializer
+import com.google.gson.JsonElement
+import com.google.gson.JsonPrimitive
+import com.google.gson.JsonSerializationContext
+import com.google.gson.JsonSerializer
+import com.google.gson.annotations.JsonAdapter
+import com.google.gson.annotations.SerializedName
+import com.google.gson.internal.bind.ExtReflectiveTypeAdapterFactory
+import kotlinx.parcelize.Parcelize
+import java.lang.reflect.Type
+
+/*
+ * APP游戏操作Cocos
+ */
+enum class GameToNativeOpType(var op: Int, val clazz: Class<*>?) {
+    UNKNOWN(0, UnknownOpData::class.java),
+    CLICK_AVATAR(1, ClickAvatarOpData::class.java), //点击头像
+    CLICK_HELP(2, ClickHelpOpData::class.java), //点击帮助
+    CLICK_SETTING(3, ClickSettingOpData::class.java), //点击设置
+    CLICK_EXIT(4, ClickExitOpData::class.java), //点击退出
+    CLICK_RESTART(5, ClickRestartOpData::class.java), //点击restart
+    VIBRATE(6, VibrateOpData::class.java), //振动
+    PLAY_SOUND(7, PlaySoundOpData::class.java), //播放音效
+    SWITCH_SOUND(8, SwitchSoundOpData::class.java), //音效开关
+    SWITCH_VIBRATE(9, SwitchVibrateOpData::class.java), //振动开关
+    CLICK_MIC(10, ClickMicOpData::class.java), //点击麦克风
+    STOP_SOUND(11, StopSoundOpData::class.java), //停止音效播放
+    CLICK_EMOTION(12, ClickEmotionOpData::class.java), //点击表情按钮
+    CLICK_MESSAGE(13, ClickMessageOpData::class.java), //点击消息按钮
+    SHOW_NEW_USER_TASK(14, ShowNewUserTaskOpData::class.java), //点击消息按钮
+    SHOW_GAME_RULE(15, ShowGameRuleOpData::class.java), //显示游戏规则
+    //SHOW_TROPHY(16, ShowTrophyOpData::class.java), //显示奖杯信息
+    CLICK_BACK_TO_HALL(17, BackToHallOpData::class.java), //返回游戏大厅
+    SHOW_RANK(18, ShowRankOpData::class.java), //显示排行榜
+    CLICK_START_GAME(19, StartGameOpData::class.java); //返回游戏大厅
+
+    companion object {
+
+        fun getGameToNativeOp(op: Int): GameToNativeOpType {
+            for (entry in values()) {
+                if (entry.op == op) {
+                    return entry
+                }
+            }
+            return UNKNOWN
+        }
+
+    }
+
+}
+
+@JsonAdapter(GameToNativeOpParser::class)
+class GameToNativeOp(@SerializedName("op") val op: GameToNativeOpType) {
+    @SerializedName("data")
+    var data: GameToNativeOpData? = null
+}
+
+object GameToNativeOpParser : JsonDeserializer<GameToNativeOp>,
+    JsonSerializer<GameToNativeOp> {
+
+    override fun deserialize(
+        json: JsonElement?,
+        typeOfT: Type?,
+        context: JsonDeserializationContext?,
+    ): GameToNativeOp? {
+        val obj = json?.asJsonObject ?: return null
+        val opInt = obj.get("op")?.asInt ?: return null
+        val op = GameToNativeOpType.getGameToNativeOp(opInt)
+        return GameToNativeOp(op).apply {
+            data = froJsonErrorNull(obj.get("data")?.asJsonObject?.toString(), op.clazz as Type)
+        }
+    }
+
+    override fun serialize(
+        src: GameToNativeOp?,
+        typeOfSrc: Type?,
+        context: JsonSerializationContext?,
+    ): JsonElement? {
+        return if (src == null) null else JsonPrimitive(toJsonErrorNull(src))
+    }
+
+}
+
+@Parcelize
+sealed class GameToNativeOpData : Parcelable
+
+/**
+ * 未知操作数据
+ */
+class UnknownOpData : GameToNativeOpData()
+
+/**
+ * 点击头像
+ */
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class ClickAvatarOpData(@SerializedName("uid") val uid: Long) : GameToNativeOpData()
+
+/**
+ * 点击帮助
+ */
+class ClickHelpOpData : GameToNativeOpData()
+
+/**
+ * 点击设置
+ */
+class ClickSettingOpData : GameToNativeOpData()
+
+/**
+ * 点击退出
+ */
+class ClickExitOpData : GameToNativeOpData()
+
+/**
+ * 点击重新开始
+ */
+class ClickRestartOpData : GameToNativeOpData()
+
+enum class VibrateType(val duration: Long) {
+    LONG(-1),
+    SHORT(0),
+    CUSTOM(-2);
+
+    companion object {
+        fun map(duration: Long): VibrateType {
+            return VibrateType.values().firstOrNull { it.duration == duration } ?: CUSTOM
+        }
+    }
+}
+
+/**
+ * 震动
+ */
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class VibrateOpData(@SerializedName("duration") val duration: Long /*单位:ms*/) :
+    GameToNativeOpData()
+
+/**
+ * 播放音效
+ */
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class PlaySoundOpData(@SerializedName("sound_name") val soundName: String) :
+    GameToNativeOpData()
+
+/**
+ * 停止播放音效
+ */
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class StopSoundOpData(@SerializedName("sound_name") val soundName: String) :
+    GameToNativeOpData()
+
+/**
+ * 音效开关
+ */
+class SwitchSoundOpData : GameToNativeOpData()
+
+/**
+ * 振动开发
+ */
+class SwitchVibrateOpData : GameToNativeOpData()
+
+/**
+ * 点击麦克风
+ */
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class ClickMicOpData(@SerializedName("uid") val uid: Long) : GameToNativeOpData()
+
+/**
+ * 点击表情按钮
+ */
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class ClickEmotionOpData(
+    @SerializedName("position") val position: CocosPosition,
+    @SerializedName("size") val size: CocosViewSize
+) : GameToNativeOpData()
+
+/**
+ * 点击消息按钮
+ */
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class ClickMessageOpData(
+    @SerializedName("position") val position: CocosPosition,
+    @SerializedName("size") val size: CocosViewSize
+) : GameToNativeOpData()
+
+
+/**
+ * 展示了新手任务
+ */
+class ShowNewUserTaskOpData : GameToNativeOpData()
+
+
+/**
+ * 展示游戏规则
+ */
+class ShowGameRuleOpData : GameToNativeOpData()
+
+
+/**
+ * 展示奖杯信息
+ */
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+class ShowTrophyOpData(
+    @SerializedName("position") val position: CocosPosition,
+    @SerializedName("size") val size: CocosViewSize
+) : GameToNativeOpData()
+
+
+/**
+ * 返回游戏大厅
+ */
+class BackToHallOpData : GameToNativeOpData()
+
+/**
+ * 显示排行榜
+ */
+class ShowRankOpData : GameToNativeOpData()
+
+/**
+ * 启动游戏
+ */
+class StartGameOpData : GameToNativeOpData()

+ 31 - 0
app/src/main/java/com/adealink/weparty/cocosgame/data/NativeToGameOpData.kt

@@ -0,0 +1,31 @@
+package com.adealink.weparty.cocosgame.data
+
+import com.google.gson.annotations.SerializedName
+
+/*
+ * Cocos操作APP游戏
+ */
+enum class NativeToGameOp(var op: Int) {
+    MIC_STATUS(2),      // 麦位状态
+    OPEN_MIC_TIPS(4),   // 引导用户开麦气泡
+}
+
+data class NativeToGameOpData(
+    @SerializedName("op") val op: Int,
+    @SerializedName("data") val data: BaseOpData,
+)
+
+sealed class BaseOpData
+
+enum class MicStatus(val status: Int) {
+    NORMAL(0), //正常
+    CLOSE(1), //关闭
+    SPEAKING(2) //说话
+}
+
+data class OpMicStatusData(
+    @SerializedName("uid") val uid: Long,
+    @SerializedName("status") val status: Int
+) : BaseOpData()
+
+class OpenMicTipsData : BaseOpData()

+ 15 - 0
app/src/main/java/com/adealink/weparty/cocosgame/data/Tags.kt

@@ -0,0 +1,15 @@
+package com.adealink.weparty.cocosgame.data
+
+const val TAG_COCOS_GAME = "tag_cocos_game"
+const val TAG_COCOS_GAME_FLOW = "${TAG_COCOS_GAME}_flow"
+const val TAG_COCOS_GAME_FLOW_ROOM = "${TAG_COCOS_GAME_FLOW}_room"
+const val TAG_COCOS_GAME_NETWORK = "${TAG_COCOS_GAME}_network"
+const val TAG_COCOS_GAME_MEDIA = "${TAG_COCOS_GAME}_media"
+const val TAG_COCOS_GAME_SEATS = "${TAG_COCOS_GAME}_seats"
+const val TAG_COCOS_GAME_MIC = "${TAG_COCOS_GAME}_mic"
+
+
+const val TAG_COCOS_GAME_WEB = "tag_cocos_web"
+const val TAG_COCOS_GAME_WEB_FLOW = "${TAG_COCOS_GAME_WEB}_flow"
+const val TAG_COCOS_GAME_WEB_NETWORK = "${TAG_COCOS_GAME_WEB}_network"
+const val TAG_COCOS_GAME_WEB_CALL = "${TAG_COCOS_GAME_WEB}_call"

+ 31 - 0
app/src/main/java/com/adealink/weparty/cocosgame/datasource/local/CocosGameLocalService.kt

@@ -0,0 +1,31 @@
+package com.adealink.weparty.cocosgame.datasource.local
+
+import android.content.Context
+import com.adealink.frame.storage.sp.TypeDelegationPrefs
+import com.adealink.frame.util.AppUtil
+import com.adealink.weparty.module.account.AccountModule
+
+object CocosGameLocalService : TypeDelegationPrefs(
+    prefs = {
+        AppUtil.appContext.getSharedPreferences("pref_cocosgame", Context.MODE_PRIVATE)
+    },
+    userId = {
+        AccountModule.uid.toString()
+    }
+) {
+
+    var recordAudioPermissionTipCount: Int by PrefUserKey(
+        "key_record_audio_permission_tip_count",
+        0
+    )
+
+    var bluetoothConnectPermissionTipCount: Int by PrefUserKey(
+        "key_bluetooth_connect_permission_tip_count",
+        0
+    )
+
+    var lastOpenGameMicTipsTime: Long by PrefUserKey(
+        "key_last_open_game_mic_tips_time",
+        0
+    )
+}

+ 166 - 0
app/src/main/java/com/adealink/weparty/cocosgame/datasource/remote/CommonGameHttpService.kt

@@ -0,0 +1,166 @@
+package com.adealink.weparty.cocosgame.datasource.remote
+
+import com.adealink.frame.base.Rlt
+import com.adealink.frame.network.data.Res
+import com.adealink.weparty.cocosgame.data.AudienceExitReq
+import com.adealink.weparty.cocosgame.data.AudienceJoinReq
+import com.adealink.weparty.cocosgame.data.ExitGameReq
+import com.adealink.weparty.cocosgame.data.GameBetCoinsReq
+import com.adealink.weparty.cocosgame.data.GameBetCoinsRes
+import com.adealink.weparty.cocosgame.data.GameClosetReq
+import com.adealink.weparty.cocosgame.data.GameConfigInfo
+import com.adealink.weparty.cocosgame.data.GameConfigReq
+import com.adealink.weparty.cocosgame.data.GameDashboardReq
+import com.adealink.weparty.cocosgame.data.GameDashboardRes
+import com.adealink.weparty.cocosgame.data.GameStartNotify
+import com.adealink.weparty.cocosgame.data.GameStartReq
+import com.adealink.weparty.cocosgame.data.GetPlayerMicStatusReq
+import com.adealink.weparty.cocosgame.data.GetPlayerMicStatusRes
+import com.adealink.weparty.cocosgame.data.OpPlayerMicStatusReq
+import com.adealink.weparty.cocosgame.data.PlayerGameInfo
+import com.adealink.weparty.cocosgame.data.RoomGameInviteReq
+import com.adealink.weparty.cocosgame.data.RoomGameJoinReq
+import com.adealink.weparty.cocosgame.data.RoomGameKickReq
+import com.adealink.weparty.cocosgame.data.RoomGameReq
+import com.adealink.weparty.cocosgame.data.SingleRoomMatchReq
+import retrofit2.http.Body
+import retrofit2.http.Core
+import retrofit2.http.GET
+import retrofit2.http.POST
+
+interface CommonGameHttpService {
+
+    /**
+     * 获取下注金额
+     */
+    @Core
+    @POST("gamePlatform/common/coin/config")
+    suspend fun getBetCoins(@Body req: GameBetCoinsReq): Rlt<Res<GameBetCoinsRes>>
+
+    /**
+     * 开始单人匹配
+     */
+    @Core
+    @POST("gamePlatform/ludo/match/single/add")
+    suspend fun singleMatch(@Body req: GameConfigReq): Rlt<Res<GameConfigInfo>>
+
+    /**
+     * 取消单人匹配
+     */
+    @Core
+    @POST("gamePlatform/ludo/match/single/exit")
+    suspend fun cancelSingleMatch(): Rlt<Res<String>>
+
+    /**
+     * 房间游戏 初始化
+     */
+    @Core
+    @POST("gamePlatform/common/roomGame/init")
+    suspend fun initRoomGame(@Body req: GameConfigInfo): Rlt<Res<GameConfigInfo>>
+
+    /**
+     * 房间游戏 关闭
+     */
+    @Core
+    @POST("gamePlatform/common/roomGame/close")
+    suspend fun closeRoomGame(@Body req: RoomGameReq): Rlt<Res<Any>>
+
+    /**
+     * 玩家加入房间游戏(同加入游戏匹配、上麦)
+     */
+    @Core
+    @POST("gamePlatform/common/roomGame/join")
+    suspend fun joinRoomGame(@Body req: RoomGameJoinReq): Rlt<Res<Any>>
+
+    /**
+     * 踢出房间游戏(同踢出游戏匹配、踢下麦)
+     */
+    @Core
+    @POST("gamePlatform/common/roomGame/kick")
+    suspend fun kickRoomGame(@Body req: RoomGameKickReq): Rlt<Res<Any>>
+
+    /**
+     * 邀请加入房间游戏(同邀请加入游戏匹配、邀请上麦)
+     */
+    @Core
+    @POST("gamePlatform/common/roomGame/invite")
+    suspend fun inviteRoomGame(@Body req: RoomGameInviteReq): Rlt<Res<Any>>
+
+    /**
+     * 开始游戏
+     */
+    @Core
+    @POST("gamePlatform/common/start")
+    suspend fun start(@Body req: GameStartReq): Rlt<Res<GameConfigInfo>>
+
+    /**
+     * 关闭游戏(同取消房间游戏模式)
+     */
+    @Core
+    @POST("gamePlatform/common/roomGame/close")
+    suspend fun closeGame(@Body req: GameClosetReq): Rlt<Res<Any>>
+
+    /**
+     * 观众加入
+     */
+    @Core
+    @POST("gamePlatform/common/audience/join")
+    suspend fun audienceJoin(@Body req: AudienceJoinReq): Rlt<Res<GameStartNotify>>
+
+    /**
+     * 观众退出
+     */
+    @Core
+    @POST("gamePlatform/common/audience/exit")
+    suspend fun audienceExit(@Body req: AudienceExitReq): Rlt<Res<Any>>
+
+    /**
+     * 获取当前游戏状态数据
+     */
+    @Core
+    @GET("gamePlatform/player/playing_game_info")
+    suspend fun getPlayingGameInfo(): Rlt<Res<PlayerGameInfo>>
+
+    /**
+     * 退出游戏
+     */
+    @Core
+    @POST("gamePlatform/common/player/exit")
+    suspend fun exitGame(@Body req: ExitGameReq): Rlt<Res<Any>>
+
+    /**
+     * 游戏榜单
+     */
+    @Core
+    @POST("gamePlatform/common/dashboard")
+    suspend fun getRankBoard(@Body req: GameDashboardReq): Rlt<Res<GameDashboardRes>>
+
+    /**
+     * 开始单人匹配(语音房玩法)
+     */
+    @Core
+    @POST("gamePlatform/ludo/match/singleRoom/add")
+    suspend fun singleRoomMatch(@Body req: SingleRoomMatchReq): Rlt<Res<GameConfigInfo>>
+
+    /**
+     * 取消单人匹配(语音房玩法)
+     */
+    @Core
+    @POST("gamePlatform/ludo/match/singleRoom/exit")
+    suspend fun cancelSingleRoomMatch(): Rlt<Res<String>>
+
+    /**
+     * 获取游戏中玩家的麦位开关
+     */
+    @Core
+    @POST("gamePlatform/mic/get")
+    suspend fun getGetPlayerMicStatus(@Body req: GetPlayerMicStatusReq): Rlt<Res<GetPlayerMicStatusRes>>
+
+    /**
+     * 操作玩家的麦位开关
+     */
+    @Core
+    @POST("gamePlatform/mic/op")
+    suspend fun opPlayerMicStatus(@Body req: OpPlayerMicStatusReq): Rlt<Res<Any>>
+
+}

+ 81 - 0
app/src/main/java/com/adealink/weparty/cocosgame/gift/adapter/SendGiftInfoRollViewBinder.kt

@@ -0,0 +1,81 @@
+package com.adealink.weparty.cocosgame.gift.adapter
+
+import android.text.SpannableStringBuilder
+import android.text.Spanned
+import android.text.style.ForegroundColorSpan
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import com.adealink.frame.aab.util.getCompatColor
+import com.adealink.frame.aab.util.getCompatString
+import com.adealink.frame.ext.safeSetSpan
+import com.adealink.weparty.R
+import com.adealink.weparty.commonui.recycleview.adapter.BindingViewHolder
+import com.adealink.weparty.commonui.recycleview.adapter.multitype.ItemViewBinder
+import com.adealink.weparty.databinding.ItemCocosgameSendGiftInfoRollBinding
+import com.adealink.weparty.module.gift.data.SendGiftItem
+
+class SendGiftInfoRollViewBinder :
+    ItemViewBinder<SendGiftItem, SendGiftInfoRollViewBinder.ViewHolder>() {
+
+    override fun onBindViewHolder(holder: ViewHolder, item: SendGiftItem) {
+        holder.update(item)
+    }
+
+    override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): ViewHolder {
+        return ViewHolder(ItemCocosgameSendGiftInfoRollBinding.inflate(inflater, parent, false))
+    }
+
+    inner class ViewHolder(binding: ItemCocosgameSendGiftInfoRollBinding) :
+        BindingViewHolder<ItemCocosgameSendGiftInfoRollBinding>(binding) {
+
+        private var sendGiftItem: SendGiftItem? = null
+
+        fun update(item: SendGiftItem) {
+            sendGiftItem = item
+            val fromMember = item.fromUserInfo
+            val toMember = item.toUserInfo
+            binding.ivAvtarGift.setImageUrl(fromMember?.url)
+            if (fromMember != null && toMember != null) {
+                val sendGiftText = getCompatString(
+                    R.string.cocosgame_send_gift_roll_msg,
+                    fromMember.name,
+                    toMember.name,
+                )
+                binding.tvGiftInfo.text = SpannableStringBuilder(sendGiftText).apply {
+                    val fromMemberName = fromMember.name
+                    if (fromMemberName.isNotEmpty()) {
+                        val fromMemberStart = sendGiftText.indexOf(fromMemberName)
+                        if (fromMemberStart >= 0) {
+                            safeSetSpan(
+                                ForegroundColorSpan(getCompatColor(R.color.white)),
+                                fromMemberStart,
+                                fromMemberStart + fromMemberName.length,
+                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
+                            )
+                        }
+                    }
+
+                    val toMemberName = toMember.name
+                    if (toMemberName.isNotEmpty()) {
+                        val toMemberStart = sendGiftText.indexOf(toMemberName)
+                        if (toMemberStart >= 0) {
+                            safeSetSpan(
+                                ForegroundColorSpan(getCompatColor(R.color.white)),
+                                toMemberStart,
+                                toMemberStart + toMemberName.length,
+                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
+                            )
+                        }
+                    }
+                }
+            } else {
+                binding.tvGiftInfo.text = null
+            }
+
+            binding.ivGift.setImageUrl(item.iconUrl)
+            binding.tvGiftNum.text = getCompatString(R.string.commonui_count, item.count)
+        }
+
+    }
+
+}

+ 17 - 0
app/src/main/java/com/adealink/weparty/cocosgame/gift/adapter/SendGiftItemDiffUtil.kt

@@ -0,0 +1,17 @@
+package com.adealink.weparty.cocosgame.gift.adapter
+
+import androidx.recyclerview.widget.DiffUtil
+import com.adealink.weparty.module.gift.data.SendGiftItem
+import com.adealink.weparty.module.gift.data.contentsTheSame
+
+class SendGiftItemDiffUtil : DiffUtil.ItemCallback<SendGiftItem>() {
+
+    override fun areItemsTheSame(oldItem: SendGiftItem, newItem: SendGiftItem): Boolean {
+        return oldItem == newItem
+    }
+
+    override fun areContentsTheSame(oldItem: SendGiftItem, newItem: SendGiftItem): Boolean {
+        return oldItem contentsTheSame newItem
+    }
+
+}

+ 124 - 0
app/src/main/java/com/adealink/weparty/cocosgame/gift/comp/SendGiftComp.kt

@@ -0,0 +1,124 @@
+package com.adealink.weparty.cocosgame.gift.comp
+
+import androidx.lifecycle.LifecycleOwner
+import androidx.recyclerview.widget.LinearLayoutManager
+import com.adealink.frame.base.Rlt
+import com.adealink.frame.base.fastLazy
+import com.adealink.frame.mvvm.view.ViewComponent
+import com.adealink.weparty.cocosgame.gift.adapter.SendGiftInfoRollViewBinder
+import com.adealink.weparty.cocosgame.gift.adapter.SendGiftItemDiffUtil
+import com.adealink.weparty.commonui.ext.dp
+import com.adealink.weparty.commonui.ext.show
+import com.adealink.weparty.commonui.recycleview.AutoRollRecycleView
+import com.adealink.weparty.commonui.recycleview.adapter.MultiTypeListAdapter
+import com.adealink.weparty.module.anchor.data.FromScene
+import com.adealink.weparty.module.gift.GiftModule
+import com.adealink.weparty.module.gift.combo.ComboGiftCounter
+import com.adealink.weparty.module.gift.combo.OnComboCallback
+import com.adealink.weparty.module.gift.data.SendGiftItem
+import com.adealink.weparty.module.gift.data.SendGiftNotify
+import com.adealink.weparty.module.gift.data.toSendGiftItems
+import com.adealink.weparty.module.profile.ProfileModule
+import com.adealink.weparty.module.room.data.RoomMember
+import com.adealink.weparty.module.room.data.toRoomMember
+
+class SendGiftComp(
+    lifecycleOwner: LifecycleOwner,
+    private val sendGiftsRollRv: AutoRollRecycleView,
+) : ViewComponent(lifecycleOwner), OnComboCallback {
+
+    private val giftViewModel by fastLazy { GiftModule.getGiftViewModel(viewModelStoreOwner) }
+    private val profileViewModel by fastLazy { ProfileModule.getProfileViewModel(viewModelStoreOwner) }
+
+    private val adapter by fastLazy { MultiTypeListAdapter(SendGiftItemDiffUtil()) }
+    private val sendGiftItemList = arrayListOf<SendGiftItem>()
+    private val comboGiftCounter = ComboGiftCounter(this)
+
+    override fun onCreate() {
+        super.onCreate()
+        initViews()
+        observeViewModel()
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        comboGiftCounter.onCleared()
+    }
+
+    private fun initViews() {
+        sendGiftsRollRv.rollDistancePx = 28.dp()
+        sendGiftsRollRv.layoutManager =
+            LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
+        adapter.register(SendGiftInfoRollViewBinder())
+        sendGiftsRollRv.adapter = adapter
+    }
+
+    private fun observeViewModel() {
+        giftViewModel?.sendGiftNotifyLD?.observe(viewLifecycleOwner) { sendGiftNotify ->
+            if (sendGiftNotify.giftScene != FromScene.IN_ROOM.scene) {
+                return@observe
+            }
+            handleSendGiftNotify(sendGiftNotify)
+        }
+    }
+
+    private fun handleSendGiftNotify(sendGiftNotify: SendGiftNotify) {
+        if (!sendGiftNotify.gitInfo.isVisibleToUser()) {
+            //该礼物对用户不可见
+            return
+        }
+
+        val toUsersList = mutableListOf<RoomMember>()
+        sendGiftNotify.toUidSet.forEach { toUid ->
+            sendGiftNotify.notifyUserInfo?.get(toUid)?.let {
+                toUsersList.add(it.toRoomMember())
+            }
+        }
+
+        if (sendGiftNotify.gitInfo.canCombo()) {
+            //combo礼物
+            comboGiftCounter.onGiftNotify(sendGiftNotify)
+        } else {
+            val sendGiftItems = sendGiftNotify.toSendGiftItems()
+            fillGiftInfo(sendGiftItems) {
+                sendGiftItemList.addAll(it)
+                adapter.submitList(sendGiftItemList)
+                sendGiftsRollRv.show()
+                sendGiftsRollRv.startRoll()
+            }
+        }
+    }
+
+    private fun fillGiftInfo(items: List<SendGiftItem>, callback: (gifts: List<SendGiftItem>) -> Unit) {
+        val reqUidList = mutableSetOf<Long>().apply {
+            items.onEach { item ->
+                this.add(item.fromUid)
+                this.add(item.toUid)
+            }
+        }
+        profileViewModel?.getUsersInfoByUid(reqUidList, true)?.observe(viewLifecycleOwner) {
+            val userInfoMap = (it as? Rlt.Success)?.data
+            items.onEach { item ->
+                item.fromUserInfo = userInfoMap?.get(item.fromUid)
+                item.toUserInfo = userInfoMap?.get(item.toUid)
+            }
+            callback.invoke(items)
+        } ?: let {
+            callback.invoke(items)
+        }
+
+    }
+
+    override fun onComboFinish(items: List<SendGiftItem>) {
+        if (!isValid) {
+            return
+        }
+        fillGiftInfo(items) {
+            sendGiftItemList.addAll(it)
+            adapter.submitList(sendGiftItemList)
+            sendGiftsRollRv.show()
+            sendGiftsRollRv.startRoll()
+        }
+    }
+
+}

+ 20 - 0
app/src/main/java/com/adealink/weparty/cocosgame/listener/ICocosGameOperateListener.kt

@@ -0,0 +1,20 @@
+package com.adealink.weparty.cocosgame.listener
+
+interface ICocosGameOperateListener {
+
+    /**
+     * 点击头像
+     */
+    fun onAvatarClick(uid: Long) {}
+
+    /**
+     * 自己是否为创建者
+     */
+    fun isIAmCreator(): Boolean = false
+
+    /**
+     * 点击麦克风
+     */
+    fun onMicClick(uid: Long) {}
+
+}

+ 329 - 0
app/src/main/java/com/adealink/weparty/cocosgame/manager/CocosWebGameManager.kt

@@ -0,0 +1,329 @@
+package com.adealink.weparty.cocosgame.manager
+
+import com.adealink.frame.coroutine.dispatcher.Dispatcher
+import com.adealink.frame.game.ICallback
+import com.adealink.frame.log.Log
+import com.adealink.frame.network.INotifyInterceptor
+import com.adealink.frame.util.OnNetworkListener
+import com.adealink.frame.util.registerNetworkListener
+import com.adealink.frame.util.unregisterNetworkListener
+import com.adealink.weparty.App
+import com.adealink.weparty.cocosgame.data.BaseOpData
+import com.adealink.weparty.cocosgame.data.CocosViewData
+import com.adealink.weparty.cocosgame.data.EmptyData
+import com.adealink.weparty.cocosgame.data.Game
+import com.adealink.weparty.cocosgame.data.GamePlayerInfo
+import com.adealink.weparty.cocosgame.data.GameRecordData
+import com.adealink.weparty.cocosgame.data.InitGameData
+import com.adealink.weparty.cocosgame.data.NativeToGameOp
+import com.adealink.weparty.cocosgame.data.NativeToGameOpData
+import com.adealink.weparty.cocosgame.data.NetworkStatusData
+import com.adealink.weparty.cocosgame.data.NodeViewData
+import com.adealink.weparty.cocosgame.data.NodeViewType
+import com.adealink.weparty.cocosgame.data.PlayerData
+import com.adealink.weparty.cocosgame.data.PlayerStatus
+import com.adealink.weparty.cocosgame.data.PlayerStatusData
+import com.adealink.weparty.cocosgame.data.RestGameData
+import com.adealink.weparty.cocosgame.data.StartGameData
+import com.adealink.weparty.cocosgame.data.TAG_COCOS_GAME_WEB_CALL
+import com.adealink.weparty.cocosgame.data.TAG_COCOS_GAME_WEB_FLOW
+import com.adealink.weparty.cocosgame.data.TAG_COCOS_GAME_WEB_NETWORK
+import com.adealink.weparty.cocosgame.manager.listener.ICocosGameListener
+import com.adealink.weparty.cocosgame.method.OnDeeplinkJsMethod
+import com.adealink.weparty.cocosgame.method.OnDismissJsMethod
+import com.adealink.weparty.cocosgame.method.OnGameEndJsMethod
+import com.adealink.weparty.cocosgame.method.OnGameOpJsMethod
+import com.adealink.weparty.cocosgame.method.OnGameRecoverFailJsMethod
+import com.adealink.weparty.cocosgame.method.OnGameStartJsMethod
+import com.adealink.weparty.cocosgame.method.OnGetAppEnvJsMethod
+import com.adealink.weparty.cocosgame.method.OnGetCurrencyJsMethod
+import com.adealink.weparty.cocosgame.method.OnGetGameViewJsMethod
+import com.adealink.weparty.cocosgame.method.OnInitNodeViewJsMethod
+import com.adealink.weparty.cocosgame.method.OnLogJsMethod
+import com.adealink.weparty.cocosgame.method.OnNetworkRequestJsMethod
+import com.adealink.weparty.cocosgame.method.OnNodeVisibleChangedJsMethod
+import com.adealink.weparty.cocosgame.method.OnStatJsMethod
+import com.adealink.weparty.cocosgame.web.CocosGameWebView
+import com.adealink.weparty.module.gamehub.carrom.method.OnCarromShowDialogJsMethod
+import com.adealink.weparty.module.gamehub.ludo.method.OnLudoShowDialogJsMethod
+import com.adealink.weparty.module.gamehub.uno.method.OnUnoShowDialogJsMethod
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import java.lang.ref.WeakReference
+
+
+/**
+ * Cocos游戏服务通知
+ * game_[游戏代码]_XXX ---> 小写的是游戏底层通知
+ *
+ * GAME_[游戏代码]_XXX ---> 大写的是游戏业务上层通知
+ */
+const val COCOS_NOTIFY_PREFIX = "game"
+
+const val COCOS_TRICKY_EMOTION_NOTIFY = "game_ludo_emotion_broadcast"
+
+class CocosWebGameManager(private val l: ICocosGameListener) :
+    ICocosWebGameManager,
+    OnNetworkListener {
+
+    private var webViewRef: WeakReference<CocosGameWebView>? = null
+
+    @Volatile
+    private var gameInit: Boolean = false
+    private var startGameData: StartGameData? = null
+    private var gameMetaDataJson: String? = null
+    private var gameId: String? = null
+    private var gamePlayerMap: MutableMap<Long, GamePlayerInfo> = hashMapOf()
+    private var playerAvatarSizePx: Int = 0
+
+    //用于区分游戏广播
+    private var gameCode: String? = null
+
+    private val notifyInterceptor = object : INotifyInterceptor {
+
+        override fun intercept(uri: String, data: String, msg: String): Boolean {
+            Log.d(TAG_COCOS_GAME_WEB_NETWORK, "notifyInterceptor, uri:$uri, data:$data, msg: $msg")
+            if (uri == COCOS_TRICKY_EMOTION_NOTIFY) {
+                //游戏内整蛊表情
+                networkNotify(msg)
+                return true
+            }
+            val currentGameCode = gameCode
+            if (currentGameCode.isNullOrEmpty()) {
+                Log.d(TAG_COCOS_GAME_WEB_NETWORK, "notifyInterceptor, uri:$uri, intercept")
+                return true
+            }
+            val uriSplits = uri.split("_")
+            if (uriSplits.size < 3) {
+                Log.d(TAG_COCOS_GAME_WEB_NETWORK, "notifyInterceptor, uri:$uri, uri is not valid")
+                return false
+            }
+
+            val handle = COCOS_NOTIFY_PREFIX == uriSplits[0] && currentGameCode == uriSplits[1]
+            if (handle && uriSplits[2] == "start") {
+                /**
+                 * game_XXX_start 交由业务上层处理, 底层不处理
+                 */
+                Log.d(TAG_COCOS_GAME_WEB_NETWORK, "notifyInterceptor, uri:$uri, game start notify")
+                return false
+            }
+
+            if (handle) {
+                networkNotify(msg)
+            }
+            Log.d(TAG_COCOS_GAME_WEB_NETWORK, "notifyInterceptor, uri:$uri, handle:$handle")
+            return handle
+        }
+
+    }
+
+    override fun setWebView(webView: CocosGameWebView) {
+        webViewRef = WeakReference(webView)
+    }
+
+    override fun initGame(data: InitGameData) {
+        Log.i(TAG_COCOS_GAME_WEB_FLOW, "initGame, data:${data}")
+        gameCode = data.game.code
+        playerAvatarSizePx = data.avatarSizePx
+        val webView = webViewRef?.get()
+        if (webView != null) {
+            //游戏生命进程回调
+            webView.addJSNativeMethod(OnGetGameViewJsMethod(l) {
+                onGameInit()
+            })
+            webView.addJSNativeMethod(OnGameStartJsMethod(l))
+            webView.addJSNativeMethod(OnInitNodeViewJsMethod(l))
+            webView.addJSNativeMethod(OnGameEndJsMethod(l))
+            webView.addJSNativeMethod(OnNetworkRequestJsMethod {
+                gameMetaDataJson
+            })
+
+            //游戏业务回调
+            webView.addJSNativeMethod(OnGameOpJsMethod(l))
+            webView.addJSNativeMethod(OnGetCurrencyJsMethod())
+            when (data.game) {
+                Game.LUDO -> {
+                    webView.addJSNativeMethod(OnLudoShowDialogJsMethod(l) {
+                        return@OnLudoShowDialogJsMethod gamePlayerMap.toMap()
+                    })
+                }
+                Game.CARROM -> {
+                    webView.addJSNativeMethod(OnCarromShowDialogJsMethod(l) {
+                        return@OnCarromShowDialogJsMethod gamePlayerMap.toMap()
+                    })
+                }
+                Game.UNO -> {
+                    webView.addJSNativeMethod(OnUnoShowDialogJsMethod(l) {
+                        return@OnUnoShowDialogJsMethod gamePlayerMap.toMap()
+                    })
+                }
+//                Game.DOMINO -> {
+//                    webView.addJSNativeMethod(OnDominoShowDialogJsMethod(l) {
+//                        return@OnDominoShowDialogJsMethod gamePlayerMap.toMap()
+//                    })
+//                }
+            }
+            webView.addJSNativeMethod(OnDismissJsMethod(l))
+            webView.addJSNativeMethod(OnDeeplinkJsMethod())
+            webView.addJSNativeMethod(OnLogJsMethod())
+            webView.addJSNativeMethod(OnStatJsMethod())
+            webView.addJSNativeMethod(OnGameRecoverFailJsMethod(l))
+            webView.addJSNativeMethod(OnGetAppEnvJsMethod())
+            webView.addJSNativeMethod(OnNodeVisibleChangedJsMethod(l))
+            App.instance.networkService.addNotifyInterceptor(notifyInterceptor)
+            registerNetworkListener(this)
+        }
+    }
+
+    override fun isGameInit(): Boolean {
+        return gameInit
+    }
+
+    fun onGameInit() {
+        Log.i(TAG_COCOS_GAME_WEB_FLOW, "onGameInit")
+        gameInit = true
+        l.onGameInit()
+        val startGameData = startGameData ?: return
+        doStartGame(startGameData)
+        this.startGameData = null
+    }
+
+    override fun startGame(data: StartGameData) {
+        CoroutineScope(Dispatcher.UI).launch {
+            Log.i(TAG_COCOS_GAME_WEB_FLOW, "startGame, data:${data}")
+            gameId = data.gameId
+            gamePlayerMap = data.gamePlayerMap?.toMutableMap() ?: mutableMapOf()
+            gameMetaDataJson = data.gameMetaData
+            Log.i(TAG_COCOS_GAME_WEB_FLOW, "enterGame, data:${data}")
+            if (gameInit) {
+                doStartGame(data)
+            } else {
+                startGameData = data
+            }
+        }
+    }
+
+    private fun doStartGame(data: StartGameData) {
+        Log.i(TAG_COCOS_GAME_WEB_FLOW, "doStartGame, data:${data}")
+        resetGame(RestGameData())
+        callGame("startGame", data)
+    }
+
+    override fun opGame(op: NativeToGameOp, data: BaseOpData, callback: ICallback<Any>?) {
+        callGame("opGame", NativeToGameOpData(op.op, data), callback)
+    }
+
+    override fun networkNotify(data: String) {
+        Log.i(TAG_COCOS_GAME_WEB_NETWORK, "notify, data:$data")
+        callGame("networkNotify", data)
+    }
+
+    override fun resetGame(data: RestGameData) {
+        Log.i(TAG_COCOS_GAME_WEB_FLOW, "resetGame, data:${data}")
+        callGame("resetGame", data)
+    }
+
+    override fun getGamePlayers(): Map<Long, GamePlayerInfo> {
+        return gamePlayerMap
+    }
+
+    @Suppress("UNCHECKED_CAST")
+    override fun getPlayerGameStatus(uid: Long, callback: ICallback<PlayerStatus>) {
+
+        val c = object : ICallback<PlayerStatusData> {
+
+            override fun onSuccess(data: PlayerStatusData) {
+                callback.onSuccess(PlayerStatus.map(data.status))
+            }
+
+            override fun onFailed(code: Int) {
+                callback.onFailed(code)
+            }
+
+        }
+        callGame("getPlayerGameStatus", PlayerData(uid), c as ICallback<Any>)
+    }
+
+    override fun getPlayerAvatarSizeInPx(): Int {
+        return playerAvatarSizePx
+    }
+
+    @Suppress("UNCHECKED_CAST")
+    override fun getPlayerAvatarView(uid: Long, callback: ICallback<CocosViewData>) {
+        val c = object : ICallback<CocosViewData> {
+
+            override fun onSuccess(data: CocosViewData) {
+                callback.onSuccess(data)
+            }
+
+            override fun onFailed(code: Int) {
+                callback.onFailed(code)
+            }
+
+        }
+        callGame("getPlayerAvatarView", PlayerData(uid), c as ICallback<Any>)
+    }
+
+    @Suppress("UNCHECKED_CAST")
+    override fun getNodeView(type: NodeViewType, callback: ICallback<CocosViewData>) {
+        val c = object : ICallback<CocosViewData> {
+
+            override fun onSuccess(data: CocosViewData) {
+                callback.onSuccess(data)
+            }
+
+            override fun onFailed(code: Int) {
+                callback.onFailed(code)
+            }
+
+        }
+        callGame("getNodeView", NodeViewData(type.type), c as ICallback<Any>)
+    }
+
+    @Suppress("UNCHECKED_CAST")
+    override fun getGameRecord(callback: ICallback<GameRecordData>) {
+        val c = object : ICallback<GameRecordData> {
+
+            override fun onSuccess(data: GameRecordData) {
+                callback.onSuccess(data)
+            }
+
+            override fun onFailed(code: Int) {
+                callback.onFailed(code)
+            }
+
+        }
+        callGame("getGameRecord", EmptyData(), c as ICallback<Any>)
+    }
+
+    override fun onNetChanged(available: Boolean) {
+        callGame("networkStatus", NetworkStatusData(available))
+    }
+
+    private fun callGame(
+        methodName: String,
+        data: Any,
+        callback: ICallback<Any>? = null
+    ) {
+        if (!gameInit) {
+            Log.e(
+                TAG_COCOS_GAME_WEB_CALL,
+                "callGame, game not init, methodName:${methodName} data:${data}"
+            )
+            return
+        }
+
+        val jsBridge = webViewRef?.get()?.getJsBridge()
+        jsBridge?.callGame(methodName, data, callback)
+    }
+
+
+    override fun destroyGame() {
+        Log.i(TAG_COCOS_GAME_WEB_FLOW, "destroyGame")
+        App.instance.networkService.removeNotifyInterceptor(notifyInterceptor)
+        unregisterNetworkListener(this)
+        gameInit = false
+        gameId = null
+        gameCode = null
+    }
+}

+ 46 - 0
app/src/main/java/com/adealink/weparty/cocosgame/manager/ICocosWebGameManager.kt

@@ -0,0 +1,46 @@
+package com.adealink.weparty.cocosgame.manager
+
+import com.adealink.frame.game.ICallback
+import com.adealink.weparty.cocosgame.data.BaseOpData
+import com.adealink.weparty.cocosgame.data.CocosViewData
+import com.adealink.weparty.cocosgame.data.GamePlayerInfo
+import com.adealink.weparty.cocosgame.data.GameRecordData
+import com.adealink.weparty.cocosgame.data.InitGameData
+import com.adealink.weparty.cocosgame.data.NativeToGameOp
+import com.adealink.weparty.cocosgame.data.NodeViewType
+import com.adealink.weparty.cocosgame.data.PlayerStatus
+import com.adealink.weparty.cocosgame.data.RestGameData
+import com.adealink.weparty.cocosgame.data.StartGameData
+import com.adealink.weparty.cocosgame.web.CocosGameWebView
+
+interface ICocosWebGameManager {
+
+    fun initGame(data: InitGameData)
+
+    fun isGameInit(): Boolean
+
+    fun startGame(data: StartGameData)
+
+    fun opGame(op: NativeToGameOp, data: BaseOpData, callback: ICallback<Any>?)
+
+    fun networkNotify(data: String)
+
+    fun resetGame(data: RestGameData)
+
+    fun getGamePlayers(): Map<Long, GamePlayerInfo>
+
+    fun getPlayerGameStatus(uid: Long, callback: ICallback<PlayerStatus>)
+
+    fun getPlayerAvatarSizeInPx(): Int
+
+    fun getPlayerAvatarView(uid: Long, callback: ICallback<CocosViewData>)
+
+    fun getNodeView(type: NodeViewType, callback: ICallback<CocosViewData>)
+
+    fun getGameRecord(callback: ICallback<GameRecordData>)
+
+    fun destroyGame()
+
+    fun setWebView(webView: CocosGameWebView)
+
+}

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor