feat : app/src/module_game
@@ -0,0 +1,301 @@
|
||||
package com.chwl.app.game.core
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import androidx.activity.viewModels
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.viewbinding.ViewBinding
|
||||
import com.chwl.app.R
|
||||
import com.chwl.app.base.BaseViewBindingActivity
|
||||
import com.chwl.app.common.widget.dialog.DialogManager
|
||||
import com.chwl.app.game.core.engine.GameEngineAbility
|
||||
import com.chwl.app.game.data.bean.GameInfoUiState
|
||||
import com.chwl.app.game.ui.game.GameViewModel
|
||||
import com.chwl.app.game.ui.result.GameResultDialog
|
||||
import com.chwl.app.ui.pay.ChargeActivity
|
||||
import com.chwl.core.bean.game.GameResultBean
|
||||
import com.chwl.core.support.room.RoomContext
|
||||
import com.chwl.core.support.room.RoomView
|
||||
import com.chwl.core.support.room.RoomWidget
|
||||
import com.chwl.core.utils.net.BalanceNotEnoughExeption
|
||||
import com.chwl.core.utils.net.ServerException
|
||||
import com.chwl.library.utils.ResUtil
|
||||
import com.netease.nim.uikit.StatusBarUtil
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
abstract class BaseGameActivity<T : ViewBinding> : BaseViewBindingActivity<T>(), RoomView {
|
||||
|
||||
protected val viewModel: GameViewModel by viewModels()
|
||||
|
||||
protected var widgets: HashMap<String, RoomWidget> = HashMap()
|
||||
protected val gameContext get() = viewModel.gameContextLiveData.value
|
||||
|
||||
protected val stateAbility get() = gameContext?.findAbility<GameStateAbility>(GameStateAbility::class.java.simpleName)
|
||||
protected val gameEngineAbility
|
||||
get() = gameContext?.findAbility<GameEngineAbility>(
|
||||
GameEngineAbility::class.java.simpleName
|
||||
)
|
||||
|
||||
@Deprecated("Dialog管理后续需要优化")
|
||||
private var gameInfoDialogManager: DialogManager? = null
|
||||
|
||||
@Deprecated("Dialog管理后续需要优化")
|
||||
protected var loadingDialogManager: DialogManager? = null
|
||||
|
||||
@Deprecated("Dialog管理后续需要优化")
|
||||
protected var matchFailedDialogManager: DialogManager? = null
|
||||
|
||||
@Deprecated("Dialog管理后续需要优化")
|
||||
protected var balanceNotEnoughDialogManager: DialogManager? = null
|
||||
|
||||
private var resultDialog: GameResultDialog? = null
|
||||
override fun init() {
|
||||
initView()
|
||||
initEvent()
|
||||
initObserver()
|
||||
initWidgets()
|
||||
}
|
||||
|
||||
protected open fun initView() {
|
||||
loadingDialogManager = DialogManager(this)
|
||||
}
|
||||
|
||||
protected open fun initEvent() {
|
||||
}
|
||||
|
||||
protected open fun initObserver() {
|
||||
lifecycleScope.launch {
|
||||
viewModel.closeRoomFlow.collectLatest {
|
||||
loadingDialogManager?.dismissDialog()
|
||||
finish()
|
||||
}
|
||||
}
|
||||
viewModel.gameContextLiveData.observe(this) {
|
||||
onGameContext(it)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun initWidgets() {
|
||||
}
|
||||
|
||||
protected open fun onGameContext(gameContext: GameContext) {
|
||||
gameContext.onViewAttach(this)
|
||||
initGameEngine(gameContext)
|
||||
stateAbility?.let {
|
||||
observeGameInfoUiState(it)
|
||||
|
||||
lifecycleScope.launch {
|
||||
it.matchFailedFlow.collectLatest {
|
||||
onMatchFailed()
|
||||
}
|
||||
}
|
||||
|
||||
lifecycleScope.launch {
|
||||
it.gameResultFlow.collectLatest { list ->
|
||||
onGameResult(list)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeGameInfoUiState(stateAbility: GameStateAbility) {
|
||||
lifecycleScope.launch {
|
||||
stateAbility.gameInfoUiStateFlow.collectLatest {
|
||||
when (it) {
|
||||
is GameInfoUiState.Loading -> {
|
||||
if (gameInfoDialogManager == null) {
|
||||
gameInfoDialogManager = DialogManager(context)
|
||||
}
|
||||
gameInfoDialogManager?.showProgressDialog(context)
|
||||
}
|
||||
|
||||
is GameInfoUiState.Success -> {
|
||||
matchFailedDialogManager?.dismissDialog()
|
||||
balanceNotEnoughDialogManager?.dismissDialog()
|
||||
dismissResultDialog()
|
||||
gameInfoDialogManager?.dismissDialog()
|
||||
}
|
||||
|
||||
is GameInfoUiState.Failed -> {
|
||||
gameInfoDialogManager?.dismissDialog()
|
||||
if (it.throwable is ServerException && it.throwable.code == BalanceNotEnoughExeption.code) {
|
||||
onBalanceNotEnough()
|
||||
} else {
|
||||
it.throwable.message?.let { msg ->
|
||||
toast(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is GameInfoUiState.Empty -> {
|
||||
gameInfoDialogManager?.dismissDialog()
|
||||
toast(R.string.game_end_tips)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun initGameEngine(gameContext: GameContext) {
|
||||
val gameEngineAbility = gameContext.findAbility<GameEngineAbility>(GameEngineAbility::class.java.simpleName)
|
||||
lifecycleScope.launch {
|
||||
gameEngineAbility?.gameViewFlow?.collectLatest {
|
||||
|
||||
onGameViewChanged(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun onGameResult(list: List<GameResultBean>?) {
|
||||
if (list == null) {
|
||||
return
|
||||
}
|
||||
dismissResultDialog()
|
||||
resultDialog = GameResultDialog(list, onClose = {
|
||||
closeRoom()
|
||||
}, onRestart = {
|
||||
restart()
|
||||
})
|
||||
resultDialog?.show(supportFragmentManager, "GAME_RESULT")
|
||||
}
|
||||
|
||||
private fun onMatchFailed() {
|
||||
if (matchFailedDialogManager == null) {
|
||||
matchFailedDialogManager = DialogManager(context)
|
||||
}
|
||||
matchFailedDialogManager?.showOkCancelDialog(null,
|
||||
getString(R.string.game_match_failed),
|
||||
getString(R.string.game_rematch),
|
||||
getString(R.string.exit_text), false, false, object :
|
||||
DialogManager.OkCancelDialogListener {
|
||||
override fun onOk() {
|
||||
restart()
|
||||
}
|
||||
|
||||
override fun onCancel() {
|
||||
super.onCancel()
|
||||
closeRoom()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
protected open fun onBalanceNotEnough() {
|
||||
if (balanceNotEnoughDialogManager == null) {
|
||||
balanceNotEnoughDialogManager = DialogManager(this)
|
||||
}
|
||||
balanceNotEnoughDialogManager?.showOkCancelDialog(
|
||||
ResUtil.getString(R.string.widget_dialog_dialoguihelper_04),
|
||||
ResUtil.getString(R.string.treasure_to_charge)
|
||||
) {
|
||||
ChargeActivity.start(context)
|
||||
}
|
||||
}
|
||||
|
||||
private fun closeRoom() {
|
||||
loadingDialogManager?.showProgressDialog(this@BaseGameActivity)
|
||||
viewModel.closeRoom()
|
||||
}
|
||||
|
||||
private fun restart() {
|
||||
val intent = viewModel.getGameIntent()
|
||||
if (intent == null) {
|
||||
toast(R.string.data_error)
|
||||
return
|
||||
}
|
||||
stateAbility?.restart(intent)
|
||||
}
|
||||
|
||||
protected open fun onGameViewChanged(view: View?) {
|
||||
if (view == null) {
|
||||
getGameViewGroup().removeAllViews()
|
||||
} else {
|
||||
getGameViewGroup().addView(
|
||||
view,
|
||||
FrameLayout.LayoutParams(
|
||||
FrameLayout.LayoutParams.MATCH_PARENT,
|
||||
FrameLayout.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun getGameViewGroup(): FrameLayout
|
||||
|
||||
/**
|
||||
* 注册组件
|
||||
*/
|
||||
protected open fun registerWidget(name: String, widget: RoomWidget) {
|
||||
widgets[name] = widget
|
||||
widget.onStart(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消注册组件
|
||||
*/
|
||||
protected open fun unregisterWidgets() {
|
||||
widgets.values.forEach {
|
||||
it.onStop()
|
||||
}
|
||||
widgets.clear()
|
||||
}
|
||||
|
||||
override fun needSteepStateBar(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun setStatusBar() {
|
||||
super.setStatusBar()
|
||||
StatusBarUtil.transparencyBar(this)
|
||||
}
|
||||
|
||||
override fun getLifecycleOwner(): LifecycleOwner {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun getActivity(): Activity? {
|
||||
return this
|
||||
}
|
||||
|
||||
override fun getRoomContext(): RoomContext? {
|
||||
return viewModel.gameContextLiveData.value
|
||||
}
|
||||
|
||||
override fun getViewFragmentManager(): FragmentManager {
|
||||
return supportFragmentManager
|
||||
}
|
||||
|
||||
override fun getRoomContextLiveData(): LiveData<out RoomContext?> {
|
||||
return viewModel.gameContextLiveData
|
||||
}
|
||||
|
||||
override fun findWidget(name: String): RoomWidget? {
|
||||
return widgets[name]
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
balanceNotEnoughDialogManager?.dismissDialog()
|
||||
balanceNotEnoughDialogManager = null
|
||||
gameInfoDialogManager?.dismissDialog()
|
||||
gameInfoDialogManager = null
|
||||
loadingDialogManager?.dismissDialog()
|
||||
loadingDialogManager = null
|
||||
dismissResultDialog()
|
||||
resultDialog = null
|
||||
matchFailedDialogManager?.dismissDialog()
|
||||
matchFailedDialogManager = null
|
||||
}
|
||||
|
||||
private fun dismissResultDialog() {
|
||||
try {
|
||||
resultDialog?.dismissAllowingStateLoss()
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
package com.chwl.app.game.core
|
||||
|
||||
import com.chwl.app.game.core.engine.GameEngineAbility
|
||||
import com.chwl.core.bean.game.GameRoomInfo
|
||||
import com.chwl.core.support.room.RoomAbility
|
||||
import com.chwl.core.support.room.RoomContext
|
||||
|
||||
class GameContext(roomId: Long) : RoomContext(roomId) {
|
||||
|
||||
override fun loadAbility(list: MutableMap<String, RoomAbility>) {
|
||||
super.loadAbility(list)
|
||||
list[GameStateAbility::class.java.simpleName] = GameStateAbility()
|
||||
list[GameEngineAbility::class.java.simpleName] = GameEngineAbility()
|
||||
list[GameIMEngineAbility::class.java.simpleName] = GameIMEngineAbility()
|
||||
list[GameQueueAbility::class.java.simpleName] = GameQueueAbility()
|
||||
list[GameMessageAbility::class.java.simpleName] = GameMessageAbility()
|
||||
}
|
||||
|
||||
override fun performStart() {
|
||||
super.performStart()
|
||||
}
|
||||
|
||||
fun restart(roomInfo: GameRoomInfo) {
|
||||
val stateAbility = findAbility<GameStateAbility>(GameStateAbility::class.java.simpleName)
|
||||
}
|
||||
}
|
@@ -0,0 +1,137 @@
|
||||
package com.chwl.app.game.core
|
||||
|
||||
import com.chwl.app.public_chat.core.ChatRoomClient
|
||||
import com.chwl.app.public_chat.core.ChatRoomClientManager
|
||||
import com.chwl.core.manager.IMNetEaseManager
|
||||
import com.chwl.core.support.listener.ListenerOwner
|
||||
import com.chwl.core.support.listener.ListenerStore
|
||||
import com.chwl.core.support.listener.SafeListenerOwner
|
||||
import com.chwl.core.support.room.RoomAbility
|
||||
import com.chwl.core.support.room.RoomContext
|
||||
import com.netease.nimlib.sdk.StatusCode
|
||||
import com.netease.nimlib.sdk.chatroom.ChatRoomMessageBuilder
|
||||
import com.netease.nimlib.sdk.chatroom.model.ChatRoomMessage
|
||||
import io.reactivex.Single
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
|
||||
class GameIMEngineAbility(private val listenerOwner: ListenerOwner<GameIMEngineAbility.Listener> = SafeListenerOwner()) :
|
||||
RoomAbility(), ListenerStore<GameIMEngineAbility.Listener> by listenerOwner {
|
||||
|
||||
val stateFlow = MutableStateFlow<StatusCode>(StatusCode.INVALID)
|
||||
|
||||
// 恢复连接(中断后恢复)
|
||||
val reconnectedFlow = MutableSharedFlow<Boolean>()
|
||||
|
||||
private var chatRoomClient: ChatRoomClient? = null
|
||||
|
||||
private var netBrokenTag = false
|
||||
|
||||
private val stateAbility: GameStateAbility?
|
||||
get() = roomContext?.findAbility<GameStateAbility>(
|
||||
GameStateAbility::class.java.simpleName
|
||||
)
|
||||
|
||||
override fun onStart(context: RoomContext) {
|
||||
super.onStart(context)
|
||||
safeLaunch {
|
||||
stateAbility?.let {
|
||||
combine(
|
||||
*arrayOf(
|
||||
it.imIdFlow,
|
||||
it.gameStateFlow
|
||||
), transform = {
|
||||
true
|
||||
})
|
||||
.collectLatest {
|
||||
checkRunning()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkRunning() {
|
||||
val stateAbility = this.stateAbility
|
||||
val gameState = stateAbility?.gameStateFlow?.value
|
||||
val sessionId = stateAbility?.imIdFlow?.value?.toString()
|
||||
logD("checkRunning() gameState:$gameState sessionId:$sessionId chatRoomClient?.sessionId = ${chatRoomClient?.sessionId} GameIMEngineAbility ")
|
||||
if (sessionId != null && (gameState == GameStateAbility.STATE_MATCHING || gameState == GameStateAbility.STATE_MATCH_SUCCESS)) {
|
||||
if (chatRoomClient?.sessionId != sessionId) {
|
||||
logD("checkRunning() 1 GameIMEngineAbility ")
|
||||
exitRoom()
|
||||
initClient(sessionId)
|
||||
}
|
||||
} else {
|
||||
logD("checkRunning() 2 GameIMEngineAbility ")
|
||||
exitRoom()
|
||||
}
|
||||
}
|
||||
|
||||
private fun exitRoom() {
|
||||
logD("exitRoom() id:${chatRoomClient?.sessionId}")
|
||||
IMNetEaseManager.gameChatRoomId = null
|
||||
chatRoomClient?.onCleared()
|
||||
chatRoomClient = null
|
||||
}
|
||||
|
||||
private fun initClient(sessionId: String) {
|
||||
logD("GameIMEngineAbility initClient() sessionId:$sessionId")
|
||||
netBrokenTag = false
|
||||
chatRoomClient = ChatRoomClientManager.getClient(sessionId)
|
||||
chatRoomClient?.let {
|
||||
safeLaunch {
|
||||
it.stateFlow.collect {
|
||||
if (it == StatusCode.NET_BROKEN) {
|
||||
netBrokenTag = true
|
||||
}
|
||||
if (it == StatusCode.LOGINED) {
|
||||
if (netBrokenTag) {
|
||||
reconnectedFlow.emit(true)
|
||||
}
|
||||
netBrokenTag = false
|
||||
}
|
||||
this@GameIMEngineAbility.stateFlow.value = it
|
||||
}
|
||||
}
|
||||
addDisposable(it.messageObservable.subscribe { list ->
|
||||
listenerOwner.postEvent { it.onReceiveMessage(list) }
|
||||
})
|
||||
enterChatRoom(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun enterChatRoom(client: ChatRoomClient) {
|
||||
IMNetEaseManager.gameChatRoomId = client.sessionId
|
||||
addDisposable(client.enterChatRoom().subscribe({
|
||||
}, {
|
||||
}))
|
||||
}
|
||||
|
||||
fun sendMessage(message: ChatRoomMessage) {
|
||||
chatRoomClient?.let {
|
||||
addDisposable(it.sendMessage(message).subscribe({}, {}))
|
||||
}
|
||||
}
|
||||
|
||||
fun buildTextMessage(text: String): ChatRoomMessage? {
|
||||
val client = chatRoomClient ?: return null
|
||||
return ChatRoomMessageBuilder.createChatRoomTextMessage(client.sessionId, text)
|
||||
}
|
||||
|
||||
fun sendMessageRx(message: ChatRoomMessage): Single<Any> {
|
||||
val client = chatRoomClient ?: return Single.error(IllegalStateException("client is NULL"))
|
||||
return client.sendMessage(message)
|
||||
}
|
||||
|
||||
override fun onStop(context: RoomContext) {
|
||||
super.onStop(context)
|
||||
listenerOwner.removeAllListener()
|
||||
exitRoom()
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun onReceiveMessage(messages: List<ChatRoomMessage>)
|
||||
}
|
||||
}
|
@@ -0,0 +1,121 @@
|
||||
package com.chwl.app.game.core
|
||||
|
||||
import com.chwl.app.R
|
||||
import com.chwl.core.support.listener.ListenerOwner
|
||||
import com.chwl.core.support.listener.ListenerStore
|
||||
import com.chwl.core.support.listener.SafeListenerOwner
|
||||
import com.chwl.core.support.room.RoomAbility
|
||||
import com.chwl.core.support.room.RoomContext
|
||||
import com.chwl.library.utils.ResUtil
|
||||
import com.netease.nimlib.sdk.chatroom.ChatRoomMessageBuilder
|
||||
import com.netease.nimlib.sdk.chatroom.model.ChatRoomMessage
|
||||
import com.netease.nimlib.sdk.msg.constant.MsgTypeEnum
|
||||
import io.reactivex.Single
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import java.lang.IllegalStateException
|
||||
|
||||
class GameMessageAbility(private val listenerOwner: ListenerOwner<GameMessageAbility.Listener> = SafeListenerOwner()) :
|
||||
RoomAbility(), ListenerStore<GameMessageAbility.Listener> by listenerOwner,
|
||||
GameIMEngineAbility.Listener {
|
||||
|
||||
private val dataList = ArrayList<Any>()
|
||||
|
||||
private val imEngineAbility
|
||||
get() =
|
||||
roomContext?.findAbility<GameIMEngineAbility>(GameIMEngineAbility::class.java.simpleName)
|
||||
|
||||
override fun onStart(context: RoomContext) {
|
||||
super.onStart(context)
|
||||
val imEngineAbility =
|
||||
context.findAbility<GameIMEngineAbility>(GameIMEngineAbility::class.java.simpleName)
|
||||
imEngineAbility?.addListener(this)
|
||||
val stateAbility =
|
||||
context.findAbility<GameStateAbility>(GameStateAbility::class.java.simpleName)
|
||||
stateAbility?.let {
|
||||
safeLaunch {
|
||||
it.imIdFlow.collectLatest {
|
||||
resetMessageList()
|
||||
}
|
||||
}
|
||||
|
||||
safeLaunch {
|
||||
it.gameStateFlow.collectLatest {
|
||||
if (it == GameStateAbility.STATE_GAME_END) {
|
||||
resetMessageList()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetMessageList() {
|
||||
val stateAbility =
|
||||
roomContext?.findAbility<GameStateAbility>(GameStateAbility::class.java.simpleName)
|
||||
dataList.clear()
|
||||
val sessionId = stateAbility?.imIdFlow?.value
|
||||
if (sessionId != null) {
|
||||
dataList.add(getSystemNotifyMessage(sessionId.toString()))
|
||||
}
|
||||
listenerOwner.postEvent {
|
||||
it.onReplaceMessageList(dataList)
|
||||
}
|
||||
}
|
||||
|
||||
fun getMessageList(): List<Any> {
|
||||
return dataList
|
||||
}
|
||||
|
||||
fun addMessage(message: Any) {
|
||||
dataList.add(message)
|
||||
listenerOwner.postEvent {
|
||||
it.onAddMessage(message)
|
||||
}
|
||||
}
|
||||
|
||||
fun sendTextMessage(text: String): Single<Any> {
|
||||
imEngineAbility?.let {
|
||||
val textMessage = it.buildTextMessage(text)
|
||||
if (textMessage == null) {
|
||||
return Single.error(IllegalStateException("message is NULL"))
|
||||
} else {
|
||||
return it.sendMessageRx(textMessage).map {
|
||||
addMessage(textMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
return Single.error(IllegalStateException("engine is NULL"))
|
||||
}
|
||||
|
||||
private fun getSystemNotifyMessage(sessionId: String): ChatRoomMessage {
|
||||
val message = ChatRoomMessageBuilder.createTipMessage(
|
||||
sessionId
|
||||
)
|
||||
message.content = ResUtil.getString(
|
||||
R.string.yizhuan_xchat_android_constants_xchatconstants_01
|
||||
)
|
||||
return message
|
||||
}
|
||||
|
||||
override fun onStop(context: RoomContext) {
|
||||
super.onStop(context)
|
||||
listenerOwner.removeAllListener()
|
||||
}
|
||||
|
||||
override fun onReceiveMessage(messages: List<ChatRoomMessage>) {
|
||||
messages.forEach {
|
||||
onReceiveMessage(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onReceiveMessage(message: ChatRoomMessage) {
|
||||
if (message.msgType == MsgTypeEnum.text) {
|
||||
addMessage(message)
|
||||
}
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun onAddMessage(message: Any)
|
||||
|
||||
fun onReplaceMessageList(list: List<Any>)
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package com.chwl.app.game.core
|
||||
|
||||
import com.chwl.core.bean.room.RoomMicBean
|
||||
import com.chwl.core.support.room.RoomAbility
|
||||
import com.chwl.core.support.room.RoomContext
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
class GameQueueAbility : RoomAbility() {
|
||||
|
||||
val queueFlow = MutableStateFlow<List<RoomMicBean>?>(null)
|
||||
|
||||
override fun onStart(context: RoomContext) {
|
||||
super.onStart(context)
|
||||
}
|
||||
|
||||
suspend fun updateQueue(data: List<RoomMicBean>?) {
|
||||
queueFlow.value = data
|
||||
}
|
||||
|
||||
fun findQueueItem(uid: Long): RoomMicBean? {
|
||||
return queueFlow.value?.firstOrNull {
|
||||
it.micUser?.uid == uid
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,237 @@
|
||||
package com.chwl.app.game.core
|
||||
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.alibaba.fastjson2.JSON
|
||||
import com.chwl.app.game.data.GameModel2
|
||||
import com.chwl.app.game.data.bean.GameInfoUiState
|
||||
import com.chwl.app.game.ui.game.GameIntent
|
||||
import com.chwl.core.bean.game.GameResultBean
|
||||
import com.chwl.core.bean.game.GameRoomInfo
|
||||
import com.chwl.core.im.custom.bean.CustomAttachment
|
||||
import com.chwl.core.im.custom.bean.GameForcedEndAttachment
|
||||
import com.chwl.core.im.custom.bean.GameQueueChangedAttachment
|
||||
import com.chwl.core.support.room.RoomAbility
|
||||
import com.chwl.core.support.room.RoomContext
|
||||
import com.chwl.library.common.util.doLog
|
||||
import com.netease.nimlib.sdk.chatroom.model.ChatRoomMessage
|
||||
import com.netease.nimlib.sdk.msg.constant.MsgTypeEnum
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
||||
class GameStateAbility : RoomAbility(), GameIMEngineAbility.Listener {
|
||||
|
||||
companion object {
|
||||
// 匹配中
|
||||
const val STATE_MATCHING = 0
|
||||
|
||||
// 匹配成功
|
||||
const val STATE_MATCH_SUCCESS = 1
|
||||
|
||||
// 游戏结束
|
||||
const val STATE_GAME_END = 2
|
||||
|
||||
// 匹配失败
|
||||
const val STATE_MATCH_FAILED = 3
|
||||
|
||||
// 错误状态
|
||||
const val STATE_ERROR = -1
|
||||
}
|
||||
|
||||
/**
|
||||
* 游戏状态:
|
||||
* 匹配状态(服务端定义):(0:匹配中、1:匹配成功、2:游戏结束、3:匹配失败)
|
||||
*/
|
||||
val gameStateFlow: MutableStateFlow<Int?> = MutableStateFlow(null)
|
||||
|
||||
val gameIconFlow = MutableStateFlow<String?>(null)
|
||||
|
||||
val gameIdFlow = MutableStateFlow<Long?>(null)
|
||||
|
||||
val roomIdFlow = MutableStateFlow<Long?>(null)
|
||||
|
||||
val imIdFlow = MutableStateFlow<Long?>(null)
|
||||
|
||||
val matchRoundIdFlow = MutableStateFlow<Long?>(null)
|
||||
|
||||
val scoresFlow = MutableStateFlow<List<Double>?>(null)
|
||||
|
||||
val gameConfigFlow = MutableStateFlow<String?>(null)
|
||||
|
||||
@Deprecated("里面的属性有可能不是最新的,建议通过具体属性flow获取数据")
|
||||
val gameInfoFlow = MutableStateFlow<GameRoomInfo?>(null)
|
||||
|
||||
val gameInfoUiStateFlow = MutableStateFlow<GameInfoUiState>(GameInfoUiState.Loading)
|
||||
|
||||
val matchFailedFlow = MutableSharedFlow<Boolean>()
|
||||
|
||||
val gameResultFlow = MutableSharedFlow<List<GameResultBean>?>()
|
||||
|
||||
private var viewBackground = false
|
||||
|
||||
private val queueAbility get() = roomContext?.findAbility<GameQueueAbility>(GameQueueAbility::class.java.simpleName)
|
||||
|
||||
override fun onStart(context: RoomContext) {
|
||||
super.onStart(context)
|
||||
requestRoomInfo(true)
|
||||
val imEngineAbility =
|
||||
context.findAbility<GameIMEngineAbility>(GameIMEngineAbility::class.java.simpleName)
|
||||
imEngineAbility?.addListener(this)
|
||||
safeLaunch {
|
||||
imEngineAbility?.reconnectedFlow?.collectLatest {
|
||||
requestSyncState(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
|
||||
super.onStateChanged(source, event)
|
||||
when (event) {
|
||||
Lifecycle.Event.ON_RESUME -> {
|
||||
if (viewBackground) {
|
||||
if (gameStateFlow.value == STATE_MATCHING || gameStateFlow.value == STATE_MATCH_SUCCESS) {
|
||||
requestSyncState(false)
|
||||
}
|
||||
}
|
||||
viewBackground = false
|
||||
}
|
||||
|
||||
Lifecycle.Event.ON_STOP -> {
|
||||
//todo do 暂时注释这个 , 游戏目前没有最小化
|
||||
// viewBackground = true
|
||||
}
|
||||
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 请求同步服务端的最新状态
|
||||
private fun requestSyncState(needLoading: Boolean) {
|
||||
logD("requestSyncState")
|
||||
requestRoomInfo(needLoading)
|
||||
}
|
||||
|
||||
private fun requestRoomInfo(needLoading: Boolean) {
|
||||
logD("requestRoomInfo needLoading:$needLoading")
|
||||
if (needLoading) {
|
||||
gameInfoUiStateFlow.value = GameInfoUiState.Loading
|
||||
}
|
||||
safeLaunch(onError = {
|
||||
logD("requestRoomInfo Failed:$it")
|
||||
gameInfoUiStateFlow.value = GameInfoUiState.Failed(it, gameInfoFlow.value)
|
||||
}) {
|
||||
val info = GameModel2.getGameRoomInfo() //chatRoom/getByType
|
||||
if (info != null) {
|
||||
gameInfoUiStateFlow.value = GameInfoUiState.Success
|
||||
info.data?.matchStatus = 0
|
||||
logD("飞行棋 请求 游戏信息成功 requestRoomInfo Success =${JSON.toJSONString(info)}")
|
||||
syncRoomInfo(info)
|
||||
} else {
|
||||
logD("requestRoomInfo Empty")
|
||||
gameInfoUiStateFlow.value = GameInfoUiState.Empty(gameInfoFlow.value)
|
||||
syncGameState(STATE_ERROR)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun restart(intent: GameIntent) {
|
||||
gameInfoUiStateFlow.value = GameInfoUiState.Loading
|
||||
safeLaunch(onError = {
|
||||
gameInfoUiStateFlow.value = GameInfoUiState.Failed(it, gameInfoFlow.value)
|
||||
}) {
|
||||
GameModel2.startGame(intent.gameId.toString(), intent.gameMode)
|
||||
requestRoomInfo(true)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun syncRoomInfo(info: GameRoomInfo) {
|
||||
"飞行棋 同步房间信息 syncRoomInfo ${info}".doLog()
|
||||
roomContext?.roomId = info.roomId ?: 0
|
||||
|
||||
gameInfoFlow.value = info
|
||||
roomIdFlow.value = info.roomId
|
||||
imIdFlow.value = info.roomId
|
||||
gameIdFlow.value = info.data?.mgId?.toLongOrNull()
|
||||
gameIconFlow.value = info.data?.gameRoomIcon
|
||||
info.data?.scores?.let {
|
||||
scoresFlow.value = it
|
||||
}
|
||||
gameConfigFlow.value = info.data?.configJson
|
||||
matchRoundIdFlow.value = info.data?.matchRoundId
|
||||
|
||||
syncQueue(info)
|
||||
// 最后变更状态
|
||||
syncGameState(info.data?.matchStatus)
|
||||
}
|
||||
|
||||
fun syncGameState(matchStatus: Int?) {
|
||||
gameStateFlow.value = matchStatus
|
||||
}
|
||||
|
||||
private suspend fun syncQueue(info: GameRoomInfo) {
|
||||
"飞行棋 同步麦位信息 syncQueue ${info}".doLog()
|
||||
gameIdFlow?.value = info.data?.mgId?.toLongOrNull()
|
||||
info.data?.scores?.let {
|
||||
scoresFlow.value = it
|
||||
}
|
||||
queueAbility?.updateQueue(info.roomMics)
|
||||
matchRoundIdFlow.value = info.data?.matchRoundId
|
||||
syncGameState(info.data?.matchStatus)
|
||||
}
|
||||
|
||||
override fun onReceiveMessage(messages: List<ChatRoomMessage>) {
|
||||
messages.forEach {
|
||||
if (it.msgType == MsgTypeEnum.custom) {
|
||||
val attachment: CustomAttachment = (it.attachment as? CustomAttachment) ?: return
|
||||
onReceiveCustomMessage(it, attachment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onReceiveCustomMessage(message: ChatRoomMessage, attachment: CustomAttachment) {
|
||||
when (attachment.first) {
|
||||
CustomAttachment.CUSTOM_MSG_MINI_GAME -> {
|
||||
when (attachment.second) {
|
||||
// 麦位变更
|
||||
CustomAttachment.CUSTOM_MSG_MINI_GAME_QUEUE_CHANGED -> {
|
||||
logD("飞行棋 onReceiveMessage 麦位变更")
|
||||
val gameInfo = (attachment as? GameQueueChangedAttachment)?.gameInfo ?: return
|
||||
if (gameInfo.roomId != roomContext?.roomId) {
|
||||
logD("onReceiveMessage 房间变更")
|
||||
safeLaunch {
|
||||
syncRoomInfo(gameInfo)
|
||||
}
|
||||
} else {
|
||||
safeLaunch {
|
||||
syncQueue(gameInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 匹配失败
|
||||
CustomAttachment.CUSTOM_MSG_MINI_GAME_MATCH_FAILED -> {
|
||||
logD("onReceiveMessage 匹配失败")
|
||||
safeLaunch {
|
||||
syncGameState(STATE_MATCH_FAILED)
|
||||
matchFailedFlow.emit(true)
|
||||
}
|
||||
}
|
||||
|
||||
// 提前结束
|
||||
CustomAttachment.CUSTOM_MSG_MINI_GAME_FORCED_END -> {
|
||||
logD("onReceiveMessage 提前结束")
|
||||
val data =
|
||||
(attachment as? GameForcedEndAttachment)?.msgData ?: return
|
||||
val results = data.results
|
||||
safeLaunch {
|
||||
syncGameState(data.matchStatus)
|
||||
gameResultFlow.emit(results)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,226 @@
|
||||
package com.chwl.app.game.core.engine
|
||||
|
||||
import com.chwl.app.game.core.GameQueueAbility
|
||||
import com.chwl.app.game.core.GameStateAbility
|
||||
import com.chwl.core.auth.AuthModel
|
||||
import com.chwl.core.bean.game.GameResultBean
|
||||
import com.chwl.core.bean.game.SudGameConfigBean
|
||||
import com.chwl.core.sud.model.GameViewInfoModel
|
||||
import com.chwl.core.sud.state.SudMGPAPPState
|
||||
import com.chwl.core.sud.state.SudMGPMGState
|
||||
import com.chwl.core.support.room.RoomContext
|
||||
import com.chwl.library.utils.json.JsonUtils
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import org.json.JSONObject
|
||||
import tech.sud.mgp.core.ISudFSMStateHandle
|
||||
|
||||
class GameEngineAbility : SudEngineAbility() {
|
||||
private val stateAbility get() = roomContext?.findAbility<GameStateAbility>(GameStateAbility::class.java.simpleName)
|
||||
private var gameConfig: SudGameConfigBean? = null
|
||||
val gameViewRectFlow = MutableStateFlow<GameViewInfoModel.GameViewRectModel?>(null)
|
||||
|
||||
override fun onStart(context: RoomContext) {
|
||||
super.onStart(context)
|
||||
safeLaunch {
|
||||
stateAbility?.gameConfigFlow?.collectLatest {
|
||||
parseGameConfig(it)
|
||||
}
|
||||
}
|
||||
safeLaunch {
|
||||
stateAbility?.let {
|
||||
combine(
|
||||
*arrayOf(
|
||||
gameViewRectFlow,
|
||||
it.gameIdFlow,
|
||||
it.roomIdFlow,
|
||||
it.gameStateFlow
|
||||
), transform = {
|
||||
true
|
||||
})
|
||||
.collectLatest {
|
||||
checkRunning()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkRunning() {
|
||||
logD("checkRunning() roomIdFlow = ${stateAbility?.roomIdFlow?.value} GameEngineAbility ")
|
||||
if (gameViewRectFlow.value == null) {
|
||||
logD("checkRunning() gameViewRect:null GameEngineAbility ")
|
||||
return
|
||||
}
|
||||
val stateAbility = this.stateAbility
|
||||
val gameState = stateAbility?.gameStateFlow?.value
|
||||
logD("checkRunning() gameState:$gameState GameEngineAbility ")
|
||||
if (gameState == GameStateAbility.STATE_MATCH_SUCCESS) {
|
||||
logD("checkRunning() gameState:$gameState roomView =${roomView?.getActivity()} roomIdFlow=${stateAbility.roomIdFlow.value} gameIdFlow=${stateAbility.gameIdFlow.value} GameEngineAbility -> loadGame()")
|
||||
val activity = roomView?.getActivity() ?: return
|
||||
val roomId = stateAbility.roomIdFlow.value ?: return
|
||||
val gameId = stateAbility.gameIdFlow.value ?: return
|
||||
loadGame(activity, roomId.toString(), gameId)
|
||||
} else {
|
||||
logD("checkRunning() destroyGame GameEngineAbility ")
|
||||
exitGame()
|
||||
destroyGame()
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseGameConfig(json: String?) {
|
||||
if (json != null) {
|
||||
gameConfig = JsonUtils.fromJson(json, SudGameConfigBean::class.java)
|
||||
} else {
|
||||
gameConfig = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun autoPlayGame() {
|
||||
if (!isRunning) {
|
||||
return
|
||||
}
|
||||
val stateAbility = stateAbility ?: return
|
||||
logD("autoPlayGame state:${stateAbility.gameStateFlow.value}")
|
||||
if (stateAbility.gameStateFlow.value != GameStateAbility.STATE_MATCH_SUCCESS) {
|
||||
return
|
||||
}
|
||||
val queueAbility =
|
||||
roomContext?.findAbility<GameQueueAbility>(GameQueueAbility::class.java.simpleName)
|
||||
?: return
|
||||
val queueSize = queueAbility.queueFlow.value?.size ?: 0
|
||||
if (queueSize == 0) {
|
||||
return
|
||||
}
|
||||
logD("autoPlayGame queueSize:$queueSize size:${sudFSMMG.sudFSMMGCache.playerReadySet.size}")
|
||||
if (sudFSMMG.sudFSMMGCache.playerReadySet.size >= queueSize) {
|
||||
val roundId = stateAbility.matchRoundIdFlow.value
|
||||
val jsonObject = JSONObject()
|
||||
jsonObject.put("value0", roundId)
|
||||
val extras = jsonObject.toString()
|
||||
logD("autoPlayGame roundId:${roundId}")
|
||||
sudFSTAPP.notifyAPPCommonSelfPlaying(true, extras, "value0")
|
||||
}
|
||||
}
|
||||
|
||||
private fun autoJoinGame() {
|
||||
if (!isRunning) {
|
||||
return
|
||||
}
|
||||
logD("autoJoinGame")
|
||||
sudFSTAPP.notifyAPPCommonSelfIn(true, -1, true, 1)
|
||||
sudFSTAPP.notifyAPPCommonSelfReady(true)
|
||||
|
||||
|
||||
|
||||
val queueAbility = roomContext?.findAbility<GameQueueAbility>(GameQueueAbility::class.java.simpleName)
|
||||
val firstOrNull = queueAbility?.queueFlow?.value?.firstOrNull { it?.micUser?.uid != AuthModel.get().currentUid }
|
||||
firstOrNull?.micUser?.let { user->
|
||||
if (user.aiLevel != null) {
|
||||
sudFSTAPP.notifyAPPCommonGameAddAIPlayers(listOf(SudMGPAPPState.AIPlayers().apply {
|
||||
userId = user.uid?.toString()
|
||||
name = user.nick
|
||||
avatar = user.avatar
|
||||
level = user.aiLevel?:SudMGPAPPState.AIPlayers.LEVEL_MODERATE
|
||||
// gender
|
||||
}),1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setGameViewRect(rect: GameViewInfoModel.GameViewRectModel) {
|
||||
this.gameViewRectFlow.value = rect
|
||||
}
|
||||
|
||||
override fun getGameViewRect(): GameViewInfoModel.GameViewRectModel {
|
||||
return this.gameViewRectFlow.value ?: super.getGameViewRect()
|
||||
}
|
||||
|
||||
override fun onGameStarted() {
|
||||
super.onGameStarted()
|
||||
logD("onGameStarted")
|
||||
autoJoinGame()
|
||||
autoPlayGame()
|
||||
updateGameSettingSelectConfig()
|
||||
}
|
||||
|
||||
private fun updateGameSettingSelectConfig() {
|
||||
val configStr = gameConfig?.getGameSettingSelectConfigStr()
|
||||
if (!configStr.isNullOrEmpty()) {
|
||||
logD("updateGameSettingSelectConfig() configStr:$configStr")
|
||||
sudFSTAPP.notifyStateChange(SudMGPAPPState.APP_COMMON_GAME_SETTING_SELECT_INFO, configStr)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPlayerMGCommonPlayerReady(
|
||||
handle: ISudFSMStateHandle?,
|
||||
userId: String?,
|
||||
model: SudMGPMGState.MGCommonPlayerReady?
|
||||
) {
|
||||
super.onPlayerMGCommonPlayerReady(handle, userId, model)
|
||||
logD("onPlayerMGCommonPlayerReady userId:$userId model:${model?.isReady}")
|
||||
if (model?.isReady != false) {
|
||||
autoPlayGame()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGameMGCommonGameSettle(
|
||||
handle: ISudFSMStateHandle,
|
||||
model: SudMGPMGState.MGCommonGameSettle?
|
||||
) {
|
||||
super.onGameMGCommonGameSettle(handle, model)
|
||||
val list = ArrayList<GameResultBean>()
|
||||
val queueAbility = roomContext?.findAbility<GameQueueAbility>(GameQueueAbility::class.java.simpleName)
|
||||
val stateAbility = roomContext?.findAbility<GameStateAbility>(GameStateAbility::class.java.simpleName)
|
||||
val scoresList = stateAbility?.scoresFlow?.value
|
||||
model?.results?.sortedBy { it.rank }?.forEachIndexed { index, it ->
|
||||
val uid = it.uid?.toLong() ?: -1
|
||||
val queueItem = queueAbility?.findQueueItem(uid)
|
||||
val scores = scoresList?.getOrNull(index)
|
||||
val item = GameResultBean().apply {
|
||||
this.uid = it.uid.toLongOrNull()
|
||||
this.rank = it.rank
|
||||
this.nick = queueItem?.micUser?.nick
|
||||
this.avatar = queueItem?.micUser?.avatar
|
||||
this.winNum = scores
|
||||
// 目前1v1:第一名成功(后面需要调整这个逻辑)
|
||||
this.isWin = index == 0
|
||||
this.isEscaped = it.isEscaped == 1
|
||||
}
|
||||
list.add(item)
|
||||
}
|
||||
safeLaunch {
|
||||
stateAbility?.gameResultFlow?.emit(list)
|
||||
stateAbility?.syncGameState(GameStateAbility.STATE_GAME_END)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGetGameCfg(handle: ISudFSMStateHandle, p1: String?) {
|
||||
val configStr = gameConfig?.getGameConfigStr()
|
||||
logD("onGetGameCfg() configStr:$configStr")
|
||||
if (configStr.isNullOrEmpty()) {
|
||||
super.onGetGameCfg(handle, p1)
|
||||
} else {
|
||||
handle.success(configStr)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPlayerStateChange(
|
||||
handle: ISudFSMStateHandle?,
|
||||
userId: String?,
|
||||
state: String?,
|
||||
dataJson: String?
|
||||
): Boolean {
|
||||
logD("onPlayerStateChange userId:$userId state:$state dataJson:$dataJson")
|
||||
return super.onPlayerStateChange(handle, userId, state, dataJson)
|
||||
}
|
||||
|
||||
override fun onGameStateChange(
|
||||
handle: ISudFSMStateHandle?,
|
||||
state: String?,
|
||||
dataJson: String?
|
||||
): Boolean {
|
||||
logD("onGameStateChange state:$state dataJson:${dataJson}")
|
||||
return super.onGameStateChange(handle, state, dataJson)
|
||||
}
|
||||
}
|
@@ -0,0 +1,298 @@
|
||||
package com.chwl.app.game.core.engine
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.view.View
|
||||
import android.view.ViewTreeObserver
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.chwl.app.R
|
||||
import com.chwl.app.avroom.game.AppConfig
|
||||
import com.chwl.core.auth.AuthModel
|
||||
import com.chwl.core.room.game.GameModel
|
||||
import com.chwl.core.sud.decorator.SudFSMMGDecorator
|
||||
import com.chwl.core.sud.decorator.SudFSMMGListener
|
||||
import com.chwl.core.sud.decorator.SudFSTAPPDecorator
|
||||
import com.chwl.core.sud.model.GameConfigModel
|
||||
import com.chwl.core.sud.model.GameViewInfoModel
|
||||
import com.chwl.core.sud.state.MGStateResponse
|
||||
import com.chwl.core.sud.state.SudMGPMGState
|
||||
import com.chwl.core.support.room.RoomAbility
|
||||
import com.chwl.core.support.room.RoomContext
|
||||
import com.chwl.library.language.LanguageHelper
|
||||
import com.chwl.library.utils.SingleToastUtil
|
||||
import com.chwl.library.utils.json.JsonUtils
|
||||
import com.example.lib_utils.log.ILog
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import tech.sud.mgp.core.ISudFSMStateHandle
|
||||
import tech.sud.mgp.core.ISudListenerInitSDK
|
||||
import tech.sud.mgp.core.SudMGP
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
open class SudEngineAbility : RoomAbility(), SudFSMMGListener, ILog {
|
||||
|
||||
val gameViewFlow = MutableStateFlow<View?>(null)
|
||||
protected var isRunning = false
|
||||
protected val sudFSMMG = SudFSMMGDecorator().apply {
|
||||
setSudFSMMGListener(this@SudEngineAbility)
|
||||
}
|
||||
protected val sudFSTAPP = SudFSTAPPDecorator()
|
||||
private var roomId: String = ""
|
||||
private var gameId: Long = 0
|
||||
|
||||
fun loadGame(activity: Activity, roomId: String, gameId: Long) {
|
||||
logD("loadGame() roomId:$roomId gameId:$gameId")
|
||||
if (isRunning && roomId == getRoomId() && gameId == getGameId()) {
|
||||
logD("loadGame() 重复")
|
||||
return
|
||||
}
|
||||
if (activity.isFinishing || activity.isDestroyed) {
|
||||
logD("loadGame() activity 状态异常")
|
||||
return
|
||||
}
|
||||
exitGame()
|
||||
destroyGame()
|
||||
this.isRunning = true
|
||||
this.roomId = roomId
|
||||
this.gameId = gameId
|
||||
getGameCode({
|
||||
initSDK(activity, gameId, it)
|
||||
}, {
|
||||
toast(it.message)
|
||||
})
|
||||
}
|
||||
|
||||
// 该用户是否在游戏中
|
||||
fun playerIsPlaying(userId: Long): Boolean {
|
||||
return sudFSMMG.playerIsPlaying(userId.toString())
|
||||
}
|
||||
|
||||
// 返回该玩家是否已加入了游戏
|
||||
fun playerIsIn(userId: Long): Boolean {
|
||||
return sudFSMMG.playerIsIn(userId.toString())
|
||||
}
|
||||
|
||||
// 自己是否已加入了游戏
|
||||
fun isSelfInGame(): Boolean {
|
||||
return playerIsIn(getUserId())
|
||||
}
|
||||
|
||||
fun exitGame() {
|
||||
if (!isRunning) {
|
||||
return
|
||||
}
|
||||
val userId = getUserId()
|
||||
if (playerIsPlaying(userId)) {
|
||||
logD("exitGame() 1")
|
||||
// 用户正在游戏中,先退出本局游戏,再退出游戏
|
||||
sudFSTAPP.notifyAPPCommonSelfPlaying(false, null, null)
|
||||
sudFSTAPP.notifyAPPCommonSelfReady(false)
|
||||
sudFSTAPP.notifyAPPCommonSelfIn(false, -1, true, 1)
|
||||
} else if (sudFSMMG.playerIsReady(userId.toString())) {
|
||||
logD("exitGame() 2")
|
||||
// 用户已加入并且已经准备,先取消准备,再退出游戏
|
||||
sudFSTAPP.notifyAPPCommonSelfReady(false)
|
||||
sudFSTAPP.notifyAPPCommonSelfIn(false, -1, true, 1)
|
||||
} else if (isSelfInGame()) {
|
||||
logD("exitGame() 3")
|
||||
// 用户已加入游戏 退出游戏
|
||||
sudFSTAPP.notifyAPPCommonSelfIn(false, -1, true, 1)
|
||||
}
|
||||
}
|
||||
|
||||
fun destroyGame() {
|
||||
if (!isRunning) {
|
||||
return
|
||||
}
|
||||
logD("destroyGame()")
|
||||
isRunning = false
|
||||
sudFSTAPP.destroyMG()
|
||||
sudFSMMG.destroyMG()
|
||||
gameViewFlow.value = null
|
||||
roomId = ""
|
||||
gameId = 0
|
||||
}
|
||||
|
||||
private fun initSDK(activity: Activity, gameId: Long, code: String) {
|
||||
logD("initSDK isTestEnv() = ${isTestEnv()}")
|
||||
SudMGP.initSDK(
|
||||
activity,
|
||||
getAppId(),
|
||||
getAppKey(),
|
||||
isTestEnv(),
|
||||
object : ISudListenerInitSDK {
|
||||
override fun onSuccess() {
|
||||
loadGameSDK(activity, gameId, code)
|
||||
}
|
||||
|
||||
override fun onFailure(code: Int, errInfo: String) {
|
||||
logD("initSDK onFailure code:$code errInfo:$errInfo")
|
||||
val msg =
|
||||
activity.getString(R.string.game_failed_tips).format("$code-${errInfo}")
|
||||
toast(msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun loadGameSDK(activity: Activity, gameId: Long, code: String) {
|
||||
if (!isRunning) {
|
||||
return
|
||||
}
|
||||
logD("loadGameSDK")
|
||||
sudFSTAPP.destroyMG()
|
||||
sudFSMMG.destroyMG()
|
||||
sudFSMMG.setSudFSMMGListener(this)
|
||||
val iSudFSTAPP =
|
||||
SudMGP.loadMG(activity, getUserId().toString(), getRoomId(), code, gameId, getGameLanguage(), sudFSMMG)
|
||||
sudFSTAPP.setISudFSTAPP(iSudFSTAPP)
|
||||
gameViewFlow.value = iSudFSTAPP.gameView
|
||||
|
||||
}
|
||||
|
||||
private fun getGameCode(onSuccess: (String) -> Unit, onFailed: (Throwable) -> Unit) {
|
||||
addDisposable(
|
||||
GameModel.getGameCode()
|
||||
.subscribe({
|
||||
onSuccess.invoke(it.code)
|
||||
}, {
|
||||
onFailed.invoke(it)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
private fun toast(message: String?) {
|
||||
if (!message.isNullOrEmpty()) {
|
||||
SingleToastUtil.showToast(message)
|
||||
}
|
||||
}
|
||||
|
||||
private fun notifyGameViewInfo(handle: ISudFSMStateHandle, width: Int, height: Int) {
|
||||
val response = GameViewInfoModel()
|
||||
response.ret_code = 0
|
||||
response.view_size.width = width
|
||||
response.view_size.height = height
|
||||
response.view_game_rect = getGameViewRect()
|
||||
handle.success(JsonUtils.toJson(response))
|
||||
}
|
||||
|
||||
override fun onStop(context: RoomContext) {
|
||||
super.onStop(context)
|
||||
logD("onStop")
|
||||
destroyGame()
|
||||
}
|
||||
|
||||
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
|
||||
super.onStateChanged(source, event)
|
||||
logD("onStateChanged event:$event")
|
||||
when (event) {
|
||||
Lifecycle.Event.ON_START -> {
|
||||
sudFSTAPP.startMG()
|
||||
}
|
||||
|
||||
Lifecycle.Event.ON_STOP -> {
|
||||
sudFSTAPP.stopMG()
|
||||
}
|
||||
|
||||
Lifecycle.Event.ON_RESUME -> {
|
||||
sudFSTAPP.playMG()
|
||||
}
|
||||
|
||||
Lifecycle.Event.ON_PAUSE -> {
|
||||
sudFSTAPP.pauseMG()
|
||||
}
|
||||
|
||||
Lifecycle.Event.ON_DESTROY -> {
|
||||
destroyGame()
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGameStarted() {
|
||||
logD("onGameStarted")
|
||||
}
|
||||
|
||||
override fun onGameDestroyed() {
|
||||
logD("onGameDestroyed")
|
||||
}
|
||||
|
||||
override fun onExpireCode(handle: ISudFSMStateHandle, p1: String?) {
|
||||
logD("onExpireCode")
|
||||
getGameCode({
|
||||
handle.success(MGStateResponse.success().toJson())
|
||||
sudFSTAPP.updateCode(it, null)
|
||||
}, {
|
||||
logE(it)
|
||||
})
|
||||
}
|
||||
|
||||
override fun onGetGameViewInfo(handle: ISudFSMStateHandle, p1: String?) {
|
||||
logD("onGetGameViewInfo")
|
||||
val gameLayout = gameViewFlow.value
|
||||
if (gameLayout == null) {
|
||||
handle.failure("gameLayout is NULL")
|
||||
return
|
||||
}
|
||||
val gameViewWidth = gameLayout.measuredWidth
|
||||
val gameViewHeight = gameLayout.measuredHeight
|
||||
if (gameViewWidth > 0 && gameViewHeight > 0) {
|
||||
notifyGameViewInfo(handle, gameViewWidth, gameViewHeight)
|
||||
return
|
||||
}
|
||||
gameLayout.viewTreeObserver.addOnGlobalLayoutListener(object :
|
||||
ViewTreeObserver.OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
gameLayout.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
notifyGameViewInfo(handle, gameLayout.measuredWidth, gameLayout.measuredHeight)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onGetGameCfg(handle: ISudFSMStateHandle, p1: String?) {
|
||||
logD("onGetGameCfg")
|
||||
handle.success(JsonUtils.toJson(GameConfigModel()))
|
||||
}
|
||||
|
||||
override fun onGameMGCommonSelfClickJoinBtn(
|
||||
handle: ISudFSMStateHandle?,
|
||||
model: SudMGPMGState.MGCommonSelfClickJoinBtn?
|
||||
) {
|
||||
logD("onGameMGCommonSelfClickJoinBtn")
|
||||
sudFSTAPP.notifyAPPCommonSelfIn(true, -1, true, 1)
|
||||
}
|
||||
|
||||
private fun getRoomId() = roomId
|
||||
|
||||
private fun getGameId() = gameId
|
||||
|
||||
protected open fun getUserId(): Long = AuthModel.get().currentUid
|
||||
|
||||
protected open fun getAppId() = AppConfig.APP_ID
|
||||
|
||||
protected open fun getAppKey() = AppConfig.APP_KEY
|
||||
|
||||
protected open fun isTestEnv() = AppConfig.isTestEnv
|
||||
|
||||
protected open fun getGameViewRect(): GameViewInfoModel.GameViewRectModel {
|
||||
return GameViewInfoModel.GameViewRectModel()
|
||||
}
|
||||
|
||||
protected open fun getGameLanguage(): String {
|
||||
return when (LanguageHelper.getCurrentLanguageType()) {
|
||||
LanguageHelper.ZH -> {
|
||||
"zh-TW"
|
||||
}
|
||||
|
||||
LanguageHelper.AR -> {
|
||||
"ar-SA"
|
||||
}
|
||||
|
||||
else -> {
|
||||
"en-US"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,99 @@
|
||||
package com.chwl.app.game.data
|
||||
|
||||
import com.chwl.core.base.BaseModel
|
||||
import com.chwl.core.bean.game.GameConfigBean
|
||||
import com.chwl.core.bean.game.GameRoomInfo
|
||||
import com.chwl.core.bean.response.ServiceResult
|
||||
import com.chwl.core.bean.room.BaseRoomInfo
|
||||
import com.chwl.core.pay.PayModel
|
||||
import com.chwl.core.utils.net.RxHelper
|
||||
import com.chwl.core.utils.net.launchRequest
|
||||
import com.chwl.library.net.rxnet.RxNet
|
||||
import io.reactivex.Single
|
||||
import retrofit2.http.Field
|
||||
import retrofit2.http.FormUrlEncoded
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Query
|
||||
|
||||
object GameModel2 : BaseModel() {
|
||||
|
||||
private val api = RxNet.create(Api::class.java)
|
||||
|
||||
suspend fun getHomeGameConfig(): GameConfigBean? =
|
||||
launchRequest {
|
||||
api.getHomeGameConfig()
|
||||
}
|
||||
|
||||
fun getResumeGameRoomInfo(): Single<GameRoomInfo?> {
|
||||
return api.getResumeGameRoom()
|
||||
.compose(RxHelper.handleCommon(RxHelper.NullHandle { return@NullHandle GameRoomInfo() }))
|
||||
.compose(RxHelper.handleSchAndExce())
|
||||
}
|
||||
|
||||
suspend fun getGameRoomInfo(): GameRoomInfo? =
|
||||
launchRequest {
|
||||
api.getChatRoomInfo(BaseRoomInfo.ROOM_TYPE_GAME)
|
||||
}
|
||||
|
||||
suspend fun startGame(gameId: String, gameMode: Int): String? =
|
||||
launchRequest {
|
||||
val result = api.startGame(gameId, gameMode)
|
||||
PayModel.get().refreshWalletInfo(true)
|
||||
result
|
||||
}
|
||||
|
||||
suspend fun closeGame(roomId: Long): String? =
|
||||
launchRequest {
|
||||
api.closeGame(roomId)
|
||||
}
|
||||
|
||||
fun closeGameRx(roomId: Long): Single<String?> {
|
||||
return api.closeGameRx(roomId)
|
||||
.compose(RxHelper.handleBeanData())
|
||||
.compose(RxHelper.handleSchAndExce())
|
||||
}
|
||||
|
||||
private interface Api {
|
||||
/**
|
||||
* 恢复游戏房间信息
|
||||
*/
|
||||
@GET("miniGame/nav/resumeRoom")
|
||||
fun getResumeGameRoom(): Single<ServiceResult<GameRoomInfo>>
|
||||
|
||||
/**
|
||||
* 房间信息
|
||||
*/
|
||||
@GET("chatRoom/getByType")
|
||||
suspend fun getChatRoomInfo(@Query("roomType") roomType: Int): ServiceResult<GameRoomInfo>
|
||||
|
||||
/**
|
||||
* 首页游戏配置
|
||||
*/
|
||||
@GET("miniGame/nav/config")
|
||||
suspend fun getHomeGameConfig(): ServiceResult<GameConfigBean>
|
||||
|
||||
/**
|
||||
* 开始游戏
|
||||
*/
|
||||
@POST("/miniGame/nav/start")
|
||||
@FormUrlEncoded
|
||||
suspend fun startGame(
|
||||
@Field("mgId") mgId: String,
|
||||
@Field("gameMode") gameMode: Int
|
||||
): ServiceResult<String>
|
||||
|
||||
/**
|
||||
* 关闭游戏
|
||||
*/
|
||||
@GET("miniGame/nav/close")
|
||||
suspend fun closeGame(@Query("roomId") roomId: Long): ServiceResult<String>
|
||||
|
||||
/**
|
||||
* 关闭游戏
|
||||
*/
|
||||
@GET("miniGame/nav/close")
|
||||
fun closeGameRx(@Query("roomId") roomId: Long):Single<ServiceResult<String>>
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
package com.chwl.app.game.data.bean
|
||||
|
||||
import com.chwl.core.bean.game.GameRoomInfo
|
||||
|
||||
|
||||
sealed class GameInfoUiState {
|
||||
|
||||
object Loading : GameInfoUiState()
|
||||
|
||||
object Success : GameInfoUiState()
|
||||
|
||||
data class Failed(val throwable: Throwable, val gameInfo: GameRoomInfo?) : GameInfoUiState()
|
||||
|
||||
data class Empty(val gameInfo: GameRoomInfo?) : GameInfoUiState()
|
||||
}
|
@@ -0,0 +1,114 @@
|
||||
package com.chwl.app.game.ui.buy
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.chwl.app.R
|
||||
import com.chwl.app.common.widget.dialog.DialogManager
|
||||
import com.chwl.app.databinding.GameBuyDialogBinding
|
||||
import com.chwl.app.game.ui.game.GameActivity
|
||||
import com.chwl.app.game.ui.game.GameIntent
|
||||
import com.chwl.app.game.ui.home.GameHomeViewModel
|
||||
import com.chwl.app.ui.pay.ChargeActivity
|
||||
import com.chwl.app.ui.webview.DialogWebViewActivity
|
||||
import com.chwl.core.bean.game.GameConfigBean
|
||||
import com.chwl.core.bean.game.GameModeBean
|
||||
import com.chwl.core.utils.net.BalanceNotEnoughExeption
|
||||
import com.chwl.library.utils.ResUtil
|
||||
import com.chwl.library.utils.SingleToastUtil
|
||||
import com.example.lib_utils.ktx.singleClick
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class GameBuyDialog(private val gameConfig: GameConfigBean, private val gameMode: GameModeBean) :
|
||||
BottomSheetDialogFragment() {
|
||||
|
||||
private var binding: GameBuyDialogBinding? = null
|
||||
|
||||
private val viewModel: GameHomeViewModel by activityViewModels()
|
||||
|
||||
private var dialogManager: DialogManager? = null
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
dialogManager = DialogManager(requireContext())
|
||||
initView()
|
||||
initObserver()
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
binding?.tvTitle?.text = context?.getString(R.string.game_ticket_format, gameMode.modeName)
|
||||
binding?.tvCoins?.text = gameMode.ticket?.toString()
|
||||
binding?.ivHelp?.singleClick {
|
||||
DialogWebViewActivity.start(context, gameMode.ruleUrl ?: "", true)
|
||||
}
|
||||
binding?.tvStart?.singleClick {
|
||||
dialogManager?.showProgressDialog(requireContext())
|
||||
viewModel.startGame(gameConfig.mgIdStr ?: "", gameMode.gameMode ?: -1)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initObserver() {
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
viewModel.startGameFlow.collectLatest {
|
||||
dialogManager?.dismissDialog()
|
||||
if (it.isSuccess) {
|
||||
dismissAllowingStateLoss()
|
||||
val intent = GameIntent(gameConfig.mgId ?: 0L, gameMode.gameMode ?: -1)
|
||||
GameActivity.start(requireContext(), intent)
|
||||
} else {
|
||||
if (it.code == BalanceNotEnoughExeption.code) {
|
||||
showBalanceNotEnoughDialog()
|
||||
} else {
|
||||
it.message?.let { msg ->
|
||||
SingleToastUtil.showToast(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showBalanceNotEnoughDialog() {
|
||||
dialogManager?.showOkCancelDialog(
|
||||
ResUtil.getString(R.string.widget_dialog_dialoguihelper_04),
|
||||
ResUtil.getString(R.string.treasure_to_charge)
|
||||
) {
|
||||
ChargeActivity.start(context)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
dialogManager?.dismissDialog()
|
||||
dialogManager = null
|
||||
}
|
||||
|
||||
override fun getTheme(): Int {
|
||||
return R.style.ErbanBottomSheetDialog
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = GameBuyDialogBinding.inflate(LayoutInflater.from(context))
|
||||
return binding?.root
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return super.onCreateDialog(savedInstanceState).apply {
|
||||
this.setCanceledOnTouchOutside(true)
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,153 @@
|
||||
package com.chwl.app.game.ui.game
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.view.isInvisible
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.chwl.app.R
|
||||
import com.chwl.app.common.widget.dialog.DialogManager.OkCancelDialogListener
|
||||
import com.chwl.app.databinding.GameActivityBinding
|
||||
import com.chwl.app.game.core.BaseGameActivity
|
||||
import com.chwl.app.game.core.GameContext
|
||||
import com.chwl.app.game.core.GameStateAbility
|
||||
import com.chwl.app.game.ui.game.widgets.bottom.GameBottomWidget
|
||||
import com.chwl.app.game.ui.game.widgets.message.GameMessageWidget
|
||||
import com.chwl.app.game.ui.game.widgets.queue.GameQueueWidget
|
||||
import com.chwl.app.ui.utils.loadImage
|
||||
import com.chwl.core.sud.model.GameViewInfoModel
|
||||
import com.chwl.core.support.room.RoomView
|
||||
import com.chwl.library.common.util.doLog
|
||||
import com.example.lib_utils.ktx.getColorById
|
||||
import com.example.lib_utils.ktx.singleClick
|
||||
import com.example.lib_utils.log.ILog
|
||||
import com.example.lib_utils.spannable.SpannableTextBuilder
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class GameActivity : BaseGameActivity<GameActivityBinding>(), RoomView, ILog {
|
||||
|
||||
companion object {
|
||||
fun start(context: Context, intent: GameIntent) {
|
||||
context.startActivity(Intent(context, GameActivity::class.java).apply {
|
||||
putExtra("intent", intent)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
val intentData = intent.getSerializableExtra("intent") as? GameIntent
|
||||
if (intentData == null) {
|
||||
toast(R.string.utils_net_beanobserver_05)
|
||||
finish()
|
||||
return
|
||||
}
|
||||
super.init()
|
||||
viewModel.init(intentData)
|
||||
}
|
||||
|
||||
override fun initView() {
|
||||
super.initView()
|
||||
}
|
||||
|
||||
override fun initEvent() {
|
||||
super.initEvent()
|
||||
binding.ivClose.singleClick {
|
||||
onBackPressed()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onGameContext(gameContext: GameContext) {
|
||||
super.onGameContext(gameContext)
|
||||
val stateAbility =
|
||||
gameContext.findAbility<GameStateAbility>(GameStateAbility::class.java.simpleName)
|
||||
?: return
|
||||
lifecycleScope.launch {
|
||||
stateAbility.gameIconFlow.collectLatest {
|
||||
binding.ivLogo.loadImage(it)
|
||||
}
|
||||
}
|
||||
|
||||
lifecycleScope.launch {
|
||||
stateAbility.scoresFlow.collectLatest {
|
||||
"飞行棋 游戏奖励钱币的变化 ${it}".doLog()
|
||||
updateAward(it?.first())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun initGameEngine(gameContext: GameContext) {
|
||||
binding.spaceGameRect.post {
|
||||
val rect = GameViewInfoModel.GameViewRectModel().apply {
|
||||
top = binding.spaceGameRect.top
|
||||
bottom = binding.root.height - binding.spaceGameRect.bottom
|
||||
}
|
||||
logD("initGameEngine() height:${binding.root.height}")
|
||||
logD("initGameEngine() top:${rect.top} bottom:${rect.bottom}")
|
||||
gameEngineAbility?.setGameViewRect(rect)
|
||||
}
|
||||
super.initGameEngine(gameContext)
|
||||
}
|
||||
|
||||
private fun updateAward(number: Double?) {
|
||||
val numberStr = number?.toInt()?.toString() ?: ""
|
||||
if (numberStr.isEmpty()) {
|
||||
binding.layoutAward.isInvisible = true
|
||||
binding.tvAwardTips.isInvisible = true
|
||||
} else {
|
||||
binding.layoutAward.isVisible = true
|
||||
binding.tvAwardTips.isVisible = true
|
||||
binding.tvAwardValue.text = numberStr
|
||||
val awardTips = getString(R.string.game_award_tips_format, numberStr)
|
||||
SpannableTextBuilder(binding.tvAwardTips).appendText(awardTips)
|
||||
.setTextStyle(numberStr, textColor = getColorById(R.color.color_FF6629))
|
||||
.setTextStyle(getString(R.string.game_award_tips_coins_text), textColor = getColorById(R.color.color_FF6629))
|
||||
.apply()
|
||||
}
|
||||
}
|
||||
|
||||
override fun initObserver() {
|
||||
super.initObserver()
|
||||
}
|
||||
|
||||
override fun initWidgets() {
|
||||
super.initWidgets()
|
||||
registerWidget(GameMessageWidget::class.java.simpleName, binding.messageWidget)
|
||||
registerWidget(GameQueueWidget::class.java.simpleName, binding.queueWidget)
|
||||
registerWidget(GameBottomWidget::class.java.simpleName, binding.bottomWidget)
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
// super.onBackPressed()
|
||||
showExitTips()
|
||||
}
|
||||
|
||||
override fun getGameViewGroup(): FrameLayout {
|
||||
return binding.layoutGame
|
||||
}
|
||||
|
||||
private fun showExitTips() {
|
||||
val stateAbility =
|
||||
getRoomContext()?.findAbility<GameStateAbility>(GameStateAbility::class.java.simpleName)
|
||||
val message = if (stateAbility?.gameStateFlow?.value == GameStateAbility.STATE_MATCHING) {
|
||||
getString(R.string.game_exit_tips_matching)
|
||||
} else {
|
||||
getString(R.string.game_exit_tips)
|
||||
}
|
||||
dialogManager.showOkCancelDialog(null,
|
||||
message,
|
||||
getString(R.string.continue_game),
|
||||
getString(R.string.exit_text), false, false, object : OkCancelDialogListener {
|
||||
override fun onOk() {
|
||||
dialogManager.dismissDialog()
|
||||
}
|
||||
|
||||
override fun onCancel() {
|
||||
super.onCancel()
|
||||
loadingDialogManager?.showProgressDialog(this@GameActivity)
|
||||
viewModel.closeRoom()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
package com.chwl.app.game.ui.game
|
||||
|
||||
import androidx.annotation.Keep
|
||||
import java.io.Serializable
|
||||
|
||||
@Keep
|
||||
data class GameIntent(val gameId: Long, val gameMode: Int) : Serializable {
|
||||
}
|
@@ -0,0 +1,49 @@
|
||||
package com.chwl.app.game.ui.game
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.chwl.app.base.BaseViewModel
|
||||
import com.chwl.app.game.core.GameContext
|
||||
import com.chwl.app.game.data.GameModel2
|
||||
import com.chwl.core.bean.response.BeanResult
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
|
||||
class GameViewModel : BaseViewModel() {
|
||||
|
||||
val gameContextLiveData = MutableLiveData<GameContext>()
|
||||
|
||||
val closeRoomFlow = MutableSharedFlow<BeanResult<String?>>()
|
||||
|
||||
private var gameIntent: GameIntent? = null
|
||||
|
||||
fun init(intent: GameIntent) {
|
||||
this.gameIntent = intent
|
||||
val gameContext = GameContext(0)
|
||||
gameContext.performStart()
|
||||
gameContextLiveData.value = gameContext
|
||||
}
|
||||
|
||||
fun getGameIntent(): GameIntent? {
|
||||
return gameIntent
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
gameContextLiveData.value?.performStop()
|
||||
}
|
||||
|
||||
fun closeRoom() {
|
||||
val roomId = gameContextLiveData.value?.roomId
|
||||
if (roomId != null) {
|
||||
safeLaunch(onError = {
|
||||
closeRoomFlow.emit(BeanResult.failed(it))
|
||||
}) {
|
||||
GameModel2.closeGame(roomId)
|
||||
closeRoomFlow.emit(BeanResult.success(null))
|
||||
}
|
||||
} else {
|
||||
safeLaunch {
|
||||
closeRoomFlow.emit(BeanResult.success(null))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,95 @@
|
||||
package com.chwl.app.game.ui.game.input
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.KeyEvent
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import com.chwl.app.R
|
||||
import com.chwl.app.common.widget.dialog.DialogManager
|
||||
import com.chwl.app.databinding.GameMessageInputDialogBinding
|
||||
import com.chwl.app.game.core.GameContext
|
||||
import com.chwl.app.game.core.GameMessageAbility
|
||||
import com.chwl.core.support.room.RoomContext
|
||||
import com.chwl.library.utils.SingleToastUtil
|
||||
import com.chwl.library.utils.keyboard.KeyboardUtil
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class GameMessageInputDialog(private var onSend: ((GameMessageInputDialog, String) -> Unit)?) :
|
||||
BottomSheetDialogFragment() {
|
||||
|
||||
private var binding: GameMessageInputDialogBinding? = null
|
||||
|
||||
private var dialogManager: DialogManager? = null
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
dialogManager = DialogManager(requireContext())
|
||||
initView()
|
||||
initObserver()
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
binding?.inputEdit?.let {
|
||||
KeyboardUtil.showKeyboardInDialog(this.dialog, it)
|
||||
}
|
||||
binding?.inputEdit?.setOnKeyListener { _, keyCode, event ->
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER && event.action == KeyEvent.ACTION_UP) {
|
||||
send()
|
||||
return@setOnKeyListener true
|
||||
}
|
||||
false
|
||||
}
|
||||
binding?.inputSend?.setOnClickListener {
|
||||
send()
|
||||
}
|
||||
}
|
||||
|
||||
private fun send() {
|
||||
val text = binding?.inputEdit?.text?.toString()?.trim()
|
||||
onSend?.invoke(this, text ?: "")
|
||||
}
|
||||
|
||||
private fun initObserver() {
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
dialogManager?.dismissDialog()
|
||||
dialogManager = null
|
||||
}
|
||||
|
||||
override fun getTheme(): Int {
|
||||
return R.style.ErbanBottomSheetDialogDimFalse
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = GameMessageInputDialogBinding.inflate(LayoutInflater.from(context))
|
||||
return binding?.root
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return super.onCreateDialog(savedInstanceState).apply {
|
||||
this.setCanceledOnTouchOutside(true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
onSend = null
|
||||
}
|
||||
}
|
@@ -0,0 +1,74 @@
|
||||
package com.chwl.app.game.ui.game.widgets.bottom
|
||||
|
||||
import android.content.Context
|
||||
import android.text.TextUtils
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import com.chwl.app.R
|
||||
import com.chwl.app.databinding.GameBottomWidgetBinding
|
||||
import com.chwl.app.game.core.GameMessageAbility
|
||||
import com.chwl.app.game.ui.game.input.GameMessageInputDialog
|
||||
import com.chwl.core.support.room.FrameLayoutRoomWidget
|
||||
import com.chwl.library.utils.ResUtil
|
||||
import com.chwl.library.utils.SingleToastUtil
|
||||
|
||||
class GameBottomWidget : FrameLayoutRoomWidget {
|
||||
|
||||
private val binding: GameBottomWidgetBinding =
|
||||
DataBindingUtil.inflate(
|
||||
LayoutInflater.from(
|
||||
context
|
||||
), R.layout.game_bottom_widget, this, true
|
||||
)
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
|
||||
context,
|
||||
attrs,
|
||||
defStyleAttr
|
||||
)
|
||||
|
||||
constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet?,
|
||||
defStyleAttr: Int,
|
||||
defStyleRes: Int
|
||||
) : super(context, attrs, defStyleAttr, defStyleRes)
|
||||
|
||||
init {
|
||||
binding.tvInput.setOnClickListener {
|
||||
roomView?.let {
|
||||
roomContext?.let { context ->
|
||||
GameMessageInputDialog { dialog, text ->
|
||||
sendMessage(dialog, text)
|
||||
}.show(
|
||||
it.getViewFragmentManager(),
|
||||
"MESSAGE_INPUT"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sendMessage(dialog: GameMessageInputDialog, message: String) {
|
||||
if (TextUtils.isEmpty(message)) {
|
||||
SingleToastUtil.showToast(ResUtil.getString(R.string.avroom_fragment_baseroomfragment_08))
|
||||
return
|
||||
}
|
||||
val messageAbility =
|
||||
roomContext?.findAbility<GameMessageAbility>(GameMessageAbility::class.java.simpleName)
|
||||
if (messageAbility == null) {
|
||||
dialog.dismissAllowingStateLoss()
|
||||
SingleToastUtil.showToast(ResUtil.getString(R.string.retryTips))
|
||||
return
|
||||
}
|
||||
addDisposable(messageAbility.sendTextMessage(message).subscribe({
|
||||
dialog.dismissAllowingStateLoss()
|
||||
}, {
|
||||
dialog.dismissAllowingStateLoss()
|
||||
SingleToastUtil.showToast(it.message)
|
||||
}))
|
||||
}
|
||||
}
|
@@ -0,0 +1,224 @@
|
||||
package com.chwl.app.game.ui.game.widgets.message
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.TextUtils
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.text.style.ImageSpan
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.chwl.app.R
|
||||
import com.chwl.app.common.widget.CustomImageSpan
|
||||
import com.chwl.app.ui.utils.ImageLoadUtils
|
||||
import com.chwl.app.ui.widget.TextSpannableBuilder
|
||||
import com.chwl.core.auth.AuthModel
|
||||
import com.chwl.core.level.UserLevelResourceType
|
||||
import com.chwl.core.manager.AvRoomDataManager
|
||||
import com.chwl.core.noble.NobleUtil
|
||||
import com.chwl.core.user.bean.UserInfo
|
||||
import com.chwl.library.common.util.Utils
|
||||
import com.chwl.library.utils.ResUtil
|
||||
import com.chwl.library.utils.SizeUtils
|
||||
import com.example.lib_utils.AppUtils
|
||||
import com.example.lib_utils.UiUtils.isRtl
|
||||
import com.example.lib_utils.ktx.getColorById
|
||||
import com.netease.nim.uikit.business.session.emoji.MoonUtil
|
||||
import com.netease.nim.uikit.common.util.sys.ScreenUtil
|
||||
import com.netease.nimlib.sdk.chatroom.model.ChatRoomMessage
|
||||
import com.netease.nimlib.sdk.msg.constant.MsgTypeEnum
|
||||
|
||||
class GameMessageAdapter(val dataList: MutableList<Any>) :
|
||||
RecyclerView.Adapter<RecyclerView.ViewHolder>(), View.OnClickListener {
|
||||
|
||||
private val itemPaddingHorizontal = Utils.dip2px(AppUtils.getApp(), 11f)
|
||||
private val itemPaddingVertical = Utils.dip2px(AppUtils.getApp(), 6f)
|
||||
private val expLevelHeight = Utils.dip2px(AppUtils.getApp(), 18f)
|
||||
private val badgeWidth = Utils.dip2px(AppUtils.getApp(), 15f)
|
||||
private val badgeHeight = Utils.dip2px(AppUtils.getApp(), 15f)
|
||||
private val greyColor = ContextCompat.getColor(AppUtils.getApp(), R.color.white_transparent_50)
|
||||
private val imageSpanFactory = object : Function1<Drawable, ImageSpan> {
|
||||
override fun invoke(p1: Drawable): ImageSpan {
|
||||
return CustomImageSpan(p1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameMessageNormalViewHolder {
|
||||
return GameMessageNormalViewHolder(
|
||||
LayoutInflater.from(parent.context)
|
||||
.inflate(R.layout.game_message_widget_item_text, parent, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
val item = getItem(position)
|
||||
val viewHolder = holder as GameMessageNormalViewHolder
|
||||
viewHolder.textView.let {
|
||||
it.setLineSpacing(0f, 1f)
|
||||
it.setTextColor(Color.WHITE)
|
||||
it.textSize = 12f
|
||||
it.setOnClickListener(this)
|
||||
it.setOnLongClickListener(null)
|
||||
it.tag = item
|
||||
it.gravity = Gravity.START or Gravity.CENTER_VERTICAL
|
||||
if (isRtl(it.context)) {
|
||||
it.textDirection = View.TEXT_DIRECTION_RTL
|
||||
}
|
||||
resetItemBackground(it)
|
||||
}
|
||||
if (item is ChatRoomMessage) {
|
||||
if (item.msgType == MsgTypeEnum.text) {
|
||||
convertText(viewHolder, item)
|
||||
} else if (item.msgType == MsgTypeEnum.tip) {
|
||||
convertTips(viewHolder, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun convertTips(holder: GameMessageNormalViewHolder, message: ChatRoomMessage) {
|
||||
val textView = holder.textView
|
||||
textView.setTextColor(ContextCompat.getColor(textView.context, R.color.color_white))
|
||||
textView.text = message.content
|
||||
textView.setBackgroundResource(R.drawable.shape_room_message_tip_bg)
|
||||
}
|
||||
|
||||
private fun convertText(holder: GameMessageNormalViewHolder, message: ChatRoomMessage) {
|
||||
val textView = holder.textView
|
||||
val text = TextSpannableBuilder(textView)
|
||||
addUserTag(message, text, textView)
|
||||
val nickName =
|
||||
if (message.fromAccount != null && message.fromAccount == AuthModel.get().currentUid.toString() + "") {
|
||||
ResUtil.getString(R.string.avroom_widget_messageview_0116)
|
||||
} else {
|
||||
NobleUtil.getNamePlate(UserInfo.NICK, message)
|
||||
}
|
||||
text.append(nickName, ForegroundColorSpan(greyColor))
|
||||
text.append(
|
||||
": " + message.content,
|
||||
ForegroundColorSpan(textView.context.getColorById(R.color.white))
|
||||
)
|
||||
MoonUtil.replaceEmoticons(
|
||||
textView.context,
|
||||
text.builder.toString(),
|
||||
text.builder,
|
||||
imageSpanFactory
|
||||
)
|
||||
textView.text = text.build()
|
||||
loadItemBackgroundVip(message, textView)
|
||||
}
|
||||
|
||||
private fun addUserTag(
|
||||
chatRoomMessage: ChatRoomMessage,
|
||||
builder: TextSpannableBuilder,
|
||||
textView: TextView
|
||||
) {
|
||||
val extension = chatRoomMessage.chatRoomMessageExtension
|
||||
val userLevel = NobleUtil.getLevel(UserLevelResourceType.EXPER_URL, chatRoomMessage)
|
||||
val isOfficial = NobleUtil.getIsOfficial(UserInfo.IS_OFFICIAL, chatRoomMessage)
|
||||
val vipIcon = NobleUtil.getResource(UserInfo.VIP_ICON, chatRoomMessage)
|
||||
builder.append(vipIcon, expLevelHeight)
|
||||
.append(
|
||||
if (isOfficial) ResourcesCompat.getDrawable(
|
||||
textView.resources,
|
||||
R.mipmap.ic_user_official_13dp, null
|
||||
) else null,
|
||||
badgeWidth, badgeHeight
|
||||
)
|
||||
.append(getNewUserDrawable(textView.context, chatRoomMessage), badgeWidth, badgeHeight)
|
||||
.append(
|
||||
if (AvRoomDataManager.get()
|
||||
.isSuperAdmin(chatRoomMessage.fromAccount)
|
||||
) ResourcesCompat.getDrawable(
|
||||
textView.resources,
|
||||
R.drawable.ic_room_super_admin, null
|
||||
) else null,
|
||||
SizeUtils.dp2px(textView.context, 23f), expLevelHeight
|
||||
)
|
||||
|
||||
// 官方主播認證
|
||||
val tvOfficialMask =
|
||||
NobleUtil.getLevel(UserInfo.OAC_NAME, chatRoomMessage).trim { it <= ' ' }
|
||||
val ivOfficialMask = NobleUtil.getLevel(UserInfo.OAC_ICON, chatRoomMessage)
|
||||
if (!TextUtils.isEmpty(tvOfficialMask) && !TextUtils.isEmpty(ivOfficialMask) && extension != null) { // extension != null 表示自己
|
||||
builder.appendBgAndContent(ivOfficialMask, tvOfficialMask)
|
||||
} else if (!TextUtils.isEmpty(ivOfficialMask)) {
|
||||
builder.append(ivOfficialMask, SizeUtils.dp2px(textView.context, 62f), expLevelHeight)
|
||||
}
|
||||
//等級
|
||||
builder.append(userLevel, expLevelHeight)
|
||||
//銘牌
|
||||
val tvNamePlate =
|
||||
NobleUtil.getNamePlate(UserInfo.NAMEPLATE_WORD, chatRoomMessage).trim { it <= ' ' }
|
||||
val ivNamePlate = NobleUtil.getNamePlate(UserInfo.NAMEPLATE_PIC, chatRoomMessage)
|
||||
if (!TextUtils.isEmpty(tvNamePlate) && !TextUtils.isEmpty(ivNamePlate)) { // extension != null 表示自己
|
||||
builder.appendBgAndContent(ivNamePlate, tvNamePlate)
|
||||
} else if (!TextUtils.isEmpty(ivNamePlate)) {
|
||||
builder.append(ivNamePlate, expLevelHeight)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNewUserDrawable(context: Context, chatRoomMessage: ChatRoomMessage): Drawable? {
|
||||
val newUser = NobleUtil.getIsNewUser(UserInfo.IS_NEW_USER, chatRoomMessage)
|
||||
val isHelloUser =
|
||||
NobleUtil.getIsNewUser(UserInfo.IS_FROM_SAY_HELLO_CHANNEL, chatRoomMessage)
|
||||
return if (newUser) {
|
||||
ResourcesCompat.getDrawable(
|
||||
context.resources,
|
||||
if (isHelloUser) R.drawable.ic_new_user_hello else R.drawable.ic_new_user,
|
||||
null
|
||||
)
|
||||
} else null
|
||||
}
|
||||
|
||||
private fun resetItemBackground(textView: TextView) {
|
||||
textView.text = ""
|
||||
textView.setBackgroundResource(R.drawable.shape_room_message_bg)
|
||||
textView.setPadding(
|
||||
itemPaddingHorizontal,
|
||||
itemPaddingVertical,
|
||||
itemPaddingHorizontal,
|
||||
itemPaddingVertical
|
||||
)
|
||||
}
|
||||
|
||||
private fun loadItemBackgroundVip(chatRoomMessage: ChatRoomMessage?, view: View) {
|
||||
val androidBubbleUrl = NobleUtil.getResource(UserInfo.BUBBLE_URL_ANDROID, chatRoomMessage)
|
||||
if (TextUtils.isEmpty(androidBubbleUrl)) return
|
||||
view.setPadding(
|
||||
itemPaddingHorizontal,
|
||||
ScreenUtil.dip2px(10f),
|
||||
itemPaddingHorizontal,
|
||||
ScreenUtil.dip2px(10f)
|
||||
)
|
||||
ImageLoadUtils.loadNinePatchBg(view, androidBubbleUrl)
|
||||
}
|
||||
|
||||
fun setDataList(list: List<Any>) {
|
||||
dataList.clear()
|
||||
dataList.addAll(list)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun addData(item: Any) {
|
||||
dataList.add(item)
|
||||
notifyItemInserted(dataList.size)
|
||||
}
|
||||
|
||||
fun getItem(position: Int): Any? {
|
||||
return dataList.get(position)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return dataList.size
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.chwl.app.game.ui.game.widgets.message
|
||||
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.chwl.app.R
|
||||
|
||||
class GameMessageNormalViewHolder(val itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
var textView: TextView = itemView.findViewById(R.id.tv_content)
|
||||
|
||||
}
|
@@ -0,0 +1,121 @@
|
||||
package com.chwl.app.game.ui.game.widgets.message
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.chwl.app.R
|
||||
import com.chwl.app.game.core.GameMessageAbility
|
||||
import com.chwl.app.ui.widget.DividerItemDecoration
|
||||
import com.chwl.app.ui.widget.MyItemAnimator
|
||||
import com.chwl.app.ui.widget.RecyclerViewNoViewpagerScroll
|
||||
import com.chwl.core.support.room.FrameLayoutRoomWidget
|
||||
import com.chwl.core.support.room.RoomContext
|
||||
import com.chwl.core.support.room.RoomView
|
||||
|
||||
class GameMessageWidget : FrameLayoutRoomWidget, GameMessageAbility.Listener {
|
||||
|
||||
private val recyclerView = RecyclerViewNoViewpagerScroll(context)
|
||||
private val adapter = GameMessageAdapter(ArrayList())
|
||||
private var isAtBottom = true
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
|
||||
context,
|
||||
attrs,
|
||||
defStyleAttr
|
||||
)
|
||||
|
||||
constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet?,
|
||||
defStyleAttr: Int,
|
||||
defStyleRes: Int
|
||||
) : super(context, attrs, defStyleAttr, defStyleRes)
|
||||
|
||||
init {
|
||||
initListView()
|
||||
initEvent()
|
||||
}
|
||||
|
||||
private fun initListView() {
|
||||
recyclerView.layoutParams =
|
||||
ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
|
||||
recyclerView.setFadingEdgeLength(60)
|
||||
recyclerView.isVerticalFadingEdgeEnabled = true
|
||||
recyclerView.overScrollMode = OVER_SCROLL_NEVER
|
||||
recyclerView.isHorizontalScrollBarEnabled = false
|
||||
addView(recyclerView)
|
||||
val layoutManger = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
|
||||
recyclerView.layoutManager = layoutManger
|
||||
recyclerView.addItemDecoration(
|
||||
DividerItemDecoration(
|
||||
context,
|
||||
layoutManger.orientation,
|
||||
16,
|
||||
R.color.transparent
|
||||
)
|
||||
)
|
||||
recyclerView.itemAnimator = MyItemAnimator().apply {
|
||||
this.supportsChangeAnimations = false
|
||||
this.addDuration = 0
|
||||
this.changeDuration = 0
|
||||
this.moveDuration = 0
|
||||
this.removeDuration = 0
|
||||
}
|
||||
recyclerView.adapter = adapter
|
||||
}
|
||||
|
||||
private fun initEvent() {
|
||||
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
|
||||
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
super.onScrollStateChanged(recyclerView, newState)
|
||||
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
val lastPosition = layoutManager.findLastVisibleItemPosition()
|
||||
if (lastPosition == RecyclerView.NO_POSITION) {
|
||||
isAtBottom = true
|
||||
} else isAtBottom = lastPosition == adapter.itemCount - 1
|
||||
}
|
||||
}
|
||||
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
if (dy < 0) {
|
||||
isAtBottom = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onInitialize(roomView: RoomView, roomContext: RoomContext) {
|
||||
super.onInitialize(roomView, roomContext)
|
||||
val messageAbility =
|
||||
roomContext.findAbility<GameMessageAbility>(GameMessageAbility::class.java.simpleName)
|
||||
messageAbility?.getMessageList()?.let {
|
||||
adapter.setDataList(it)
|
||||
}
|
||||
messageAbility?.addListener(this)
|
||||
}
|
||||
|
||||
override fun onAddMessage(message: Any) {
|
||||
adapter.addData(message)
|
||||
tryScrollToBottom()
|
||||
}
|
||||
|
||||
override fun onReplaceMessageList(list: List<Any>) {
|
||||
adapter.setDataList(list)
|
||||
tryScrollToBottom()
|
||||
}
|
||||
|
||||
private fun tryScrollToBottom() {
|
||||
logD("tryScrollToBottom isAtBottom:$isAtBottom")
|
||||
if (isAtBottom) {
|
||||
if (adapter.itemCount > 0) {
|
||||
recyclerView.smoothScrollToPosition(adapter.itemCount - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,96 @@
|
||||
package com.chwl.app.game.ui.game.widgets.queue
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import com.chwl.app.R
|
||||
import com.chwl.app.databinding.GameQueueWidgetBinding
|
||||
import com.chwl.app.game.core.GameQueueAbility
|
||||
import com.chwl.app.game.core.GameStateAbility
|
||||
import com.chwl.app.ui.utils.loadAvatar
|
||||
import com.chwl.core.auth.AuthModel
|
||||
import com.chwl.core.bean.room.RoomMicBean
|
||||
import com.chwl.core.support.room.FrameLayoutRoomWidget
|
||||
import com.chwl.core.support.room.RoomContext
|
||||
import com.chwl.core.support.room.RoomView
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
||||
class GameQueueWidget : FrameLayoutRoomWidget {
|
||||
|
||||
private val binding: GameQueueWidgetBinding =
|
||||
DataBindingUtil.inflate(
|
||||
LayoutInflater.from(
|
||||
context
|
||||
), R.layout.game_queue_widget, this, true
|
||||
)
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(
|
||||
context,
|
||||
attrs,
|
||||
defStyleAttr
|
||||
)
|
||||
|
||||
constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet?,
|
||||
defStyleAttr: Int,
|
||||
defStyleRes: Int
|
||||
) : super(context, attrs, defStyleAttr, defStyleRes)
|
||||
|
||||
override fun onInitialize(roomView: RoomView, roomContext: RoomContext) {
|
||||
super.onInitialize(roomView, roomContext)
|
||||
val stateAbility =
|
||||
roomContext.findAbility<GameStateAbility>(GameStateAbility::class.java.simpleName)
|
||||
val queueAbility =
|
||||
roomContext.findAbility<GameQueueAbility>(GameQueueAbility::class.java.simpleName)
|
||||
queueAbility?.let {
|
||||
safeLaunch {
|
||||
it.queueFlow.collectLatest {
|
||||
updateQueue(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
stateAbility?.let {
|
||||
safeLaunch {
|
||||
it.gameStateFlow.collectLatest {
|
||||
updateState(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateQueue(list: List<RoomMicBean>?) {
|
||||
val selfInfo = list?.firstOrNull {
|
||||
it.micUser?.uid == AuthModel.get().currentUid
|
||||
}
|
||||
|
||||
val targetInfo = list?.firstOrNull {
|
||||
it.micUser?.uid != AuthModel.get().currentUid
|
||||
}
|
||||
|
||||
binding.ivQueue1.loadAvatar(selfInfo?.micUser?.avatar)
|
||||
binding.ivQueue2.loadAvatar(targetInfo?.micUser?.avatar)
|
||||
}
|
||||
|
||||
private fun updateState(gameState: Int?) {
|
||||
when (gameState) {
|
||||
GameStateAbility.STATE_MATCH_SUCCESS, GameStateAbility.STATE_GAME_END -> {
|
||||
binding.tvState.setText(R.string.match_successfully)
|
||||
binding.ivState.setImageResource(R.drawable.game_ic_vs)
|
||||
}
|
||||
|
||||
GameStateAbility.STATE_MATCH_FAILED -> {
|
||||
binding.tvState.setText(R.string.match_failed)
|
||||
binding.ivState.setImageResource(R.drawable.game_ic_link)
|
||||
}
|
||||
|
||||
else -> {
|
||||
binding.tvState.setText(R.string.matchmaking)
|
||||
binding.ivState.setImageResource(R.drawable.game_ic_link)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
package com.chwl.app.game.ui.home
|
||||
|
||||
import android.widget.ImageView
|
||||
import com.chad.library.adapter.base.BaseQuickAdapter
|
||||
import com.chad.library.adapter.base.BaseViewHolder
|
||||
import com.chwl.app.R
|
||||
import com.chwl.core.bean.game.GameModeBean
|
||||
import com.chwl.app.ui.utils.load
|
||||
|
||||
class GameHomeAdapter : BaseQuickAdapter<GameModeBean, BaseViewHolder>(R.layout.game_home_item) {
|
||||
override fun convert(helper: BaseViewHolder, item: GameModeBean?) {
|
||||
helper.getView<ImageView>(R.id.iv_game).load(item?.modeIcon)
|
||||
}
|
||||
}
|
@@ -0,0 +1,139 @@
|
||||
package com.chwl.app.game.ui.home
|
||||
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import com.chwl.app.MainTabContentView
|
||||
import com.chwl.app.base.BaseViewBindingFragment
|
||||
import com.chwl.app.databinding.GameHomeFragmentBinding
|
||||
import com.chwl.app.game.ui.buy.GameBuyDialog
|
||||
import com.chwl.app.support.FragmentVisibleStateHelper
|
||||
import com.chwl.app.ui.pay.ChargeActivity
|
||||
import com.chwl.app.ui.utils.loadAvatar
|
||||
import com.chwl.app.ui.webview.CommonWebViewActivity
|
||||
import com.chwl.app.ui.widget.recyclerview.decoration.GridSpacingItemNewDecoration
|
||||
import com.chwl.app.utils.HomeUIManager
|
||||
import com.chwl.core.UriProvider
|
||||
import com.chwl.core.bean.game.GameConfigBean
|
||||
import com.chwl.core.pay.PayModel
|
||||
import com.chwl.core.pay.event.GetWalletInfoEvent
|
||||
import com.chwl.core.pay.event.UpdateWalletInfoEvent
|
||||
import com.chwl.core.user.UserModel
|
||||
import com.chwl.core.user.event.LoginUserInfoUpdateEvent
|
||||
import com.chwl.core.utils.CoreTextUtils
|
||||
import com.chwl.library.utils.FormatUtils
|
||||
import com.example.lib_utils.UiUtils
|
||||
import com.example.lib_utils.ktx.singleClick
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
|
||||
class GameHomeFragment : BaseViewBindingFragment<GameHomeFragmentBinding>(), MainTabContentView {
|
||||
private val adapter = GameHomeAdapter()
|
||||
private val viewModel: GameHomeViewModel by activityViewModels()
|
||||
override fun init() {
|
||||
EventBus.getDefault().register(this)
|
||||
initView()
|
||||
initEvent()
|
||||
initObserver()
|
||||
FragmentVisibleStateHelper(this).start {
|
||||
onVisibleChanged(it)
|
||||
}
|
||||
PayModel.get().refreshWalletInfo(true)
|
||||
refreshWalletInfo()
|
||||
refreshUserInfo()
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
HomeUIManager.setHomeUI(binding.root)
|
||||
binding.recyclerView.addItemDecoration(
|
||||
GridSpacingItemNewDecoration(
|
||||
UiUtils.dip2px(10f),
|
||||
UiUtils.dip2px(18.5f),
|
||||
false
|
||||
)
|
||||
)
|
||||
binding.recyclerView.adapter = adapter
|
||||
}
|
||||
|
||||
private fun initEvent() {
|
||||
binding.layoutWallet.singleClick {
|
||||
ChargeActivity.start(context)
|
||||
}
|
||||
binding.ivRank.singleClick {
|
||||
CommonWebViewActivity.start(requireContext(), UriProvider.getGameRank())
|
||||
}
|
||||
adapter.setOnItemClickListener { _, view, position ->
|
||||
val config = viewModel.gameConfigLiveData.value?.data
|
||||
val gameMode = adapter.getItem(position)
|
||||
if (config == null || gameMode == null) {
|
||||
return@setOnItemClickListener
|
||||
}
|
||||
GameBuyDialog(config, gameMode).show(childFragmentManager, "GAME_BUY")
|
||||
}
|
||||
}
|
||||
|
||||
private fun initObserver() {
|
||||
viewModel.gameConfigLiveData.observe(this) {
|
||||
if (it == null) {
|
||||
return@observe
|
||||
}
|
||||
val configData = it.data
|
||||
if (it.isSuccess && configData != null) {
|
||||
loadGameInfo(configData)
|
||||
} else {
|
||||
toast(it.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onVisibleChanged(isVisible: Boolean) {
|
||||
if (isVisible) {
|
||||
if (viewModel.gameConfigLiveData.value?.isSuccess != true) {
|
||||
viewModel.getGameList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadGameInfo(config: GameConfigBean) {
|
||||
// binding.ivGameLogo.load(config.pic)
|
||||
adapter.setNewData(config.gameModes)
|
||||
}
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onWalletInfoUpdate(event: UpdateWalletInfoEvent?) {
|
||||
if (_binding == null) {
|
||||
return
|
||||
}
|
||||
refreshWalletInfo()
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onGetWalletInfoEvent(event: GetWalletInfoEvent?) {
|
||||
if (_binding == null) {
|
||||
return
|
||||
}
|
||||
refreshWalletInfo()
|
||||
}
|
||||
|
||||
private fun refreshWalletInfo() {
|
||||
binding.tvWalletValue.text =
|
||||
FormatUtils.formatBigInteger(PayModel.get().currentWalletInfo?.diamondNum ?: 0.0)
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onLoginUserInfoUpdateEvent(event: LoginUserInfoUpdateEvent?) {
|
||||
if (_binding == null) {
|
||||
return
|
||||
}
|
||||
refreshUserInfo()
|
||||
}
|
||||
|
||||
private fun refreshUserInfo() {
|
||||
val userInfo = UserModel.get().cacheLoginUserInfo
|
||||
binding.ivAvatar.loadAvatar(if (CoreTextUtils.isEmptyText(userInfo?.newAvatar)) userInfo?.avatar else userInfo?.newAvatar)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
EventBus.getDefault().unregister(this)
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
package com.chwl.app.game.ui.home
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.chwl.app.base.BaseViewModel
|
||||
import com.chwl.app.game.data.GameModel2
|
||||
import com.chwl.core.bean.game.GameConfigBean
|
||||
import com.chwl.core.bean.response.BeanResult
|
||||
import com.chwl.core.pay.PayModel
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
|
||||
class GameHomeViewModel : BaseViewModel() {
|
||||
val gameConfigLiveData = MutableLiveData<BeanResult<GameConfigBean>?>()
|
||||
|
||||
val startGameFlow = MutableSharedFlow<BeanResult<String?>>()
|
||||
|
||||
fun getGameList() {
|
||||
safeLaunch(onError = {
|
||||
gameConfigLiveData.postValue(BeanResult.Companion.failed(it))
|
||||
}) {
|
||||
val configBean = GameModel2.getHomeGameConfig()
|
||||
gameConfigLiveData.postValue(BeanResult.success(configBean))
|
||||
}
|
||||
}
|
||||
|
||||
fun startGame(gameId: String, gameMode: Int) {
|
||||
safeLaunch(onError = {
|
||||
startGameFlow.emit(BeanResult.Companion.failed(it))
|
||||
}) {
|
||||
GameModel2.startGame(gameId, gameMode)
|
||||
startGameFlow.emit(BeanResult.success(null))
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,44 @@
|
||||
package com.chwl.app.game.ui.result
|
||||
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import com.chad.library.adapter.base.BaseQuickAdapter
|
||||
import com.chad.library.adapter.base.BaseViewHolder
|
||||
import com.chwl.app.R
|
||||
import com.chwl.core.bean.game.GameResultBean
|
||||
import com.chwl.app.ui.utils.loadAvatar
|
||||
|
||||
class GameResultAdapter :
|
||||
BaseQuickAdapter<GameResultBean, BaseViewHolder>(R.layout.game_result_item) {
|
||||
override fun convert(helper: BaseViewHolder, item: GameResultBean?) {
|
||||
helper.setText(R.id.tv_name, item?.nick ?: "")
|
||||
helper.setText(R.id.tv_coins, item?.getScoreStr() ?: "0")
|
||||
helper.getView<ImageView>(R.id.iv_user_avatar).loadAvatar(item?.avatar)
|
||||
val rank = item?.rank ?: (helper.bindingAdapterPosition + 1)
|
||||
val rankView = helper.getView<TextView>(R.id.tv_rank)
|
||||
val rootView = helper.getView<View>(R.id.layout_root)
|
||||
val coinsBgView = helper.getView<View>(R.id.iv_coins_bg)
|
||||
val avatarBorderView = helper.getView<View>(R.id.iv_user_avatar_border)
|
||||
if (rank == 1) {
|
||||
avatarBorderView.setBackgroundResource(R.drawable.game_result_avatar_border_top_1)
|
||||
rankView.setBackgroundResource(R.drawable.game_result_ic_top1)
|
||||
rankView.text = ""
|
||||
} else if (rank == 2) {
|
||||
avatarBorderView.setBackgroundResource(R.drawable.game_result_avatar_border_top_2)
|
||||
rankView.setBackgroundResource(R.drawable.game_result_ic_top2)
|
||||
rankView.text = ""
|
||||
} else {
|
||||
avatarBorderView.background = null
|
||||
rankView.background = null
|
||||
rankView.text = "$rank"
|
||||
}
|
||||
if (rank % 2 == 1) {
|
||||
rootView.setBackgroundResource(R.drawable.game_result_item_bg_top1)
|
||||
coinsBgView.setBackgroundResource(R.drawable.game_result_coins_top1)
|
||||
} else {
|
||||
rootView.setBackgroundResource(R.drawable.game_result_item_bg_top2)
|
||||
coinsBgView.setBackgroundResource(R.drawable.game_result_coins_top2)
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,93 @@
|
||||
package com.chwl.app.game.ui.result
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import com.alibaba.fastjson2.JSON
|
||||
import com.chwl.app.R
|
||||
import com.chwl.app.databinding.GameResultDialogBinding
|
||||
import com.chwl.app.ui.widget.recyclerview.decoration.SpacingDecoration
|
||||
import com.chwl.core.auth.AuthModel
|
||||
import com.chwl.core.bean.game.GameResultBean
|
||||
import com.chwl.library.common.util.Utils
|
||||
import com.example.lib_utils.ktx.singleClick
|
||||
import com.example.lib_utils.log.ILog
|
||||
import com.example.lib_utils.log.LogUtil
|
||||
|
||||
class GameResultDialog(
|
||||
private val list: List<GameResultBean>,
|
||||
private var onClose: (() -> Unit)?,
|
||||
private var onRestart: (() -> Unit)?
|
||||
) : DialogFragment(), ILog {
|
||||
|
||||
private var binding: GameResultDialogBinding? = null
|
||||
|
||||
private val adapter = GameResultAdapter()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
initView()
|
||||
adapter.setNewData(list.sortedBy { it.rank })
|
||||
val isWin = list.firstOrNull {
|
||||
it.uid == AuthModel.get().currentUid
|
||||
}?.isWin
|
||||
if (isWin == true) {
|
||||
binding?.ivState?.setImageResource(R.drawable.game_result_bg_win)
|
||||
} else {
|
||||
binding?.ivState?.setImageResource(R.drawable.game_result_bg_fail)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
LogUtil.d(" 游戏结束 弹窗 ${JSON.toJSONString(list)}")
|
||||
binding?.tvClose?.singleClick {
|
||||
onClose?.invoke()
|
||||
}
|
||||
binding?.tvRestart?.singleClick {
|
||||
onRestart?.invoke()
|
||||
}
|
||||
binding?.recyclerView?.addItemDecoration(
|
||||
SpacingDecoration(
|
||||
0,
|
||||
Utils.dip2px(context, 10f),
|
||||
false
|
||||
)
|
||||
)
|
||||
binding?.recyclerView?.adapter = adapter
|
||||
}
|
||||
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
onClose = null
|
||||
onRestart = null
|
||||
}
|
||||
|
||||
override fun getTheme(): Int {
|
||||
return R.style.full_screen_dialog
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
binding = GameResultDialogBinding.inflate(LayoutInflater.from(context))
|
||||
return binding?.root
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return super.onCreateDialog(savedInstanceState).apply {
|
||||
this.setCancelable(false)
|
||||
this.setCanceledOnTouchOutside(false)
|
||||
window?.setLayout(
|
||||
WindowManager.LayoutParams.MATCH_PARENT,
|
||||
WindowManager.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 310 KiB |
After Width: | Height: | Size: 330 KiB |
BIN
app/src/module_game/res/drawable-xxhdpi/game_bg.webp
Normal file
After Width: | Height: | Size: 124 KiB |
BIN
app/src/module_game/res/drawable-xxhdpi/game_buy_bg.webp
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
app/src/module_game/res/drawable-xxhdpi/game_buy_bg_body.webp
Normal file
After Width: | Height: | Size: 4.3 KiB |
BIN
app/src/module_game/res/drawable-xxhdpi/game_buy_btn.webp
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
app/src/module_game/res/drawable-xxhdpi/game_buy_ic_help.webp
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
app/src/module_game/res/drawable-xxhdpi/game_home_ic_add.webp
Normal file
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 2.1 KiB |
BIN
app/src/module_game/res/drawable-xxhdpi/game_home_ic_rank.webp
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
app/src/module_game/res/drawable-xxhdpi/game_ic_close.webp
Normal file
After Width: | Height: | Size: 856 B |
BIN
app/src/module_game/res/drawable-xxhdpi/game_ic_coins.webp
Normal file
After Width: | Height: | Size: 8.1 KiB |
BIN
app/src/module_game/res/drawable-xxhdpi/game_ic_link.webp
Normal file
After Width: | Height: | Size: 30 KiB |
After Width: | Height: | Size: 14 KiB |
BIN
app/src/module_game/res/drawable-xxhdpi/game_ic_vs.webp
Normal file
After Width: | Height: | Size: 76 KiB |
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 6.3 KiB |
BIN
app/src/module_game/res/drawable-xxhdpi/game_result_bg_fail.webp
Normal file
After Width: | Height: | Size: 308 KiB |
After Width: | Height: | Size: 6.2 KiB |
BIN
app/src/module_game/res/drawable-xxhdpi/game_result_bg_win.webp
Normal file
After Width: | Height: | Size: 331 KiB |
BIN
app/src/module_game/res/drawable-xxhdpi/game_result_ic_top1.webp
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
app/src/module_game/res/drawable-xxhdpi/game_result_ic_top2.webp
Normal file
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 420 KiB |
After Width: | Height: | Size: 437 KiB |
9
app/src/module_game/res/drawable/game_bg_award.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<corners android:radius="@dimen/dp_25" />
|
||||
<solid android:color="#66006263" />
|
||||
<stroke
|
||||
android:width="@dimen/dp_0_5"
|
||||
android:color="#36C0C8" />
|
||||
</shape>
|
12
app/src/module_game/res/drawable/game_bg_bottom.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<solid android:color="#33ffffff" />
|
||||
<corners
|
||||
android:bottomLeftRadius="0dp"
|
||||
android:bottomRightRadius="0dp"
|
||||
android:topLeftRadius="@dimen/dp_23"
|
||||
android:topRightRadius="@dimen/dp_23" />
|
||||
|
||||
</shape>
|
9
app/src/module_game/res/drawable/game_home_bg.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<gradient
|
||||
android:angle="270"
|
||||
android:centerColor="#BEFFEB"
|
||||
android:endColor="#F9FFFD"
|
||||
android:startColor="#FAFEFD" />
|
||||
</shape>
|
9
app/src/module_game/res/drawable/game_home_bg_wallet.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<corners android:radius="@dimen/dp_18" />
|
||||
<solid android:color="#FAF6D3" />
|
||||
<stroke
|
||||
android:width="@dimen/dp_0_5"
|
||||
android:color="#FFE358" />
|
||||
</shape>
|
5
app/src/module_game/res/drawable/game_result_bg_1.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#1ADCE5" />
|
||||
<corners android:radius="@dimen/dp_16" />
|
||||
</shape>
|
5
app/src/module_game/res/drawable/game_result_bg_2.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#E2F6FF" />
|
||||
<corners android:radius="@dimen/dp_12" />
|
||||
</shape>
|
10
app/src/module_game/res/drawable/game_result_coins_top1.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<corners
|
||||
android:radius="@dimen/dp_5"/>
|
||||
<solid android:color="#9B4300" />
|
||||
<stroke
|
||||
android:width="@dimen/dp_1"
|
||||
android:color="#FFBC1E" />
|
||||
</shape>
|
10
app/src/module_game/res/drawable/game_result_coins_top2.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<corners
|
||||
android:radius="@dimen/dp_5"/>
|
||||
<solid android:color="#00666B" />
|
||||
<stroke
|
||||
android:width="@dimen/dp_1"
|
||||
android:color="#FCFF1E" />
|
||||
</shape>
|
145
app/src/module_game/res/layout/game_activity.xml
Normal file
@@ -0,0 +1,145 @@
|
||||
<?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="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_bg"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/game_bg" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/layout_game"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<Space
|
||||
android:id="@+id/space_title_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_44"
|
||||
android:layout_marginTop="@dimen/dp_44"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_logo"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="@dimen/dp_11"
|
||||
app:layout_constraintBottom_toBottomOf="@id/space_title_bar"
|
||||
app:layout_constraintDimensionRatio="90:80"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/space_title_bar"
|
||||
app:layout_constraintWidth_percent="0.24" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_close"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/dp_21"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/game_ic_close"
|
||||
app:layout_constraintBottom_toBottomOf="@id/space_title_bar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/space_title_bar" />
|
||||
|
||||
<com.chwl.app.game.ui.game.widgets.queue.GameQueueWidget
|
||||
tools:ignore="Instantiatable"
|
||||
android:id="@+id/queue_widget"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/dp_13"
|
||||
app:layout_constraintTop_toTopOf="@id/space_title_bar" />
|
||||
|
||||
<Space
|
||||
android:id="@+id/space_award_tips_top"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/dp_3"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/queue_widget" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_award_tips"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/dp_14"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/space_award_tips_top"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:text="@string/game_award_tips_format"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/layout_award"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dp_21"
|
||||
android:layout_marginTop="@dimen/dp_5"
|
||||
android:visibility="invisible"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tv_award_tips"
|
||||
tools:visibility="visible">
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/dp_25"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:background="@drawable/game_bg_award">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_award_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/dp_25"
|
||||
android:layout_gravity="start"
|
||||
android:layout_marginStart="@dimen/dp_21"
|
||||
android:gravity="center|center_vertical"
|
||||
android:includeFontPadding="false"
|
||||
android:minWidth="@dimen/dp_56"
|
||||
android:paddingStart="@dimen/dp_10"
|
||||
android:paddingEnd="@dimen/dp_10"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/dp_17"
|
||||
tools:text="0" />
|
||||
</FrameLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_award"
|
||||
android:layout_width="@dimen/dp_26"
|
||||
android:layout_height="@dimen/dp_26"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:src="@drawable/ic_coin_84" />
|
||||
</FrameLayout>
|
||||
|
||||
<com.chwl.app.game.ui.game.widgets.message.GameMessageWidget
|
||||
tools:ignore="Instantiatable"
|
||||
android:id="@+id/message_widget"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottom_widget"
|
||||
app:layout_constraintDimensionRatio="283:129"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.277"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintWidth_percent="0.75" />
|
||||
|
||||
<com.chwl.app.game.ui.game.widgets.bottom.GameBottomWidget
|
||||
tools:ignore="Instantiatable"
|
||||
android:id="@+id/bottom_widget"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
|
||||
<Space
|
||||
android:id="@+id/space_game_rect"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/message_widget"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/layout_award" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
29
app/src/module_game/res/layout/game_bottom_widget.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:id="@+id/layout_root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_61"
|
||||
android:background="@drawable/game_bg_bottom">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_input"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_28"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginHorizontal="@dimen/dp_39"
|
||||
android:background="@drawable/base_shape_190b032d_14dp"
|
||||
android:gravity="center_vertical|start"
|
||||
android:paddingHorizontal="@dimen/dp_21"
|
||||
android:text="@string/room_say_something"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/dp_10"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</layout>
|
111
app/src/module_game/res/layout/game_buy_dialog.xml
Normal file
@@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/game_buy_bg"
|
||||
app:layout_constraintDimensionRatio="375:252"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:includeFontPadding="false"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/dp_18"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.13"
|
||||
tools:text="@string/game_ticket_format" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_help"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/dp_21"
|
||||
android:src="@drawable/game_buy_ic_help"
|
||||
app:layout_constraintBottom_toBottomOf="@id/tv_title"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_title" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_body"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:src="@drawable/game_buy_bg_body"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="348.5:162.5"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.77"
|
||||
app:layout_constraintWidth_percent="0.929" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_coins_bg"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:background="@drawable/base_shape_00757b_8dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="297:44"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintWidth_percent="0.792" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_coins"
|
||||
android:layout_width="@dimen/dp_22"
|
||||
android:layout_height="@dimen/dp_22"
|
||||
android:layout_marginEnd="@dimen/dp_6"
|
||||
android:src="@drawable/ic_coin_84"
|
||||
app:layout_constraintBottom_toBottomOf="@id/iv_coins_bg"
|
||||
app:layout_constraintEnd_toStartOf="@id/tv_coins"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/iv_coins_bg" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_coins"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="#F2EF25"
|
||||
android:textSize="@dimen/dp_18"
|
||||
app:layout_constraintBottom_toBottomOf="@id/iv_coins_bg"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_coins"
|
||||
app:layout_constraintTop_toTopOf="@id/iv_coins_bg"
|
||||
tools:text="0" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_start"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/game_buy_btn"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:paddingHorizontal="@dimen/dp_5"
|
||||
android:text="@string/start"
|
||||
android:textColor="#A75906"
|
||||
android:textSize="@dimen/dp_20"
|
||||
app:autoSizeMaxTextSize="@dimen/dp_20"
|
||||
app:autoSizeMinTextSize="@dimen/dp_12"
|
||||
app:autoSizeStepGranularity="1px"
|
||||
app:autoSizeTextType="uniform"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.809" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</layout>
|
150
app/src/module_game/res/layout/game_home_fragment.xml
Normal file
@@ -0,0 +1,150 @@
|
||||
<?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:id="@+id/homeRootView"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/color_FCF4DF">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/homeTopImg"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true"
|
||||
android:src="@drawable/bg_theme_top_home"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
|
||||
<Space
|
||||
android:id="@+id/space_title_bar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_44"
|
||||
android:layout_marginTop="@dimen/dp_44"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/iv_avatar"
|
||||
android:layout_width="@dimen/dp_34"
|
||||
android:layout_height="@dimen/dp_34"
|
||||
android:layout_marginStart="@dimen/dp_20"
|
||||
android:src="@drawable/default_avatar"
|
||||
app:layout_constraintBottom_toBottomOf="@id/space_title_bar"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/space_title_bar"
|
||||
app:shapeAppearance="@style/shape_circle" />
|
||||
|
||||
<!-- <ImageView-->
|
||||
<!-- android:id="@+id/iv_avatar_border"-->
|
||||
<!-- android:layout_width="@dimen/dp_37"-->
|
||||
<!-- android:layout_height="@dimen/dp_37"-->
|
||||
<!-- android:src="@drawable/game_home_ic_avatar_border"-->
|
||||
<!-- app:layout_constraintBottom_toBottomOf="@id/iv_avatar"-->
|
||||
<!-- app:layout_constraintEnd_toEndOf="@id/iv_avatar"-->
|
||||
<!-- app:layout_constraintStart_toStartOf="@id/iv_avatar"-->
|
||||
<!-- app:layout_constraintTop_toTopOf="@id/iv_avatar" />-->
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_wallet"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/dp_36"
|
||||
android:layout_marginStart="@dimen/dp_13"
|
||||
android:background="@drawable/game_home_bg_wallet"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingEnd="@dimen/dp_41"
|
||||
app:layout_constraintBottom_toBottomOf="@id/space_title_bar"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_avatar"
|
||||
app:layout_constraintTop_toTopOf="@id/space_title_bar">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_wallet_icon"
|
||||
android:layout_width="@dimen/dp_22"
|
||||
android:layout_height="@dimen/dp_22"
|
||||
android:layout_marginStart="@dimen/dp_6"
|
||||
android:src="@drawable/ic_coin_84" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_wallet_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dp_2"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:text=" "
|
||||
android:textColor="#273132"
|
||||
android:textSize="@dimen/dp_17"
|
||||
tools:text="123" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="@dimen/dp_37"
|
||||
android:layout_height="@dimen/dp_37"
|
||||
android:src="@drawable/game_home_ic_add"
|
||||
app:layout_constraintBottom_toBottomOf="@id/layout_wallet"
|
||||
app:layout_constraintEnd_toEndOf="@id/layout_wallet"
|
||||
app:layout_constraintTop_toTopOf="@id/layout_wallet" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_rank"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/dp_44"
|
||||
android:layout_marginEnd="@dimen/dp_21"
|
||||
android:src="@drawable/game_home_ic_rank"
|
||||
app:layout_constraintBottom_toBottomOf="@id/space_title_bar"
|
||||
app:layout_constraintEnd_toEndOf="@id/space_title_bar"
|
||||
app:layout_constraintTop_toTopOf="@id/space_title_bar" />
|
||||
|
||||
<!-- <TextView-->
|
||||
<!-- android:id="@+id/tv_rank"-->
|
||||
<!-- android:layout_width="0dp"-->
|
||||
<!-- android:layout_height="wrap_content"-->
|
||||
<!-- android:layout_marginHorizontal="@dimen/dp_3"-->
|
||||
<!-- android:gravity="center"-->
|
||||
<!-- android:includeFontPadding="false"-->
|
||||
<!-- android:maxLines="1"-->
|
||||
<!-- android:text="Rank"-->
|
||||
<!-- android:textColor="#FFEB6F"-->
|
||||
<!-- android:textSize="@dimen/dp_9"-->
|
||||
<!-- app:autoSizeMaxTextSize="@dimen/dp_9"-->
|
||||
<!-- app:autoSizeMinTextSize="@dimen/dp_6"-->
|
||||
<!-- app:autoSizeStepGranularity="1px"-->
|
||||
<!-- app:autoSizeTextType="uniform"-->
|
||||
<!-- app:layout_constraintBottom_toBottomOf="@id/iv_rank"-->
|
||||
<!-- app:layout_constraintEnd_toEndOf="@id/iv_rank"-->
|
||||
<!-- app:layout_constraintStart_toStartOf="@id/iv_rank" />-->
|
||||
|
||||
<androidx.constraintlayout.widget.Guideline
|
||||
android:id="@+id/line_logo_top"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_percent="0.184" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_game_logo"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ludo_game_logo"
|
||||
app:layout_constraintDimensionRatio="187:139"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/line_logo_top"
|
||||
app:layout_constraintWidth_percent="0.377" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="@dimen/dp_10"
|
||||
android:orientation="vertical"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/iv_game_logo"
|
||||
app:layout_constraintVertical_bias="1.0"
|
||||
app:spanCount="2"
|
||||
tools:layout_editor_absoluteX="-23dp"
|
||||
tools:listitem="@layout/game_home_item" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
17
app/src/module_game/res/layout/game_home_item.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?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">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_game"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/default_cover"
|
||||
app:layout_constraintDimensionRatio="177.5:247"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
39
app/src/module_game/res/layout/game_message_input_dialog.xml
Normal file
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout>
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:background="@android:color/white"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/input_edit"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:background="@android:color/white"
|
||||
android:hint="@string/room_say_here"
|
||||
android:imeOptions="actionSend"
|
||||
android:inputType="text"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingEnd="0dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:textColor="#888889"
|
||||
android:textColorHint="#c8c8c8"
|
||||
android:textSize="12sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/input_send"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="10dp"
|
||||
android:background="@drawable/click_white_gray_selector"
|
||||
android:scaleType="center"
|
||||
android:src="@android:drawable/ic_menu_send" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</layout>
|
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
tools:background="#ff0">
|
||||
|
||||
<com.chwl.app.avroom.widget.FixRoomTitleTextView
|
||||
android:id="@+id/tv_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:background="@drawable/shape_room_message_bg"
|
||||
android:gravity="start|center_vertical"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="12dp"
|
||||
tools:text="@string/layout_list_item_chatrrom_msg_01"
|
||||
tools:textColor="#000" />
|
||||
|
||||
</FrameLayout>
|
82
app/src/module_game/res/layout/game_queue_widget.xml
Normal file
@@ -0,0 +1,82 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout>
|
||||
|
||||
<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">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_state"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/dp_18"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="@string/matchmaking" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_state"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/game_ic_link"
|
||||
app:layout_constraintBottom_toBottomOf="@id/iv_queue1_border"
|
||||
app:layout_constraintEnd_toStartOf="@id/iv_queue2_border"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_queue1_border"
|
||||
app:layout_constraintTop_toTopOf="@id/iv_queue1_border"
|
||||
app:layout_constraintWidth_percent="0.242"
|
||||
tools:src="@drawable/game_ic_vs" />
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/iv_queue1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_margin="@dimen/dp_2"
|
||||
android:src="@drawable/default_avatar"
|
||||
app:layout_constraintBottom_toBottomOf="@id/iv_queue1_border"
|
||||
app:layout_constraintEnd_toEndOf="@id/iv_queue1_border"
|
||||
app:layout_constraintStart_toStartOf="@id/iv_queue1_border"
|
||||
app:layout_constraintTop_toTopOf="@id/iv_queue1_border" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_queue1_border"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="@dimen/dp_26"
|
||||
android:src="@drawable/game_ic_queue_avatar_border"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.311"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintWidth_percent="0.152" />
|
||||
|
||||
<com.google.android.material.imageview.ShapeableImageView
|
||||
android:id="@+id/iv_queue2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_margin="@dimen/dp_2"
|
||||
android:src="@drawable/default_avatar"
|
||||
app:layout_constraintBottom_toBottomOf="@id/iv_queue2_border"
|
||||
app:layout_constraintEnd_toEndOf="@id/iv_queue2_border"
|
||||
app:layout_constraintStart_toStartOf="@id/iv_queue2_border"
|
||||
app:layout_constraintTop_toTopOf="@id/iv_queue2_border" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_queue2_border"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:src="@drawable/game_ic_queue_avatar_border"
|
||||
app:layout_constraintDimensionRatio="1:1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.688"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/iv_queue1_border"
|
||||
app:layout_constraintWidth_percent="0.152" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</layout>
|
96
app/src/module_game/res/layout/game_result_dialog.xml
Normal file
@@ -0,0 +1,96 @@
|
||||
<?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="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/layout_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/game_result_bg_1"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.47"
|
||||
app:layout_constraintWidth_percent="0.832">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/dp_7"
|
||||
android:layout_marginTop="@dimen/dp_41"
|
||||
android:layout_marginBottom="@dimen/dp_8"
|
||||
android:background="@drawable/game_result_bg_2">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginHorizontal="@dimen/dp_13"
|
||||
android:layout_marginTop="@dimen/dp_15"
|
||||
android:maxHeight="@dimen/dp_150"
|
||||
android:minHeight="@dimen/dp_120"
|
||||
android:orientation="vertical"
|
||||
android:scrollbars="none"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:layout_height="@dimen/dp_120" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_close"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="@dimen/dp_14"
|
||||
android:layout_marginTop="@dimen/dp_30"
|
||||
android:layout_marginBottom="@dimen/dp_20"
|
||||
android:background="@drawable/game_result_bg_close"
|
||||
android:gravity="center"
|
||||
android:text="@string/close"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/dp_18"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintDimensionRatio="132:40"
|
||||
app:layout_constraintEnd_toStartOf="@id/tv_restart"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/recycler_view" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_restart"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="@dimen/dp_6"
|
||||
android:layout_marginEnd="@dimen/dp_14"
|
||||
android:background="@drawable/game_result_bg_restart"
|
||||
android:gravity="center"
|
||||
android:text="@string/game_rematch"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/dp_18"
|
||||
app:layout_constraintDimensionRatio="132:40"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/tv_close"
|
||||
app:layout_constraintTop_toTopOf="@id/tv_close" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<androidx.legacy.widget.Space
|
||||
android:id="@+id/space_state_bottom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintDimensionRatio="375:85"
|
||||
app:layout_constraintTop_toTopOf="@id/layout_content" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_state"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:src="@drawable/game_result_bg_win"
|
||||
app:layout_constraintBottom_toBottomOf="@id/space_state_bottom"
|
||||
app:layout_constraintDimensionRatio="375:317" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
107
app/src/module_game/res/layout/game_result_item.xml
Normal file
@@ -0,0 +1,107 @@
|
||||
<?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:id="@+id/layout_root"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/dp_60"
|
||||
android:background="@drawable/game_result_item_bg_top1">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_rank"
|
||||
android:layout_width="@dimen/dp_42"
|
||||
android:layout_height="@dimen/dp_42"
|
||||
android:layout_marginStart="@dimen/dp_7"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/dp_20"
|
||||
android:textStyle="bold"
|
||||
app:autoSizeMaxTextSize="@dimen/dp_20"
|
||||
app:autoSizeMinTextSize="@dimen/dp_12"
|
||||
app:autoSizeStepGranularity="1px"
|
||||
app:autoSizeTextType="uniform"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="1" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_user_avatar"
|
||||
android:layout_width="@dimen/dp_46"
|
||||
android:layout_height="@dimen/dp_46"
|
||||
android:layout_marginStart="@dimen/dp_5"
|
||||
android:src="@drawable/default_avatar"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@id/tv_rank"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_user_avatar_border"
|
||||
android:layout_width="@dimen/dp_49"
|
||||
android:layout_height="@dimen/dp_49"
|
||||
android:layout_marginStart="@dimen/dp_2"
|
||||
app:layout_constraintBottom_toBottomOf="@id/iv_user_avatar"
|
||||
app:layout_constraintEnd_toEndOf="@id/iv_user_avatar"
|
||||
app:layout_constraintStart_toStartOf="@id/iv_user_avatar"
|
||||
app:layout_constraintTop_toTopOf="@id/iv_user_avatar"
|
||||
tools:src="@drawable/game_result_avatar_border_top_1" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_coins_bg"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="@dimen/dp_22"
|
||||
android:layout_marginStart="@dimen/dp_9"
|
||||
android:background="@drawable/game_result_coins_top1"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="@id/tv_coins"
|
||||
app:layout_constraintStart_toStartOf="@id/iv_coins"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_coins"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/dp_10"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:maxLines="1"
|
||||
android:minWidth="@dimen/dp_48"
|
||||
android:paddingStart="@dimen/dp_5"
|
||||
android:paddingEnd="@dimen/dp_6"
|
||||
android:text="0"
|
||||
android:textColor="#FFE829"
|
||||
android:textSize="@dimen/dp_15"
|
||||
app:autoSizeMaxTextSize="@dimen/dp_15"
|
||||
app:autoSizeMinTextSize="@dimen/dp_10"
|
||||
app:autoSizeStepGranularity="1px"
|
||||
app:autoSizeTextType="uniform"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_coins"
|
||||
android:layout_width="@dimen/dp_24"
|
||||
android:layout_height="@dimen/dp_24"
|
||||
android:src="@drawable/ic_coin_84"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/tv_coins"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_name"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dp_8"
|
||||
android:layout_marginEnd="@dimen/dp_3"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="@dimen/dp_14"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/iv_coins"
|
||||
app:layout_constraintStart_toEndOf="@id/iv_user_avatar"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:text="Name" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
17
app/src/module_game/res/values-ar/strings.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="matchmaking">جاري المطابقة</string>
|
||||
<string name="match_successfully">تمت المطابقة</string>
|
||||
<string name="match_failed">فشلت المطابقة</string>
|
||||
<string name="game_award_tips_coins_text">عملة ذهبية</string>
|
||||
<string name="game_award_tips_format">الفوز بمكافأة %s عملة ذهبية</string>
|
||||
<string name="start">ابدأ</string>
|
||||
<string name="game_ticket_format">(%s)رسوم الدخول</string>
|
||||
<string name="game_exit_tips">لقد بدأت اللعبة. سيؤدي الخروج من الغرفة إلى فشل اللعبة. هل أنت متأكد من الخروج من الغرفة؟</string>
|
||||
<string name="game_exit_tips_matching">جاري المطابقة، الخروج من الغرفة ،نهاية المطابقة. هل تريد الخروج؟</string>
|
||||
<string name="game_match_failed">فشلت المطابقة، هل تريد المحاولة مرة أخرى؟</string>
|
||||
<string name="game_rematch">إعادة المطابقة</string>
|
||||
<string name="resume_game_tips">جاري التحميل، هل تريد العودة إلى الغرفة؟</string>
|
||||
<string name="game_end_tips">انتهت اللعبة~</string>
|
||||
<string name="continue_game">متابعة اللعبة</string>
|
||||
</resources>
|
19
app/src/module_game/res/values-tr/strings.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="matchmaking">Eşleştirme</string>
|
||||
<string name="match_successfully">Eşleştirme başarılı</string>
|
||||
<string name="match_failed">Eşleştirme başarısız</string>
|
||||
<string name="game_award_tips_coins_text">Altın</string>
|
||||
<string name="game_award_tips_format">%s altın kazandınız</string>
|
||||
<string name="start">Başlat</string>
|
||||
<string name="game_ticket_format">Giriş ücreti(%s)</string>
|
||||
<string name="game_exit_tips">Oyun başladı, odadan çıkarsanız oyunu kaybedersiniz, çıkmak istediğinize emin misiniz?</string>
|
||||
<string name="game_exit_tips_matching">Oyun eşleştirme aşamasında, odadan çıkarsanız eşleştirme sona erecek, çıkmak istiyor musunuz?</string>
|
||||
<string name="game_match_failed">Eşleştirme başarısız, tekrar denemek ister misiniz?</string>
|
||||
<string name="game_rematch">Rövanş</string>
|
||||
<string name="resume_game_tips">Oyun devam ediyor, odasına geri dönmek istiyor musunuz?</string>
|
||||
<string name="game_end_tips">Oyun sona erdi~</string>
|
||||
<string name="continue_game">Oyuna devam et</string>
|
||||
|
||||
</resources>
|
18
app/src/module_game/res/values-zh-rTW/strings.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="matchmaking">匹配中</string>
|
||||
<string name="match_successfully">匹配成功</string>
|
||||
<string name="match_failed">匹配失敗</string>
|
||||
<string name="game_award_tips_coins_text">金幣</string>
|
||||
<string name="game_award_tips_format">獲勝獎勵%s金幣</string>
|
||||
<string name="start">開始</string>
|
||||
<string name="game_ticket_format">入场费(%s)</string>
|
||||
<string name="game_exit_tips">遊戲已經開始,退出房間將默認遊戲失敗,確認退出房間?</string>
|
||||
<string name="game_exit_tips_matching">遊戲匹配中,退出房間則結束匹配,是否退出?</string>
|
||||
<string name="game_match_failed">匹配失敗,是否重新開始?</string>
|
||||
<string name="game_rematch">重新匹配</string>
|
||||
<string name="resume_game_tips">遊戲進行中,是否返回房間?</string>
|
||||
<string name="game_end_tips">遊戲已結束~</string>
|
||||
<string name="continue_game">繼續遊戲</string>
|
||||
</resources>
|
3
app/src/module_game/res/values/colors.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
</resources>
|
17
app/src/module_game/res/values/strings.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="matchmaking">Matching</string>
|
||||
<string name="match_successfully">Match successful</string>
|
||||
<string name="match_failed">Match failed</string>
|
||||
<string name="game_award_tips_coins_text">gold coins</string>
|
||||
<string name="game_award_tips_format">Winning reward %s gold coins</string>
|
||||
<string name="start">Start</string>
|
||||
<string name="game_ticket_format">Admission Fee (%s)</string>
|
||||
<string name="game_exit_tips">The game has started. Exiting the room will be considered a game failure. Are you sure you want to exit the room?</string>
|
||||
<string name="game_exit_tips_matching">During a match, exiting a room will end the match. Do you want to exit?</string>
|
||||
<string name="game_match_failed">Match failed, do you want to rematch?</string>
|
||||
<string name="game_rematch">Rematch</string>
|
||||
<string name="resume_game_tips">Game in progress, do you want to return to the room?</string>
|
||||
<string name="game_end_tips">The game is over ~</string>
|
||||
<string name="continue_game">Continue</string>
|
||||
</resources>
|