PlayGameActivity.kt 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. package com.yingyangfly.game.play
  2. import android.annotation.SuppressLint
  3. import android.app.Activity
  4. import android.app.ActivityManager
  5. import android.content.Context
  6. import android.content.Intent
  7. import android.os.Build
  8. import android.os.Bundle
  9. import android.text.TextUtils
  10. import android.util.Log
  11. import android.view.MotionEvent
  12. import android.view.View
  13. import android.view.ViewTreeObserver
  14. import android.webkit.WebResourceRequest
  15. import android.webkit.WebSettings
  16. import android.webkit.WebView
  17. import android.webkit.WebViewClient
  18. import android.widget.FrameLayout
  19. import androidx.constraintlayout.widget.ConstraintLayout
  20. import androidx.constraintlayout.widget.ConstraintSet
  21. import com.alibaba.android.arouter.facade.annotation.Route
  22. import com.alibaba.android.arouter.launcher.ARouter
  23. import com.bumptech.glide.Glide
  24. import com.yingyangfly.baselib.bean.GameDataBean
  25. import com.yingyangfly.baselib.bean.GetSaveGameRecordBean
  26. import com.yingyangfly.baselib.db.VoicePlayerBean
  27. import com.yingyangfly.baselib.ext.*
  28. import com.yingyangfly.baselib.mvvm.BaseMVVMActivity
  29. import com.yingyangfly.baselib.player.VoicePlayer
  30. import com.yingyangfly.baselib.router.RouterUrlCommon
  31. import com.yingyangfly.baselib.utils.GsonUtil
  32. import com.yingyangfly.baselib.utils.LiveEventBusUtil
  33. import com.yingyangfly.baselib.utils.RxBusCodes
  34. import com.yingyangfly.baselib.utils.img.ImgUtil
  35. import com.yingyangfly.game.R
  36. import com.yingyangfly.game.databinding.ActivityPlayGameBinding
  37. import com.yingyangfly.game.dialog.CompletionTrainingDialog
  38. import com.yingyangfly.game.dialog.CountdownDialog
  39. import com.yingyangfly.game.dialog.GameResultDialog
  40. import com.yingyangfly.game.utils.AndroidToJs
  41. /**
  42. * 玩游戏页面
  43. */
  44. @Route(path = RouterUrlCommon.playGame)
  45. class PlayGameActivity : BaseMVVMActivity<ActivityPlayGameBinding, PlayGameViewModel>(),
  46. View.OnTouchListener {
  47. /**
  48. * 游戏是否加载完成
  49. */
  50. private var loadingOver = false
  51. /**
  52. * 是否第一次启动activity
  53. */
  54. private var newStartGame = true
  55. /**
  56. * 倒计时是否结束
  57. */
  58. private var countdownSuccess = false
  59. private var gameCode: String = ""
  60. private var playClass: String = ""
  61. private var gameType: String = ""
  62. private var url: String = ""
  63. private var gameTotalScore: String = ""
  64. private var gameInbetweenImage = ""
  65. private var gameBackgroundImage = ""
  66. private var gameShortDesn = ""
  67. private lateinit var webSettings: WebSettings
  68. private var voicePlayer: VoicePlayer? = null
  69. private var gameResultDialog: GameResultDialog? = null
  70. var mWebViewHeight = 0
  71. var mWebViewWidth = 0
  72. private var mConstrainSet_one: ConstraintSet? = ConstraintSet()
  73. override fun onCreate(savedInstanceState: Bundle?) {
  74. gameCode = intent.getStringExtra("gameCode") ?: ""
  75. playClass = intent.getStringExtra("playClass") ?: ""
  76. url = intent.getStringExtra("url") ?: ""
  77. gameType = intent.getStringExtra("gameType") ?: ""
  78. gameTotalScore = intent.getStringExtra("gameTotalScore") ?: ""
  79. gameInbetweenImage = intent.getStringExtra("gameInbetweenImage") ?: ""
  80. gameBackgroundImage = intent.getStringExtra("gameBackgroundImage") ?: ""
  81. gameShortDesn = intent.getStringExtra("gameShortDesn") ?: ""
  82. super.onCreate(savedInstanceState)
  83. }
  84. override fun initViews() {
  85. voicePlayer = VoicePlayer.getInstance(mContext)
  86. if (TextUtils.isEmpty(url).not()) {
  87. initWebView(url)
  88. }
  89. ImgUtil.loadGameBackground(mContext, gameInbetweenImage, binding.imageBg)
  90. if (TextUtils.isEmpty(gameShortDesn).not()) {
  91. binding.tvPlay.text = gameShortDesn
  92. binding.tvIntroduce.text = gameShortDesn
  93. }
  94. }
  95. @SuppressLint("ClickableViewAccessibility")
  96. override fun initListener() {
  97. binding {
  98. tvPause.setOnTouchListener(this@PlayGameActivity)
  99. tvPauses.setOnTouchListener(this@PlayGameActivity)
  100. tvFullScreen.setOnTouchListener(this@PlayGameActivity)
  101. tvReduce.setOnSingleClickListener {
  102. setReduce(true)
  103. }
  104. }
  105. binding.gameLayout.viewTreeObserver.addOnGlobalLayoutListener(object :
  106. ViewTreeObserver.OnGlobalLayoutListener {
  107. override fun onGlobalLayout() {
  108. mWebViewHeight = binding.gameLayout.height
  109. mWebViewWidth = binding.gameLayout.width
  110. binding.gameLayout.viewTreeObserver.removeGlobalOnLayoutListener(this)
  111. }
  112. })
  113. }
  114. @SuppressLint("SetTextI18n")
  115. override fun initData() {
  116. Glide.with(mContext)
  117. .asGif()
  118. .load(R.drawable.fish)
  119. .into(binding.loadingImage)
  120. initLiveData()
  121. }
  122. override fun onNewIntent(intent: Intent?) {
  123. super.onNewIntent(intent)
  124. newStartGame = false
  125. runOnUiThread {
  126. binding.webView.evaluateJavascript(
  127. "javascript:CallContinueGame()"
  128. ) {
  129. }
  130. }
  131. }
  132. /**
  133. * 保存游戏进度
  134. */
  135. private fun saveData(gameDataBean: GameDataBean) {
  136. val requestBean = GetSaveGameRecordBean()
  137. requestBean.gameCode = gameCode
  138. requestBean.playClass = playClass
  139. requestBean.isPass = gameDataBean.isPass
  140. requestBean.duration = gameDataBean.curLevelTime
  141. requestBean.gameLevel = gameDataBean.curLevel
  142. requestBean.gameScore = gameDataBean.curLevelScore
  143. LiveEventBusUtil.send(RxBusCodes.REPORTGAMERECORDS, requestBean)
  144. }
  145. @SuppressLint("JavascriptInterface", "SetJavaScriptEnabled")
  146. private fun initWebView(gameUrl: String) {
  147. webSettings = binding.webView.settings
  148. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  149. webSettings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
  150. }
  151. val ua = webSettings.userAgentString
  152. //必须设置
  153. webSettings.userAgentString = "$ua; app/lottchina Android"
  154. webSettings.javaScriptCanOpenWindowsAutomatically = true
  155. webSettings.javaScriptEnabled = true
  156. webSettings.setAppCacheEnabled(true)
  157. webSettings.loadWithOverviewMode = true
  158. webSettings.allowFileAccess = true
  159. webSettings.allowUniversalAccessFromFileURLs = true
  160. webSettings.cacheMode = WebSettings.LOAD_DEFAULT
  161. webSettings.builtInZoomControls = false
  162. webSettings.setSupportZoom(false)
  163. webSettings.displayZoomControls = false
  164. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  165. webSettings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
  166. }
  167. webSettings.layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN
  168. webSettings.useWideViewPort = true//关键点
  169. //视频自动播放
  170. webSettings.mediaPlaybackRequiresUserGesture = false
  171. /**必须的设置, 访问网页版的H5,一定要设置。该方法是设置支持DomStorage,
  172. * DOM Storage 分为 sessionStorage 和 localStorage。
  173. * localStorage 对象和 sessionStorage 对象使用方法基本相同,它们的区别在于作用的范围不同。
  174. * sessionStorage 用来存储与页面相关的数据,它在页面关闭后无法使用。而 localStorage 则持久存在,在页面关闭后也可以使用。
  175. */
  176. webSettings.domStorageEnabled = true
  177. // 通过addJavascriptInterface()将Java对象映射到JS对象 下面一行代码是 JS调用原生方法
  178. binding.webView.addJavascriptInterface(AndroidToJs(), "AndroidInterface")
  179. binding.webView.isDrawingCacheEnabled = true
  180. binding.webView.buildDrawingCache()
  181. binding.webView.buildLayer()
  182. binding.webView.webViewClient = object : WebViewClient() {
  183. override fun shouldOverrideUrlLoading(
  184. view: WebView?,
  185. request: WebResourceRequest?
  186. ): Boolean {
  187. if (request != null) {
  188. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  189. view?.loadUrl(request.url.toString())
  190. } else {
  191. view?.loadUrl(request.toString())
  192. }
  193. } else {
  194. gameUrl.let {
  195. view?.loadUrl(it)
  196. }
  197. }
  198. return true
  199. }
  200. override fun onPageFinished(view: WebView?, url: String?) {
  201. super.onPageFinished(view, url)
  202. }
  203. }
  204. binding.webView.loadUrl(gameUrl)
  205. }
  206. @SuppressLint("ClickableViewAccessibility")
  207. override fun onTouch(v: View, event: MotionEvent): Boolean {
  208. when (event.action) {
  209. MotionEvent.ACTION_DOWN -> {
  210. if (v.id == R.id.tvPause || v.id == R.id.tvPauses || v.id == R.id.tvFullScreen) {
  211. v.startAnimation(getScaleAnimation())
  212. }
  213. }
  214. MotionEvent.ACTION_UP -> {
  215. v.startAnimation(getEndAnimation())
  216. if (v.id == R.id.tvPause || v.id == R.id.tvPauses) {
  217. runOnUiThread {
  218. binding.webView.evaluateJavascript(
  219. "javascript:CallPauseGame()"
  220. ) {
  221. ARouter.getInstance().build(RouterUrlCommon.gameIntroduction)
  222. .withString("gameCode", gameCode)
  223. .withString("playClass", playClass)
  224. .withTransition(R.anim.leftin, R.anim.leftout)
  225. .navigation(mContext)
  226. }
  227. }
  228. } else if (v.id == R.id.tvFullScreen) {
  229. setReduce(false)
  230. }
  231. }
  232. MotionEvent.ACTION_CANCEL -> {
  233. if (v.id == R.id.tvPause || v.id == R.id.tvPauses || v.id == R.id.tvFullScreen) {
  234. v.startAnimation(getEndAnimation())
  235. }
  236. }
  237. }
  238. return true
  239. }
  240. override fun onResume() {
  241. super.onResume()
  242. if (newStartGame) {
  243. newStartGame = false
  244. val countdownDialog = CountdownDialog()
  245. countdownDialog.setContentBackground(gameInbetweenImage)
  246. countdownDialog.onDialogClickListener = {
  247. if (loadingOver) {
  248. callStartGame()
  249. }
  250. }
  251. countdownDialog.show(supportFragmentManager, "countdownDialog")
  252. }
  253. mConstrainSet_one?.clone(binding.gameParentLayout)
  254. }
  255. override fun onDestroy() {
  256. destoryWebView()
  257. super.onDestroy()
  258. }
  259. /**
  260. * 结束webview清空缓存
  261. */
  262. private fun destoryWebView() {
  263. binding.webView.stopLoading() // 停止加载
  264. binding.webView.clearCache(true) // 清除缓存
  265. binding.webView.clearHistory() // 清楚历史
  266. binding.webView.loadUrl("about:blank")
  267. binding.webView.onPause()
  268. binding.webView.removeAllViews() // 移除webview上子view
  269. binding.webView.destroy() // 销毁WebView自身
  270. }
  271. /**
  272. * webview调用游戏中方法
  273. */
  274. private fun loadJs(it: String) {
  275. if (TextUtils.equals("0", it)) {
  276. binding.webView.evaluateJavascript(
  277. "javascript:CallNextLevel()"
  278. ) {
  279. }
  280. } else {
  281. binding.webView.evaluateJavascript(
  282. "javascript:CallRestartGame()"
  283. ) {
  284. }
  285. }
  286. }
  287. override fun onPause() {
  288. super.onPause()
  289. if (voicePlayer != null) {
  290. if (voicePlayer!!.isPlaying) {
  291. voicePlayer?.stop()
  292. }
  293. }
  294. }
  295. /**
  296. * 游戏开始
  297. */
  298. private fun callStartGame() {
  299. runOnUiThread {
  300. binding.webView.evaluateJavascript(
  301. "javascript:CallStartGame()"
  302. ) {
  303. }
  304. }
  305. }
  306. /**
  307. * 游戏进度监听
  308. */
  309. @SuppressLint("SetTextI18n")
  310. private fun initLiveData() {
  311. //游戏时间
  312. LiveEventBusUtil.observer<String>(this, RxBusCodes.SETTIME) {
  313. binding.tvTime.post {
  314. if (TextUtils.isEmpty(it).not()) {
  315. binding.tvTime.text = it
  316. binding.tvTimes.text = it
  317. }
  318. }
  319. }
  320. //分数
  321. LiveEventBusUtil.observer<String>(this, RxBusCodes.SETSCORE) {
  322. binding.tvScore.post {
  323. if (TextUtils.isEmpty(it).not()) {
  324. binding.tvScore.text = it
  325. binding.tvScores.text = "得分 $it"
  326. }
  327. }
  328. }
  329. //游戏关卡
  330. LiveEventBusUtil.observer<String>(this, RxBusCodes.SETLEVEL) {
  331. binding.tvGameLevel.post {
  332. if (TextUtils.isEmpty(it).not()) {
  333. binding.tvGameLevel.text = "第" + it + "关"
  334. binding.tvGameLevels.text = "第" + it + "关"
  335. }
  336. }
  337. }
  338. //游戏进度
  339. LiveEventBusUtil.observer<String>(this, RxBusCodes.SAVEDATA) {
  340. binding.tvScore.postDelayed({
  341. if (TextUtils.isEmpty(it).not()) {
  342. Log.e("wpp", "-----------" + it)
  343. val gameDataBean = GsonUtil.GsonToBean(it, GameDataBean::class.java)
  344. if (gameDataBean != null) {
  345. val isPass = gameDataBean.isPass
  346. if (TextUtils.equals("4", gameDataBean.isPass) || TextUtils.equals(
  347. "2",
  348. gameDataBean.isPass
  349. )
  350. ) {
  351. gameDataBean.isPass = "1"
  352. } else if (TextUtils.equals("3", gameDataBean.isPass)) {
  353. gameDataBean.isPass = "0"
  354. }
  355. saveData(gameDataBean)
  356. parsingData(gameDataBean, isPass)
  357. }
  358. }
  359. }, 100)
  360. }
  361. //游戏加载完成
  362. LiveEventBusUtil.observer<String>(this, RxBusCodes.LOADINGOVER) {
  363. loadingOver = true
  364. if (countdownSuccess) {
  365. callStartGame()
  366. }
  367. }
  368. //播放语音
  369. LiveEventBusUtil.observer<String>(this, RxBusCodes.PLAYVOICE) {
  370. if (it.isNullOrEmpty().not()) {
  371. val msg = it.split("voice:")
  372. if (msg.isNullOrEmpty().not()) {
  373. binding.tvIntroduce.text = msg[1]
  374. speak(msg[1])
  375. }
  376. }
  377. }
  378. //游戏超时退出
  379. LiveEventBusUtil.observer<String>(this, RxBusCodes.GAMEOVERTIME) {
  380. finish()
  381. }
  382. //返回首页结束游戏
  383. LiveEventBusUtil.observer<String>(this, RxBusCodes.CALLQUITGAME) {
  384. runOnUiThread {
  385. binding.webView.evaluateJavascript(
  386. "javascript:CallQuitGame()"
  387. ) {
  388. }
  389. }
  390. }
  391. //结束游戏
  392. LiveEventBusUtil.observer<String>(this, RxBusCodes.FINISHGAME) {
  393. runOnUiThread {
  394. finish()
  395. }
  396. }
  397. //游戏倒计时弹框消失
  398. LiveEventBusUtil.observer<String>(this, RxBusCodes.COUNTDOWNSUCCESS) {
  399. countdownSuccess = true
  400. }
  401. }
  402. /**
  403. * 语音合成
  404. */
  405. private fun speak(desn: String) {
  406. if (db != null) {
  407. val voicePlayerDao = db?.getVoicePlayerDao()
  408. if (voicePlayerDao != null) {
  409. val voicePlayerBean = voicePlayerDao.getVoicePlayerBean(desn)
  410. if (voicePlayerBean != null) {
  411. if (voicePlayer != null) {
  412. if (isTopActivity(this)) {
  413. voicePlayer?.play(voicePlayerBean.url) {
  414. binding.tvIntroduce.text = gameShortDesn
  415. callPlayBgMusic()
  416. }
  417. }
  418. }
  419. } else {
  420. getVoiceUrl(desn)
  421. }
  422. } else {
  423. getVoiceUrl(desn)
  424. }
  425. } else {
  426. getVoiceUrl(desn)
  427. }
  428. }
  429. /**
  430. * 获取声音url
  431. */
  432. private fun getVoiceUrl(taskDesn: String) {
  433. viewModel.getVoiceUrl(taskDesn, fail = {
  434. it.toast()
  435. }, success = {
  436. if (TextUtils.isEmpty(it).not()) {
  437. if (db != null) {
  438. val voicePlayerDao = db?.getVoicePlayerDao()
  439. if (voicePlayerDao != null) {
  440. val voicePlayerBean = VoicePlayerBean().apply {
  441. url = it
  442. words = taskDesn
  443. }
  444. voicePlayerDao.insert(voicePlayerBean)
  445. }
  446. }
  447. if (voicePlayer != null) {
  448. if (isTopActivity(this)) {
  449. voicePlayer?.play(it) {
  450. }
  451. }
  452. }
  453. }
  454. })
  455. }
  456. /**
  457. * 开启游戏音乐
  458. */
  459. private fun callPlayBgMusic() {
  460. runOnUiThread {
  461. binding.webView.evaluateJavascript(
  462. "javascript:CallPlayBgMusic()"
  463. ) {
  464. }
  465. }
  466. }
  467. /**
  468. * 游戏保存接口
  469. */
  470. private fun parsingData(gameDataBean: GameDataBean, isPass: String) {
  471. runOnUiThread {
  472. runOnUiThread {
  473. if (TextUtils.equals("2", isPass)) {
  474. LiveEventBusUtil.send(RxBusCodes.QUITGAME, "")
  475. finish()
  476. } else if (TextUtils.equals("3", isPass)) {
  477. val score: Int = gameTotalScore.toInt() + gameDataBean.curLevelScore.toInt()
  478. gameTotalScore = score.toString()
  479. if (TextUtils.equals("A", playClass)) {
  480. showCompletionTrainingDialog(gameDataBean)
  481. } else {
  482. //游戏通关
  483. ARouter.getInstance().build(RouterUrlCommon.gameSettlement)
  484. .withString("gameCode", gameCode)
  485. .withString("playClass", playClass)
  486. .withString("gameType", gameType)
  487. .withTransition(R.anim.leftin, R.anim.leftout)
  488. .navigation(mContext)
  489. }
  490. } else {
  491. val score: Int = gameTotalScore.toInt() + gameDataBean.curLevelScore.toInt()
  492. gameTotalScore = score.toString()
  493. if (TextUtils.equals("A", playClass)) {
  494. showCompletionTrainingDialog(gameDataBean)
  495. } else {
  496. //游戏成功/失败
  497. gameResultDialog = GameResultDialog()
  498. gameResultDialog?.setData(
  499. gameDataBean.curLevel,
  500. gameTotalScore,
  501. gameDataBean.curLevelScore,
  502. gameDataBean.isPass
  503. )
  504. gameResultDialog?.onBackHomeDialogClickListener = {
  505. finish()
  506. LiveEventBusUtil.send(RxBusCodes.GAMEOVERTIME, "")
  507. }
  508. gameResultDialog?.onGameDialogClickListener = {
  509. loadJs(it)
  510. }
  511. gameResultDialog?.show(supportFragmentManager, "gameResultDialog")
  512. }
  513. }
  514. }
  515. }
  516. }
  517. /**
  518. * 展示游戏结束弹窗
  519. */
  520. private fun showCompletionTrainingDialog(gameDataBean: GameDataBean) {
  521. val completionTrainingDialog = CompletionTrainingDialog()
  522. completionTrainingDialog.setData(
  523. gameTotalScore,
  524. gameDataBean.curLevelScore
  525. )
  526. completionTrainingDialog.onDialogClickListener = {
  527. finish()
  528. LiveEventBusUtil.send(RxBusCodes.GAMEOVERTIME, "")
  529. }
  530. completionTrainingDialog.show(
  531. supportFragmentManager,
  532. "completionTrainingDialog"
  533. )
  534. }
  535. /**
  536. * 判断当前activity是否位于栈顶
  537. */
  538. private fun isTopActivity(activity: Activity): Boolean {
  539. val am = activity.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
  540. val cn = am.getRunningTasks(1)[0].topActivity
  541. return cn!!.className == activity.javaClass.name
  542. }
  543. /**
  544. * 是否缩放
  545. */
  546. private fun setReduce(isReduce: Boolean) {
  547. binding.webView.post {
  548. binding.tvFullScreen.show(isReduce)
  549. binding.tvReduce.show(isReduce.not())
  550. if (isReduce) {
  551. val params = ConstraintLayout.LayoutParams(
  552. mWebViewWidth,
  553. mWebViewHeight
  554. )
  555. binding.gameLayout.radius = 49F
  556. binding.gameLayout.layoutParams = params
  557. binding.gameFullLayout.show(isReduce)
  558. mConstrainSet_one?.applyTo(binding.gameParentLayout)
  559. } else {
  560. val params = FrameLayout.LayoutParams(
  561. FrameLayout.LayoutParams.MATCH_PARENT,
  562. FrameLayout.LayoutParams.MATCH_PARENT
  563. )
  564. binding.gameLayout.radius = 0F
  565. binding.gameLayout.layoutParams = params
  566. binding.gameFullLayout.show(isReduce.not())
  567. }
  568. }
  569. }
  570. }