Compare commits
21 Commits
fix/galler
...
feat/cloud
| Author | SHA1 | Date | |
|---|---|---|---|
| 718ed2d209 | |||
| e3f883b784 | |||
| fbb81d7e45 | |||
| 750ec04c9d | |||
| e23a3d3938 | |||
| d75020cdb5 | |||
| c56c71d1f1 | |||
| ccfa50d748 | |||
| 7176a5a4c4 | |||
| d007092193 | |||
| 2552743ff2 | |||
| 4faf15ffe7 | |||
| 33d6ed75ad | |||
| 2eb4878b12 | |||
| 2bea3c72d7 | |||
| fa3db9d521 | |||
| efee9409bf | |||
| 38a7eaf780 | |||
| a1b4233fdb | |||
| 60c24e7457 | |||
| 729685e764 |
@ -538,6 +538,10 @@ dependencies {
|
||||
if(!gradle.ext.excludeOptionalModules || gradle.ext.enableWechatPay){
|
||||
implementation(project(":feature:wechat_pay"))
|
||||
}
|
||||
|
||||
if(!gradle.ext.excludeOptionalModules || gradle.ext.enableCloudGame){
|
||||
implementation(project(":feature:cloud_game"))
|
||||
}
|
||||
}
|
||||
|
||||
File propFile = file('sign.properties')
|
||||
|
||||
@ -18,6 +18,7 @@ import com.gh.gamecenter.common.base.GlobalActivityManager
|
||||
import com.gh.gamecenter.common.provider.IHelpAndFeedbackProvider
|
||||
import com.gh.gamecenter.common.utils.PackageFlavorHelper
|
||||
import com.gh.gamecenter.core.provider.IPushProvider
|
||||
import com.gh.gamecenter.feature.cloudgame.CloudGameHelper
|
||||
import com.gh.gamecenter.login.utils.QuickLoginHelper
|
||||
import com.gh.gamecenter.login.view.LoginActivity
|
||||
import com.gh.gamecenter.va.VCore
|
||||
@ -106,6 +107,8 @@ class GlobalActivityLifecycleObserver : Application.ActivityLifecycleCallbacks {
|
||||
|
||||
if (PackageFlavorHelper.IS_TEST_FLAVOR && activity is AppCompatActivity) {
|
||||
DarkModeSwitchHelper.showDarkModeSwitchFloatingView(activity)
|
||||
|
||||
CloudGameHelper.showCloudGameFloat(activity)
|
||||
}
|
||||
|
||||
if (activity is AppCompatActivity
|
||||
|
||||
@ -114,7 +114,6 @@ class GameDetailActivity : DownloadToolbarActivity() {
|
||||
view,
|
||||
listOf(
|
||||
R.id.menu_download_iv,
|
||||
R.id.gameBigEvent,
|
||||
R.id.cardContainer,
|
||||
R.id.iv_reserve,
|
||||
R.id.iv_concern,
|
||||
|
||||
@ -127,6 +127,8 @@ class GameDetailViewModel(
|
||||
var isSkipOnPageSelected = false // 是否触发论坛/专区Tab跳转
|
||||
|
||||
var defaultCoverEntity: CoverEntity? = null
|
||||
var coverTabSequence = 1 // 用于埋点,详情视频/图集tab 当前选中tab的序号,从1开始
|
||||
var coverTabName = "" // 用于埋点,详情视频/图集tab 详情视频/图集tab 当前选中tab的tab名称
|
||||
|
||||
var isGameInstalled = false
|
||||
private var isGameUpdatable = false
|
||||
|
||||
@ -187,15 +187,19 @@ class GameDetailFragment : LazyFragment(), IScrollable {
|
||||
coverSfv.goneIf(!shouldShowCoverFilter) {
|
||||
val defaultTabPosition =
|
||||
tabNameList.indexOfFirst { tabName -> viewModel.defaultCoverEntity?.tabName == tabName }
|
||||
viewModel.coverTabSequence = defaultTabPosition + 1
|
||||
viewModel.coverTabName = viewModel.defaultCoverEntity?.tabName ?: ""
|
||||
coverSfv.setItemList(tabNameList, if (defaultTabPosition != -1) defaultTabPosition else 0)
|
||||
coverSfv.setOnCheckedCallback { position ->
|
||||
val checkedText = tabNameList.getOrNull(position)
|
||||
val currentCoverEntity = it.getOrNull(coverPosition)
|
||||
viewModel.coverTabSequence = position + 1
|
||||
viewModel.coverTabName = checkedText ?: ""
|
||||
SensorsBridge.trackEvent("GameDetailMediaTabClick", json {
|
||||
"game_id" to gameEntity?.id
|
||||
"game_name" to gameEntity?.name
|
||||
"sequence" to position + 1
|
||||
"tab_name" to checkedText
|
||||
"sequence" to viewModel.coverTabSequence
|
||||
"tab_name" to viewModel.coverTabName
|
||||
})
|
||||
if (currentCoverEntity?.tabName == checkedText) return@setOnCheckedCallback
|
||||
|
||||
|
||||
@ -115,7 +115,6 @@ class GameDetailCoverAdapter(
|
||||
})
|
||||
.build(holder.binding.player)
|
||||
|
||||
holder.binding.player.gameName = viewModel.game?.name ?: ""
|
||||
holder.binding.player.viewModel = viewModel
|
||||
holder.binding.player.showOrHideCoverFilter = showOrHideCoverFilter
|
||||
holder.binding.player.video = topVideo
|
||||
|
||||
@ -143,13 +143,11 @@ class ServersCalendarActivity : ToolBarActivity() {
|
||||
setToolbarMenu(R.menu.menu_server_calendar_more)
|
||||
mBinding.subscribe.visibility = View.GONE
|
||||
mBinding.subscribeHint1.visibility = View.GONE
|
||||
mBinding.subscribeHint2.visibility = View.GONE
|
||||
mBinding.subscribe.setOnClickListener(null)
|
||||
} else {// 用户已登录并处于未订阅状态
|
||||
clearMenu()
|
||||
mBinding.subscribe.visibility = View.VISIBLE
|
||||
mBinding.subscribeHint1.visibility = View.VISIBLE
|
||||
mBinding.subscribeHint2.visibility = View.VISIBLE
|
||||
mBinding.subscribe.setOnClickListener {
|
||||
CheckLoginUtils.checkLogin(this, "游戏详情-开服日历表-开启订阅") {
|
||||
mViewModel.subscribeServer()
|
||||
|
||||
@ -238,7 +238,7 @@ class ServersCalendarDetailNoDataDialog : BottomSheetDialogFragment() {
|
||||
wechatRemindCheckIv.alpha = 1.0F
|
||||
wechatRemind.setOnClickListener {
|
||||
wechatRemindCheckIv.isChecked = !wechatRemindCheckIv.isChecked
|
||||
viewModel.wechatRemind = wechatRemindCheckIv.isChecked
|
||||
// viewModel.wechatRemind = wechatRemindCheckIv.isChecked
|
||||
SPUtils.setBoolean(Constants.SP_SERVERS_CALENDAR_BY_WECHAT, wechatRemindCheckIv.isChecked)
|
||||
}
|
||||
|
||||
|
||||
@ -30,14 +30,14 @@ class ServersCalendarDetailNoDataViewModel(
|
||||
|
||||
var appRemind: Boolean = false
|
||||
|
||||
var wechatRemind: Boolean = false
|
||||
val wechatRemind: Boolean = false
|
||||
|
||||
val timeInSeconds: Int = (serverTimeInMills / 1000).toInt()
|
||||
|
||||
fun initData(notifySetting: ServerCalendarNotifySetting?) {
|
||||
if (isDataInit) return
|
||||
appRemind = notifySetting?.byApp ?: SPUtils.getBoolean(Constants.SP_SERVERS_CALENDAR_BY_APP, true)
|
||||
wechatRemind = notifySetting?.byWechat ?: SPUtils.getBoolean(Constants.SP_SERVERS_CALENDAR_BY_WECHAT, true)
|
||||
// wechatRemind = notifySetting?.byWechat ?: SPUtils.getBoolean(Constants.SP_SERVERS_CALENDAR_BY_WECHAT, true)
|
||||
isDataInit = true
|
||||
}
|
||||
|
||||
|
||||
@ -308,7 +308,7 @@ class ServersCalendarDetailsRemindDialog : BottomSheetDialogFragment() {
|
||||
|
||||
viewBinding.wechatRemind.setOnClickListener {
|
||||
viewBinding.wechatRemindCheckIv.isChecked = !viewBinding.wechatRemindCheckIv.isChecked
|
||||
viewModel.wechatRemind = viewBinding.wechatRemindCheckIv.isChecked
|
||||
// viewModel.wechatRemind = viewBinding.wechatRemindCheckIv.isChecked
|
||||
SPUtils.setBoolean(Constants.SP_SERVERS_CALENDAR_BY_WECHAT, viewBinding.wechatRemindCheckIv.isChecked)
|
||||
}
|
||||
|
||||
|
||||
@ -36,7 +36,7 @@ class ServersCalendarDetailsRemindViewModel(
|
||||
|
||||
var appRemind: Boolean = false
|
||||
|
||||
var wechatRemind: Boolean = false
|
||||
val wechatRemind: Boolean = false
|
||||
|
||||
val selectedNotifySeconds: Int get() = (selectedNotifyTime / 1000).toInt()
|
||||
|
||||
@ -58,7 +58,7 @@ class ServersCalendarDetailsRemindViewModel(
|
||||
selectedAdvancedTime = ServersCalendarAdvancedTime.valueOf(notifySetting?.secondsAdvance)
|
||||
selectedNotifyTime = (notifySetting?.notifyTime ?: calendarEntity.getTime()) * 1000
|
||||
appRemind = notifySetting?.byApp ?: SPUtils.getBoolean(Constants.SP_SERVERS_CALENDAR_BY_APP, true)
|
||||
wechatRemind = notifySetting?.byWechat ?: SPUtils.getBoolean(Constants.SP_SERVERS_CALENDAR_BY_WECHAT, true)
|
||||
// wechatRemind = notifySetting?.byWechat ?: SPUtils.getBoolean(Constants.SP_SERVERS_CALENDAR_BY_WECHAT, true)
|
||||
isDataInit = true
|
||||
}
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ import androidx.fragment.app.Fragment
|
||||
import com.gh.common.util.LogUtils
|
||||
import com.gh.download.cache.ExoCacheManager
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.base.GlobalActivityManager
|
||||
import com.gh.gamecenter.common.observer.MuteCallback
|
||||
import com.gh.gamecenter.common.observer.VolumeObserver
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
@ -40,7 +41,6 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS
|
||||
private var mMuteCallback: MuteCallback
|
||||
private var mVolumeObserver: VolumeObserver? = null
|
||||
|
||||
var gameName = ""
|
||||
var video: CoverTabEntity.Video? = null
|
||||
var viewModel: GameDetailViewModel? = null
|
||||
var uuid = UUID.randomUUID().toString()
|
||||
@ -87,11 +87,21 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS
|
||||
}
|
||||
|
||||
setBackFromFullScreenListener {
|
||||
// if (it.id == R.id.fullscreen) {
|
||||
// MtaHelper.onEvent("游戏详情_顶部视频", "${getMtaKeyPrefix()}-退出全屏", combinedTitleAndId)
|
||||
// } else if (it.id == R.id.back) {
|
||||
// MtaHelper.onEvent("游戏详情_顶部视频", "${getMtaKeyPrefix()}-点击返回", combinedTitleAndId)
|
||||
// }
|
||||
if (it.id == R.id.fullscreen || it.id == R.id.back) {
|
||||
SensorsBridge.trackGameDetailVideoClick(
|
||||
gameName = viewModel?.game?.name ?: "",
|
||||
gameId = viewModel?.game?.id ?: "",
|
||||
gameType = viewModel?.game?.categoryChinese ?: "",
|
||||
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
|
||||
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
|
||||
action = if (it.id == R.id.fullscreen) "退出全屏" else "点击返回",
|
||||
playType = if (mIsAutoPlay) "自动播放" else "主动播放",
|
||||
isFullScreen = mIfCurrentIsFullscreen,
|
||||
sequence = viewModel?.coverTabSequence ?: 1,
|
||||
tabName = viewModel?.coverTabName ?: "",
|
||||
playLength = (currentPositionWhenPlaying / 1000).toString()
|
||||
)
|
||||
}
|
||||
clearFullscreenLayout()
|
||||
}
|
||||
|
||||
@ -126,13 +136,22 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS
|
||||
fun startPlayLogic(isAutoPlay: Boolean) {
|
||||
mIsAutoPlay = isAutoPlay
|
||||
violenceUpdateMuteStatus()
|
||||
// if (isAutoPlay) {
|
||||
// MtaHelper.onEvent("游戏详情_顶部视频", "视频播放方式", "自动播放")
|
||||
// MtaHelper.onEvent("游戏详情_顶部视频", "顶部视频-自动播放", combinedTitleAndId)
|
||||
// } else {
|
||||
// MtaHelper.onEvent("游戏详情_顶部视频", "视频播放方式", "手动播放")
|
||||
// }
|
||||
|
||||
if (isAutoPlay) {
|
||||
SensorsBridge.trackGameDetailVideoClick(
|
||||
gameName = viewModel?.game?.name ?: "",
|
||||
gameId = viewModel?.game?.id ?: "",
|
||||
gameType = viewModel?.game?.categoryChinese ?: "",
|
||||
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
|
||||
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
|
||||
action = "自动播放",
|
||||
playType = if (mIsAutoPlay) "自动播放" else "主动播放",
|
||||
isFullScreen = mIfCurrentIsFullscreen,
|
||||
sequence = viewModel?.coverTabSequence ?: 1,
|
||||
tabName = viewModel?.coverTabName ?: "",
|
||||
playLength = (currentPositionWhenPlaying / 1000).toString()
|
||||
)
|
||||
|
||||
val seekTime = ScrollCalculatorHelper.getPlaySchedule(MD5Utils.getContentMD5(video?.url))
|
||||
seekOnStart = seekTime
|
||||
mTouchingProgressBar = false
|
||||
@ -289,7 +308,7 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar?) {
|
||||
super.onStopTrackingTouch(seekBar)
|
||||
uploadVideoStreamingPlaying("拖动")
|
||||
uploadVideoStreamingPlaying("拖动进度条", seekBar)
|
||||
}
|
||||
|
||||
override fun isShowNetConfirm(): Boolean {
|
||||
@ -533,7 +552,7 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS
|
||||
}
|
||||
|
||||
override fun releaseVideos() {
|
||||
uploadVideoStreamingPlaying("结束播放")
|
||||
uploadVideoStreamingPlaying("播放完毕")
|
||||
CustomManager.releaseAllVideos(getKey())
|
||||
}
|
||||
|
||||
@ -556,11 +575,21 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.start -> {
|
||||
// if (currentState == GSYVideoView.CURRENT_STATE_PLAYING) {
|
||||
// MtaHelper.onEvent("游戏详情_顶部视频", "${getMtaKeyPrefix()}-点击暂停", combinedTitleAndId)
|
||||
// } else {
|
||||
// MtaHelper.onEvent("游戏详情_顶部视频", "${getMtaKeyPrefix()}-点击播放", combinedTitleAndId)
|
||||
// }
|
||||
SensorsBridge.trackGameDetailVideoClick(
|
||||
gameName = viewModel?.game?.name ?: "",
|
||||
gameId = viewModel?.game?.id ?: "",
|
||||
gameType = viewModel?.game?.categoryChinese ?: "",
|
||||
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
|
||||
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
|
||||
action = if (currentState == GSYVideoView.CURRENT_STATE_PLAYING) "点击暂停" else "点击播放",
|
||||
playType = if (mIsAutoPlay) "自动播放" else "主动播放",
|
||||
isFullScreen = mIfCurrentIsFullscreen,
|
||||
sequence = viewModel?.coverTabSequence ?: 1,
|
||||
tabName = viewModel?.coverTabName ?: "",
|
||||
playLength = (currentPositionWhenPlaying / 1000).toString()
|
||||
)
|
||||
// 手动触发暂停/播放,后续行为都算主动播放
|
||||
mIsAutoPlay = false
|
||||
super.onClick(v)
|
||||
}
|
||||
|
||||
@ -568,13 +597,11 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMtaKeyPrefix() = if (mIfCurrentIsFullscreen) "顶部视频(全屏)" else "顶部视频"
|
||||
|
||||
fun getCurrentPosition(): Long {
|
||||
return mCurrentPosition
|
||||
}
|
||||
|
||||
fun uploadVideoStreamingPlaying(action: String) {
|
||||
fun uploadVideoStreamingPlaying(action: String, seekBar: SeekBar? = null) {
|
||||
if (video == null || video?.url.isNullOrEmpty()) return
|
||||
runOnIoThread(isHeavyWightTask = true) {
|
||||
val isLandscape = mOrientationUtils != null
|
||||
@ -598,6 +625,26 @@ class TopVideoView @JvmOverloads constructor(context: Context, attrs: AttributeS
|
||||
action, video?.videoId, video?.title, viewModel?.game?.id, viewModel?.game?.name,
|
||||
videoPlayModel, videoPlayStatus(), mContentLength, videoTotalTime, videoPlayTs, progress.toInt()
|
||||
)
|
||||
if (action != "开始播放" && action != "暂停播放" && action != "退出全屏") {
|
||||
val playLength = when (action) {
|
||||
"播放完毕" -> (duration / 1000).toString()
|
||||
"拖动进度条" -> ((seekBar?.progress ?: 0) * duration / 100000).toString()
|
||||
else -> (currentPositionWhenPlaying / 1000).toString()
|
||||
}
|
||||
SensorsBridge.trackGameDetailVideoClick(
|
||||
gameName = viewModel?.game?.name ?: "",
|
||||
gameId = viewModel?.game?.id ?: "",
|
||||
gameType = viewModel?.game?.categoryChinese ?: "",
|
||||
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
|
||||
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
|
||||
action = action,
|
||||
playType = if (mIsAutoPlay) "自动播放" else "主动播放",
|
||||
isFullScreen = mIfCurrentIsFullscreen,
|
||||
sequence = viewModel?.coverTabSequence ?: 1,
|
||||
tabName = viewModel?.coverTabName ?: "",
|
||||
playLength = playLength
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ object CustomViewExt {
|
||||
private fun getTestDescription(game: GameEntity): String {
|
||||
val timeText = TimeUtils.formatTestTime(game.test?.start ?: 0L)
|
||||
val eventName = if (game.test?.type == "删档内测") {
|
||||
R.string.first_release.toResString()
|
||||
R.string.delete_test.toResString()
|
||||
} else {
|
||||
R.string.go_live.toResString()
|
||||
}
|
||||
|
||||
@ -37,6 +37,8 @@ class NotificationColumnViewHolder(
|
||||
|
||||
private var isScrolling = false
|
||||
|
||||
private var targetPosition = -1
|
||||
|
||||
private val bannerController = BannerInRecyclerController {
|
||||
nextToPage()
|
||||
}
|
||||
@ -190,6 +192,10 @@ class NotificationColumnViewHolder(
|
||||
override fun onViewAttach(parent: RecyclerView?) {
|
||||
viewModel.shareHiddenNotifications.observe(lifecycleOwner, hiddenNotifiesObserver)
|
||||
bannerController.onViewAttachedToWindow(parent)
|
||||
val selectedPosition = (_item as? CustomCommonContentCollectionItem)?.selectedPosition ?: 0
|
||||
if (targetPosition != -1 && targetPosition != selectedPosition) {
|
||||
binding.rvNotification.scrollToPosition(targetPosition)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewDetach(parent: RecyclerView?) {
|
||||
@ -203,7 +209,10 @@ class NotificationColumnViewHolder(
|
||||
if (layoutManager is LinearLayoutManager) {
|
||||
val firstPosition = layoutManager.findFirstCompletelyVisibleItemPosition()
|
||||
if (firstPosition != -1) {
|
||||
binding.rvNotification.smoothScrollToPosition(firstPosition + 1)
|
||||
// 请注意,有可能smoothScrollToPosition正在执行滚动动画时,当前ViewHolder会调用onViewDetach,导致RecyclerView无法滚动到目标位置,所以这里需要先记录目标位置
|
||||
// 当ViewHolder Detach之后,再次Attach时,检查targetPosition是否等于selectedPosition,如果不相等,说明发生了以上情况,需要再次调用scrollToPosition将RecyclerView滚动到指定位置
|
||||
targetPosition = firstPosition + 1
|
||||
binding.rvNotification.smoothScrollToPosition(targetPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,23 +2,32 @@ package com.gh.vspace
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.exposure.ExposureListener
|
||||
import com.gh.common.util.NewFlatLogUtils
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.common.baselist.ListAdapter
|
||||
import com.gh.gamecenter.common.baselist.ListFragment
|
||||
import com.gh.gamecenter.common.baselist.LoadType
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.setSwitchAnimation
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.common.utils.viewModelProvider
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.databinding.FragmentVdownloadManagerBinding
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.history.IBatchDelete
|
||||
import com.gh.gamecenter.history.ManageOption
|
||||
import com.lightgame.download.DataWatcher
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class VDownloadManagerFragment :
|
||||
ListFragment<GameEntity, VDownloadManagerViewModel>(), IBatchDelete {
|
||||
@ -93,8 +102,16 @@ class VDownloadManagerFragment :
|
||||
mListRv.addOnScrollListener(mExposureListener)
|
||||
}
|
||||
|
||||
VHelper.vGameDao.getAllLiveData().observe(viewLifecycleOwner) {
|
||||
onLoadRefresh()
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
mViewModel.vGames
|
||||
.catch {
|
||||
ToastUtils.showToast(it.message ?: "")
|
||||
}
|
||||
.collectLatest {
|
||||
onLoadRefresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -38,6 +38,8 @@ class VDownloadManagerViewModel(application: Application) :
|
||||
load(it)
|
||||
}
|
||||
|
||||
val vGames = VHelper.vGameDao.getAllGames()
|
||||
|
||||
fun refresh() {
|
||||
loadPublishSubject.onNext(LoadType.REFRESH)
|
||||
}
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
package com.gh.vspace.db
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.room.*
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface VGameDao {
|
||||
@ -13,7 +16,7 @@ interface VGameDao {
|
||||
fun getAll(): List<VGameEntity>
|
||||
|
||||
@Query("SELECT * FROM v_game")
|
||||
fun getAllLiveData(): LiveData<List<VGameEntity>>
|
||||
fun getAllGames(): Flow<List<VGameEntity>>
|
||||
|
||||
@Query("DELETE FROM v_game WHERE packageName = :packageName")
|
||||
fun delete(packageName: String)
|
||||
|
||||
@ -187,6 +187,7 @@ class RealNameInfoFragment : ToolbarFragment() {
|
||||
mBinding.nameEt.inputType = InputType.TYPE_NULL
|
||||
mBinding.idCardEt.inputType = InputType.TYPE_NULL
|
||||
mBinding.nameEt.visibility = View.GONE
|
||||
mBinding.textDotIndicator.visibility = View.GONE
|
||||
mBinding.idCardEt.visibility = View.GONE
|
||||
mBinding.nameTv.visibility = View.GONE
|
||||
mBinding.idCardTv.visibility = View.GONE
|
||||
|
||||
@ -153,21 +153,8 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:includeFontPadding="false"
|
||||
android:layout_marginBottom="7dp"
|
||||
android:text="@string/servers_calendar_subscribe_hint_1"
|
||||
android:textColor="@color/text_tertiary"
|
||||
android:textSize="11sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="@id/subscribe"
|
||||
app:layout_constraintBottom_toTopOf="@id/subscribe_hint_2" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/subscribe_hint_2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:includeFontPadding="false"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:text="@string/servers_calendar_subscribe_hint_2"
|
||||
android:text="@string/servers_calendar_subscribe_hint_1"
|
||||
android:textColor="@color/text_tertiary"
|
||||
android:textSize="11sp"
|
||||
android:visibility="gone"
|
||||
|
||||
@ -128,6 +128,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="20dp"
|
||||
android:gravity="center"
|
||||
android:visibility="invisible"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintTop_toTopOf="@id/app_remind"
|
||||
app:layout_constraintStart_toEndOf="@id/app_remind">
|
||||
|
||||
@ -1,310 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout 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"
|
||||
android:fitsSystemWindows="true"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/gamedetail_appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/ui_surface"
|
||||
android:fitsSystemWindows="true"
|
||||
android:gravity="center"
|
||||
app:layout_behavior="com.gh.gamecenter.common.view.FixAppBarLayoutBehavior"
|
||||
tools:visibility="visible">
|
||||
|
||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
android:id="@+id/collapsingToolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
app:contentScrim="@color/ui_surface"
|
||||
app:layout_scrollFlags="scroll|exitUntilCollapsed"
|
||||
app:scrimAnimationDuration="0"
|
||||
app:scrimVisibleHeightTrigger="105dp"
|
||||
app:titleEnabled="false">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fitsSystemWindows="true"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include
|
||||
android:id="@+id/game_detail_video"
|
||||
layout="@layout/piece_game_detail_video" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="12dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingBottom="12dp">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/gameIconContainer"
|
||||
android:layout_width="96dp"
|
||||
android:layout_height="88dp"
|
||||
android:layout_marginRight="12dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<com.gh.gamecenter.feature.view.GameIconView
|
||||
android:id="@+id/gamedetail_iv_thumb"
|
||||
android:layout_width="88dp"
|
||||
android:layout_height="88dp"
|
||||
android:layout_centerHorizontal="true"
|
||||
app:gameIconFadeDuration="0" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/gameDetailRankLl"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="25dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:background="@drawable/bg_game_detail_rank"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="6dp"
|
||||
android:layout_marginTop="7dp"
|
||||
android:src="@drawable/ic_game_detail_rank_trophy" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gameDetailRankTv"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginRight="2dp"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="marquee"
|
||||
android:gravity="center"
|
||||
android:includeFontPadding="false"
|
||||
android:marqueeRepeatLimit="marquee_forever"
|
||||
android:singleLine="true"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="9sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="预约榜第1名" />
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginRight="6dp"
|
||||
android:src="@drawable/ic_game_detail_rank_arrow" />
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/gameTitleContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="70dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toStartOf="@+id/rating_score_container"
|
||||
app:layout_constraintStart_toEndOf="@+id/gameIconContainer"
|
||||
app:layout_constraintTop_toTopOf="@+id/gameIconContainer">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gamedetail_tv_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:ellipsize="end"
|
||||
android:includeFontPadding="false"
|
||||
android:lineSpacingExtra="2dp"
|
||||
android:maxLines="2"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="地海争霸2" />
|
||||
|
||||
<com.gh.common.view.FlexLinearLayout
|
||||
android:id="@+id/gamedetail_gametag"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:clipToPadding="false"
|
||||
android:orientation="horizontal" />
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/recommendAgeIv"
|
||||
android:layout_width="58dp"
|
||||
android:layout_height="14dp"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/gameIconContainer"
|
||||
app:layout_constraintStart_toEndOf="@+id/gameIconContainer"
|
||||
tools:src="@drawable/ic_recommend_age8"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/realnameHintTv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="14dp"
|
||||
android:layout_marginStart="8dp"
|
||||
android:background="@drawable/bg_ebf8ff_radius_2"
|
||||
android:gravity="center"
|
||||
android:paddingEnd="6dp"
|
||||
android:paddingStart="6dp"
|
||||
android:paddingTop="0.5dp"
|
||||
android:text="登陆游戏后需进行实名认证"
|
||||
android:textColor="@color/text_theme"
|
||||
android:textSize="@dimen/tag_text_size"
|
||||
android:includeFontPadding="false"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@id/gameIconContainer"
|
||||
app:layout_constraintStart_toEndOf="@id/recommendAgeIv"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/rating_score_container"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_marginLeft="6dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/gameTitleContainer"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:src="@drawable/bg_game_detail_rating_score" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/rating_score_average"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
tools:text="8.5" />
|
||||
|
||||
</RelativeLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/gameBigEvent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="left"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:background="@drawable/bg_game_big_event"
|
||||
android:drawableLeft="@drawable/ic_game_detail_big_event_gray"
|
||||
android:drawableRight="@drawable/ic_game_detail_big_event_arrow_gray"
|
||||
android:drawablePadding="3dp"
|
||||
android:ellipsize="end"
|
||||
android:includeFontPadding="false"
|
||||
android:maxLines="1"
|
||||
android:padding="4dp"
|
||||
android:textColor="@color/text_tertiary"
|
||||
android:textSize="10sp"
|
||||
android:visibility="gone"
|
||||
tools:text="游戏大事件游戏大事件"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/contentCardContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<View
|
||||
android:id="@+id/toolbarGapView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="8dp"
|
||||
android:background="@color/ui_background" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
style="@style/Base_ToolbarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="44dp"
|
||||
android:background="@android:color/transparent"
|
||||
app:contentInsetStartWithNavigation="0dp"
|
||||
app:layout_collapseMode="pin">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/toolbarContainer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/transparent"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:translationX="-10dp">
|
||||
|
||||
<com.gh.gamecenter.feature.view.GameIconView
|
||||
android:id="@+id/gamedetail_thumb_small"
|
||||
android:layout_width="28dp"
|
||||
android:layout_height="28dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/titleTv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="8dp"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/gamedetail_tabbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.gh.gamecenter.common.view.TabIndicatorView
|
||||
android:id="@+id/tab_indicator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/default_tab_indicator_height"
|
||||
android:layout_alignParentBottom="true" />
|
||||
|
||||
<com.google.android.material.tabs.TabLayout
|
||||
android:id="@+id/tab_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/tab_layout_height"
|
||||
app:tabIndicator="@null"
|
||||
app:tabTextAppearance="@style/TabLayoutTextAppearance" />
|
||||
|
||||
</RelativeLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<com.lightgame.view.NoScrollableViewPager
|
||||
android:id="@+id/gamedetail_vp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:overScrollMode="never"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@ -69,6 +69,7 @@
|
||||
android:layout_marginTop="12dp"
|
||||
android:layout_marginStart="20dp"
|
||||
android:gravity="center"
|
||||
android:visibility="gone"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintTop_toBottomOf="@id/remind_title"
|
||||
app:layout_constraintStart_toEndOf="@id/app_remind"
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
android:layout_height="112dp"
|
||||
android:background="@color/ui_surface"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
tools:showIn="@layout/activity_main">
|
||||
tools:showIn="@layout/activity_cloudgame">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
@ -1,39 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/video_placeholder"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<com.gh.gamecenter.common.view.StatusBarView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/appbar_height" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.gh.gamecenter.gamedetail.video.TopVideoView
|
||||
android:id="@+id/player"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintDimensionRatio="h,180:101"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</LinearLayout>
|
||||
@ -480,7 +480,6 @@
|
||||
<string name="archive_limit_description">請選擇替換覆蓋的存檔</string>
|
||||
<string name="servers_calendar_subscribe">加入訂閱</string>
|
||||
<string name="servers_calendar_subscribe_hint_1">訂閱開服表,訂閱後助你獲得一手新服信息</string>
|
||||
<string name="servers_calendar_subscribe_hint_2">通知時間:8:00、12:00、18:00</string>
|
||||
<string name="servers_calendar_more_dialog_title">操作</string>
|
||||
<string name="servers_calendar_dialog_unsubscribe_title">取消訂閱</string>
|
||||
<string name="servers_calendar_dialog_unsubscribe_hint">不再接收開服消息</string>
|
||||
@ -686,9 +685,9 @@
|
||||
<string name="select_the_region">選擇加速區服</string>
|
||||
<string name="tips_for_new_users">新用戶免費3小時</string>
|
||||
<string name="recent_played">最近在玩</string>
|
||||
<string name="first_release">先發</string>
|
||||
<string name="go_live">上線</string>
|
||||
<string name="number_of_reservations">%1$s人預約</string>
|
||||
<string name="wechat_app_not_install_tips">請檢查是否安裝微信客戶端</string>
|
||||
<string name="delete_test">刪測</string>
|
||||
</resources>
|
||||
|
||||
|
||||
@ -493,13 +493,12 @@
|
||||
<string name="archive_limit_description">请选择替换覆盖的存档</string>
|
||||
<string name="servers_calendar_subscribe">加入订阅</string>
|
||||
<string name="servers_calendar_subscribe_hint_1">订阅开服表,订阅后助你获得一手新服信息</string>
|
||||
<string name="servers_calendar_subscribe_hint_2">通知时间:8:00、12:00、18:00</string>
|
||||
<string name="servers_calendar_more_dialog_title">操作</string>
|
||||
<string name="servers_calendar_dialog_unsubscribe_title">取消订阅</string>
|
||||
<string name="servers_calendar_dialog_unsubscribe_hint">不再接收开服消息</string>
|
||||
<string name="servers_calendar_more_dialog_cancel_title">取消</string>
|
||||
<string name="servers_calendar_subscription_dialog_wechat_content">游戏发布新服信息时,您将在消息中心收到通知。为了避免错过通知,建议您开启微信公众号提醒</string>
|
||||
<string name="servers_calendar_subscription_dialog_content">游戏发布新服信息时,您将在消息中心和微信公众号收到通知,不会错过任何开服的消息</string>
|
||||
<string name="servers_calendar_subscription_dialog_content">游戏发布新服信息时,您将在消息中心收到通知,不会错过任何开服信息</string>
|
||||
<string name="servers_calendar_subscription_dialog_title">游戏订阅成功</string>
|
||||
<string name="servers_calendar_subscription_dialog_confirm">我知道了</string>
|
||||
<string name="servers_calendar_remind_time_setting_dialog_title">设置提醒时间</string>
|
||||
@ -686,8 +685,8 @@
|
||||
<string name="select_the_region">选择加速区服</string>
|
||||
<string name="tips_for_new_users">新用户免费3小时</string>
|
||||
<string name="recent_played">最近在玩</string>
|
||||
<string name="first_release">首发</string>
|
||||
<string name="go_live">上线</string>
|
||||
<string name="number_of_reservations">%1$s人预约</string>
|
||||
<string name="wechat_app_not_install_tips">请检查是否安装微信客户端</string>
|
||||
<string name="delete_test">删测</string>
|
||||
</resources>
|
||||
|
||||
1
feature/cloud_game/.gitignore
vendored
Normal file
1
feature/cloud_game/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/build
|
||||
40
feature/cloud_game/build.gradle
Normal file
40
feature/cloud_game/build.gradle
Normal file
@ -0,0 +1,40 @@
|
||||
apply plugin: "com.android.library"
|
||||
apply plugin: "org.jetbrains.kotlin.android"
|
||||
apply plugin: "kotlin-kapt"
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdk rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode rootProject.ext.versionCode
|
||||
versionName rootProject.ext.versionName
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(path: ":module_common")) {
|
||||
exclude group: 'androidx.swiperefreshlayout'
|
||||
}
|
||||
implementation(project(':module_core_feature')) {
|
||||
exclude group: 'androidx.swiperefreshlayout'
|
||||
}
|
||||
|
||||
implementation "com.tencent.tcr:tcrsdk-full:3.23.0"
|
||||
implementation "com.tencent.tcr:tcr-gamepad:2.2.4"
|
||||
implementation "com.lg:easyfloat:2.0.4-fix_proguard"
|
||||
}
|
||||
21
feature/cloud_game/proguard-rules.pro
vendored
Normal file
21
feature/cloud_game/proguard-rules.pro
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
||||
10
feature/cloud_game/src/main/AndroidManifest.xml
Normal file
10
feature/cloud_game/src/main/AndroidManifest.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.gh.gamecenter.cloudgame">
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.feature.cloudgame.CloudGameActivity"
|
||||
android:screenOrientation="landscape" />
|
||||
</application>
|
||||
</manifest>
|
||||
1
feature/cloud_game/src/main/assets/lol_5v5.cfg
Normal file
1
feature/cloud_game/src/main/assets/lol_5v5.cfg
Normal file
File diff suppressed because one or more lines are too long
@ -0,0 +1,500 @@
|
||||
package com.gh.gamecenter.feature.cloudgame
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import android.view.WindowManager
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.isVisible
|
||||
import com.gh.gamecenter.cloudgame.R
|
||||
import com.gh.gamecenter.cloudgame.databinding.ActivityCloudgameBinding
|
||||
import com.gh.gamecenter.core.AppExecutor
|
||||
import com.gh.gamecenter.core.AppExecutor.ioExecutor
|
||||
import com.gh.gamecenter.feature.cloudgame.OkHelper.obtainServerSession
|
||||
import com.tencent.tcr.sdk.api.AsyncCallback
|
||||
import com.tencent.tcr.sdk.api.CustomDataChannel
|
||||
import com.tencent.tcr.sdk.api.TcrSdk
|
||||
import com.tencent.tcr.sdk.api.TcrSession
|
||||
import com.tencent.tcr.sdk.api.TcrSessionConfig
|
||||
import com.tencent.tcr.sdk.api.data.CursorState
|
||||
import com.tencent.tcr.sdk.api.data.ScreenConfig
|
||||
import com.tencent.tcr.sdk.api.data.StatsInfo
|
||||
import com.tencent.tcr.sdk.api.data.VideoStreamConfig
|
||||
import com.tencent.tcr.sdk.api.view.MobileTouchListener
|
||||
import com.tencent.tcr.sdk.api.view.PcClickListener
|
||||
import com.tencent.tcr.sdk.api.view.PcTouchListener
|
||||
import com.tencent.tcr.sdk.api.view.TcrRenderView
|
||||
import com.tencent.tcr.sdk.api.view.TcrRenderView.TcrRenderViewType
|
||||
import com.tencent.tcr.sdk.api.view.TcrRenderView.VideoRotation
|
||||
import com.tencent.tcrgamepad.GamepadManager
|
||||
import com.tencent.tcrgamepad.GamepadManager.OnEditListener
|
||||
import com.tencent.tcrgui.keyboard.KeyboardView
|
||||
import java.io.InputStreamReader
|
||||
import java.nio.ByteBuffer
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.text.DecimalFormat
|
||||
|
||||
/**
|
||||
* 该类演示了如何初始化TcrSdk,创建会话、请求远端会话、启动云应用,并将云应用画面展示到界面上的基础流程。<br></br>
|
||||
*
|
||||
* 要使用TcrSdk,你需要先调用[TcrSdk.init]接口初始化TcrSdk,<br></br>
|
||||
* 在[AsyncCallback.onSuccess] 回调以后才能做进一步操作,例如创建[TcrSession]以及[TcrRenderView]。
|
||||
*
|
||||
* 在启动会话[TcrSession.start]后才可以与云端实例进行交互。<br></br>
|
||||
* 具体的流程如下:
|
||||
*
|
||||
* <pre>
|
||||
* `┌──────────────┐ ┌────────────┐ ┌────────────┐ ┌───────────┐
|
||||
* │ MainActivity │ │ TcrSession │ │ App Server │ │ Cloud Api │
|
||||
* └──────┬───────┘ └──────┬─────┘ └──────┬─────┘ └─────┬─────┘
|
||||
* │ setObserver() │ │ │
|
||||
* ├────────────────►│ │ │
|
||||
* │ ├─────┐ │ │
|
||||
* │ │ │ │ │
|
||||
* │ Event.INITED │◄────┘ │ │
|
||||
* │◄────────────────┤ │ │
|
||||
* │ │ │ │
|
||||
* │ startGame(clientSession) │ │
|
||||
* ├─────────────────┬─────────────►│ │
|
||||
* │ │ │ tryLock │
|
||||
* │ │ ├─────────────►│
|
||||
* │ │ │ │
|
||||
* │ │ │createSession │
|
||||
* │ │ ├─────────────►│
|
||||
* │ │ │ │
|
||||
* │ onSuccess(serverSession) │ │
|
||||
* │◄────────────────┬──────────────┤ │
|
||||
* │ │ │ │
|
||||
* │ start() │ │ │
|
||||
* ├────────────────►│ │ │
|
||||
* │ │ │ │
|
||||
* SDK调用流程 后台交互流程
|
||||
` *
|
||||
</pre> *
|
||||
*
|
||||
* 1.调用[TcrSdk.init]初始化SDK,初始化成功后创建TcrSession,
|
||||
* 并通过[TcrSessionConfig.Builder.observer]将Observer设置好,通过Observer的回调拿到TcrSession对外的通知<br></br>
|
||||
* 2.通过Observer的回调得到TcrSession初始化成功[TcrSession.Event.STATE_INITED]的事件后,将事件传递的数据解析为clientSession<br></br>
|
||||
* 3.调用业务后台接口, 将`clientSession`传递给云端实例,并获取`serverSession`。<br></br>
|
||||
* 4.拿到`serverSession`之后,调用[TcrSession.start]启动会话。<br></br>
|
||||
* 5.当会话启动成功之后用户便可以和云端实例进行交互。<br></br>
|
||||
*
|
||||
* 详细的TcrSdk接口如何使用,请参考文档<br></br>
|
||||
* 业务后台的搭建,请参考链接<br></br>
|
||||
* @see [TcrSdK API](https://tencentyun.github.io/cloudgame-android-sdk/tcrsdk/index.html)
|
||||
*
|
||||
* @see [搭建业务后台](https://github.com/tencentyun/gs-server-demo)
|
||||
*/
|
||||
class CloudGameActivity : Activity() {
|
||||
private val mDf = DecimalFormat("#.##")
|
||||
|
||||
// 渲染视图
|
||||
private var mRenderView: TcrRenderView? = null
|
||||
|
||||
// 云渲染会话
|
||||
private var mTcrSession: TcrSession? = null
|
||||
|
||||
// 创建的数据通道
|
||||
private var mCustomDataChannel: CustomDataChannel? = null
|
||||
|
||||
// 云端横竖屏信息
|
||||
private var mScreenConfig: ScreenConfig? = null
|
||||
|
||||
// 视频流分辨率信息
|
||||
private var mVideoStreamConfig: VideoStreamConfig? = null
|
||||
|
||||
// 记录云端屏幕配置是否发生变化
|
||||
private var mScreenConfigChanged = false
|
||||
|
||||
// 记录视频分辨率是否发生变化
|
||||
private var mVideoStreamConfigChanged = false
|
||||
|
||||
// 游戏手柄管理器
|
||||
private var gamepadManager: GamepadManager? = null
|
||||
|
||||
// 键盘管理器
|
||||
private var keyboardView: KeyboardView? = null
|
||||
|
||||
private var isCursorEnabled = true
|
||||
|
||||
private val binding by lazy { ActivityCloudgameBinding.inflate(layoutInflater) }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
initWindow()
|
||||
setContentView(binding.root)
|
||||
|
||||
initView()
|
||||
}
|
||||
|
||||
private fun initWindow() {
|
||||
// 不显示标题栏
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||
// 全屏展示
|
||||
window.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN,
|
||||
WindowManager.LayoutParams.FLAG_FULLSCREEN
|
||||
)
|
||||
// 屏幕常亮
|
||||
window.setFlags(
|
||||
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
|
||||
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
||||
)
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
binding.run {
|
||||
joystickBtn.setOnClickListener {
|
||||
if (joystickContainer.isVisible) {
|
||||
joystickContainer.visibility = View.GONE
|
||||
} else {
|
||||
if (mTcrSession == null) return@setOnClickListener
|
||||
|
||||
if (gamepadManager == null) {
|
||||
gamepadManager = GamepadManager(this@CloudGameActivity, mTcrSession)
|
||||
joystickContainer.addView(gamepadManager)
|
||||
|
||||
val customGamePadCfg =
|
||||
readConfigFile(this@CloudGameActivity, "lol_5v5.cfg")
|
||||
gamepadManager?.showGamepad(customGamePadCfg)
|
||||
gamepadManager?.setEditListener(OnEditListener { isChanged: Boolean, newCfg: String ->
|
||||
if (isChanged) {
|
||||
gamepadManager?.showGamepad(newCfg)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
joystickContainer.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
joystickEditBtn.setOnClickListener {
|
||||
if (mTcrSession == null || gamepadManager == null) return@setOnClickListener
|
||||
|
||||
val customGamePadCfg =
|
||||
readConfigFile(this@CloudGameActivity, "lol_5v5.cfg")
|
||||
gamepadManager?.editGamepad(customGamePadCfg)
|
||||
}
|
||||
|
||||
keyboardBtn.setOnClickListener {
|
||||
if (keyboardContainer.isVisible) {
|
||||
keyboardContainer.visibility = View.GONE
|
||||
} else {
|
||||
if (mTcrSession == null) return@setOnClickListener
|
||||
|
||||
if (keyboardView == null) {
|
||||
keyboardView = KeyboardView(this@CloudGameActivity, mTcrSession)
|
||||
binding.keyboardContainer.addView(keyboardView)
|
||||
}
|
||||
|
||||
keyboardContainer.visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
cursorBtn.setOnClickListener {
|
||||
if (mTcrSession == null) return@setOnClickListener
|
||||
|
||||
if (isCursorEnabled) {
|
||||
mRenderView?.setOnTouchListener(null)
|
||||
} else {
|
||||
setTouchHandler(
|
||||
mTcrSession!!,
|
||||
mRenderView!!,
|
||||
PC_GAME
|
||||
)
|
||||
}
|
||||
|
||||
isCursorEnabled = !isCursorEnabled
|
||||
}
|
||||
|
||||
connectBtn.setOnClickListener {
|
||||
// 初始化TcrSdk,初始化成功后创建TcrSession
|
||||
TcrSdk.getInstance().init(this@CloudGameActivity, null, object : AsyncCallback<Void?> {
|
||||
override fun onSuccess(result: Void?) {
|
||||
Log.i(TAG, "init SDK success")
|
||||
showToast("sdk 初始化成功", Toast.LENGTH_SHORT)
|
||||
// 为TcrSession创建配置参数对象。参考https://tencentyun.github.io/cloudgame-android-sdk/tcrsdk/com/tencent/tcr/sdk/api/config/TcrSessionConfig.Builder.html
|
||||
val tcrSessionConfig = TcrSessionConfig.builder()
|
||||
.observer(mSessionEventObserver)
|
||||
.idleThreshold(30000)
|
||||
.build()
|
||||
// 创建会话对象
|
||||
mTcrSession = TcrSdk.getInstance().createTcrSession(tcrSessionConfig)
|
||||
if (mTcrSession == null) {
|
||||
Log.e(TAG, "mTcrSession = null")
|
||||
showToast("创建TcrSession失败,请查看日志", Toast.LENGTH_SHORT)
|
||||
return
|
||||
}
|
||||
// 创建和初始化渲染视图
|
||||
runOnUiThread { initTcrRenderView() }
|
||||
}
|
||||
|
||||
override fun onFailure(code: Int, msg: String) {
|
||||
val errorMsg = "init SDK failed:$code msg:$msg"
|
||||
Log.e(TAG, errorMsg)
|
||||
showToast(errorMsg, Toast.LENGTH_LONG)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
disconnectBtn.setOnClickListener {
|
||||
AppExecutor.ioExecutor.execute {
|
||||
OkHelper.stopServerSession("rbmk")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化渲染视图
|
||||
*/
|
||||
private fun initTcrRenderView() {
|
||||
// 创建渲染视图
|
||||
mRenderView = TcrSdk.getInstance()
|
||||
.createTcrRenderView(this@CloudGameActivity, mTcrSession!!, TcrRenderViewType.SURFACE)
|
||||
if (mRenderView == null) {
|
||||
Log.e(TAG, "mRenderView = null")
|
||||
showToast("创建TcrRenderView失败,请查看日志", Toast.LENGTH_SHORT)
|
||||
return
|
||||
}
|
||||
// 将渲染视图添加到界面上
|
||||
(findViewById<View>(R.id.render_view_parent) as FrameLayout).addView(mRenderView)
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过http请求业务后台并获取ServerSession,拿到ServerSession后启动会话<br></br>
|
||||
* 如果您需要启动云应用请调用CloudRenderBiz.getInstance().startProject()<br></br>
|
||||
* 如果您需要启动云游戏请调用CloudRenderBiz.getInstance().startGame()<br></br>
|
||||
* <br></br>
|
||||
* 无论启动的是云应用还是云游戏,都需要接入方准备相应的业务后台环境。<br></br>
|
||||
* 云渲染团队提供了一个测试环境,可通过[TcrTestEnv]工具进行调用,方便您做客户端SDK接入测试。<br></br>
|
||||
*/
|
||||
private fun requestServerSession(clientSession: String) {
|
||||
Log.i(TAG, "init session success:$clientSession")
|
||||
|
||||
ioExecutor.execute {
|
||||
val session = obtainServerSession(clientSession, "rbmk")
|
||||
if (!TextUtils.isEmpty(session)) {
|
||||
mTcrSession!!.start(session)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 旋转屏幕方向, 以便本地的屏幕方向和云端保持一致<br></br>
|
||||
* 注意: 请确保Manifest中的Activity有android:configChanges="orientation|screenSize"配置, 避免Activity因旋转而被销毁.<br></br>
|
||||
*/
|
||||
@SuppressLint("SourceLockedOrientationActivity")
|
||||
private fun updateOrientation() {
|
||||
Log.i(TAG, "updateOrientation:" + mScreenConfig!!.orientation)
|
||||
if (mScreenConfig!!.orientation == "portrait") {
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
|
||||
} else if (mScreenConfig!!.orientation == "landscape") {
|
||||
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据云端屏幕配置旋转视频画面
|
||||
*/
|
||||
private fun updateRotation() {
|
||||
if (!mScreenConfigChanged || !mVideoStreamConfigChanged) {
|
||||
Log.w(
|
||||
TAG, ("updateRotation failed,mScreenConfigChanged=" + mScreenConfigChanged
|
||||
+ " mVideoStreamConfigChanged=" + mScreenConfigChanged)
|
||||
)
|
||||
return
|
||||
}
|
||||
if (mVideoStreamConfig!!.width > mVideoStreamConfig!!.height) {
|
||||
if (mScreenConfig!!.orientation == "portrait") {
|
||||
mRenderView!!.setVideoRotation(VideoRotation.ROTATION_90)
|
||||
} else {
|
||||
mRenderView!!.setVideoRotation(VideoRotation.ROTATION_0)
|
||||
}
|
||||
} else {
|
||||
if (mScreenConfig!!.orientation == "landscape") {
|
||||
mRenderView!!.setVideoRotation(VideoRotation.ROTATION_270)
|
||||
} else {
|
||||
mRenderView!!.setVideoRotation(VideoRotation.ROTATION_0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为不同的云端实例设置处理器
|
||||
*
|
||||
*
|
||||
* 对于云端PC应用可以使用[PcTouchListener]
|
||||
* (云端PC应用在支持windows多点触摸时可使用[MobileTouchListener])
|
||||
*
|
||||
* 对于手游要使用[MobileTouchListener]
|
||||
*/
|
||||
private fun setTouchHandler(session: TcrSession, renderView: TcrRenderView, gameType: Int) {
|
||||
when (gameType) {
|
||||
MOBILE_GAME -> renderView.setOnTouchListener(MobileTouchListener(session))
|
||||
PC_GAME -> {
|
||||
val pcTouchListener = PcTouchListener(session)
|
||||
pcTouchListener.zoomHandler.setZoomRatio(1f, 5f)
|
||||
renderView.setOnTouchListener(pcTouchListener)
|
||||
pcTouchListener.setShortClickListener(PcClickListener(session))
|
||||
}
|
||||
|
||||
else -> Log.e(TAG, "UNKNOWN DeviceMode!!")
|
||||
}
|
||||
}
|
||||
|
||||
private fun showToast(msg: String, duration: Int) {
|
||||
runOnUiThread {
|
||||
Toast.makeText(this@CloudGameActivity, msg, duration)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 观察TcrSession通知出的各类事件,处理各类事件通知的消息和数据
|
||||
*
|
||||
* @see TcrSession.Event
|
||||
*/
|
||||
private val mSessionEventObserver = TcrSession.Observer { event, eventData ->
|
||||
when (event) {
|
||||
TcrSession.Event.STATE_INITED -> {
|
||||
// 回调数据中拿到client session并请求ServerSession
|
||||
val clientSession = eventData as String
|
||||
requestServerSession(clientSession)
|
||||
}
|
||||
|
||||
TcrSession.Event.STATE_CONNECTED -> {
|
||||
// 连接成功后设置操作模式
|
||||
// 与云端的交互需在此事件回调后开始调用接口
|
||||
runOnUiThread {
|
||||
setTouchHandler(
|
||||
mTcrSession!!,
|
||||
mRenderView!!, PC_GAME
|
||||
)
|
||||
}
|
||||
createCustomDataChannel()
|
||||
}
|
||||
|
||||
TcrSession.Event.STATE_RECONNECTING -> showToast("重连中...", Toast.LENGTH_LONG)
|
||||
TcrSession.Event.STATE_CLOSED -> {
|
||||
showToast("会话关闭", Toast.LENGTH_SHORT)
|
||||
finish()
|
||||
}
|
||||
|
||||
TcrSession.Event.SCREEN_CONFIG_CHANGE -> {
|
||||
mScreenConfig = eventData as ScreenConfig
|
||||
updateOrientation()
|
||||
mScreenConfigChanged = true
|
||||
updateRotation()
|
||||
}
|
||||
|
||||
TcrSession.Event.VIDEO_STREAM_CONFIG_CHANGED -> {
|
||||
mVideoStreamConfig = eventData as VideoStreamConfig
|
||||
mVideoStreamConfigChanged = true
|
||||
updateRotation()
|
||||
}
|
||||
|
||||
TcrSession.Event.CLIENT_STATS -> {
|
||||
val statsInfo = eventData as StatsInfo
|
||||
runOnUiThread {
|
||||
binding.statsValue.text =
|
||||
(" fps: " + statsInfo.fps + " bitrate: " + mDf.format(
|
||||
statsInfo.bitrate / 1024.0 / 1024.0
|
||||
)
|
||||
+ "Mb/s rtt: " + statsInfo.rtt + "ms")
|
||||
}
|
||||
}
|
||||
|
||||
TcrSession.Event.CURSOR_STATE_CHANGE -> {
|
||||
val cursorState = eventData as CursorState
|
||||
Log.i(
|
||||
TAG,
|
||||
"cursor showing state changed, $cursorState"
|
||||
)
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建自定义数据通道
|
||||
*/
|
||||
private fun createCustomDataChannel() {
|
||||
if (mTcrSession == null) {
|
||||
return
|
||||
}
|
||||
// 10000为数据通道端口,请替换为你们自己业务的端口
|
||||
mCustomDataChannel =
|
||||
mTcrSession!!.createCustomDataChannel(10000, object : CustomDataChannel.Observer {
|
||||
override fun onConnected(port: Int) {
|
||||
val msg = "Your message"
|
||||
mCustomDataChannel!!.send(ByteBuffer.wrap(msg.toByteArray(StandardCharsets.UTF_8)))
|
||||
Log.i(
|
||||
TAG,
|
||||
"onConnected() send data to port $port: $msg"
|
||||
)
|
||||
}
|
||||
|
||||
override fun onError(port: Int, code: Int, msg: String) {
|
||||
Log.e(TAG, "onError() $port msg:$msg")
|
||||
}
|
||||
|
||||
override fun onMessage(port: Int, data: ByteBuffer) {
|
||||
Log.i(
|
||||
TAG,
|
||||
"onMessage() port=" + port + " data=" + StandardCharsets.UTF_8.decode(data)
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (mTcrSession != null) return
|
||||
|
||||
super.onBackPressed()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
if (mTcrSession != null) {
|
||||
mTcrSession!!.release()
|
||||
}
|
||||
if (mRenderView != null) {
|
||||
mRenderView!!.release()
|
||||
}
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun readConfigFile(context: Context, fileName: String): String? {
|
||||
try {
|
||||
val am = context.assets
|
||||
val `is` = am.open(fileName)
|
||||
val isr = InputStreamReader(`is`, StandardCharsets.UTF_8)
|
||||
val input = CharArray(`is`.available())
|
||||
isr.read(input)
|
||||
isr.close()
|
||||
`is`.close()
|
||||
return String(input)
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
TAG,
|
||||
"readConfigFile failed:$e"
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TAG = "CloudGameActivity"
|
||||
|
||||
private const val MOBILE_GAME = 1
|
||||
private const val PC_GAME = 2
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,41 @@
|
||||
package com.gh.gamecenter.feature.cloudgame
|
||||
|
||||
import android.content.Intent
|
||||
import android.view.Gravity
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.gh.gamecenter.cloudgame.R
|
||||
import com.gh.gamecenter.common.utils.PackageFlavorHelper
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.lzf.easyfloat.EasyFloat
|
||||
import com.lzf.easyfloat.enums.ShowPattern
|
||||
import com.lzf.easyfloat.enums.SidePattern
|
||||
|
||||
object CloudGameHelper {
|
||||
|
||||
fun showCloudGameFloat(activity: AppCompatActivity) {
|
||||
if (PackageFlavorHelper.IS_TEST_FLAVOR) {
|
||||
EasyFloat.with(activity)
|
||||
.setLayout(R.layout.layout_cloud_window)
|
||||
.setTag("cloud_game_float")
|
||||
.setAnimator(null)
|
||||
.setGravity(Gravity.TOP.xor(Gravity.END), 0, 250F.dip2px())
|
||||
.setSidePattern(SidePattern.RESULT_SIDE)
|
||||
.setDragEnable(true)
|
||||
.setShowPattern(ShowPattern.CURRENT_ACTIVITY)
|
||||
.registerCallback {
|
||||
createResult { _, _, view ->
|
||||
val tv = view?.findViewById<TextView>(R.id.iconTv)
|
||||
tv?.text = "☁\uFE0F\uD83C\uDFAE"
|
||||
view?.setOnClickListener {
|
||||
// Handle click event
|
||||
activity.startActivity(Intent(activity, CloudGameActivity::class.java))
|
||||
}
|
||||
}
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package com.gh.gamecenter.feature.cloudgame
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import com.gh.gamecenter.core.iinterface.IApplication
|
||||
import com.google.auto.service.AutoService
|
||||
|
||||
@AutoService(IApplication::class)
|
||||
class HaloApp : IApplication {
|
||||
|
||||
override fun attachBaseContext(base: Context) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
override fun onCreate(application: Application) {
|
||||
mApp = application
|
||||
}
|
||||
|
||||
override fun onLowMemory() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
override fun onTerminate() {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
override fun onTrimMemory(level: Int) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
// Do nothing
|
||||
}
|
||||
|
||||
companion object {
|
||||
private lateinit var mApp: Application
|
||||
|
||||
@JvmStatic
|
||||
fun getInstance(): Application {
|
||||
return mApp
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
package com.gh.gamecenter.feature.cloudgame
|
||||
|
||||
import androidx.annotation.WorkerThread
|
||||
import com.gh.gamecenter.common.utils.EnvHelper
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import org.json.JSONObject
|
||||
|
||||
object OkHelper {
|
||||
|
||||
@WorkerThread
|
||||
fun obtainServerSession(clientSession: String, userId: String): String {
|
||||
val client = OkHttpClient()
|
||||
|
||||
// Define the JSON body as a string
|
||||
val json = """
|
||||
{
|
||||
"user_id": "${userId}",
|
||||
"client_session": "${clientSession}",
|
||||
"application_id": "app-attadogx"
|
||||
}
|
||||
"""
|
||||
|
||||
// Create a RequestBody with the JSON and the appropriate content type
|
||||
val mediaType = "application/json; charset=utf-8".toMediaType()
|
||||
val requestBody = json.toRequestBody(mediaType)
|
||||
|
||||
// Create the Request object
|
||||
val request = Request.Builder()
|
||||
.url(EnvHelper.getNewHost() + "tencent/car/start_app")
|
||||
.post(requestBody)
|
||||
.build()
|
||||
|
||||
try {
|
||||
val response = client.newCall(request).execute()
|
||||
if (response.isSuccessful) {
|
||||
val resultString = response.body?.string() ?: ""
|
||||
|
||||
if (resultString.isNotEmpty()) {
|
||||
return JSONObject(resultString).optString("server_session", "")
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
} else {
|
||||
"Request failed with code: ${response.code}" // Handle error case
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
"Error: ${e.message}" // Handle exceptions
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun stopServerSession(userId: String): String {
|
||||
val client = OkHttpClient()
|
||||
|
||||
// Define the JSON body as a string
|
||||
val json = """
|
||||
{
|
||||
"user_id": "${userId}"
|
||||
}
|
||||
"""
|
||||
|
||||
// Create a RequestBody with the JSON and the appropriate content type
|
||||
val mediaType = "application/json; charset=utf-8".toMediaType()
|
||||
val requestBody = json.toRequestBody(mediaType)
|
||||
|
||||
// Create the Request object
|
||||
val request = Request.Builder()
|
||||
.url(EnvHelper.getNewHost() + "tencent/car/stop_app")
|
||||
.post(requestBody)
|
||||
.build()
|
||||
|
||||
try {
|
||||
val response = client.newCall(request).execute()
|
||||
if (response.isSuccessful) {
|
||||
val resultString = response.body?.string() ?: ""
|
||||
|
||||
if (resultString.isNotEmpty()) {
|
||||
return JSONObject(resultString).optString("server_session", "")
|
||||
} else {
|
||||
return ""
|
||||
}
|
||||
} else {
|
||||
"Request failed with code: ${response.code}" // Handle error case
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
"Error: ${e.message}" // Handle exceptions
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<!-- First Stroke Layer -->
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="4dp" />
|
||||
<stroke
|
||||
android:width="3dp"
|
||||
android:color="@color/black" /> <!-- Second stroke color -->
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<!-- Second Stroke Layer -->
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="4dp" />
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="@color/primary_theme" /> <!-- First stroke color -->
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<!-- Transparent Content Layer -->
|
||||
<item
|
||||
android:bottom="4dp"
|
||||
android:left="4dp"
|
||||
android:right="4dp"
|
||||
android:top="4dp">
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="2dp" />
|
||||
<solid android:color="@android:color/transparent" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</layer-list>
|
||||
@ -0,0 +1,17 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="48dp"
|
||||
android:height="48dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M0,0h48v48h-48z"/>
|
||||
<path
|
||||
android:fillAlpha="0.6"
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M24,24m-24,0a24,24 0,1 1,48 0a24,24 0,1 1,-48 0"/>
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M18,17.608C18,16.038 19.728,15.08 21.06,15.913L31.286,22.304C32.54,23.087 32.54,24.913 31.286,25.696L21.06,32.088C19.728,32.92 18,31.962 18,30.392V17.608Z"/>
|
||||
</group>
|
||||
</vector>
|
||||
112
feature/cloud_game/src/main/res/layout/activity_cloudgame.xml
Normal file
112
feature/cloud_game/src/main/res/layout/activity_cloudgame.xml
Normal file
@ -0,0 +1,112 @@
|
||||
<?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"
|
||||
android:background="@color/black">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/render_view_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/rightController"
|
||||
app:layout_constraintStart_toEndOf="@id/leftController" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/keyboardContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="bottom"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/rightController"
|
||||
app:layout_constraintStart_toEndOf="@id/leftController" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/joystickContainer"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="bottom"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/rightController"
|
||||
app:layout_constraintStart_toEndOf="@id/leftController" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/stats_value"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingEnd="10dp"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="11sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/leftController"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<Button
|
||||
android:id="@+id/connectBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="启动" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/disconnectBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="关闭" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/rightController"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<Button
|
||||
android:id="@+id/keyboardBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="键盘" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/joystickBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="手柄" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/joystickEditBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="编辑手柄" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/cursorBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="鼠标" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
147
feature/cloud_game/src/main/res/layout/fragment_local_media.xml
Normal file
147
feature/cloud_game/src/main/res/layout/fragment_local_media.xml
Normal file
@ -0,0 +1,147 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<com.gh.gamecenter.common.view.MaterializedConstraintLayout 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"
|
||||
android:background="@color/ui_background_fixed_dark"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.gh.gamecenter.common.view.StatusBarView
|
||||
android:id="@+id/statusBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/ui_surface_fixed_dark" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/normal_toolbar_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/appbar_height"
|
||||
app:layout_constraintTop_toBottomOf="@id/statusBar">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/normal_toolbar"
|
||||
style="@style/Base_ToolbarStyle"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/ui_surface_fixed_dark"
|
||||
app:contentInsetEnd="0dp"
|
||||
app:contentInsetStartWithNavigation="0dp"
|
||||
app:navigationIcon="@null">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/backContainer"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/backBtn"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
app:srcCompat="@drawable/ic_function_close" />
|
||||
</FrameLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/normal_title"
|
||||
style="@style/toolbar_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:textColor="@color/text_aw_primary"
|
||||
android:textSize="16sp"
|
||||
tools:text="12345" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/arrowIv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true"
|
||||
android:layout_marginLeft="-12dp"
|
||||
android:layout_toRightOf="@+id/normal_title"
|
||||
android:padding="16dp"
|
||||
app:srcCompat="@drawable/ic_pin_down" />
|
||||
</RelativeLayout>
|
||||
</androidx.appcompat.widget.Toolbar>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:background="@color/ui_background_fixed_dark"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/normal_toolbar_container">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginLeft="2dp"
|
||||
android:layout_marginRight="2dp">
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/list_refresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list_rv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<include
|
||||
android:id="@+id/pieceMediaControl"
|
||||
layout="@layout/piece_media_control" />
|
||||
|
||||
<com.gh.gamecenter.common.view.NavigationBarView
|
||||
android:id="@+id/navigationBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/ui_surface_fixed_dark" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_ll_loading"
|
||||
layout="@layout/reuse_loading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true" />
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_no_connection"
|
||||
layout="@layout/reuse_no_connection" />
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_none_data"
|
||||
layout="@layout/reuse_none_data" />
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_data_exception"
|
||||
layout="@layout/reuse_data_exception" />
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/layout_activity_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</com.gh.gamecenter.common.view.MaterializedConstraintLayout>
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:cardCornerRadius="100dp"
|
||||
app:cardBackgroundColor="@color/secondary_yellow">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/iconTv"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp" />
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
3
feature/cloud_game/src/main/res/values-night/themes.xml
Normal file
3
feature/cloud_game/src/main/res/values-night/themes.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
</resources>
|
||||
@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
|
||||
</resources>
|
||||
10
feature/cloud_game/src/main/res/values/colors.xml
Normal file
10
feature/cloud_game/src/main/res/values/colors.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
</resources>
|
||||
3
feature/cloud_game/src/main/res/values/strings.xml
Normal file
3
feature/cloud_game/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">SimpleDemo</string>
|
||||
</resources>
|
||||
16
feature/cloud_game/src/main/res/values/themes.xml
Normal file
16
feature/cloud_game/src/main/res/values/themes.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<!-- Base application theme. -->
|
||||
<style name="Theme.SimpleDemo" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
|
||||
<!-- Primary brand color. -->
|
||||
<item name="colorPrimary">@color/purple_500</item>
|
||||
<item name="colorPrimaryVariant">@color/purple_700</item>
|
||||
<item name="colorOnPrimary">@color/white</item>
|
||||
<!-- Secondary brand color. -->
|
||||
<item name="colorSecondary">@color/teal_200</item>
|
||||
<item name="colorSecondaryVariant">@color/teal_700</item>
|
||||
<item name="colorOnSecondary">@color/black</item>
|
||||
<!-- Status bar color. -->
|
||||
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
</resources>
|
||||
@ -73,6 +73,7 @@ dependencies {
|
||||
api "com.tencent.mm.opensdk:wechat-sdk-android-without-mta:${mta}"
|
||||
api "androidx.room:room-runtime:${room}"
|
||||
api "androidx.room:room-rxjava2:${room}"
|
||||
api "androidx.room:room-ktx:${room}"
|
||||
ksp("androidx.room:room-compiler:${room}")
|
||||
api "androidx.collection:collection-ktx:${collection}"
|
||||
api "androidx.activity:activity:${activity}"
|
||||
|
||||
@ -337,6 +337,7 @@ object SensorsBridge {
|
||||
private const val EVENT_CALENDAR_PERMISSIONS_DIALOG_SHOW = "CalendarPermissionsDialogShow"
|
||||
private const val EVENT_CALENDAR_PERMISSIONS_DIALOG_CLICK = "CalendarPermissionsDialogClick"
|
||||
private const val CALENDAR_PERMISSIONS_DIALOG_RESULT = "CalendarPermissionsDialogResult"
|
||||
private const val EVENT_GAME_DETAIL_VIDEO_CLICK= "GameDetailVideoClick"
|
||||
|
||||
private var mIsSensorsEnabled = false
|
||||
|
||||
@ -5340,4 +5341,37 @@ object SensorsBridge {
|
||||
}
|
||||
trackEvent(CALENDAR_PERMISSIONS_DIALOG_RESULT, json)
|
||||
}
|
||||
|
||||
/**
|
||||
* 事件ID:GameDetailVideoClick
|
||||
* 事件名称:游戏详情视频点击事件
|
||||
*/
|
||||
fun trackGameDetailVideoClick(
|
||||
gameName: String,
|
||||
gameId: String,
|
||||
gameType: String,
|
||||
lastPageName: String,
|
||||
lastPageId: String,
|
||||
action: String,
|
||||
playType: String,
|
||||
isFullScreen: Boolean,
|
||||
sequence: Int,
|
||||
tabName: String,
|
||||
playLength: String
|
||||
) {
|
||||
val json = json {
|
||||
KEY_GAME_ID to gameId
|
||||
KEY_GAME_NAME to gameName
|
||||
KEY_GAME_TYPE to gameType
|
||||
KEY_LAST_PAGE_ID to lastPageId
|
||||
KEY_LAST_PAGE_NAME to lastPageName
|
||||
KEY_ACTION to action
|
||||
KEY_PLAY_TYPE to playType
|
||||
"is_full_screen" to isFullScreen
|
||||
KEY_SEQUENCE to sequence
|
||||
"tab_name" to tabName
|
||||
"play_length" to playLength
|
||||
}
|
||||
trackEvent(EVENT_GAME_DETAIL_VIDEO_CLICK, json)
|
||||
}
|
||||
}
|
||||
@ -105,6 +105,7 @@ class SegmentedFilterView @JvmOverloads constructor(context: Context, attrs: Att
|
||||
if (mContainerBackground != null) background = mContainerBackground
|
||||
mContainer = FrameLayout(context)
|
||||
mIndicator = View(context).apply {
|
||||
visibility = View.GONE
|
||||
background = mIndicatorBackground
|
||||
}
|
||||
mRadioGroup = RadioGroup(context).apply {
|
||||
@ -127,6 +128,7 @@ class SegmentedFilterView @JvmOverloads constructor(context: Context, attrs: Att
|
||||
mIndicator.updateLayoutParams<ViewGroup.LayoutParams> {
|
||||
width = mRadioGroup.getChildAt(defaultCheckPosition)?.width ?: mItemWidth
|
||||
}
|
||||
mIndicator.visibility = View.VISIBLE
|
||||
setChecked(defaultCheckPosition)
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,7 @@ gradle.ext.enableQuickLogin = gradle.startParameter.projectProperties.get('ENABL
|
||||
gradle.ext.enableAccelerator = gradle.startParameter.projectProperties.get('ENABLE_ACCELERATOR')?.toBoolean() ?: false
|
||||
gradle.ext.enableWechatPay = gradle.startParameter.projectProperties.get('ENABLE_WECHAT_PAY')?.toBoolean() ?: false
|
||||
gradle.ext.enableAliPay = gradle.startParameter.projectProperties.get('ENABLE_ALI_PAY')?.toBoolean() ?: false
|
||||
gradle.ext.enableCloudGame = gradle.startParameter.projectProperties.get('ENABLE_CLOUD_GAME')?.toBoolean() ?: false
|
||||
|
||||
// 是否启用路由文档输出
|
||||
gradle.ext.enableRouteDoc = gradle.startParameter.projectProperties.get('ENABLE_ROUTE_DOC')?.toBoolean() ?: false
|
||||
@ -68,7 +69,8 @@ def optionalModules = [
|
||||
':feature:ali_pay',
|
||||
':module_message',
|
||||
':module_sensors_data',
|
||||
':module_va_impl'
|
||||
':module_va_impl',
|
||||
':feature:cloud_game',
|
||||
]
|
||||
|
||||
// 定义 va 可选模块
|
||||
@ -164,4 +166,7 @@ if (gradle.ext.excludeOptionalModules == false) { // 默认全部模块都包含
|
||||
if (gradle.ext.enableAliPay) {
|
||||
include ':feature:ali_pay'
|
||||
}
|
||||
if (gradle.ext.enableCloudGame) {
|
||||
include ':feature:cloud_game'
|
||||
}
|
||||
}
|
||||
|
||||
2
vasdk
2
vasdk
Submodule vasdk updated: 932474708c...0da6ac4194
Reference in New Issue
Block a user