LearnDetailActivity.kt 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  1. package com.dlc.exam.ui.learn
  2. import android.annotation.SuppressLint
  3. import android.content.Intent
  4. import android.graphics.Bitmap
  5. import android.os.Build
  6. import android.os.Bundle
  7. import android.os.Handler
  8. import android.os.Looper
  9. import android.os.Message
  10. import android.util.Log
  11. import android.view.LayoutInflater
  12. import android.view.View
  13. import android.view.ViewGroup
  14. import android.webkit.ConsoleMessage
  15. import android.webkit.WebChromeClient
  16. import android.webkit.WebResourceError
  17. import android.webkit.WebResourceRequest
  18. import android.webkit.WebSettings
  19. import android.webkit.WebView
  20. import android.webkit.WebViewClient
  21. import android.widget.LinearLayout
  22. import androidx.annotation.RequiresApi
  23. import com.blankj.utilcode.util.LogUtils
  24. import com.bumptech.glide.Glide
  25. import com.dlc.exam.R
  26. import com.dlc.exam.common.CommonUtils
  27. import com.dlc.exam.databinding.ActivityLearnDetailBinding
  28. import com.dlc.exam.ui.common.BaseCountDownActivity
  29. import com.dlc.exam.ui.learn.test.ClassTestActivity
  30. import com.google.android.exoplayer2.ExoPlayer
  31. import com.google.android.exoplayer2.MediaItem
  32. import com.rc.core.log.RcLog
  33. import com.rc.core.util.EscapeUnescape
  34. import com.rc.httpcore.HttpConfig
  35. import com.rc.httpcore.client.ApiRepository
  36. import com.rc.httpcore.vo.request.ExamLearnReq
  37. /**
  38. * 学习详情
  39. *
  40. * @author ReiChin_
  41. */
  42. class LearnDetailActivity : BaseCountDownActivity<ActivityLearnDetailBinding>() {
  43. override fun createViewBinding() = ActivityLearnDetailBinding.inflate(LayoutInflater.from(this))
  44. private var mNextChapterList: ArrayList<LearnChapterBean>? = null
  45. private var mCurrentPosition = 0
  46. private lateinit var mCurrentChapter: LearnChapterBean
  47. private var mLearnCompleted = false
  48. private var mLearnTimeSecond = 0L
  49. private var mRelearn = false
  50. private var mVideoDraggable = false // 视频是否可以拖拽
  51. private var mAssessStatus = false // 是否需要课后考核
  52. private var exoPlayer: ExoPlayer? = null
  53. private var webView: WebView? = null
  54. private val indexUrl = "file:///android_asset/web/index.html"
  55. companion object {
  56. const val WHAT_LEARN_COUNT_DOWN = 1
  57. }
  58. override fun onResume() {
  59. super.onResume()
  60. exoPlayer?.play()
  61. }
  62. override fun initViews(savedInstanceState: Bundle?) {
  63. super.initViews(savedInstanceState)
  64. mNextChapterList = intent.getParcelableArrayListExtra("nextChapter")
  65. if (mNextChapterList.isNullOrEmpty()) {
  66. finish()
  67. return
  68. }
  69. mRelearn = intent.getBooleanExtra("relearn", false)
  70. mVideoDraggable = intent.getBooleanExtra("videoDraggable", false)
  71. mAssessStatus = intent.getBooleanExtra("assessStatus", false)
  72. exoPlayer = ExoPlayer.Builder(this).build()
  73. viewBinding.styledPlayerView.player = exoPlayer
  74. viewBinding.styledPlayerView.useController = false
  75. viewBinding.back.setOnClickListener { onBackPressed() }
  76. viewBinding.learnCompleted.visibility = if (mRelearn) View.GONE else View.VISIBLE
  77. viewBinding.learnCompleted.setOnClickListener {
  78. mLearnTimeSecond = 0L
  79. mLearnCountDownHandler.removeMessages(WHAT_LEARN_COUNT_DOWN)
  80. // 完成学习
  81. callFinishLearnApi()
  82. }
  83. initWebView()
  84. //TODO 等待webview初始化完毕后调用
  85. // showChapterInfo()
  86. }
  87. private fun initWebView() {
  88. val params = LinearLayout.LayoutParams(
  89. ViewGroup.LayoutParams.MATCH_PARENT,
  90. ViewGroup.LayoutParams.MATCH_PARENT
  91. )
  92. webView = WebView(applicationContext)
  93. webView?.layoutParams = params
  94. viewBinding.mainLL.addView(webView)
  95. WebView.setWebContentsDebuggingEnabled(true)
  96. val webSettings: WebSettings? = webView?.getSettings()
  97. webSettings?.javaScriptCanOpenWindowsAutomatically = true
  98. webSettings?.setSupportZoom(true)
  99. webSettings?.loadWithOverviewMode = true
  100. webSettings?.useWideViewPort = true
  101. webSettings?.domStorageEnabled = true //DOM Storage
  102. webSettings?.allowFileAccessFromFileURLs = true
  103. webSettings?.allowFileAccess = true
  104. webSettings?.allowContentAccess = true
  105. webSettings?.allowUniversalAccessFromFileURLs = true
  106. webSettings?.builtInZoomControls = true
  107. webSettings?.defaultTextEncodingName = "utf-8"
  108. webSettings?.javaScriptEnabled = true //设置webview支持javascript脚本
  109. webSettings?.mixedContentMode = WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE
  110. webView!!.webChromeClient = object : WebChromeClient() {
  111. override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean {
  112. if (ConsoleMessage.MessageLevel.ERROR == consoleMessage.messageLevel()) {
  113. LogUtils.e(
  114. "Jayce",
  115. "${consoleMessage.message()}+\n+${consoleMessage.lineNumber()}+\n+${consoleMessage.sourceId()}"
  116. )
  117. // pptxjs.js 该库异常会奔溃
  118. if (consoleMessage.sourceId().contains("pptxjs.js")) {
  119. showToast("该ppt不可用")
  120. this@LearnDetailActivity.finish()
  121. }
  122. } else {
  123. LogUtils.d(
  124. "Jayce",
  125. "${consoleMessage.message()}+\n+${consoleMessage.lineNumber()}+\n+${consoleMessage.sourceId()}"
  126. )
  127. }
  128. return true
  129. }
  130. override fun onProgressChanged(view: WebView, newProgress: Int) {
  131. super.onProgressChanged(view, newProgress)
  132. Log.d("Jayce", "p:$newProgress")
  133. }
  134. override fun onCloseWindow(window: WebView) {
  135. super.onCloseWindow(window)
  136. val js = "javascript:objPreviewerDestroy()"
  137. webView!!.evaluateJavascript(
  138. js
  139. ) { value -> }
  140. }
  141. }
  142. webView!!.webViewClient = object : WebViewClient() {
  143. /**
  144. * 页面开始加载
  145. * @param view The WebView that is initiating the callback.
  146. * @param url The url to be loaded.
  147. * @param favicon The favicon for this page if it already exists in the
  148. * database.
  149. */
  150. override fun onPageStarted(view: WebView, url: String, favicon: Bitmap) {
  151. super.onPageStarted(view, url, favicon)
  152. }
  153. /**
  154. * 页面加载完成
  155. * @param view The WebView that is initiating the callback.
  156. * @param url The url of the page.
  157. */
  158. override fun onPageFinished(view: WebView, url: String) {
  159. super.onPageFinished(view, url)
  160. // 等页面初始化完成后再去请求接口展示详情
  161. runOnUiThread {
  162. showChapterInfo()
  163. }
  164. }
  165. /**
  166. * 页面加载错误
  167. * @param view The WebView that is initiating the callback.
  168. * @param request The originating request.
  169. * @param error Information about the error occurred.
  170. */
  171. @RequiresApi(Build.VERSION_CODES.M)
  172. override fun onReceivedError(
  173. view: WebView,
  174. request: WebResourceRequest,
  175. error: WebResourceError
  176. ) {
  177. super.onReceivedError(view, request, error)
  178. Log.d(
  179. "Jayce",
  180. "${error.description}"
  181. )
  182. }
  183. }
  184. webView!!.requestFocus()
  185. webView!!.loadUrl(indexUrl)
  186. }
  187. private fun callFinishLearnApi() {
  188. showLoading("保存中...")
  189. val param = ExamLearnReq().apply {
  190. chapterId = mCurrentChapter.chapterId
  191. courseId = mCurrentChapter.courseId
  192. }
  193. val disposable = ApiRepository.examLearnFinish(param)
  194. .subscribe({ data ->
  195. dismissLoading()
  196. mLearnCompleted = true
  197. // 显示完成学习的Dialog
  198. val endChapter = mCurrentPosition == mNextChapterList!!.size - 1
  199. LearnCompletedDialog(this, data, endChapter, mCurrentChapter.assessStatus) {
  200. viewBinding.learnCompleted.isEnabled = false
  201. if (mCurrentChapter.assessStatus) {
  202. // 需要课后考核
  203. val intent = Intent(this, ClassTestActivity::class.java)
  204. intent.putExtra("chapterId", mCurrentChapter.chapterId)
  205. startActivity(intent)
  206. setResult(RESULT_OK)
  207. finish()
  208. } else {
  209. mCurrentPosition++
  210. showChapterInfo()
  211. }
  212. }.show()
  213. }, { throwable ->
  214. dismissLoading()
  215. throwable.printStackTrace()
  216. showNetError(throwable)
  217. })
  218. addDisposable(disposable)
  219. }
  220. @SuppressLint("SetTextI18n")
  221. private fun showChapterInfo() {
  222. Log.d("Jayce", "showChapterInfo")
  223. if (mCurrentPosition >= mNextChapterList!!.size) return
  224. mCurrentChapter = mNextChapterList!![mCurrentPosition]
  225. viewBinding.titleText.text = mCurrentChapter.title
  226. // 显示学习内容
  227. showLearnContent()
  228. if (mRelearn) {
  229. viewBinding.learnDuration.text =
  230. "已学习时长:${CommonUtils.formatLearnTime(mCurrentChapter.duration)}"
  231. } else {
  232. // 调用开始学习的API
  233. callStartLearnApi()
  234. }
  235. }
  236. // var str =
  237. // "http://192.168.251.2//labSystem//statics//bigFile//2023040314//2c7593c7-c6ba-4bf2-9dab-da7db456494e.mp4"
  238. var str =
  239. "https://vd4.bdstatic.com/mda-kfmi6042m94u0pdb/sc/mda-kfmi6042m94u0pdb.mp4"
  240. private fun showLearnContent() {
  241. when (mCurrentChapter.type) {
  242. "1" -> {
  243. // 文档
  244. webView?.visibility = View.VISIBLE
  245. viewBinding.progressbar.visibility = View.VISIBLE
  246. viewBinding.unknownFile.visibility = View.INVISIBLE
  247. viewBinding.imageContent.visibility = View.INVISIBLE
  248. viewBinding.styledPlayerView.visibility = View.INVISIBLE
  249. var url = if (mCurrentChapter.chapterData.startsWith("http")) {
  250. mCurrentChapter.chapterData
  251. } else {
  252. HttpConfig.API_BASE_URL + mCurrentChapter.chapterData
  253. }
  254. // RcLog.info("fileBrowsers_s:${s}")
  255. // val infoData =
  256. //// Base64.encodeToString(mCurrentChapter.chapterData.toByteArray(), Base64.DEFAULT)
  257. // Base64.encodeToString(s.toByteArray(), Base64.DEFAULT)
  258. // val baseurl = "${HttpConfig.FILE_BROWSER_BASE_URL}onlinePreview?url=$infoData"
  259. // RcLog.info("fileBrowser:${baseurl}")
  260. var function = ""
  261. var loadUrl = ""
  262. // ppt
  263. if (url.endsWith(".ppt") || url.endsWith(".pptx")) {
  264. function = "openPPT"
  265. loadUrl = "http://192.168.1.43:81/666/ppt.pptx"
  266. }
  267. // excel
  268. else if (url.endsWith(".xlsx") || url.endsWith(".xls")) {
  269. function = "openEXCEL"
  270. loadUrl = "http://192.168.1.43:81/666/excel.xlsx"
  271. }
  272. // word
  273. else if (url.endsWith(".docx") || url.endsWith(".doc")) {
  274. function = "openWORD"
  275. loadUrl = "http://192.168.1.43:81/666/word.docx"
  276. } else if (url.endsWith(".pdf")) {
  277. function = "openPDF"
  278. loadUrl = "http://192.168.1.43:81/666/pdf.pdf"
  279. } else {
  280. showToast("不支持的文件")
  281. this.finish()
  282. return
  283. }
  284. val js = "javascript:$function(\"$loadUrl\")"
  285. webView!!.evaluateJavascript(js) { value -> }
  286. // webView?.loadUrl(url)
  287. }
  288. "2" -> {
  289. // 视频 VISIBLE
  290. webView?.visibility = View.GONE
  291. viewBinding.progressbar.visibility = View.GONE
  292. viewBinding.unknownFile.visibility = View.GONE
  293. viewBinding.imageContent.visibility = View.GONE
  294. viewBinding.styledPlayerView.visibility = View.VISIBLE
  295. var url = if (mCurrentChapter.chapterData.startsWith("http")) {
  296. mCurrentChapter.chapterData
  297. // viewBinding.jzVideo.setUp(mCurrentChapter.chapterData, mCurrentChapter.title)
  298. } else {
  299. HttpConfig.API_BASE_URL + mCurrentChapter.chapterData
  300. // viewBinding.jzVideo.setUp(HttpConfig.API_BASE_URL + mCurrentChapter.chapterData,
  301. // mCurrentChapter.title)
  302. }
  303. // showToast("视频地址:" + HttpConfig.API_BASE_URL + mCurrentChapter.chapterData)
  304. val mediaItem = MediaItem.fromUri(url)
  305. exoPlayer?.let {
  306. it.setMediaItem(mediaItem)
  307. it.prepare()
  308. it.play()
  309. }
  310. // viewBinding.webView.loadUrl(str)
  311. // viewBinding.webView.loadUrl(mCurrentChapter.chapterData)
  312. // 这个对宿主没什么影响,建议声明
  313. // window.setFormat(PixelFormat.TRANSLUCENT)
  314. // viewBinding.webView.settings.mediaPlaybackRequiresUserGesture = false //设置自动播放
  315. // viewBinding.webView.overScrollMode = View.OVER_SCROLL_ALWAYS
  316. // //TODO 饺子播放器在内网环境 不可使用 加载缓慢
  317. // val bundle = Bundle()
  318. // bundle.putBoolean("standardFullScreen", false)
  319. // bundle.putBoolean("supportLiteWnd", true)
  320. // bundle.putInt("DefaultVideoScreen", 1)
  321. //// TbsVideo.openVideo(this, str, bundle)
  322. // TbsVideo.openVideo(this, url, bundle)
  323. }
  324. "3" -> {
  325. // 图片
  326. webView?.visibility = View.INVISIBLE
  327. viewBinding.progressbar.visibility = View.INVISIBLE
  328. viewBinding.unknownFile.visibility = View.INVISIBLE
  329. viewBinding.imageContent.visibility = View.VISIBLE
  330. viewBinding.styledPlayerView.visibility = View.INVISIBLE
  331. // var s = if (mCurrentChapter.chapterData.startsWith("http")) {
  332. // mCurrentChapter.chapterData
  333. // } else {
  334. // mCurrentChapter.chapterData
  335. // }
  336. Glide.with(this)
  337. .load(mCurrentChapter.chapterData)
  338. .placeholder(R.mipmap.img_bg_jzz)
  339. .error(R.mipmap.img_error)
  340. .into(viewBinding.imageContent)
  341. }
  342. "5" -> {
  343. // 富文本
  344. webView?.visibility = View.VISIBLE
  345. viewBinding.progressbar.visibility = View.VISIBLE
  346. viewBinding.unknownFile.visibility = View.INVISIBLE
  347. viewBinding.imageContent.visibility = View.INVISIBLE
  348. viewBinding.styledPlayerView.visibility = View.INVISIBLE
  349. webView?.loadDataWithBaseURL(
  350. null,
  351. EscapeUnescape.unescape(mCurrentChapter.chapterData),
  352. "text/html",
  353. "utf-8",
  354. null
  355. )
  356. }
  357. else -> {
  358. webView?.visibility = View.INVISIBLE
  359. viewBinding.progressbar.visibility = View.INVISIBLE
  360. viewBinding.unknownFile.visibility = View.VISIBLE
  361. viewBinding.imageContent.visibility = View.INVISIBLE
  362. viewBinding.styledPlayerView.visibility = View.INVISIBLE
  363. }
  364. }
  365. }
  366. private fun callStartLearnApi() {
  367. showLoading("加载中...")
  368. val param = ExamLearnReq().apply {
  369. chapterId = mCurrentChapter.chapterId
  370. courseId = mCurrentChapter.courseId
  371. }
  372. val disposable = ApiRepository.examLearnStart(param)
  373. .subscribe({
  374. dismissLoading()
  375. // 开始倒计时
  376. mLearnTimeSecond = 0
  377. mLearnCountDownHandler.sendEmptyMessage(WHAT_LEARN_COUNT_DOWN)
  378. }, { throwable ->
  379. dismissLoading()
  380. throwable.printStackTrace()
  381. showNetError(throwable)
  382. })
  383. addDisposable(disposable)
  384. }
  385. private val mLearnCountDownHandler = object : Handler(Looper.getMainLooper()) {
  386. @SuppressLint("SetTextI18n")
  387. override fun handleMessage(msg: Message) {
  388. mLearnTimeSecond++
  389. viewBinding.learnDuration.text =
  390. "已学习时长:${CommonUtils.formatLearnTime(mLearnTimeSecond)}"
  391. sendEmptyMessageDelayed(WHAT_LEARN_COUNT_DOWN, 1000)
  392. if (mLearnTimeSecond >= mCurrentChapter.duration) {
  393. viewBinding.learnCompleted.isEnabled = true
  394. }
  395. }
  396. }
  397. override fun onWindowFocusChanged(hasFocus: Boolean) {
  398. super.onWindowFocusChanged(hasFocus)
  399. if (hasFocus) {
  400. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  401. window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
  402. or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
  403. or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
  404. or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
  405. or View.SYSTEM_UI_FLAG_FULLSCREEN
  406. or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY)
  407. }
  408. }
  409. }
  410. override fun onPause() {
  411. RcLog.info("onPause=======================================")
  412. super.onPause()
  413. exoPlayer?.pause()
  414. }
  415. override fun onDestroy() {
  416. mLearnCountDownHandler.removeMessages(WHAT_LEARN_COUNT_DOWN)
  417. mLearnCountDownHandler.removeCallbacksAndMessages(null)
  418. super.onDestroy()
  419. exoPlayer?.release()
  420. }
  421. override fun onBackPressed() {
  422. RcLog.info("onBackPressed============================")
  423. if (mLearnCompleted) {
  424. setResult(RESULT_OK)
  425. }
  426. super.onBackPressed()
  427. }
  428. }