Эх сурвалжийг харах

feat: mini_slot 小老虎机 (#14)

* feat(mini-slot): 接入小老虎机入口和资源、适配新桥街

* feat(mini-slot): 国际化文案

* feat(mini-slot): 移除Debug数据

* refactor: 修改显示动画

* feat: 游戏埋点相关,CocosWindowViewData

* feat: test支持半屏web

* feat: goWebTest, debug页面

* refactor: 更改Lv等级计算逻辑

* refactor: 更改Lv等级计算逻辑

* bugfix: KeyNum始终显示

* refactor: 请求参数变更

* feat: 增加游戏对应关系

* feat: openGreedyPro

* feat: getPlayCenterData强制不走缓存

---------

Co-authored-by: XiaodongLin <450468291@qq.com>
evanbiz08 8 сар өмнө
parent
commit
140cbc55b6
73 өөрчлөгдсөн 739 нэмэгдсэн , 83 устгасан
  1. 34 1
      app/src/main/java/com/adealink/weparty/cocosgame/data/CocosWindowViewData.kt
  2. 1 1
      app/src/main/java/com/adealink/weparty/cocosgame/manager/CocosWebGameManager.kt
  3. 12 2
      app/src/main/java/com/adealink/weparty/cocosgame/method/OnDeeplink.kt
  4. 2 1
      app/src/main/java/com/adealink/weparty/config/Data.kt
  5. 2 3
      app/src/main/java/com/adealink/weparty/debug/DebugActivity.kt
  6. 11 0
      app/src/main/java/com/adealink/weparty/module/game/GameModule.kt
  7. 4 0
      app/src/main/java/com/adealink/weparty/module/game/IGameService.kt
  8. 30 1
      app/src/main/java/com/adealink/weparty/module/game/data/GameEntranceData.kt
  9. 37 0
      app/src/main/java/com/adealink/weparty/module/game/data/MiniSlotData.kt
  10. 1 2
      app/src/main/java/com/adealink/weparty/module/profile/decorate/data/UserDecorateData.kt
  11. 57 30
      app/src/main/java/com/adealink/weparty/util/IntentUtil.kt
  12. 1 0
      app/src/main/res/values/colors.xml
  13. 1 0
      app/src/main/res/values/strings.xml
  14. BIN
      audio/minislot_bgm.mp3
  15. BIN
      audio/minislot_click_auto.mp3
  16. BIN
      audio/minislot_click_spin.mp3
  17. BIN
      audio/minislot_coin_add.mp3
  18. BIN
      audio/minislot_coin_fly.mp3
  19. BIN
      audio/minislot_key_fly.mp3
  20. BIN
      audio/minislot_key_get.mp3
  21. BIN
      audio/minislot_pregress_add.mp3
  22. BIN
      audio/minislot_scrolling.mp3
  23. BIN
      audio/minislot_stop_1.mp3
  24. BIN
      audio/minislot_stop_2.mp3
  25. BIN
      audio/minislot_stop_3.mp3
  26. BIN
      audio/minislot_win_big.mp3
  27. BIN
      audio/minislot_win_small.mp3
  28. BIN
      audio/minislot_win_zero.mp3
  29. 7 0
      module/game/src/main/java/com/adealink/weparty/game/GameServiceImpl.kt
  30. 7 0
      module/game/src/main/java/com/adealink/weparty/game/datasource/remote/GameHttpService.kt
  31. 1 0
      module/game/src/main/java/com/adealink/weparty/game/manager/GameEntranceManager.kt
  32. 8 0
      module/room/src/main/java/com/adealink/weparty/room/game/RoomGameCenterPanelFragment.kt
  33. 9 14
      module/room/src/main/java/com/adealink/weparty/room/playcenter/RoomPlayCenterPanelFragment.kt
  34. 136 0
      module/room/src/main/java/com/adealink/weparty/room/playcenter/adapter/RoomPlayCenterMiniSlotViewBinder.kt
  35. 11 1
      module/room/src/main/java/com/adealink/weparty/room/playcenter/data/RoomPlayCenterData.kt
  36. 81 21
      module/room/src/main/java/com/adealink/weparty/room/playcenter/manager/PlayCenterManager.kt
  37. 2 0
      module/room/src/main/java/com/adealink/weparty/room/playcenter/util/PlayCenterUtil.kt
  38. 1 1
      module/room/src/main/java/com/adealink/weparty/room/playcenter/viewmodel/RoomPlayCenterViewModel.kt
  39. BIN
      module/room/src/main/res/drawable-xhdpi/room_game_mini_slot_bg.png
  40. BIN
      module/room/src/main/res/drawable-xhdpi/room_game_mini_slot_ic.png
  41. BIN
      module/room/src/main/res/drawable-xhdpi/room_game_mini_slot_task_ic.png
  42. 6 0
      module/room/src/main/res/drawable/mini_slot_icon_bg.xml
  43. 23 0
      module/room/src/main/res/drawable/room_mini_slot_task_progress_bar.xml
  44. 147 0
      module/room/src/main/res/layout/layout_room_play_center_top_game.xml
  45. 1 0
      module/room/src/main/res/values-ar/strings.xml
  46. 1 0
      module/room/src/main/res/values-zh/strings.xml
  47. 1 0
      module/room/src/main/res/values/strings.xml
  48. BIN
      module/webview/src/main/assets/loading_mini_slot.svga
  49. 1 1
      module/webview/src/main/java/com/adealink/weparty/webview/WeNextWebView.kt
  50. 3 2
      module/webview/src/main/java/com/adealink/weparty/webview/WebViewDialogFragment.kt
  51. 34 2
      module/webview/src/main/java/com/adealink/weparty/webview/cocosgame/data/CocosWindowViewData.kt
  52. 42 0
      module/webview/src/main/java/com/adealink/weparty/webview/component/GameLoadingComp.kt
  53. BIN
      module/webview/src/main/res/drawable-ar-xhdpi/game_mini_slot_loading_title_text.webp
  54. BIN
      module/webview/src/main/res/drawable-bn-xhdpi/game_mini_slot_loading_title_text.webp
  55. BIN
      module/webview/src/main/res/drawable-es-xhdpi/game_mini_slot_loading_title_text.webp
  56. BIN
      module/webview/src/main/res/drawable-hi-xhdpi/game_mini_slot_loading_title_text.webp
  57. BIN
      module/webview/src/main/res/drawable-in-xhdpi/game_mini_slot_loading_title_text.webp
  58. BIN
      module/webview/src/main/res/drawable-pa-xhdpi/game_mini_slot_loading_title_text.webp
  59. BIN
      module/webview/src/main/res/drawable-pt-xhdpi/game_mini_slot_loading_title_text.webp
  60. BIN
      module/webview/src/main/res/drawable-ru-xhdpi/game_mini_slot_loading_title_text.webp
  61. BIN
      module/webview/src/main/res/drawable-ta-xhdpi/game_mini_slot_loading_title_text.webp
  62. BIN
      module/webview/src/main/res/drawable-te-xhdpi/game_mini_slot_loading_title_text.webp
  63. BIN
      module/webview/src/main/res/drawable-th-xhdpi/game_mini_slot_loading_title_text.webp
  64. BIN
      module/webview/src/main/res/drawable-tl-xhdpi/game_mini_slot_loading_title_text.webp
  65. BIN
      module/webview/src/main/res/drawable-tr-xhdpi/game_mini_slot_loading_title_text.webp
  66. BIN
      module/webview/src/main/res/drawable-ur-xhdpi/game_mini_slot_loading_title_text.webp
  67. BIN
      module/webview/src/main/res/drawable-vi-xhdpi/game_mini_slot_loading_title_text.webp
  68. BIN
      module/webview/src/main/res/drawable-xhdpi/game_mini_slot_loading_title_text.webp
  69. BIN
      module/webview/src/main/res/drawable-xhdpi/web_loading_game_bg_mini_slot.webp
  70. BIN
      module/webview/src/main/res/drawable-xhdpi/web_loading_game_img_mini_slot.webp
  71. BIN
      module/webview/src/main/res/drawable-zh-rTW-xhdpi/game_mini_slot_loading_title_text.webp
  72. 16 0
      module/webview/src/main/res/drawable/web_loading_game_mini_slot_inner_progress_bg.xml
  73. 8 0
      module/webview/src/main/res/drawable/web_loading_game_mini_slot_progress_bg.xml

+ 34 - 1
app/src/main/java/com/adealink/weparty/cocosgame/data/CocosWindowViewData.kt

@@ -5,7 +5,20 @@ 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.locale.country.getNewCountryCode
 import com.adealink.frame.router.Router
+import com.adealink.frame.util.PackageUtil
+import com.adealink.frame.util.getApiLevel
+import com.adealink.frame.util.getDeviceName
+import com.adealink.frame.util.getNetworkOperator
+import com.adealink.frame.util.getNetworkType
+import com.adealink.frame.util.getOsVersion
+import com.adealink.frame.util.isNetworkAvailable
+import com.adealink.weparty.App
+import com.adealink.weparty.channel.getChannel
+import com.adealink.weparty.module.attribution.AttributionModule
+import com.adealink.weparty.module.profile.ProfileModule
+import com.adealink.weparty.stat.StatConfig
 import com.google.gson.JsonDeserializationContext
 import com.google.gson.JsonDeserializer
 import com.google.gson.JsonElement
@@ -170,7 +183,27 @@ data class CocosWindowViewData(
     @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
+    @SerializedName("app_name") val appName: String = AppBaseInfo.appName,
+    @SerializedName("package_name") val packageName: String = PackageUtil.getPackageName(),
+    //埋点数据相关,跟StatConfig保持一致
+    @SerializedName("report_url") val reportUrl: String = "https://api-log-upload.wenext.technology/api/log",
+    @SerializedName("app") val app: String = "0",
+    @SerializedName("app_channel") val appChannel: String = getChannel(),
+    @SerializedName("background") val background: Int = 1,
+    @SerializedName("country_code") val countryCode: String = ProfileModule.getMyUserInfo()?.country ?: getNewCountryCode(),
+    @SerializedName("device_id") val deviceId: String = App.instance.deviceIdService.getLocalDeviceId(),
+    @SerializedName("device_name") val deviceName: String = getDeviceName(),
+    @SerializedName("network_available") val networkAvailable: Boolean = isNetworkAvailable(),
+    @SerializedName("network_operator") val networkOperator: String = getNetworkOperator(),
+    @SerializedName("network_type") val networkType: String = getNetworkType(),
+    @SerializedName("organic_install") val organicInstall: Boolean = !AttributionModule.isInorganicInstall(),
+    @SerializedName("os_version") val osVersion: String = getOsVersion(),
+    @SerializedName("api_level") val apiLevel: Int = getApiLevel(),
+    @SerializedName("platform") val platform: String = AppBaseInfo.platform,
+    @SerializedName("region") val region: String? = ProfileModule.getMyUserInfo()?.region,
+    @SerializedName("uid") val uid: Long = ProfileModule.getMyUid(),
+    @SerializedName("version_code") val versionCode: Int = PackageUtil.getVersionCode(),
+    @SerializedName("version_name") val versionName: String = PackageUtil.getVersionName()
 )
 
 data class CocosWindowSafeArea(

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

@@ -166,7 +166,7 @@ class CocosWebGameManager(private val l: ICocosGameListener) :
 //                }
             }
             webView.addJSNativeMethod(OnDismissJsMethod(l))
-            webView.addJSNativeMethod(OnDeeplinkJsMethod())
+            webView.addJSNativeMethod(OnDeeplinkJsMethod(webView))
             webView.addJSNativeMethod(OnLogJsMethod())
             webView.addJSNativeMethod(OnStatJsMethod())
             webView.addJSNativeMethod(OnGameRecoverFailJsMethod(l))

+ 12 - 2
app/src/main/java/com/adealink/weparty/cocosgame/method/OnDeeplink.kt

@@ -1,9 +1,12 @@
 package com.adealink.weparty.cocosgame.method
 
+import androidx.core.net.toUri
 import com.adealink.frame.log.Log
 import com.adealink.frame.util.AppUtil
+import com.adealink.frame.util.getParamsFromUri
 import com.adealink.weparty.cocosgame.data.TAG_COCOS_GAME_WEB
 import com.adealink.weparty.util.goLocalLinkPage
+import com.adealink.weparty.webview.IWebView
 import com.adealink.weparty.webview.jsbridge.callback.JSBridgeCallback
 import com.adealink.weparty.webview.jsbridge.method.JSNativeMethod
 import com.google.gson.annotations.SerializedName
@@ -12,14 +15,21 @@ private const val METHOD_NAME = "onDeeplink"
 
 data class DeeplinkData(@SerializedName("deeplink") val deeplink: String)
 
-class OnDeeplinkJsMethod : JSNativeMethod<DeeplinkData, Any> {
+class OnDeeplinkJsMethod(private val webView: IWebView) : JSNativeMethod<DeeplinkData, Any> {
 
     override val methodName: String = METHOD_NAME
 
     override fun handleMethodCall(data: DeeplinkData, callback: JSBridgeCallback<Any>?) {
         Log.d(TAG_COCOS_GAME_WEB, "onDeeplink, data:${data}")
+        val deeplink = data.deeplink
+        // 是否关闭当前Web窗口(DF:true)
+        if (deeplink.isNotEmpty() && deeplink.startsWith("openGame")) {
+            val params = getParamsFromUri(deeplink.toUri())
+            val closePrev = params["closePrev"]?.toBooleanStrictOrNull() ?: true
+            if (closePrev) webView.webViewCallback?.closeWebView()
+        }
         AppUtil.currentActivity?.let {
-            goLocalLinkPage(it, data.deeplink)
+            goLocalLinkPage(it, deeplink)
         }
     }
 

+ 2 - 1
app/src/main/java/com/adealink/weparty/config/Data.kt

@@ -85,7 +85,8 @@ enum class GlobalConfigType(val value: Int) {
     GLOBAL_CERTIFICATION_CONFIG(102),//真人认证配置
     GLOBAL_VOICE_CALL_REFUSE_TIMES_THRESHOLD(103),//拒接实时通话次数触发弹窗提示
 
-    GLOBAL_FEMALE_DAILY_REPORT(104) // 女性每日上报配置
+    GLOBAL_FEMALE_DAILY_REPORT(104), // 女性每日上报配置
+    GLOBAL_MINI_SLOT(107) // 小老虎机配置
     ;
 
     companion object {

+ 2 - 3
app/src/main/java/com/adealink/weparty/debug/DebugActivity.kt

@@ -31,6 +31,7 @@ import com.adealink.weparty.module.call.CallModule
 import com.adealink.weparty.module.setting.Setting
 import com.adealink.weparty.module.webview.Web
 import com.adealink.weparty.storage.AppPref
+import com.adealink.weparty.util.goLocalLinkPage
 import com.google.gson.annotations.SerializedName
 import com.qmuiteam.qmui.widget.util.QMUIStatusBarHelper
 import kotlinx.coroutines.launch
@@ -245,9 +246,7 @@ class DebugActivity : BaseActivity(), OnReturnValue {
             return
         }
 
-        Router.build(this, Web.FullScreen.PATH)
-            .putExtra(Web.Common.EXTRA_URL, webTestUrl)
-            .start()
+        goLocalLinkPage(context = this,webTestUrl)
     }
 
     private fun addWhiteHost() {

+ 11 - 0
app/src/main/java/com/adealink/weparty/module/game/GameModule.kt

@@ -2,7 +2,9 @@ package com.adealink.weparty.module.game
 
 import androidx.lifecycle.ViewModelStoreOwner
 import com.adealink.frame.aab.BaseDynamicModule
+import com.adealink.frame.base.IError
 import com.adealink.frame.base.Rlt
+import com.adealink.frame.network.data.Res
 import com.adealink.weparty.R
 import com.adealink.weparty.cocosgame.data.Game
 import com.adealink.weparty.commonui.widget.floatview.data.IFloatData
@@ -11,6 +13,8 @@ import com.adealink.weparty.module.game.data.CommonActivityRewardInfoReq
 import com.adealink.weparty.module.game.data.GameEntranceType
 import com.adealink.weparty.module.game.data.GameShowConfig
 import com.adealink.weparty.module.game.data.GameType
+import com.adealink.weparty.module.game.data.MiniSlotConfigInfoResponse
+import com.adealink.weparty.module.game.data.MiniSlotNeedData
 import com.adealink.weparty.module.game.data.ReceiveRewardReq
 import com.adealink.weparty.module.game.data.UserGameLevelInfoResult
 import com.adealink.weparty.module.game.rocket.viewmodel.IRocketViewModel
@@ -115,6 +119,10 @@ object GameModule : BaseDynamicModule<IGameService>(IGameService::class), IGameS
         return getService().getRocketHeadlineFloatView(data)
     }
 
+    override suspend fun getMiniSlotConfigInfo(needType: MiniSlotNeedData): Rlt<Res<MiniSlotConfigInfoResponse>>  {
+        return getService().getMiniSlotConfigInfo(needType)
+    }
+
     override fun emptyService(): IGameService {
         return object : IGameService {
 
@@ -203,6 +211,9 @@ object GameModule : BaseDynamicModule<IGameService>(IGameService::class), IGameS
                 return null
             }
 
+            override suspend fun getMiniSlotConfigInfo(needType: MiniSlotNeedData): Rlt<Res<MiniSlotConfigInfoResponse>>  {
+                return Rlt.Failed(IError())
+            }
         }
 
     }

+ 4 - 0
app/src/main/java/com/adealink/weparty/module/game/IGameService.kt

@@ -3,6 +3,7 @@ package com.adealink.weparty.module.game
 import androidx.lifecycle.ViewModelStoreOwner
 import com.adealink.frame.aab.IService
 import com.adealink.frame.base.Rlt
+import com.adealink.frame.network.data.Res
 import com.adealink.weparty.cocosgame.data.Game
 import com.adealink.weparty.commonui.widget.floatview.data.IFloatData
 import com.adealink.weparty.commonui.widget.floatview.view.BaseFloatView
@@ -11,6 +12,8 @@ import com.adealink.weparty.module.game.data.GameActivityRewardInfo
 import com.adealink.weparty.module.game.data.GameEntranceType
 import com.adealink.weparty.module.game.data.GameShowConfig
 import com.adealink.weparty.module.game.data.GameType
+import com.adealink.weparty.module.game.data.MiniSlotConfigInfoResponse
+import com.adealink.weparty.module.game.data.MiniSlotNeedData
 import com.adealink.weparty.module.game.data.ReceiveRewardReq
 import com.adealink.weparty.module.game.data.UserGameLevelInfoResult
 import com.adealink.weparty.module.game.rocket.viewmodel.IRocketViewModel
@@ -53,4 +56,5 @@ interface IGameService : IService<IGameService> {
     suspend fun isGameShow(configTypes: List<DecorType>): Map<DecorType, GameShowConfig>
     suspend fun getAllGameShowConfigs(): Map<DecorType, GameShowConfig>
     fun navigateToGame(gameType: GameEntranceType, canceledOnTouchOutside: Boolean = true)
+    suspend fun getMiniSlotConfigInfo(needType: MiniSlotNeedData): Rlt<Res<MiniSlotConfigInfoResponse>>
 }

+ 30 - 1
app/src/main/java/com/adealink/weparty/module/game/data/GameEntranceData.kt

@@ -32,7 +32,36 @@ enum class GameEntranceType(val type: Int) {
     TEXAS_COWBOY(12),
     DRAGON_TIGER_FIGHT(13),
     ROCKET(14),
-    GREEDY_PERSONAL(15)
+    GREEDY_PERSONAL(15),
+    MINI_SLOT(16) // 小老虎机
+    ;
+
+    companion object{
+        /**
+         * Web内可打开的游戏类型映射
+         * 如需支持新游戏类型,需要在此处添加映射关系
+         */
+        fun getGameEntranceFromString(game: String): GameEntranceType? {
+            return when (game.uppercase()) {
+                "LUCKY_FRUIT" -> LUCKY_FRUIT
+                "RECHARGE_PACKAGE" -> RECHARGE_PACKAGE
+                "JACKPOT" -> JACKPOT
+                "DAILY_RECHARGE" -> DAILY_RECHARGE
+                "GREEDY_PRO" -> GREEDY_PRO
+                "LUCKY_PRO" -> LUCKY_PRO
+                "GREEDY_BOX" -> GREEDY_BOX
+                "JACKPOT_SLOT" -> JACKPOT_SLOT
+                "TEEN_PATTI" -> TEEN_PATTI
+                "RUSSIAN_ROULETTE" -> RUSSIAN_ROULETTE
+                "TEXAS_COWBOY" -> TEXAS_COWBOY
+                "DRAGON_TIGER_FIGHT" -> DRAGON_TIGER_FIGHT
+                "ROCKET" -> ROCKET
+                "GREEDY_PERSONAL" -> GREEDY_PERSONAL
+                "MINI_SLOT" -> MINI_SLOT
+                else -> null
+            }
+        }
+    }
 }
 
 sealed class GameEntrance(val icon: String, val type: GameEntranceType, val onClick: (() -> Unit) = { })

+ 37 - 0
app/src/main/java/com/adealink/weparty/module/game/data/MiniSlotData.kt

@@ -0,0 +1,37 @@
+package com.adealink.weparty.module.game.data
+
+import android.os.Parcelable
+import com.google.gson.annotations.SerializedName
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class MiniSlotConfigInfoResponse(
+    @SerializedName("balance") val balance: Long, // 用户当前金币余额
+    @SerializedName("countDown") val countDown: Long, // 今日倒计时(毫秒)
+    @SerializedName("keyConfig") val keyConfig: List<MiniSlotKeyConfigEntity>, // 获得钥匙的阶梯配置
+    @SerializedName("keyNum") val keyNum: Long = 0, // 当前钥匙数量
+    @SerializedName("lastBetCoinNum") val lastBetCoinNum: Long, // 上次进页面时看到的下注金币数量
+    @SerializedName("lastGotKeyNum") val lastGotKeyNum: Long, // 上次进页面时看到的钥匙数量
+    @SerializedName("optionList") val optionList: List<Int>, // 下注钥匙选项(如:[10, 20, 30])
+    @SerializedName("show") val show: Boolean, // 是否开启(是否展示老虎机)
+    @SerializedName("todayBetCoin") val todayBetCoin: Long = 0, // 今日下注金币总额
+    @SerializedName("todayBetCoinLimit") val todayBetCoinLimit: Long = 100, // 今日下注金币总额Limit
+    @SerializedName("todayGotKeyNum") val todayGotKeyNum: Long, // 今日获得钥匙数量
+    @SerializedName("todayWin") val todayWin: Long // 今日赢得金币总额
+) : Parcelable
+
+@Parcelize
+data class MiniSlotKeyConfigEntity(
+    @SerializedName("id") val id: Int, // 配置 ID
+    @SerializedName("region") val region: String, // 区域(如某种分区逻辑或平台)
+    @SerializedName("betCoin") val betCoin: Long, // 下注金币数
+    @SerializedName("keyNum") val keyNum: Long, // 对应获得钥匙数量
+    @SerializedName("level") val level: Int // 等级
+) : Parcelable
+
+/**
+ * 小老虎机请求获取的数据类型
+ */
+enum class MiniSlotNeedData(val type: Int) {
+    ALL_DATA(1), MIN_DATA(0)
+}

+ 1 - 2
app/src/main/java/com/adealink/weparty/module/profile/decorate/data/UserDecorateData.kt

@@ -53,6 +53,7 @@ enum class DecorType(val value: Int) {
     DRAGON_TIGER_FIGHT_GAME(86), //龙虎斗游戏
     RUSSIAN_TURNTABLE_GAME(87), //Russian Turntable游戏
     TEXAS_GAME(88), //texas cowboy游戏
+    SHOW_MINI_SLOT(89), // 小老虎机
     LUCKY_PRO_GAME(104), //旧水果机
     GREEDY_PRO_GAME(108), //新水果机
     GREEDY_PERSONAL_GAME(174),//个人水果机
@@ -84,8 +85,6 @@ enum class DecorType(val value: Int) {
         fun map(value: Int): DecorType? {
             return entries.find { it.value == value }
         }
-
-
     }
 }
 

+ 57 - 30
app/src/main/java/com/adealink/weparty/util/IntentUtil.kt

@@ -2,15 +2,19 @@ package com.adealink.weparty.util
 
 import android.content.Context
 import android.content.Intent
-import android.net.Uri
+import androidx.core.net.toUri
 import androidx.fragment.app.FragmentActivity
+import com.adealink.frame.aab.util.getCompatString
 import com.adealink.frame.base.AppBaseInfo
 import com.adealink.frame.router.Router
 import com.adealink.frame.util.DisplayUtil
 import com.adealink.frame.util.PackageUtil
 import com.adealink.frame.util.getParamsFromUri
 import com.adealink.weparty.R
+import com.adealink.weparty.commonui.BaseActivity
 import com.adealink.weparty.commonui.toast.util.showToast
+import com.adealink.weparty.module.game.GameModule
+import com.adealink.weparty.module.game.data.GameEntranceType
 import com.adealink.weparty.module.webview.Web
 import com.adealink.weparty.module.webview.WebViewDialogFragmentBuilder
 
@@ -20,10 +24,9 @@ fun goLocalLinkPage(context: Context?, link: String?) {
     }
 
     try {
-//        "partyki://yoki/main?min_android_version=XXX&min_ios_version=XXX"
-//        判断版本号信息
+        // 判断版本号信息("partyoki://yoki/main?min_android_version=XXX&min_ios_version=XXX")
         val minAndroidVersion =
-            getParamsFromUri(Uri.parse(link))["min_android_version"]?.toIntOrNull()
+            getParamsFromUri(link.toUri())["min_android_version"]?.toIntOrNull()
         if (minAndroidVersion != null) {
             val currentVersion = PackageUtil.getVersionCode()
             if (currentVersion < minAndroidVersion.toInt()) {
@@ -33,35 +36,59 @@ fun goLocalLinkPage(context: Context?, link: String?) {
             }
         }
 
-        if (link.startsWith("http") || link.startsWith("https")) {
-            val params = getParamsFromUri(Uri.parse(link))
-            val aspectRatio = params["aspect_ratio"]?.toFloatOrNull() //高度/宽度
-            val activity = context as? FragmentActivity
-            if (activity != null && aspectRatio != null && aspectRatio > 0) {
-                //有比例半屏加载
-                val height = (aspectRatio * DisplayUtil.getScreenWidth()).toInt()
-                WebViewDialogFragmentBuilder()
-                    .height(height)
-                    .build()
-                    ?.showUrl(activity.supportFragmentManager, link)
-            } else {
-                //加载一个 web 页
-                Router.build(context, Web.FullScreen.PATH)
-                    .putExtra(Web.Common.EXTRA_URL, link)
-                    .start()
+        when {
+            // 1.网页链接
+            link.startsWith("http") || link.startsWith("https") -> {
+                val params = getParamsFromUri(link.toUri())
+                val aspectRatio = params["aspect_ratio"]?.toFloatOrNull() //高度/宽度
+                val activity = context as? FragmentActivity
+                if (activity != null && aspectRatio != null && aspectRatio > 0) {
+                    //有比例半屏加载
+                    val height = (aspectRatio * DisplayUtil.getScreenWidth()).toInt()
+                    WebViewDialogFragmentBuilder()
+                        .height(height)
+                        .build()
+                        ?.showUrl(activity.supportFragmentManager, link)
+                } else {
+                    //加载一个 web 页
+                    Router.build(context, Web.FullScreen.PATH)
+                        .putExtra(Web.Common.EXTRA_URL, link)
+                        .start()
+                }
             }
-        } else if (link.startsWith(AppBaseInfo.deeplinkScheme)) {
-            // 通过DeepLink 跳转的指定页面
-            if (DeepLinkPreProcessor.processDeepLink(link)) {
-                return
+
+            // 2.Deeplink连接
+            link.startsWith(AppBaseInfo.deeplinkScheme) -> {
+                // 通过DeepLink 跳转的指定页面
+                if (DeepLinkPreProcessor.processDeepLink(link)) {
+                    return
+                }
+                val intent = Intent(Intent.ACTION_VIEW, link.toUri())
+                intent.setPackage(context.packageName)
+                context.startActivity(intent)
+            }
+
+            // 3.游戏连接
+            link.startsWith("openGame") -> {
+                val params = getParamsFromUri(link.toUri())
+                val game = params["game"]
+                if (!game.isNullOrEmpty()) {
+                    val gameType = GameEntranceType.getGameEntranceFromString(game)
+                    if (gameType != null) {
+                        val activity = context as? BaseActivity
+                        if (activity != null) {
+                            GameModule.navigateToGame(gameType)
+                            return
+                        }
+                    }
+                    showToast(getCompatString(R.string.cocosgame_un_support_tip))
+                }
+            }
+            else -> {
+                //ntd.
             }
-            val intent = Intent(Intent.ACTION_VIEW, Uri.parse(link))
-            intent.setPackage(context.packageName)
-            context.startActivity(intent)
-        } else {
-            //ntd.
         }
     } catch (e: Exception) {
-
+        //ntd.
     }
 }

+ 1 - 0
app/src/main/res/values/colors.xml

@@ -1148,6 +1148,7 @@
     <color name="color_FF04133A">#FF04133A</color>
     <color name="color_FFA8365D">#FFA8365D</color>
     <color name="color_FF022039">#FF022039</color>
+    <color name="color_FFF7D3">#FFF7D3</color>
     <color name="color_FFEAEFEF">#FFEAEFEF</color>
     <color name="color_FFB8CACB">#FFB8CACB</color>
     <color name="color_FFF9E88D">#FFF9E88D</color>

+ 1 - 0
app/src/main/res/values/strings.xml

@@ -807,4 +807,5 @@
    <string name="common_no_records">No records</string>
    <string name="commonui_mic_anim">Mic Animation</string>
    <string name="set_gif_avatar_tips">Set a GIF as your avatar.</string>
+   <string name="common_mini_slot">Mini Slot</string>
 </resources>

BIN
audio/minislot_bgm.mp3


BIN
audio/minislot_click_auto.mp3


BIN
audio/minislot_click_spin.mp3


BIN
audio/minislot_coin_add.mp3


BIN
audio/minislot_coin_fly.mp3


BIN
audio/minislot_key_fly.mp3


BIN
audio/minislot_key_get.mp3


BIN
audio/minislot_pregress_add.mp3


BIN
audio/minislot_scrolling.mp3


BIN
audio/minislot_stop_1.mp3


BIN
audio/minislot_stop_2.mp3


BIN
audio/minislot_stop_3.mp3


BIN
audio/minislot_win_big.mp3


BIN
audio/minislot_win_small.mp3


BIN
audio/minislot_win_zero.mp3


+ 7 - 0
module/game/src/main/java/com/adealink/weparty/game/GameServiceImpl.kt

@@ -3,6 +3,7 @@ package com.adealink.weparty.game
 import androidx.lifecycle.ViewModelProvider
 import androidx.lifecycle.ViewModelStoreOwner
 import com.adealink.frame.base.Rlt
+import com.adealink.frame.network.data.Res
 import com.adealink.frame.spi.RegisterService
 import com.adealink.weparty.App
 import com.adealink.weparty.cocosgame.data.Game
@@ -30,6 +31,8 @@ import com.adealink.weparty.module.game.data.GameEntranceType
 import com.adealink.weparty.module.game.data.GameShowConfig
 import com.adealink.weparty.module.game.data.GameType
 import com.adealink.weparty.module.game.data.GetUserLevelInfoReq
+import com.adealink.weparty.module.game.data.MiniSlotConfigInfoResponse
+import com.adealink.weparty.module.game.data.MiniSlotNeedData
 import com.adealink.weparty.module.game.data.ReceiveRewardReq
 import com.adealink.weparty.module.game.data.UserGameLevelInfoResult
 import com.adealink.weparty.module.game.rocket.viewmodel.IRocketViewModel
@@ -169,6 +172,10 @@ class GameServiceImpl : IGameService {
         rocketManager.init()
     }
 
+    override suspend fun getMiniSlotConfigInfo(needType: MiniSlotNeedData): Rlt<Res<MiniSlotConfigInfoResponse>> {
+        return gameHttpService.getMiniSlotConfigInfo(needType.type)
+    }
+
     override fun getRocketHeadlineFloatView(data: IFloatData): BaseFloatView<out IFloatData> {
         return RocketHeadlineFloatView(data as RocketHeadlineFloatData)
     }

+ 7 - 0
module/game/src/main/java/com/adealink/weparty/game/datasource/remote/GameHttpService.kt

@@ -9,6 +9,7 @@ import com.adealink.weparty.module.game.data.GameShowConfig
 import com.adealink.weparty.module.game.data.GameShowConfigReq
 import com.adealink.weparty.module.game.data.GetUserLevelInfoReq
 import com.adealink.weparty.module.game.data.LuckyFruitShowRes
+import com.adealink.weparty.module.game.data.MiniSlotConfigInfoResponse
 import com.adealink.weparty.module.game.data.ReceiveRewardReq
 import com.adealink.weparty.module.game.data.SlotConfigInfoRes
 import com.adealink.weparty.module.game.data.UserGameLevelInfoResult
@@ -47,4 +48,10 @@ interface GameHttpService {
 
     @POST("activity/receiveReward")
     suspend fun toReceiveReward(@Body req: ReceiveRewardReq): Rlt<Res<Any>>
+
+    /**
+     * 获取小老虎机游戏数据
+     */
+    @GET("mini_slot/getSlotConfigInfo")
+    suspend fun getMiniSlotConfigInfo(@Query("needAllData") needAllData: Int): Rlt<Res<MiniSlotConfigInfoResponse>>
 }

+ 1 - 0
module/game/src/main/java/com/adealink/weparty/game/manager/GameEntranceManager.kt

@@ -141,6 +141,7 @@ class GameEntranceManager : BaseFrame<IListener>(), IGameEntranceManager {
             GameEntranceType.RUSSIAN_ROULETTE -> openOfflineGame(GlobalConfigType.GLOBAL_RUSSIAN_TURNTABLE_VERSION_INFO, DecorType.RUSSIAN_TURNTABLE_GAME, 1123)
             GameEntranceType.TEXAS_COWBOY -> openOfflineGame(GlobalConfigType.GLOBAL_TEXAS_COWBOY_VERSION_INFO, DecorType.TEXAS_GAME, 1230)
             GameEntranceType.DRAGON_TIGER_FIGHT -> openOfflineGame(GlobalConfigType.GLOBAL_DRAGON_TIGER_FIGHT_VERSION_INFO, DecorType.DRAGON_TIGER_FIGHT_GAME, 1070)
+            GameEntranceType.MINI_SLOT -> openOfflineGame(GlobalConfigType.GLOBAL_MINI_SLOT, DecorType.SHOW_MINI_SLOT, 1286)
 
             else -> {
 

+ 8 - 0
module/room/src/main/java/com/adealink/weparty/room/game/RoomGameCenterPanelFragment.kt

@@ -30,8 +30,10 @@ import com.adealink.weparty.commonui.recycleview.diffutil.BaseListDiffUtil
 import com.adealink.weparty.commonui.toast.util.showFailedToast
 import com.adealink.weparty.commonui.toast.util.showToast
 import com.adealink.weparty.commonui.widget.BottomDialogFragment
+import com.adealink.weparty.module.game.GameModule
 import com.adealink.weparty.module.game.data.GameActivityRewardInfo.Companion.STATUS_RECEIVED
 import com.adealink.weparty.module.game.data.GameAppVersionOldCanPlayGameError
+import com.adealink.weparty.module.game.data.GameEntranceType
 import com.adealink.weparty.module.game.gamehub.GameHubViewModel
 import com.adealink.weparty.module.gamehub.carrom.Carrom
 import com.adealink.weparty.module.gamehub.carrom.data.CarromGameType
@@ -62,6 +64,7 @@ import com.adealink.weparty.room.playcenter.adapter.IGameClickListener
 import com.adealink.weparty.room.playcenter.adapter.IOperatorClickListener
 import com.adealink.weparty.room.playcenter.adapter.RoomGameCenterEntranceViewBinder
 import com.adealink.weparty.room.playcenter.adapter.RoomPlayCenterEntranceViewBinder
+import com.adealink.weparty.room.playcenter.adapter.RoomPlayCenterMiniSlotViewBinder
 import com.adealink.weparty.room.playcenter.adapter.RoomPlayCenterTitleViewBinder
 import com.adealink.weparty.room.playcenter.data.FISHING_ID
 import com.adealink.weparty.room.playcenter.data.PYRAMID_SLOTS_ID
@@ -90,6 +93,7 @@ class RoomGameCenterPanelFragment : BottomDialogFragment(R.layout.fragment_room_
 
     override fun initViews() {
         super.initViews()
+        listAdapter.register(RoomPlayCenterMiniSlotViewBinder(this))
         listAdapter.register(RoomPlayCenterTitleViewBinder(this))
         listAdapter.register(RoomPlayCenterEntranceViewBinder(this))
         listAdapter.register(RoomGameCenterEntranceViewBinder(this, gameHubViewModel.gameHubInfoMapLD, viewLifecycleOwner))
@@ -258,6 +262,10 @@ class RoomGameCenterPanelFragment : BottomDialogFragment(R.layout.fragment_room_
             RoomPlayCenterType.GREEDY_PERSONAL -> {
                 goGreedyPersonal()
             }
+
+            RoomPlayCenterType.MINI_SLOT -> {
+                GameModule.navigateToGame(GameEntranceType.MINI_SLOT)
+            }
             else -> {}
         }
     }

+ 9 - 14
module/room/src/main/java/com/adealink/weparty/room/playcenter/RoomPlayCenterPanelFragment.kt

@@ -5,7 +5,6 @@ import android.view.LayoutInflater
 import android.widget.LinearLayout
 import androidx.fragment.app.activityViewModels
 import androidx.fragment.app.viewModels
-import androidx.lifecycle.lifecycleScope
 import androidx.recyclerview.widget.GridLayoutManager
 import androidx.recyclerview.widget.RecyclerView
 import com.adealink.frame.aab.util.getCompatDimension
@@ -13,9 +12,6 @@ import com.adealink.frame.aab.util.getCompatString
 import com.adealink.frame.base.CommonSwitchStateSameError
 import com.adealink.frame.base.Rlt
 import com.adealink.frame.base.fastLazy
-import com.adealink.frame.data.json.froJsonErrorNull
-import com.adealink.frame.game.TAG_GAME
-import com.adealink.frame.log.Log
 import com.adealink.frame.mvvm.view.viewBinding
 import com.adealink.frame.room.data.RoomMicSeatMuteError
 import com.adealink.frame.router.Router
@@ -27,14 +23,10 @@ import com.adealink.weparty.commonui.recycleview.adapter.MultiTypeListAdapter
 import com.adealink.weparty.commonui.recycleview.diffutil.BaseListDiffUtil
 import com.adealink.weparty.commonui.toast.util.showToast
 import com.adealink.weparty.commonui.widget.BottomDialogFragment
-import com.adealink.weparty.config.GameVersionConfig
-import com.adealink.weparty.config.GlobalConfigType
-import com.adealink.weparty.config.globalConfigManager
 import com.adealink.weparty.module.backpack.Backpack
 import com.adealink.weparty.module.game.Game
 import com.adealink.weparty.module.game.GameModule
 import com.adealink.weparty.module.game.data.GameActivityRewardInfo.Companion.STATUS_RECEIVED
-import com.adealink.weparty.module.game.data.GameAppVersionOldCanPlayGameError
 import com.adealink.weparty.module.game.data.GameEntranceType
 import com.adealink.weparty.module.game.data.GameVersionNotMatchError
 import com.adealink.weparty.module.level.LevelModule
@@ -42,7 +34,6 @@ import com.adealink.weparty.module.music.Music
 import com.adealink.weparty.module.music.MusicModule
 import com.adealink.weparty.module.operation.OperationModule
 import com.adealink.weparty.module.pk.PK
-import com.adealink.weparty.module.profile.decorate.data.DecorType
 import com.adealink.weparty.module.rank.Rank
 import com.adealink.weparty.module.rank.data.BoardInfo.Companion.TYPE_GAME
 import com.adealink.weparty.module.room.Room
@@ -55,7 +46,6 @@ import com.adealink.weparty.module.store.data.StoreType
 import com.adealink.weparty.module.userprotect.UserProtect
 import com.adealink.weparty.module.webview.Web
 import com.adealink.weparty.module.webview.WebViewDialogFragmentBuilder
-import com.adealink.weparty.module.webview.data.OfflineH5GameInfo
 import com.adealink.weparty.room.R
 import com.adealink.weparty.room.data.AdminPermissionSetting
 import com.adealink.weparty.room.databinding.FragmentRoomPlayCenterPanelBinding
@@ -70,14 +60,15 @@ import com.adealink.weparty.room.micseat.viewmodel.RoomSeatViewModel
 import com.adealink.weparty.room.playcenter.adapter.IOperatorClickListener
 import com.adealink.weparty.room.playcenter.adapter.RoomActivityToolEntranceViewBinder
 import com.adealink.weparty.room.playcenter.adapter.RoomPlayCenterEntranceViewBinder
+import com.adealink.weparty.room.playcenter.adapter.RoomPlayCenterMiniSlotViewBinder
 import com.adealink.weparty.room.playcenter.adapter.RoomPlayCenterTitleViewBinder
 import com.adealink.weparty.room.playcenter.data.FISHING_ID
-import com.adealink.weparty.room.playcenter.data.PYRAMID_SLOTS_ID
 import com.adealink.weparty.room.playcenter.data.PlayCenterPartyEntrance
 import com.adealink.weparty.room.playcenter.data.RoomActivityToolEntrance
 import com.adealink.weparty.room.playcenter.data.RoomActivityToolType
 import com.adealink.weparty.room.playcenter.data.RoomGameCenterEntrance
 import com.adealink.weparty.room.playcenter.data.RoomPlayCenterEntrance
+import com.adealink.weparty.room.playcenter.data.RoomPlayCenterMiniSlotData
 import com.adealink.weparty.room.playcenter.data.RoomPlayCenterTitle
 import com.adealink.weparty.room.playcenter.data.RoomPlayCenterType
 import com.adealink.weparty.room.playcenter.data.RoomPlaySettingTitle
@@ -92,10 +83,7 @@ import com.adealink.weparty.room.stat.RoomBaseStatEvent.Companion.reportBtnClick
 import com.adealink.weparty.room.stat.RoomSettingStatEvent
 import com.adealink.weparty.room.viewmodel.RoomViewModelFactory
 import com.adealink.weparty.url.H5Page
-import com.adealink.weparty.url.UrlConfig
 import com.adealink.weparty.url.urlConfigService
-import com.adealink.weparty.util.goLocalLinkPage
-import kotlinx.coroutines.launch
 
 class RoomPlayCenterPanelFragment : BottomDialogFragment(R.layout.fragment_room_play_center_panel),
     IPlayCenterEntranceListener, IRoomActivityToolEntranceListener, IOperatorClickListener {
@@ -128,6 +116,7 @@ class RoomPlayCenterPanelFragment : BottomDialogFragment(R.layout.fragment_room_
 
     override fun initViews() {
         super.initViews()
+        playCenterAdapter.register(RoomPlayCenterMiniSlotViewBinder(this))
         playCenterAdapter.register(RoomPlayCenterTitleViewBinder(this))
         playCenterAdapter.register(RoomPlayCenterEntranceViewBinder(this))
         playCenterAdapter.register(RoomActivityToolEntranceViewBinder(this))
@@ -144,6 +133,7 @@ class RoomPlayCenterPanelFragment : BottomDialogFragment(R.layout.fragment_room_
                     return when (data) {
                         is RoomPlayCenterTitle -> SPAN_COUNT
                         is RoomPlaySettingTitle -> SPAN_COUNT
+                        is RoomPlayCenterMiniSlotData -> SPAN_COUNT
                         is PlayCenterPartyEntrance,
                         is RoomActivityToolEntrance,
                         is RoomGameCenterEntrance,
@@ -328,6 +318,11 @@ class RoomPlayCenterPanelFragment : BottomDialogFragment(R.layout.fragment_room_
                 dismiss()
             }
 
+            RoomPlayCenterType.MINI_SLOT -> {
+                GameModule.navigateToGame(GameEntranceType.MINI_SLOT)
+                dismiss()
+            }
+
             else -> {}
         }
     }

+ 136 - 0
module/room/src/main/java/com/adealink/weparty/room/playcenter/adapter/RoomPlayCenterMiniSlotViewBinder.kt

@@ -0,0 +1,136 @@
+package com.adealink.weparty.room.playcenter.adapter
+
+import android.annotation.SuppressLint
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import com.adealink.weparty.commonui.ext.gone
+import com.adealink.weparty.commonui.ext.show
+import com.adealink.weparty.commonui.recycleview.adapter.BindingViewHolder
+import com.adealink.weparty.commonui.recycleview.adapter.multitype.ItemViewBinder
+import com.adealink.weparty.module.game.data.MiniSlotConfigInfoResponse
+import com.adealink.weparty.room.databinding.LayoutRoomPlayCenterTopGameBinding
+import com.adealink.weparty.room.playcenter.data.RoomPlayCenterEntrance
+import com.adealink.weparty.room.playcenter.data.RoomPlayCenterMiniSlotData
+import com.adealink.weparty.room.playcenter.data.RoomPlayCenterType
+import com.adealink.weparty.room.playcenter.listener.IPlayCenterEntranceListener
+
+class RoomPlayCenterMiniSlotViewBinder(val l: IPlayCenterEntranceListener) :
+    ItemViewBinder<RoomPlayCenterMiniSlotData, BindingViewHolder<LayoutRoomPlayCenterTopGameBinding>>() {
+
+    @SuppressLint("SetTextI18n")
+    override fun onBindViewHolder(
+        holder: BindingViewHolder<LayoutRoomPlayCenterTopGameBinding>,
+        item: RoomPlayCenterMiniSlotData
+    ) {
+        val binding = holder.binding
+        binding.tvTitle.text = item.title
+
+        if (item.showMiniSlot && item.miniSlotConfigInfoRsp != null) {
+            binding.clGameRoom.show()
+            // 等级信息和进度
+            val levelInfo = calculateNextLevelAndProgress(item.miniSlotConfigInfoRsp)
+            binding.tvNextLevel.text = "Lv${levelInfo.nextLevelId}"
+            binding.progressBar.progress = levelInfo.progressPercentage
+            // 累计的剩余抽奖次数
+            val keyNum = item.miniSlotConfigInfoRsp.keyNum
+            binding.tvKeyNum.show()
+            binding.tvKeyNum.text = if (keyNum > 99) "99+" else keyNum.toString()
+        } else {
+            binding.clGameRoom.gone()
+            binding.tvKeyNum.gone()
+        }
+
+        binding.clGameRoom.setOnClickListener {
+            if (item.showMiniSlot) {
+                l.onEntranceClick(
+                    RoomPlayCenterEntrance(
+                        RoomPlayCenterType.MINI_SLOT
+                    )
+                )
+            }
+        }
+    }
+
+    /**
+     * 计算下一个等级和进度百分比
+     */
+    private fun calculateNextLevelAndProgress(config: MiniSlotConfigInfoResponse): LevelProgressInfo {
+        val todayBetCoin = config.todayBetCoin
+        val keyConfigs = config.keyConfig
+        if (keyConfigs.isEmpty()) return LevelProgressInfo(1, 0)
+        // 按照betCoin做一个排序(从小到大)
+        val sortedKeyConfigs = keyConfigs.sortedBy { it.betCoin }
+        // 如果下注为0,显示第一个等级,进度为0
+        if (todayBetCoin <= 0) return LevelProgressInfo(sortedKeyConfigs.first().level, 0)
+
+        var currentLevelIndex = -1
+        var nextLevelIndex = 0
+        for (i in sortedKeyConfigs.indices) {
+            if (todayBetCoin >= sortedKeyConfigs[i].betCoin) {
+                currentLevelIndex = i
+            } else {
+                nextLevelIndex = i
+                break
+            }
+        }
+
+        // 1.用户已经超过了所有等级
+        if (currentLevelIndex == sortedKeyConfigs.size - 1) return LevelProgressInfo(
+            sortedKeyConfigs.last().level,
+            100
+        )
+
+        // 2.用户还未达到第一个等级
+        if (currentLevelIndex == -1) {
+            val firstLevelBetCoin = sortedKeyConfigs.first().betCoin
+            val prevLevelBetCoin = 0
+            // 计算百分比:(当前进度 - 上一级目标) / (下一级目标 - 上一级目标)
+            val calculatedPercentageFloat =
+                (todayBetCoin - prevLevelBetCoin) * 100.0 / (firstLevelBetCoin - prevLevelBetCoin)
+            val progressPercentage = when {
+                calculatedPercentageFloat <= 0.0 -> 0
+                calculatedPercentageFloat < 8.0 -> 8 // 主要是为了保证进度条可见,否则比率很小显示的太短了
+                calculatedPercentageFloat >= 100.0 -> 99
+                else -> calculatedPercentageFloat.toInt()
+            }
+            return LevelProgressInfo(sortedKeyConfigs.first().level, progressPercentage)
+        }
+
+        // 3.用户等级在两个区间
+        val currentLevelBetCoin = sortedKeyConfigs[currentLevelIndex].betCoin
+        val nextLevelBetCoin = sortedKeyConfigs[nextLevelIndex].betCoin
+
+        // 分子: 当前进度 - 上一等级目标数值
+        // 分母: 下一等级目标数值 - 上一等级目标数值
+        val calculatedPercentageFloat =
+            (todayBetCoin - currentLevelBetCoin) * 100.0 / (nextLevelBetCoin - currentLevelBetCoin)
+        val progressPercentage = when {
+            calculatedPercentageFloat <= 0.0 -> 0
+            calculatedPercentageFloat < 8.0 -> 8
+            calculatedPercentageFloat >= 100.0 -> 99
+            else -> calculatedPercentageFloat.toInt()
+        }
+        return LevelProgressInfo(sortedKeyConfigs[nextLevelIndex].level, progressPercentage)
+    }
+
+    /**
+     * 等级和进度信息
+     */
+    data class LevelProgressInfo(
+        val nextLevelId: Int,
+        val progressPercentage: Int
+    )
+
+    override fun onCreateViewHolder(
+        inflater: LayoutInflater,
+        parent: ViewGroup
+    ): BindingViewHolder<LayoutRoomPlayCenterTopGameBinding> {
+        return BindingViewHolder(
+            LayoutRoomPlayCenterTopGameBinding.inflate(
+                inflater,
+                parent,
+                false
+            )
+        )
+    }
+} 

+ 11 - 1
module/room/src/main/java/com/adealink/weparty/room/playcenter/data/RoomPlayCenterData.kt

@@ -1,6 +1,7 @@
 package com.adealink.weparty.room.playcenter.data
 
 import com.adealink.weparty.cocosgame.data.Game
+import com.adealink.weparty.module.game.data.MiniSlotConfigInfoResponse
 import com.adealink.weparty.module.room.data.RoomCommonOperatorData
 import com.adealink.weparty.module.room.data.RoomData
 import com.adealink.weparty.module.room.data.RoomPlayCenterOperatorType
@@ -46,6 +47,7 @@ enum class RoomPlayCenterType {
     LUDO_TEAM,
     CARROM,
     UNO,
+    MINI_SLOT, // 小老虎机
 //    DOMINO,
 //    SPY,
 //    BRAIN_STORM,
@@ -150,4 +152,12 @@ data class PlayCenterPartyEntrance(
 
 }
 
-
+data class RoomPlayCenterMiniSlotData(
+    val title: String,
+    val showMiniSlot: Boolean = false,
+    val miniSlotConfigInfoRsp: MiniSlotConfigInfoResponse? = null
+) : RoomPlayerCenterData() {
+    override fun areItemsTheSame(newItem: Any): Boolean {
+        return newItem is RoomPlayCenterMiniSlotData && this.showMiniSlot == newItem.showMiniSlot
+    }
+}

+ 81 - 21
module/room/src/main/java/com/adealink/weparty/room/playcenter/manager/PlayCenterManager.kt

@@ -1,12 +1,14 @@
 package com.adealink.weparty.room.playcenter.manager
 
 import com.adealink.frame.aab.util.getCompatString
+import com.adealink.frame.base.Rlt
 import com.adealink.frame.frame.BaseFrame
 import com.adealink.weparty.cocosgame.data.Game
 import com.adealink.weparty.module.game.GameModule
 import com.adealink.weparty.module.game.data.ActivityType
 import com.adealink.weparty.module.game.data.CommonActivityRewardInfoReq
 import com.adealink.weparty.module.game.data.GameShowConfig
+import com.adealink.weparty.module.game.data.MiniSlotNeedData
 import com.adealink.weparty.module.level.data.LuckyGameLevel
 import com.adealink.weparty.module.music.MusicModule
 import com.adealink.weparty.module.music.data.MusicEntrance
@@ -21,6 +23,7 @@ import com.adealink.weparty.room.datasource.local.RoomAdminPermissionSetting
 import com.adealink.weparty.room.micseat.globalbroadcast.manager.roomGlobalBroadcastManager
 import com.adealink.weparty.room.playcenter.data.RoomGameCenterEntrance
 import com.adealink.weparty.room.playcenter.data.RoomPlayCenterEntrance
+import com.adealink.weparty.room.playcenter.data.RoomPlayCenterMiniSlotData
 import com.adealink.weparty.room.playcenter.data.RoomPlayCenterTitle
 import com.adealink.weparty.room.playcenter.data.RoomPlayCenterTitleOperator
 import com.adealink.weparty.room.playcenter.data.RoomPlayCenterType
@@ -76,6 +79,7 @@ class PlayCenterManager : BaseFrame<IPlayCenterListener>(), IPlayCenterManager {
                     DecorType.LUCKY_PRO_GAME,
                     DecorType.GREEDY_PRO_GAME,
                     DecorType.GREEDY_PERSONAL_GAME,
+                    DecorType.SHOW_MINI_SLOT
                 )
                 GameModule.isGameShow(reqList)
             }
@@ -100,7 +104,11 @@ class PlayCenterManager : BaseFrame<IPlayCenterListener>(), IPlayCenterManager {
         lastPullTime = 0L
     }
 
-    override fun updateAdminPlayCenterList(roomData: RoomPlayerCenterData, isAdd: Boolean, addRoomDataBefore: RoomPlayerCenterData?) {
+    override fun updateAdminPlayCenterList(
+        roomData: RoomPlayerCenterData,
+        isAdd: Boolean,
+        addRoomDataBefore: RoomPlayerCenterData?
+    ) {
         launch {
             notifyPlayCenterDataList(copyDataList())
         }
@@ -147,13 +155,29 @@ class PlayCenterManager : BaseFrame<IPlayCenterListener>(), IPlayCenterManager {
             }
             addGameList(luckyGameList)
             if (luckyGameList.isNotEmpty()) {
-                luckyGameList.add(
-                    0,
-                    RoomPlayCenterTitle(
-                        getCompatString(R.string.room_play_lucky_game),
-                        operatorList
+                // 老虎机游戏入口(和Title合并)
+                if (gameConfigMap[DecorType.SHOW_MINI_SLOT]?.show == true) {
+                    // 获取游戏数据(1->全部数据)
+                    val miniSlotConfigInfoDef = async { GameModule.getMiniSlotConfigInfo(MiniSlotNeedData.MIN_DATA) }
+                    val miniSlotConfigInfoRlt = miniSlotConfigInfoDef.await()
+                    val miniSlotConfigInfo = if (miniSlotConfigInfoRlt is Rlt.Success) miniSlotConfigInfoRlt.data.data else null
+                    luckyGameList.add(
+                        0,
+                        RoomPlayCenterMiniSlotData(
+                            title = getCompatString(R.string.room_play_lucky_game),
+                            showMiniSlot = true,
+                            miniSlotConfigInfoRsp = miniSlotConfigInfo
+                        )
                     )
-                )
+                } else {
+                    luckyGameList.add(
+                        0,
+                        RoomPlayCenterMiniSlotData(
+                            title = getCompatString(R.string.room_play_lucky_game),
+                            showMiniSlot = false
+                        )
+                    )
+                }
             }
             if (luckyGameList.isNotEmpty()) {
                 playCenterDataList.addAll(luckyGameList)
@@ -178,7 +202,8 @@ class PlayCenterManager : BaseFrame<IPlayCenterListener>(), IPlayCenterManager {
             gameCenterList.add(RoomPlayCenterEntrance(RoomPlayCenterType.RED_PACKET))
             val isRoomOwner =
                 roomService.memberController.isJoinedRoomOwner(ProfileModule.getMyUid())
-            val isRoomAdmin = roomService.memberController.isJoinedRoomAdmin(ProfileModule.getMyUid())
+            val isRoomAdmin =
+                roomService.memberController.isJoinedRoomAdmin(ProfileModule.getMyUid())
             val roomType = roomService.attrController.getRoomType()
             if (isRoomOwner) {
                 gameCenterList.add(RoomPlayCenterEntrance(RoomPlayCenterType.MIC_PK))
@@ -209,7 +234,11 @@ class PlayCenterManager : BaseFrame<IPlayCenterListener>(), IPlayCenterManager {
             val operatorList = mutableListOf<RoomPlayCenterTitleOperator>()
             if (gameConfigMap[DecorType.GAME_TAB_SWITCH]?.show == true) {
                 val gameRewardInfoAwait = async {
-                    GameModule.getActivityGameRewardInfo(com.adealink.weparty.module.game.data.CommonActivityRewardInfoReq(requestType = com.adealink.weparty.module.game.data.ActivityType.TYPE_GAME.type))
+                    GameModule.getActivityGameRewardInfo(
+                        com.adealink.weparty.module.game.data.CommonActivityRewardInfoReq(
+                            requestType = com.adealink.weparty.module.game.data.ActivityType.TYPE_GAME.type
+                        )
+                    )
                 }
                 val gameLevelInfoAwait = async {
                     GameModule.getUserGameLevelInfo(listOf(ProfileModule.getMyUid()))
@@ -240,13 +269,29 @@ class PlayCenterManager : BaseFrame<IPlayCenterListener>(), IPlayCenterManager {
             }
             addGameList(luckyGameList)
             if (luckyGameList.isNotEmpty()) {
-                luckyGameList.add(
-                    0,
-                    RoomPlayCenterTitle(
-                        getCompatString(R.string.room_play_lucky_game),
-                        operatorList
+                // 老虎机游戏入口(和Title合并)
+                if (gameConfigMap[DecorType.SHOW_MINI_SLOT]?.show == true) {
+                    // 获取游戏数据(1->全部数据)
+                    val miniSlotConfigInfoDef = async { GameModule.getMiniSlotConfigInfo(MiniSlotNeedData.MIN_DATA) }
+                    val miniSlotConfigInfoRlt = miniSlotConfigInfoDef.await()
+                    val miniSlotConfigInfo = if (miniSlotConfigInfoRlt is Rlt.Success) miniSlotConfigInfoRlt.data.data else null
+                    luckyGameList.add(
+                        0,
+                        RoomPlayCenterMiniSlotData(
+                            title = getCompatString(R.string.room_play_lucky_game),
+                            showMiniSlot = true,
+                            miniSlotConfigInfoRsp = miniSlotConfigInfo
+                        )
                     )
-                )
+                } else {
+                    luckyGameList.add(
+                        0,
+                        RoomPlayCenterMiniSlotData(
+                            title = getCompatString(R.string.room_play_lucky_game),
+                            showMiniSlot = false
+                        )
+                    )
+                }
             }
 
             //活动工具
@@ -280,19 +325,28 @@ class PlayCenterManager : BaseFrame<IPlayCenterListener>(), IPlayCenterManager {
     private fun addGameList(luckyGameList: MutableList<RoomPlayerCenterData>) {
         if (gameConfigMap[DecorType.LUCKY_FRUIT_GAME]?.show == true) {
             luckyGameList.add(
-                RoomPlayCenterEntrance(RoomPlayCenterType.LUCKY_FRUIT, url = urlConfigService.getH5Url(H5Page.LUCKY_FRUIT))
+                RoomPlayCenterEntrance(
+                    RoomPlayCenterType.LUCKY_FRUIT,
+                    url = urlConfigService.getH5Url(H5Page.LUCKY_FRUIT)
+                )
             )
         }
 
         if (gameConfigMap[DecorType.LUCKY_PRO_GAME]?.show == true) {
             luckyGameList.add(
-                RoomPlayCenterEntrance(RoomPlayCenterType.LUCKY_PRO, url = urlConfigService.getH5Url(H5Page.LUCKY_PRO))
+                RoomPlayCenterEntrance(
+                    RoomPlayCenterType.LUCKY_PRO,
+                    url = urlConfigService.getH5Url(H5Page.LUCKY_PRO)
+                )
             )
         }
 
         if (gameConfigMap[DecorType.GREEDY_PRO_GAME]?.show == true) {
             luckyGameList.add(
-                RoomPlayCenterEntrance(RoomPlayCenterType.GREEDY_PRO, url = urlConfigService.getH5Url(H5Page.GREEDY_PRO))
+                RoomPlayCenterEntrance(
+                    RoomPlayCenterType.GREEDY_PRO,
+                    url = urlConfigService.getH5Url(H5Page.GREEDY_PRO)
+                )
             )
         }
 
@@ -309,7 +363,10 @@ class PlayCenterManager : BaseFrame<IPlayCenterListener>(), IPlayCenterManager {
 
         if (gameConfigMap[DecorType.JACKPOT_GAME]?.show == true) {
             luckyGameList.add(
-                RoomPlayCenterEntrance(RoomPlayCenterType.JACKPOT, url = urlConfigService.getH5Url(H5Page.JACKPOT))
+                RoomPlayCenterEntrance(
+                    RoomPlayCenterType.JACKPOT,
+                    url = urlConfigService.getH5Url(H5Page.JACKPOT)
+                )
             )
         }
 
@@ -338,7 +395,10 @@ class PlayCenterManager : BaseFrame<IPlayCenterListener>(), IPlayCenterManager {
         }
         if (gameConfigMap[DecorType.GREEDY_PERSONAL_GAME]?.show == true) {
             luckyGameList.add(
-                RoomPlayCenterEntrance(RoomPlayCenterType.GREEDY_PERSONAL, url = urlConfigService.getH5Url(H5Page.GREEDY_PERSONAL))
+                RoomPlayCenterEntrance(
+                    RoomPlayCenterType.GREEDY_PERSONAL,
+                    url = urlConfigService.getH5Url(H5Page.GREEDY_PERSONAL)
+                )
             )
         }
     }
@@ -359,7 +419,7 @@ class PlayCenterManager : BaseFrame<IPlayCenterListener>(), IPlayCenterManager {
         val switchBroadcastLevel =
             roomGlobalBroadcastManager.getCanSwitchBroadcastLevel() ?: return true
         val userInfo = ProfileModule.getMyUserInfo()
-        return userInfo != null && (userInfo.level?:0) >= switchBroadcastLevel
+        return userInfo != null && (userInfo.level ?: 0) >= switchBroadcastLevel
     }
 
     fun logout() {

+ 2 - 0
module/room/src/main/java/com/adealink/weparty/room/playcenter/util/PlayCenterUtil.kt

@@ -46,6 +46,7 @@ fun getPlayCenterEntranceNameResId(type: RoomPlayCenterType): Int {
         RoomPlayCenterType.LUDO_TEAM -> APP_R.string.game_hub_ludo_team
         RoomPlayCenterType.CARROM -> APP_R.string.game_hub_carrom
         RoomPlayCenterType.UNO -> APP_R.string.game_hub_uno
+        RoomPlayCenterType.MINI_SLOT -> APP_R.string.common_mini_slot
 //        RoomPlayCenterType.DOMINO -> APP_R.string.game_hub_domino
 //        RoomPlayCenterType.SPY -> APP_R.string.game_hub_spy
 //        RoomPlayCenterType.BRAIN_STORM -> APP_R.string.game_hub_brainstorm
@@ -92,6 +93,7 @@ fun getPlayCenterEntranceImageResId(type: RoomPlayCenterType): Int {
         RoomPlayCenterType.LUDO_TEAM -> APP_R.drawable.common_ludo_team_ic
         RoomPlayCenterType.CARROM -> APP_R.drawable.common_carrom_ic
         RoomPlayCenterType.UNO -> APP_R.drawable.common_uno_ic
+        RoomPlayCenterType.MINI_SLOT -> R.drawable.room_game_mini_slot_task_ic
 //        RoomPlayCenterType.DOMINO -> APP_R.drawable.common_domino_ic
 //        RoomPlayCenterType.SPY -> APP_R.drawable.common_spy_ic
 //        RoomPlayCenterType.BRAIN_STORM -> APP_R.drawable.common_brain_storm_ic

+ 1 - 1
module/room/src/main/java/com/adealink/weparty/room/playcenter/viewmodel/RoomPlayCenterViewModel.kt

@@ -22,7 +22,7 @@ class RoomPlayCenterViewModel : BaseViewModel(), IRoomPlayCenterViewModel, IPlay
     }
 
     override fun getPlayCenterData(roomId: Long, firstLoad: Boolean) {
-        playCenterManager.getPlayCenterData(roomId, false, firstLoad)
+        playCenterManager.getPlayCenterData(roomId, true, firstLoad)
     }
 
     override fun getRoomGameData() {

BIN
module/room/src/main/res/drawable-xhdpi/room_game_mini_slot_bg.png


BIN
module/room/src/main/res/drawable-xhdpi/room_game_mini_slot_ic.png


BIN
module/room/src/main/res/drawable-xhdpi/room_game_mini_slot_task_ic.png


+ 6 - 0
module/room/src/main/res/drawable/mini_slot_icon_bg.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <corners android:radius="18dp" />
+    <solid android:color="#99FFFFFF" />
+</shape>

+ 23 - 0
module/room/src/main/res/drawable/room_mini_slot_task_progress_bar.xml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Background layer -->
+    <item android:id="@android:id/background">
+        <shape>
+            <corners android:radius="12dp" />
+            <solid android:color="#B96618" />
+        </shape>
+    </item>
+    <!-- Progress layer -->
+    <item android:id="@android:id/progress">
+        <scale android:scaleWidth="100%">
+            <shape android:shape="rectangle">
+                <corners android:radius="12dp" />
+                <gradient
+                    android:type="linear"
+                    android:angle="0"
+                    android:endColor="#FA7901"
+                    android:startColor="#FF9500" />
+            </shape>
+        </scale>
+    </item>
+</layer-list>

+ 147 - 0
module/room/src/main/res/layout/layout_room_play_center_top_game.xml

@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingVertical="8dp"
+    tools:background="@color/black">
+
+    <androidx.appcompat.widget.AppCompatTextView
+        android:id="@+id/tv_title"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="8dp"
+        android:gravity="center_vertical"
+        android:textColor="@color/color_FFFFFF"
+        android:textSize="14sp"
+        android:textStyle="bold"
+        app:layout_constraintBottom_toBottomOf="@id/cl_game_room"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintTop_toTopOf="@id/cl_game_room"
+        tools:text="Game Center" />
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/cl_game_room"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="6dp"
+        android:layout_marginEnd="8dp"
+        android:layout_marginBottom="2dp"
+        android:background="@drawable/room_game_mini_slot_bg"
+        android:paddingHorizontal="3dp"
+        android:paddingVertical="3dp"
+        android:visibility="gone"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintTop_toTopOf="parent"
+        tools:visibility="visible">
+
+        <androidx.appcompat.widget.AppCompatImageView
+            android:id="@+id/iv_task_icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:background="@drawable/mini_slot_icon_bg"
+            android:padding="3dp"
+            android:src="@drawable/room_game_mini_slot_task_ic"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:id="@+id/cl_task_center"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="4dp"
+            android:layout_marginEnd="4dp"
+            android:minWidth="80dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toEndOf="@id/iv_task_icon"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <ProgressBar
+                android:id="@+id/progress_bar"
+                style="@style/Widget.AppCompat.ProgressBar.Horizontal"
+                android:layout_width="0dp"
+                android:layout_height="6dp"
+                android:max="100"
+                android:progress="0"
+                android:progressDrawable="@drawable/room_mini_slot_task_progress_bar"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toTopOf="parent"
+                tools:progress="50" />
+
+            <com.adealink.weparty.commonui.widget.AutoMarqueeTextView
+                android:id="@+id/tv_task_name"
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="4dp"
+                android:layout_marginEnd="8dp"
+                android:layout_weight="1"
+                android:ellipsize="marquee"
+                android:singleLine="true"
+                android:text="@string/room_game_mini_slot_task_progress"
+                android:textColor="@color/color_FFF7D3"
+                android:textSize="10sp"
+                app:layout_constraintEnd_toStartOf="@id/tv_next_level"
+                app:layout_constraintStart_toStartOf="parent"
+                app:layout_constraintTop_toBottomOf="@id/progress_bar"
+                tools:ignore="SmallSp" />
+
+            <androidx.appcompat.widget.AppCompatTextView
+                android:id="@+id/tv_next_level"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:gravity="end"
+                android:textColor="@color/color_FFF7D3"
+                android:textSize="10sp"
+                app:layout_constraintBottom_toBottomOf="@id/tv_task_name"
+                app:layout_constraintEnd_toEndOf="parent"
+                app:layout_constraintTop_toTopOf="@id/tv_task_name"
+                tools:ignore="SmallSp"
+                tools:text="Lv27" />
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+        <!-- 分割线 -->
+        <View
+            android:id="@+id/v_divider"
+            android:layout_width="1.5dp"
+            android:layout_height="20dp"
+            android:layout_marginVertical="4dp"
+            android:layout_marginStart="8dp"
+            android:alpha="0.5"
+            android:background="@drawable/mini_slot_icon_bg"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toEndOf="@id/cl_task_center"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <androidx.appcompat.widget.AppCompatImageView
+            android:id="@+id/iv_slot_icon"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="8dp"
+            android:background="@drawable/mini_slot_icon_bg"
+            android:padding="3dp"
+            android:src="@drawable/room_game_mini_slot_ic"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toEndOf="@id/v_divider"
+            app:layout_constraintTop_toTopOf="parent" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.appcompat.widget.AppCompatTextView
+        android:id="@+id/tv_key_num"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="-6dp"
+        android:background="@drawable/room_list_audience_count_bg"
+        android:paddingHorizontal="4dp"
+        android:textColor="@color/color_FFF7D3"
+        android:textSize="10sp"
+        app:layout_constraintEnd_toEndOf="@id/cl_game_room"
+        app:layout_constraintTop_toTopOf="@id/cl_game_room"
+        tools:ignore="SmallSp"
+        tools:text="99+" />
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 1 - 0
module/room/src/main/res/values-ar/strings.xml

@@ -420,6 +420,7 @@
    <string name="room_texas_cowboy_global_broadcast">بث تكساس كاوبوي</string>
    <string name="room_select_country">اختر الدولة</string>
    <string name="setting_room_setting_gift_effect">تأثيرات الهدايا</string>
+   <string name="room_game_mini_slot_task_progress">تقدم المهمة</string>
    <string name="room_can_not_mute_tips">عذراً، المستخدم الآخر محمي بامتيازات SVIP ولا يمكن كتم صوته.</string>
    <string name="room_can_not_kick_out_tips">عذراً، المستخدم الآخر محمي بامتيازات SVIP ولا يمكن طرده من الغرفة.</string>
 </resources>

+ 1 - 0
module/room/src/main/res/values-zh/strings.xml

@@ -421,6 +421,7 @@
    <string name="room_game_bets_coins_count">Entry Coins: %1$s</string>
    <string name="room_game_rank_coins_count">第 %1$s: %2$s</string>
    <string name="setting_room_setting_gift_effect">礼物特效</string>
+   <string name="room_game_mini_slot_task_progress">任务进度</string>
    <string name="room_can_not_mute_tips">抱歉,对方受svip特权保护,无法被静音</string>
    <string name="room_can_not_kick_out_tips">抱歉,对方受svip特权保护,无法被踢出房间</string>
 </resources>

+ 1 - 0
module/room/src/main/res/values/strings.xml

@@ -422,6 +422,7 @@
    <string name="room_media_conflict_tips">You are in %1$s now, You will automatically check out when you enter room</string>
    <string name="room_media_conflict_remind">Don\'t remind me again</string>
    <string name="setting_room_setting_gift_effect">Gift effects</string>
+   <string name="room_game_mini_slot_task_progress">Task progress</string>
    <string name="room_can_not_mute_tips">Sorry, the other user is protected by SVIP privileges and cannot be muted.</string>
    <string name="room_can_not_kick_out_tips">Sorry, the other user is protected by SVIP privileges and cannot be kicked out of the room.</string>
 </resources>

BIN
module/webview/src/main/assets/loading_mini_slot.svga


+ 1 - 1
module/webview/src/main/java/com/adealink/weparty/webview/WeNextWebView.kt

@@ -128,7 +128,7 @@ class WeNextWebView : BaseWebView {
         jsBridge.addNativeMethod(SetupBrightnessJSNativeMethod(this))
         jsBridge.addNativeMethod(ClosePreloadManagerJSNativeMethod())
         jsBridge.addNativeMethod(SignInSuccessJsNativeMethod())
-        jsBridge.addNativeMethod(OnDeeplinkJsMethod())
+        jsBridge.addNativeMethod(OnDeeplinkJsMethod(this))
         jsBridge.addNativeMethod(GetLabelUrlJsNativeMethod())
         jsBridge.addNativeMethod(KVStorageSetJSNativeMethod())
         jsBridge.addNativeMethod(KVStorageGetJSNativeMethod())

+ 3 - 2
module/webview/src/main/java/com/adealink/weparty/webview/WebViewDialogFragment.kt

@@ -22,7 +22,6 @@ import com.adealink.frame.router.Router
 import com.adealink.frame.router.annotation.BindExtra
 import com.adealink.frame.router.annotation.RouterUri
 import com.adealink.frame.util.DisplayUtil
-import com.adealink.weparty.webview.cocosgame.viewmodel.CocosWebGameViewModel
 import com.adealink.weparty.commonui.widget.BottomDialogFragment
 import com.adealink.weparty.module.profile.decorate.data.DecorType
 import com.adealink.weparty.module.webview.IWebViewDialogFragment
@@ -31,6 +30,7 @@ import com.adealink.weparty.module.webview.Web.HalfScreen.Companion.EXTRA_HEIGHT
 import com.adealink.weparty.module.webview.WebModule
 import com.adealink.weparty.module.webview.data.OfflineH5GameInfo
 import com.adealink.weparty.webview.callback.IWebViewCallback
+import com.adealink.weparty.webview.cocosgame.viewmodel.CocosWebGameViewModel
 import com.adealink.weparty.webview.component.ErrorComp
 import com.adealink.weparty.webview.component.GameLoadingComp
 import com.adealink.weparty.webview.constant.COCOS_GAME_JS_BRIDGE
@@ -156,7 +156,8 @@ class WebViewDialogFragment : BottomDialogFragment(R.layout.fragment_webview),
                 || offlineH5GameInfo.gameType == DecorType.GREEDY_BOX_GAME.value
                 || offlineH5GameInfo.gameType == DecorType.TEXAS_GAME.value
                 || offlineH5GameInfo.gameType == DecorType.DRAGON_TIGER_FIGHT_GAME.value
-                || offlineH5GameInfo.gameType == DecorType.RUSSIAN_TURNTABLE_GAME.value) {
+                || offlineH5GameInfo.gameType == DecorType.RUSSIAN_TURNTABLE_GAME.value
+                || offlineH5GameInfo.gameType == DecorType.SHOW_MINI_SLOT.value) {
                 binding.webView.changeJSBridge(
                     CocosJSBridgeImpl(
                         binding.webView,

+ 34 - 2
module/webview/src/main/java/com/adealink/weparty/webview/cocosgame/data/CocosWindowViewData.kt

@@ -1,7 +1,19 @@
 package com.adealink.weparty.webview.cocosgame.data
 
 import com.adealink.frame.base.AppBaseInfo
+import com.adealink.frame.locale.country.getNewCountryCode
 import com.adealink.frame.router.Router
+import com.adealink.frame.util.PackageUtil
+import com.adealink.frame.util.getApiLevel
+import com.adealink.frame.util.getDeviceName
+import com.adealink.frame.util.getNetworkOperator
+import com.adealink.frame.util.getNetworkType
+import com.adealink.frame.util.getOsVersion
+import com.adealink.frame.util.isNetworkAvailable
+import com.adealink.weparty.App
+import com.adealink.weparty.channel.getChannel
+import com.adealink.weparty.module.attribution.AttributionModule
+import com.adealink.weparty.module.profile.ProfileModule
 import com.google.gson.annotations.SerializedName
 
 data class CocosWindowViewData(
@@ -11,10 +23,30 @@ data class CocosWindowViewData(
     @SerializedName("safe_area") val safeArea: CocosWindowSafeArea,
     @SerializedName("token") val token: String = "",
     @SerializedName("baseUrl") val baseUrl: String = "",
-    @SerializedName("uid") val uid: Long = 0,
+    @SerializedName("uid") val uid: Long = ProfileModule.getMyUid(),
     @SerializedName("room_id") val roomId: Long = 0,
     @SerializedName("scheme_host") val schemeHost: String = Router.getDeepLink(""),
-    @SerializedName("app_name") val appName: String = AppBaseInfo.appName
+    @SerializedName("app_name") val appName: String = AppBaseInfo.appName,
+
+    @SerializedName("package_name") val packageName: String = PackageUtil.getPackageName(),
+    //埋点数据相关,跟StatConfig保持一致
+    @SerializedName("report_url") val reportUrl: String = "https://api-log-upload.wenext.technology/api/log",
+    @SerializedName("app") val app: String = "0",
+    @SerializedName("app_channel") val appChannel: String = getChannel(),
+    @SerializedName("background") val background: Int = 1,
+    @SerializedName("country_code") val countryCode: String = ProfileModule.getMyUserInfo()?.country ?: getNewCountryCode(),
+    @SerializedName("device_id") val deviceId: String = App.instance.deviceIdService.getLocalDeviceId(),
+    @SerializedName("device_name") val deviceName: String = getDeviceName(),
+    @SerializedName("network_available") val networkAvailable: Boolean = isNetworkAvailable(),
+    @SerializedName("network_operator") val networkOperator: String = getNetworkOperator(),
+    @SerializedName("network_type") val networkType: String = getNetworkType(),
+    @SerializedName("organic_install") val organicInstall: Boolean = !AttributionModule.isInorganicInstall(),
+    @SerializedName("os_version") val osVersion: String = getOsVersion(),
+    @SerializedName("api_level") val apiLevel: Int = getApiLevel(),
+    @SerializedName("platform") val platform: String = AppBaseInfo.platform,
+    @SerializedName("region") val region: String? = ProfileModule.getMyUserInfo()?.region,
+    @SerializedName("version_code") val versionCode: Int = PackageUtil.getVersionCode(),
+    @SerializedName("version_name") val versionName: String = PackageUtil.getVersionName()
 )
 
 data class CocosWindowSafeArea(

+ 42 - 0
module/webview/src/main/java/com/adealink/weparty/webview/component/GameLoadingComp.kt

@@ -1,8 +1,10 @@
 package com.adealink.weparty.webview.component
 
 import android.animation.ValueAnimator
+import android.graphics.BitmapFactory
 import android.view.animation.DecelerateInterpolator
 import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
 import androidx.core.view.updateLayoutParams
 import androidx.lifecycle.LifecycleOwner
 import com.adealink.frame.aab.util.getCompatColor
@@ -15,6 +17,8 @@ import com.adealink.weparty.commonui.ext.show
 import com.adealink.weparty.module.profile.decorate.data.DecorType
 import com.adealink.weparty.webview.R
 import com.adealink.weparty.webview.databinding.LayoutGameLoadingBinding
+import com.opensource.svgaplayer.SVGADynamicEntity
+import com.adealink.weparty.R as APP_R
 
 class GameLoadingComp(
     lifecycleOwner: LifecycleOwner,
@@ -146,6 +150,44 @@ class GameLoadingComp(
                 binding.progressTv.setTextColor(getCompatColor(R.color.color_FFED27))
             }
 
+            DecorType.SHOW_MINI_SLOT.value -> {
+                //背景
+                binding.bgIv.setImageResource(R.drawable.web_loading_game_bg_mini_slot)
+                binding.gameIv.gone()
+                binding.svgaLoading.show()
+                //顶部加载图
+                binding.svgaLoading.setImageResource(R.drawable.game_mini_slot_loading_title_text)
+                if (loadingUrl.isNullOrEmpty()) {
+                    binding.svgaLoading.setAsset("loading_mini_slot.svga", supplier = {
+                        val dynamicEntity = SVGADynamicEntity()
+                        BitmapFactory.decodeResource(
+                            binding.svgaLoading.context.resources,
+                            R.drawable.game_mini_slot_loading_title_text
+                        )?.apply {
+                            dynamicEntity.setDynamicImage(this, "image")
+                        }
+                        dynamicEntity
+                    })
+                } else {
+                    binding.svgaLoading.setUrl(loadingUrl)
+                }
+
+                //加载进度条
+                binding.progressBgIv.updateLayoutParams<ConstraintLayout.LayoutParams> {
+                    width = 135.dp()
+                    height = 12.dp()
+                }
+                binding.progressContentIv.updateLayoutParams<ConstraintLayout.LayoutParams> {
+                    height = 10.dp()
+                    topMargin = 1.dp()
+                    marginStart = 1.dp()
+                    marginEnd = 1.dp()
+                }
+                binding.progressTv.setTextColor(getCompatColor(APP_R.color.white))
+                binding.progressBgIv.setImageResource(R.drawable.web_loading_game_mini_slot_progress_bg)
+                binding.progressContentIv.setImageResource(R.drawable.web_loading_game_mini_slot_inner_progress_bg)
+            }
+
             else -> {
             }
         }

BIN
module/webview/src/main/res/drawable-ar-xhdpi/game_mini_slot_loading_title_text.webp


BIN
module/webview/src/main/res/drawable-bn-xhdpi/game_mini_slot_loading_title_text.webp


BIN
module/webview/src/main/res/drawable-es-xhdpi/game_mini_slot_loading_title_text.webp


BIN
module/webview/src/main/res/drawable-hi-xhdpi/game_mini_slot_loading_title_text.webp


BIN
module/webview/src/main/res/drawable-in-xhdpi/game_mini_slot_loading_title_text.webp


BIN
module/webview/src/main/res/drawable-pa-xhdpi/game_mini_slot_loading_title_text.webp


BIN
module/webview/src/main/res/drawable-pt-xhdpi/game_mini_slot_loading_title_text.webp


BIN
module/webview/src/main/res/drawable-ru-xhdpi/game_mini_slot_loading_title_text.webp


BIN
module/webview/src/main/res/drawable-ta-xhdpi/game_mini_slot_loading_title_text.webp


BIN
module/webview/src/main/res/drawable-te-xhdpi/game_mini_slot_loading_title_text.webp


BIN
module/webview/src/main/res/drawable-th-xhdpi/game_mini_slot_loading_title_text.webp


BIN
module/webview/src/main/res/drawable-tl-xhdpi/game_mini_slot_loading_title_text.webp


BIN
module/webview/src/main/res/drawable-tr-xhdpi/game_mini_slot_loading_title_text.webp


BIN
module/webview/src/main/res/drawable-ur-xhdpi/game_mini_slot_loading_title_text.webp


BIN
module/webview/src/main/res/drawable-vi-xhdpi/game_mini_slot_loading_title_text.webp


BIN
module/webview/src/main/res/drawable-xhdpi/game_mini_slot_loading_title_text.webp


BIN
module/webview/src/main/res/drawable-xhdpi/web_loading_game_bg_mini_slot.webp


BIN
module/webview/src/main/res/drawable-xhdpi/web_loading_game_img_mini_slot.webp


BIN
module/webview/src/main/res/drawable-zh-rTW-xhdpi/game_mini_slot_loading_title_text.webp


+ 16 - 0
module/webview/src/main/res/drawable/web_loading_game_mini_slot_inner_progress_bg.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+
+    <!-- 渐变背景 -->
+    <gradient
+        android:type="linear"
+        android:angle="270"
+        android:startColor="#FFE8A6"
+        android:centerColor="#FFD70E"
+        android:endColor="#FF8A1D"
+        android:centerY="0.275" />
+
+    <!-- 圆角 -->
+    <corners android:radius="50dp" />
+</shape>

+ 8 - 0
module/webview/src/main/res/drawable/web_loading_game_mini_slot_progress_bg.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <!-- 背景颜色 -->
+    <solid android:color="#FF0058C1" />
+    <!-- 圆角 -->
+    <corners android:radius="50dp" />
+</shape>