作者 杨谦

1.文本内容加载添加默认css样式

2.考试倒计时时间添加,todo,更多细化规则
3.todo..学情反馈数据显示规则待确认等
正在显示 27 个修改的文件 包含 1291 行增加81 行删除
@@ -135,4 +135,5 @@ dependencies { @@ -135,4 +135,5 @@ dependencies {
135 // 地图组件库,包括小车平移、点聚合等组件功能,详见开发指南。 135 // 地图组件库,包括小车平移、点聚合等组件功能,详见开发指南。
136 implementation 'com.tencent.map:sdk-utilities:1.0.6' 136 implementation 'com.tencent.map:sdk-utilities:1.0.6'
137 implementation 'com.tencent.map.geolocation:TencentLocationSdk-openplatform:7.3.0' 137 implementation 'com.tencent.map.geolocation:TencentLocationSdk-openplatform:7.3.0'
  138 + api 'org.jsoup:jsoup:1.11.3'
138 } 139 }
1 -package com.br_technology.securitytrain_master.ui.view.home.activity.course  
2 -  
3 -import com.br_technology.securitytrain_master.base.common.ConstantParamKey  
4 -import com.br_technology.securitytrain_master.databinding.ActivityPracticeBinding  
5 -import com.br_technology.securitytrain_master.ui.bean.CourseParam  
6 -import com.br_technology.securitytrain_master.ui.view.home.viewmodel.CoursePracticeViewModel  
7 -import com.wjx.android.wanandroidmvvm.base.view.BaseLifeCycleActivity  
8 -  
9 -/**  
10 - * Author by YSir  
11 - * Date on 2022/1/23.  
12 - * description  
13 - * PS: Not easy to write code, please indicate.  
14 - */  
15 -/**  
16 - * 课程练习详情  
17 - */  
18 -class CourseExamActivity :  
19 - BaseLifeCycleActivity<CoursePracticeViewModel, ActivityPracticeBinding>(ActivityPracticeBinding::inflate) {  
20 - override fun initDataObserver() {  
21 - }  
22 -  
23 - private var courseBean: CourseParam? = null  
24 -  
25 - override fun initData() {  
26 - super.initData()  
27 - courseBean = intent.getParcelableExtra(ConstantParamKey.COURSE_BEAN)  
28 - courseBean?.let {  
29 - mViewModel.startExam(it.mTrainClassId.toInt())  
30 - }  
31 - }  
32 -}  
@@ -17,6 +17,10 @@ import com.br_technology.securitytrain_master.view.listener.DialogListener @@ -17,6 +17,10 @@ import com.br_technology.securitytrain_master.view.listener.DialogListener
17 import com.br_technology.securitytrain_master.view.listener.ToolBarClickListener 17 import com.br_technology.securitytrain_master.view.listener.ToolBarClickListener
18 import com.luck.picture.lib.tools.ToastUtils 18 import com.luck.picture.lib.tools.ToastUtils
19 import com.wjx.android.wanandroidmvvm.base.view.BaseLifeCycleActivity 19 import com.wjx.android.wanandroidmvvm.base.view.BaseLifeCycleActivity
  20 +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
  21 +import io.reactivex.rxjava3.core.Flowable
  22 +import java.util.concurrent.TimeUnit
  23 +
20 24
21 /** 25 /**
22 * Author by YSir 26 * Author by YSir
@@ -75,13 +79,6 @@ class CoursePractiseActivity : @@ -75,13 +79,6 @@ class CoursePractiseActivity :
75 } else { 79 } else {
76 mViewModel.startPractise(it.mTrainClassId.toInt()) 80 mViewModel.startPractise(it.mTrainClassId.toInt())
77 } 81 }
78 - binding.toolBar.setTitle(  
79 - if (it.isTrainClass) {  
80 - "班级练习"  
81 - } else {  
82 - "班级考试"  
83 - }  
84 - )  
85 } 82 }
86 binding.apply { 83 binding.apply {
87 // 禁止滑动 84 // 禁止滑动
@@ -169,6 +166,33 @@ class CoursePractiseActivity : @@ -169,6 +166,33 @@ class CoursePractiseActivity :
169 } 166 }
170 } 167 }
171 168
  169 + fun countDown(seconds: Int) {
  170 + val total = seconds.toLong()
  171 + //intervalRange四个参数分别为:从0开始、到60结束、延时0开始,单位时间(NANOSECONDS,MICROSECONDS,MILLISECONDS,SECONDS,MINUTES,HOURS,DAYS)。
  172 + Flowable.intervalRange(0, total, 0, 1, TimeUnit.SECONDS)
  173 + .observeOn(AndroidSchedulers.mainThread())
  174 + .doOnNext {
  175 + val current = total - it
  176 + val seconds = current % 60
  177 + val minutes = current / 60
  178 + val hour = minutes / 60
  179 + val hourTx = if (hour < 10) {
  180 + "0${hour}"
  181 + } else "${hour}"
  182 + val secondsx = if (seconds < 10) {
  183 + "0${seconds}"
  184 + } else "${seconds}"
  185 + val minutesTx = if (minutes < 10) {
  186 + "0${minutes}"
  187 + } else "${minutes}"
  188 + binding.toolBar.setTitle("倒计时${hourTx}:${minutesTx}:${secondsx}")
  189 + }
  190 + .doOnComplete { //倒计时完毕事件处理
  191 + finish()
  192 + }
  193 + .subscribe()
  194 + }
  195 +
172 196
173 override fun initDataObserver() { 197 override fun initDataObserver() {
174 mViewModel.exam.observe(this) { 198 mViewModel.exam.observe(this) {
@@ -179,6 +203,7 @@ class CoursePractiseActivity : @@ -179,6 +203,7 @@ class CoursePractiseActivity :
179 } else { 203 } else {
180 ToastUtils.s(baseContext, it.msg) 204 ToastUtils.s(baseContext, it.msg)
181 } 205 }
  206 + countDown(it.data.over_second)
182 } 207 }
183 208
184 mViewModel.subSingleData.observe(this) { 209 mViewModel.subSingleData.observe(this) {
@@ -189,6 +214,7 @@ class CoursePractiseActivity : @@ -189,6 +214,7 @@ class CoursePractiseActivity :
189 mPagerAdapter.notifyDataSetChanged() 214 mPagerAdapter.notifyDataSetChanged()
190 mViewModel.subFinish(userItemId) 215 mViewModel.subFinish(userItemId)
191 } 216 }
  217 + binding.toolBar.setTitle("班级练习")
192 } 218 }
193 mViewModel.subFinish.observe(this) { 219 mViewModel.subFinish.observe(this) {
194 if (!titleDialog.isShowing) { 220 if (!titleDialog.isShowing) {
@@ -10,10 +10,13 @@ import com.br_technology.securitytrain_master.base.common.ConstantParamKey @@ -10,10 +10,13 @@ import com.br_technology.securitytrain_master.base.common.ConstantParamKey
10 import com.br_technology.securitytrain_master.base.common.ConstantParamKey.COURSE_BEAN 10 import com.br_technology.securitytrain_master.base.common.ConstantParamKey.COURSE_BEAN
11 import com.br_technology.securitytrain_master.databinding.ActivityTextDetailBinding 11 import com.br_technology.securitytrain_master.databinding.ActivityTextDetailBinding
12 import com.br_technology.securitytrain_master.ui.bean.CourseParam 12 import com.br_technology.securitytrain_master.ui.bean.CourseParam
  13 +import com.br_technology.securitytrain_master.ui.bean.FileBean
13 import com.br_technology.securitytrain_master.ui.view.common.FileReadActivity 14 import com.br_technology.securitytrain_master.ui.view.common.FileReadActivity
  15 +import com.br_technology.securitytrain_master.ui.view.home.adapter.PdfAdapter
14 import com.br_technology.securitytrain_master.ui.view.home.bean.TextLessonDetailBean 16 import com.br_technology.securitytrain_master.ui.view.home.bean.TextLessonDetailBean
15 import com.br_technology.securitytrain_master.ui.view.home.viewmodel.TextDetailViewModel 17 import com.br_technology.securitytrain_master.ui.view.home.viewmodel.TextDetailViewModel
16 import com.br_technology.securitytrain_master.util.TrainFileUtil 18 import com.br_technology.securitytrain_master.util.TrainFileUtil
  19 +import com.br_technology.securitytrain_master.view.listener.OnItemClickListener
17 import com.wjx.android.wanandroidmvvm.base.view.BaseLifeCycleActivity 20 import com.wjx.android.wanandroidmvvm.base.view.BaseLifeCycleActivity
18 import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers 21 import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
19 import io.reactivex.rxjava3.core.Observable 22 import io.reactivex.rxjava3.core.Observable
@@ -38,7 +41,6 @@ class TextDetailActivity : @@ -38,7 +41,6 @@ class TextDetailActivity :
38 courseBean?.let { course -> 41 courseBean?.let { course ->
39 if (course.isTrainClass) { 42 if (course.isTrainClass) {
40 mViewModel.textLessonsDetail(course.mLessonId.toInt()) 43 mViewModel.textLessonsDetail(course.mLessonId.toInt())
41 -  
42 } else { 44 } else {
43 mViewModel.materialDetail(course.mLessonId.toInt()) 45 mViewModel.materialDetail(course.mLessonId.toInt())
44 } 46 }
@@ -75,6 +77,50 @@ class TextDetailActivity : @@ -75,6 +77,50 @@ class TextDetailActivity :
75 .startsWith("http") 77 .startsWith("http")
76 } 78 }
77 79
  80 + override fun onPageFinished(view: WebView?, url: String?) {
  81 + super.onPageFinished(view, url)
  82 + if (courseBean?.isTrainClass == true) {
  83 + mViewModel.mTextLessonDetailBean.value.let {
  84 + val data = it?.data?.detail?.new_file
  85 + binding.pdf.apply {
  86 + val adapterPdf = PdfAdapter()
  87 + adapterPdf.addList(data!!)
  88 + adapter = adapterPdf
  89 + adapterPdf.addListener(object : OnItemClickListener<FileBean> {
  90 + override fun onClick(position: Int, data: FileBean) {
  91 + startActivity(
  92 + Intent(
  93 + this@TextDetailActivity,
  94 + FileReadActivity::class.java
  95 + )
  96 + .putExtra(ConstantParamKey.COMMON_URL, data.url)
  97 + )
  98 + }
  99 + })
  100 + }
  101 + }
  102 + } else {
  103 + mViewModel.mDetailMaterialNormal.value.let {
  104 + val data = it?.data?.detail?.new_file
  105 + binding.pdf.apply {
  106 + val adapterPdf = PdfAdapter()
  107 + adapterPdf.addList(data!!)
  108 + adapter = adapterPdf
  109 + adapterPdf.addListener(object : OnItemClickListener<FileBean> {
  110 + override fun onClick(position: Int, data: FileBean) {
  111 + startActivity(
  112 + Intent(
  113 + this@TextDetailActivity,
  114 + FileReadActivity::class.java
  115 + )
  116 + .putExtra(ConstantParamKey.COMMON_URL, data.url)
  117 + )
  118 + }
  119 + })
  120 + }
  121 + }
  122 + }
  123 + }
78 } 124 }
79 content.webChromeClient = WebChromeClient() 125 content.webChromeClient = WebChromeClient()
80 back.setOnClickListener { 126 back.setOnClickListener {
@@ -42,10 +42,10 @@ class AcademicFeedbackActivity : @@ -42,10 +42,10 @@ class AcademicFeedbackActivity :
42 ++page 42 ++page
43 }) 43 })
44 44
45 - mViewModel.mAcademicFeedbackDetailBean.observe(this,{ 45 + mViewModel.mAcademicFeedbackDetailBean.observe(this, {
46 DialogTitle(this@AcademicFeedbackActivity) 46 DialogTitle(this@AcademicFeedbackActivity)
47 .setImageTitle(R.mipmap.jiangshipingxi_bg) 47 .setImageTitle(R.mipmap.jiangshipingxi_bg)
48 - .setInfo(it.data.detail.des) 48 + .setInfo(it.data.detail.des ?: "")
49 .hideCompleteButton(true) 49 .hideCompleteButton(true)
50 .setTitleIsVisible(false) 50 .setTitleIsVisible(false)
51 .show() 51 .show()
@@ -26,7 +26,6 @@ class LearningReportActivity : @@ -26,7 +26,6 @@ class LearningReportActivity :
26 26
27 @SuppressLint("SetTextI18n") 27 @SuppressLint("SetTextI18n")
28 override fun initDataObserver() { 28 override fun initDataObserver() {
29 - // todo 数据设置  
30 mViewModel.data.observe(this, { 29 mViewModel.data.observe(this, {
31 val user = it.data.report.user 30 val user = it.data.report.user
32 binding.apply { 31 binding.apply {
@@ -4,7 +4,7 @@ import com.br_technology.securitytrain_master.R @@ -4,7 +4,7 @@ import com.br_technology.securitytrain_master.R
4 import com.br_technology.securitytrain_master.databinding.ActivityLearningReportDetailBinding 4 import com.br_technology.securitytrain_master.databinding.ActivityLearningReportDetailBinding
5 import com.br_technology.securitytrain_master.ui.view.home.pojo.VideoCourse 5 import com.br_technology.securitytrain_master.ui.view.home.pojo.VideoCourse
6 import com.br_technology.securitytrain_master.ui.view.mine.adapter.LearningReportDetailAdapter 6 import com.br_technology.securitytrain_master.ui.view.mine.adapter.LearningReportDetailAdapter
7 -import com.br_technology.securitytrain_master.ui.view.mine.viewmodel.LearningReportDetailViewModel 7 +import com.br_technology.securitytrain_master.ui.view.mine.viewmodel.LearningReportViewModel
8 import com.gyf.immersionbar.ImmersionBar 8 import com.gyf.immersionbar.ImmersionBar
9 import com.wjx.android.wanandroidmvvm.base.view.BaseLifeCycleActivity 9 import com.wjx.android.wanandroidmvvm.base.view.BaseLifeCycleActivity
10 10
@@ -15,7 +15,7 @@ import com.wjx.android.wanandroidmvvm.base.view.BaseLifeCycleActivity @@ -15,7 +15,7 @@ import com.wjx.android.wanandroidmvvm.base.view.BaseLifeCycleActivity
15 * 生成学习报告详情页面 15 * 生成学习报告详情页面
16 */ 16 */
17 class LearningReportDetailActivity : 17 class LearningReportDetailActivity :
18 - BaseLifeCycleActivity<LearningReportDetailViewModel, ActivityLearningReportDetailBinding>( 18 + BaseLifeCycleActivity<LearningReportViewModel, ActivityLearningReportDetailBinding>(
19 ActivityLearningReportDetailBinding::inflate 19 ActivityLearningReportDetailBinding::inflate
20 ) { 20 ) {
21 override fun initDataObserver() { 21 override fun initDataObserver() {
@@ -40,7 +40,7 @@ class TrainTestAdapter : BaseMultiItemQuickAdapter<TrainTestData, BaseViewHolder @@ -40,7 +40,7 @@ class TrainTestAdapter : BaseMultiItemQuickAdapter<TrainTestData, BaseViewHolder
40 setComplete( 40 setComplete(
41 holder, 41 holder,
42 practice!!.is_pass, 42 practice!!.is_pass,
43 - practice.score, 43 + practice.exam_score.toInt(),
44 practice.sub_time, 44 practice.sub_time,
45 practice.name 45 practice.name
46 ) 46 )
@@ -48,7 +48,7 @@ class TrainTestAdapter : BaseMultiItemQuickAdapter<TrainTestData, BaseViewHolder @@ -48,7 +48,7 @@ class TrainTestAdapter : BaseMultiItemQuickAdapter<TrainTestData, BaseViewHolder
48 setComplete( 48 setComplete(
49 holder, 49 holder,
50 exam!!.is_pass, 50 exam!!.is_pass,
51 - exam.score, 51 + exam.exam_score,
52 exam.sub_time, 52 exam.sub_time,
53 exam.name 53 exam.name
54 ) 54 )
@@ -14,8 +14,11 @@ import com.br_technology.securitytrain_master.ui.view.mine.adapter.CourseAdapter @@ -14,8 +14,11 @@ import com.br_technology.securitytrain_master.ui.view.mine.adapter.CourseAdapter
14 import com.br_technology.securitytrain_master.ui.view.mine.adapter.TrainTestAdapter 14 import com.br_technology.securitytrain_master.ui.view.mine.adapter.TrainTestAdapter
15 import com.br_technology.securitytrain_master.ui.view.mine.viewmodel.ClassDutyCourseViewModel 15 import com.br_technology.securitytrain_master.ui.view.mine.viewmodel.ClassDutyCourseViewModel
16 import com.br_technology.securitytrain_master.util.TranslateUnit 16 import com.br_technology.securitytrain_master.util.TranslateUnit
  17 +import com.br_technology.securitytrain_master.view.DialogMention
17 import com.br_technology.securitytrain_master.view.VerticalDecoration 18 import com.br_technology.securitytrain_master.view.VerticalDecoration
18 import com.wjx.android.wanandroidmvvm.base.view.BaseLifeCycleFragment 19 import com.wjx.android.wanandroidmvvm.base.view.BaseLifeCycleFragment
  20 +import java.text.SimpleDateFormat
  21 +import java.util.*
19 22
20 /** 23 /**
21 * Time: 8/3/2021 11:24 24 * Time: 8/3/2021 11:24
@@ -210,12 +213,48 @@ class ClassDutyCourseFragment(val type: Int) : @@ -210,12 +213,48 @@ class ClassDutyCourseFragment(val type: Int) :
210 getData() 213 getData()
211 } 214 }
212 215
213 -// //todo 时间校验限制等  
214 -// fun checkTime(exam: ExamBean): Boolean {  
215 -//  
216 -// } 216 + val dialogMention: DialogMention by lazy {
  217 + DialogMention(requireContext()).apply {
  218 + setTitle("温馨提示")
  219 + setMention("目前不在考试时间,是否继续进入考试?")
  220 + }
  221 + }
  222 +
  223 + //todo 时间校验限制等
  224 + fun checkTime(exam: ExamBean) {
  225 +
  226 +
  227 + }
217 228
218 override fun itemClick(item: TrainTestData) { 229 override fun itemClick(item: TrainTestData) {
  230 + var bool = true
  231 + if (!item.isTest) {
  232 + try {
  233 + val simple = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.CHINA)
  234 + val start = simple.parse(item.exam?.exam_start_time ?: "0").time
  235 + val end = simple.parse(item.exam?.exam_end_time ?: "0").time
  236 + bool = System.currentTimeMillis() in (start + 1) until end
  237 + if (item.exam?.is_complete == 0) {
  238 + if (bool) {
  239 + startNext(item)
  240 + } else {
  241 + dialogMention.show()
  242 + dialogMention.setListener {
  243 + startNext(item)
  244 + }
  245 + }
  246 + } else {
  247 + showTip("考试已结束")
  248 + }
  249 + } catch (exc: Exception) {
  250 + exc.printStackTrace()
  251 + }
  252 + } else {
  253 + startNext(item)
  254 + }
  255 + }
  256 +
  257 + fun startNext(item: TrainTestData) {
219 this@ClassDutyCourseFragment.startActivity( 258 this@ClassDutyCourseFragment.startActivity(
220 Intent( 259 Intent(
221 requireActivity(), 260 requireActivity(),
1 -package com.br_technology.securitytrain_master.ui.view.mine.repository  
2 -  
3 -import androidx.lifecycle.MutableLiveData  
4 -import com.br_technology.securitytrain_master.base.common.State  
5 -import com.br_technology.securitytrain_master.base.repository.ApiRepository  
6 -  
7 -/**  
8 - * Time: 8/2/2021 16:47  
9 - * Author: Captain  
10 - * Description: 初见时你很迷人  
11 - */  
12 -class LearningReportDetailRepository(val loadState: MutableLiveData<State>) : ApiRepository(loadState) {  
13 -}  
1 -package com.br_technology.securitytrain_master.ui.view.mine.viewmodel  
2 -  
3 -import com.br_technology.securitytrain_master.base.view.BaseViewModel  
4 -import com.br_technology.securitytrain_master.ui.view.mine.repository.LearningReportDetailRepository  
5 -  
6 -/**  
7 - * Time: 8/2/2021 16:47  
8 - * Author: Captain  
9 - * Description: 初见时你很迷人  
10 - */  
11 -class LearningReportDetailViewModel : BaseViewModel<LearningReportDetailRepository>() {  
12 -}  
@@ -5,6 +5,7 @@ import com.br_technology.securitytrain_master.base.network.response.BaseResponse @@ -5,6 +5,7 @@ import com.br_technology.securitytrain_master.base.network.response.BaseResponse
5 import com.br_technology.securitytrain_master.base.network.response.CommonReport 5 import com.br_technology.securitytrain_master.base.network.response.CommonReport
6 import com.br_technology.securitytrain_master.base.repository.TrainRepository 6 import com.br_technology.securitytrain_master.base.repository.TrainRepository
7 import com.br_technology.securitytrain_master.base.view.BaseViewModel 7 import com.br_technology.securitytrain_master.base.view.BaseViewModel
  8 +import com.br_technology.securitytrain_master.ui.bean.TrainDetail
8 import com.br_technology.securitytrain_master.ui.bean.TrainReport 9 import com.br_technology.securitytrain_master.ui.bean.TrainReport
9 10
10 /** 11 /**
@@ -14,8 +15,14 @@ import com.br_technology.securitytrain_master.ui.bean.TrainReport @@ -14,8 +15,14 @@ import com.br_technology.securitytrain_master.ui.bean.TrainReport
14 */ 15 */
15 class LearningReportViewModel : BaseViewModel<TrainRepository>() { 16 class LearningReportViewModel : BaseViewModel<TrainRepository>() {
16 val data = MutableLiveData<BaseResponse<CommonReport<TrainReport>>>() 17 val data = MutableLiveData<BaseResponse<CommonReport<TrainReport>>>()
  18 + val data = MutableLiveData<BaseResponse<CommonReport<TrainReport>>>()
17 19
18 fun getReport(){ 20 fun getReport(){
19 mRepository.trainReport(data) 21 mRepository.trainReport(data)
20 } 22 }
  23 +
  24 + fun getDetail(){
  25 + mRepository.get(data)
  26 + TrainDetail
  27 + }
21 } 28 }
  1 +package com.br_technology.securitytrain_master.util;
  2 +
  3 +import android.graphics.Color;
  4 +import android.os.Build;
  5 +
  6 +import androidx.annotation.RequiresApi;
  7 +
  8 +/**
  9 + * Author by YSir
  10 + * Date on 2021/3/22.
  11 + * description
  12 + * PS: Not easy to write code, please indicate.
  13 + */
  14 +public class ColorTransHelp {
  15 +
  16 + //16进制字符串转color
  17 + @RequiresApi(api = Build.VERSION_CODES.O)
  18 + public static Color fromStrToARGB(String str) {
  19 + String str1 = str.substring(0, 2);
  20 + String str2 = str.substring(2, 4);
  21 + String str3 = str.substring(4, 6);
  22 + String str4 = str.substring(6, 8);
  23 + int alpha = Integer.parseInt(str1, 16);
  24 + int red = Integer.parseInt(str2, 16);
  25 + int green = Integer.parseInt(str3, 16);
  26 + int blue = Integer.parseInt(str4, 16);
  27 + return Color.valueOf(red * 1.0f, green * 1.0f, blue * 1.0f, alpha * 1.0f);
  28 + }
  29 +
  30 + //颜色转16进制字符串
  31 + public static String toHexEncoding(int color) {
  32 + String R, G, B;
  33 + StringBuilder sb = new StringBuilder();
  34 + R = Integer.toHexString(Color.red(color));
  35 + G = Integer.toHexString(Color.green(color));
  36 + B = Integer.toHexString(Color.blue(color));
  37 + R = R.length() == 1 ? "0" + R : R;
  38 + G = G.length() == 1 ? "0" + G : G;
  39 + B = B.length() == 1 ? "0" + B : B;
  40 + sb.append(R);
  41 + sb.append(G);
  42 + sb.append(B);
  43 + return "#" + sb.toString();
  44 + }
  45 +
  46 +}
  1 +package com.br_technology.securitytrain_master.util;
  2 +
  3 +import android.Manifest;
  4 +import android.content.ContentResolver;
  5 +import android.content.Context;
  6 +import android.content.pm.PackageManager;
  7 +import android.net.ConnectivityManager;
  8 +import android.net.NetworkInfo;
  9 +import android.net.wifi.WifiInfo;
  10 +import android.net.wifi.WifiManager;
  11 +import android.os.Build;
  12 +import android.provider.Settings;
  13 +import android.telephony.TelephonyManager;
  14 +import android.text.TextUtils;
  15 +
  16 +import androidx.core.content.ContextCompat;
  17 +
  18 +import com.luck.picture.lib.tools.SPUtils;
  19 +
  20 +import java.io.BufferedReader;
  21 +import java.io.FileInputStream;
  22 +import java.io.IOException;
  23 +import java.io.InputStreamReader;
  24 +import java.lang.reflect.Method;
  25 +import java.net.InetAddress;
  26 +import java.net.NetworkInterface;
  27 +import java.net.SocketException;
  28 +import java.util.Enumeration;
  29 +import java.util.UUID;
  30 +
  31 +import static android.content.Context.WIFI_SERVICE;
  32 +
  33 +public class DeviceUtils {
  34 + static final String SP_NAME_DEVICE = "device_cache";
  35 + static final String SP_KEY_DEVICE_ID = "device_id";
  36 +
  37 + /**
  38 + * 设备id缓存
  39 + */
  40 + static String deviceId = null;
  41 +
  42 + /**
  43 + * 获取一个deviceId<br/>
  44 + * 生成规则:<br/>
  45 + * <li>获取本地缓存的deviceId,如果存在直接返回, 如果没有继续执行</li>
  46 + * <li>获取AndroidId当做deviceId,如果获取不到继续执行</li>
  47 + * <li>获取设备编号,如果获取不到继续执行</li>
  48 + * <li>随机生成一个UUID当做deviceId,如果获取不到继续执行</li>
  49 + * device获取后保存到本地,保证以后每次获取都可以获得同样的值
  50 + *
  51 + * @param context {@link Context}
  52 + * @return deviceId
  53 + */
  54 + public static String getDeviceId(Context context) {
  55 + if (TextUtils.isEmpty(deviceId)) {
  56 + /*获取sp中缓存的deviceid*/
  57 + if (TextUtils.isEmpty(deviceId = getCacheDeviceId(context))) {
  58 + /*获取androidId*/
  59 + if (TextUtils.isEmpty(deviceId = getAndroidId(context))) {
  60 + /*获取设备号*/
  61 + if (TextUtils.isEmpty(deviceId = getTelDeviceId(context))) {
  62 +// /*生成一个uuid当做设备id*/
  63 + deviceId = UUID.randomUUID().toString();
  64 + }
  65 + }
  66 +// SPUtils.putString(context, SP_NAME_DEVICE, SP_KEY_DEVICE_ID, deviceId);
  67 + }
  68 + }
  69 +
  70 + return deviceId;
  71 + }
  72 +
  73 + public static void clearDevId(Context context) {
  74 + deviceId = null;
  75 +// SPUtils.putString(context, SP_NAME_DEVICE, SP_KEY_DEVICE_ID, null);
  76 + }
  77 +
  78 + public static void setDevId(Context context, String devId) {
  79 + deviceId = devId;
  80 +// SPUtils.putString(context, SP_NAME_DEVICE, SP_KEY_DEVICE_ID, deviceId);
  81 + }
  82 +
  83 + /**
  84 + * 获取缓存的设备id
  85 + *
  86 + * @param context
  87 + * @return
  88 + */
  89 + static String getCacheDeviceId(Context context) {
  90 +// return SPUtils.getString(context, SP_NAME_DEVICE, SP_KEY_DEVICE_ID, null);
  91 + return "";
  92 + }
  93 +
  94 + /**
  95 + * 获取androidid
  96 + * 通过{@linkplain Settings.Secure#getString(ContentResolver, String) getString}({@linkplain Context#getContentResolver() ContentResolver}, {@link Settings.Secure#ANDROID_ID}) 获得AndroidId
  97 + *
  98 + * @param context {@link Context}
  99 + * @return
  100 + */
  101 + static String getAndroidId(Context context) {
  102 +
  103 + if (Build.VERSION.SDK_INT != Build.VERSION_CODES.FROYO) {
  104 + String ret = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ANDROID_ID);
  105 + if (!TextUtils.equals("9774d56d682e549c", ret)) {
  106 + return ret;
  107 + }
  108 + }
  109 + return null;
  110 + }
  111 +
  112 + /**
  113 + * 获取设备id
  114 + * 需要校验权限,不然6.0+系统可能会crash!!!
  115 + *
  116 + * @param context {@link Context}
  117 + * @return
  118 + * @see TelephonyManager#getDeviceId()
  119 + */
  120 + static String getTelDeviceId(Context context) {
  121 + if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
  122 + return null;
  123 + }
  124 + TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
  125 + return tm.getDeviceId();
  126 + }
  127 +
  128 + /**
  129 + * 获取是否有网络 只判断wfii跟移动网络</br>
  130 + * 需要权限{@link Manifest.permission#ACCESS_NETWORK_STATE}
  131 + *
  132 + * @param context
  133 + * @return
  134 + */
  135 + public static boolean isNetworkConnection(Context context) {
  136 + try {
  137 + ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
  138 + NetworkInfo networkInfo = manager.getActiveNetworkInfo();
  139 + if (networkInfo == null) {
  140 + return false;
  141 + }
  142 +
  143 + return networkInfo.isAvailable() && (networkInfo.getType() == ConnectivityManager.TYPE_WIFI || networkInfo.getType() == ConnectivityManager.TYPE_MOBILE);
  144 + } catch (Exception e) {
  145 + e.printStackTrace();
  146 + }
  147 + return true;
  148 + }
  149 +
  150 + /**
  151 + * 获取是否是wifi</br>
  152 + * 需要权限{@link Manifest.permission#ACCESS_NETWORK_STATE}
  153 + *
  154 + * @param context
  155 + * @return
  156 + */
  157 + public static boolean isConnectionWifi(Context context) {
  158 + try {
  159 + ConnectivityManager manager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
  160 + NetworkInfo networkInfo = manager.getActiveNetworkInfo();
  161 + if (networkInfo == null) {
  162 + return false;
  163 + }
  164 +
  165 + return networkInfo.isAvailable() && networkInfo.getType() == ConnectivityManager.TYPE_WIFI;
  166 + } catch (Exception e) {
  167 + e.printStackTrace();
  168 + }
  169 + return false;
  170 + }
  171 +
  172 + /**
  173 + * 获取ip
  174 + *
  175 + * @param context
  176 + * @return
  177 + */
  178 + public static String getIP(Context context) {
  179 +
  180 + if (isNetworkConnection(context)) {
  181 + if (isConnectionWifi(context)) {
  182 + WifiManager wifiManager = (WifiManager) context.getSystemService(WIFI_SERVICE);
  183 + WifiInfo wifiInfo = wifiManager.getConnectionInfo();
  184 + // 获取32位整型IP地址
  185 + int ipAddress = wifiInfo.getIpAddress();
  186 +
  187 + //返回整型地址转换成“*.*.*.*”地址
  188 + return String.format("%d.%d.%d.%d",
  189 + (ipAddress & 0xff), (ipAddress >> 8 & 0xff),
  190 + (ipAddress >> 16 & 0xff), (ipAddress >> 24 & 0xff));
  191 + } else {
  192 + try {
  193 + for (Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces(); e.hasMoreElements(); ) {
  194 + NetworkInterface intf = e.nextElement();
  195 + for (Enumeration<InetAddress> enumIPAddr = intf
  196 + .getInetAddresses(); enumIPAddr.hasMoreElements(); ) {
  197 + InetAddress inetAddress = enumIPAddr.nextElement();
  198 + // 如果不是回环地址
  199 + if (!inetAddress.isLoopbackAddress()) {
  200 + // 直接返回本地IP地址
  201 + return inetAddress.getHostAddress().toString();
  202 + }
  203 + }
  204 + }
  205 + } catch (SocketException e) {
  206 + e.printStackTrace();
  207 + }
  208 + }
  209 + }
  210 +
  211 + return null;
  212 + }
  213 +
  214 + public static String getHostname() {
  215 + try {
  216 + Method method = Build.class.getDeclaredMethod("getString", new Class[]{String.class});
  217 + method.setAccessible(true);
  218 + Object ret = method.invoke(null, new Object[]{"net.hostname"});
  219 + return (String) ret;
  220 + } catch (Exception e) {
  221 + e.printStackTrace();
  222 + }
  223 + return null;
  224 + }
  225 +
  226 + public static String getCoreVersion() {
  227 +
  228 + String kernelVersion = "";
  229 + BufferedReader bufferedReader = null;
  230 + try {
  231 + //读取配置文件 /proc/version
  232 + bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream("/proc/version")), 8 * 1024);
  233 + String info = "";
  234 + String line;
  235 + while ((line = bufferedReader.readLine()) != null) {
  236 + info += line;
  237 + }
  238 +
  239 + try {
  240 + if (info != "") {
  241 + final String keyword = "version ";
  242 + int index = info.indexOf(keyword);
  243 + line = info.substring(index + keyword.length());
  244 + index = line.indexOf(" ");
  245 + kernelVersion = line.substring(0, index);
  246 + }
  247 + } catch (IndexOutOfBoundsException e) {
  248 + e.printStackTrace();
  249 + }
  250 + } catch (IOException e) {
  251 + e.printStackTrace();
  252 + } finally {
  253 + if (bufferedReader != null) {
  254 + try {
  255 + bufferedReader.close();
  256 + } catch (IOException e) {
  257 + e.printStackTrace();
  258 + }
  259 + }
  260 + }
  261 + return kernelVersion;
  262 + }
  263 +
  264 +
  265 + public static String getNetworkState(Context context) {
  266 + ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
  267 + if (null == cm) {
  268 + return null;
  269 + }
  270 + NetworkInfo networkInfo = cm.getActiveNetworkInfo();
  271 + if (networkInfo == null) {
  272 + return null;
  273 + }
  274 + String ret = networkInfo.getTypeName();
  275 + String sub = networkInfo.getSubtypeName();
  276 + if (!TextUtils.isEmpty(sub)) {
  277 + ret += "-" + sub;
  278 + }
  279 + return ret;
  280 + }
  281 +}
  1 +package com.br_technology.securitytrain_master.util;
  2 +
  3 +import android.content.Context;
  4 +import android.text.TextUtils;
  5 +
  6 +import com.br_technology.securitytrain_master.base.view.BaseApplication;
  7 +
  8 +import org.jsoup.nodes.Document;
  9 +import org.jsoup.nodes.Element;
  10 +
  11 +import java.util.List;
  12 +
  13 +public class HtmlConvert {
  14 +
  15 + public static String convert(Document doc, boolean isDark, String url, String img) {
  16 + List<Element> list = doc.select("img");
  17 + List<Element> listVideo = doc.select("video");
  18 + for (Element element : listVideo) {
  19 + String dataSource = element.attr("src");
  20 + element.attr("object-fit", "fill");
  21 + element.attr("preload", "meta");
  22 + element.removeAttr("height");
  23 +
  24 + Element parent = element.parent();
  25 + parent.attr("width", "100%");
  26 + List<Element> elements = element.children();
  27 + if (elements != null && elements.size() > 0) {
  28 +// elements.get(0).attr("src", "http://vfile1.hnntv.cn/2020/1605/0853/2443/160508532443.ssm/160508532443.m3u8");
  29 + element.attr("controls", "controls");
  30 + element.attr("poster", TextUtils.isEmpty(img) ? "file:///android_res/mipmap/loading_web.png" : img);
  31 + } else {
  32 + if (TextUtils.isEmpty(dataSource) || !dataSource.startsWith("http")) {
  33 + if (!TextUtils.isEmpty(url)) {
  34 + Element sou = doc.createElement("source");
  35 + sou.attr("src", url);
  36 +// element.attr("src", url);
  37 + sou.appendTo(element);
  38 + element.attr("controls", "controls");
  39 + element.attr("poster", TextUtils.isEmpty(img) ? "file:///android_res/mipmap/loading_web.png" : img);
  40 + } else {
  41 + element.remove();
  42 + }
  43 + }
  44 + }
  45 + }
  46 + for (Element element : list) {
  47 + element.removeAttr("class");
  48 + String souce = element.attr("src");
  49 + if (loadImage(BaseApplication.Companion.getInstance().getApplicationContext())) {
  50 +// if (!souce.startsWith("http")) {
  51 +// element.attr("src", ConnectUrl.BASE_URL_IMG() + souce);
  52 +// }
  53 + if (element.hasParent()) {
  54 + Element parent = element.parent();
  55 + if (parent.tagName().equals("a")) {
  56 + element.attr("alt", "none_click");
  57 + }
  58 + }
  59 + } else {
  60 + element.attr("src", "file:///android_res/mipmap/loading_web.png");
  61 + if (element.hasParent()) {
  62 + Element parent = element.parent();
  63 + if (parent.tagName().equals("a")) {
  64 + element.attr("alt", "none_click");
  65 + }
  66 + }
  67 + }
  68 +// element.attr("onclick",onClick(souce,arr));
  69 + }
  70 + doc.select("br").remove();
  71 +// int fontSize = SettingKits.getFontSize(BaseApplication.Companion.getInstance().getApplicationContext());
  72 + int fontSize = 0;
  73 + List<Element> spanList = doc.select("span");
  74 + for (Element element : spanList) {
  75 + updateStyle(element, "font-size", "small");
  76 + }
  77 + Element head = doc.head();
  78 + head.append("<meta charset=\"utf-8\">")
  79 + .append("<meta name=\"viewport\" content=\"initial-scale=1.0, maximum-scale=1.0, user-scalable=no\" />");
  80 + if (isDark) {
  81 + head.append("<style>" +
  82 + "body{text-indent:0rem!important;" +
  83 + "text-align:justify!important;text-justify:inter-ideograph!important;" +
  84 + "word-break:break-all!important;font-color:#ffffff;overflow: hidden;}" +
  85 + "img{max-width: 100%!important;height:auto}" +
  86 + "video{width: 100%;display: block;}" +
  87 + "p{padding:0px;overflow: hidden;" +
  88 + "font-size:" + TextKits.fontSize(fontSize) + "px;" +
  89 + "color:#ffffff;" +
  90 + "}" +
  91 + "span{color:#ffffff;text-indent:2em;}" +
  92 + "</style>");
  93 + } else {
  94 + head.append("<style>" +
  95 + "body{text-indent:0rem!important;" +
  96 + "text-align:justify!important;text-justify:inter-ideograph!important;" +
  97 + "word-break:break-all!important;}" +
  98 + "img{max-width: 100%!important;height:auto}" +
  99 + "p{font-size:" + TextKits.fontSize(fontSize) + "px" +
  100 + ";}" +
  101 + "video{width:100%;}" +
  102 + "span{text-indent:2em;}" +
  103 + "</style>");
  104 + }
  105 +// if (listVideo.size() > 0) {
  106 +// head.append("<link href=\"file:///android_asset/video-js.min.css\" rel=\"stylesheet\"></link>" +
  107 +// "<script src=\"file:///android_asset/video.min.js\"></script>");
  108 +// }
  109 + List<Element> elements = doc.select("p");
  110 + for (Element element : elements) {
  111 + List<Element> list1 = element.children();
  112 + if (list1 == null || list1.isEmpty()) {
  113 + if (!isTextCenter(element)) {
  114 + updateStyle(element, "text-indent", "2em");
  115 + }
  116 + } else {
  117 + int count = 0;
  118 + for (Element element1 : list1) {
  119 + if (element1.is("img")
  120 + || element1.is("video")
  121 + || element1.is("audio")
  122 + || element1.is("a")
  123 + || (element1.children() != null && !element1.children().isEmpty())
  124 + || isTextCenter(element1)) {
  125 + count++;
  126 + }
  127 + }
  128 + if (count == 0) {
  129 + updateStyle(element, "text-indent", "2em");
  130 + }
  131 + }
  132 + }
  133 + return doc.toString();
  134 + }
  135 +
  136 +
  137 + public static String convertNone(Document doc, boolean isDark) {
  138 + List<Element> list = doc.select("img");
  139 + List<Element> listVideo = doc.select("video");
  140 + for (Element element : listVideo) {
  141 + element.remove();
  142 + }
  143 + for (Element element : list) {
  144 + element.removeAttr("class");
  145 + String souce = element.attr("src");
  146 + if (loadImage(BaseApplication.Companion.getInstance().getApplicationContext())) {
  147 +// if (!souce.startsWith("http")) {
  148 +// element.attr("src", ConnectUrl.BASE_URL_IMG() + souce);
  149 +// }
  150 + if (element.hasParent()) {
  151 + Element parent = element.parent();
  152 + if (parent.tagName().equals("a")) {
  153 + element.attr("alt", "none_click");
  154 + }
  155 + }
  156 + } else {
  157 + String url = "file:///android_res/mipmap/loading_web.png";
  158 + element.attr("src", url);
  159 + }
  160 +// element.attr("onclick",onClick(souce,arr));
  161 + }
  162 + doc.select("br").remove();
  163 +// int fontSize = SettingKits.getFontSize(BaseApplication.Companion.getInstance().getApplicationContext());
  164 + int fontSize = 0;
  165 + List<Element> spanList = doc.select("span");
  166 + for (Element element : spanList) {
  167 + updateStyle(element, "font-size", "small");
  168 + }
  169 + Element head = doc.head();
  170 + head.append("<meta charset=\"utf-8\">")
  171 + .append("<meta name=\"viewport\" content=\"initial-scale=1.0, maximum-scale=1.0, user-scalable=no\" />");
  172 + if (isDark) {
  173 + head.append("<style>" +
  174 + "body{text-indent:0rem!important;" +
  175 + "text-align:justify!important;text-justify:inter-ideograph!important;" +
  176 + "word-break:break-all!important;font-color:#ffffff;overflow: hidden;}" +
  177 + "img{max-width: 100%!important;height:auto}" +
  178 + "video{width: 100%;display: block;}" +
  179 + "p{padding:0px;overflow: hidden;" +
  180 + "font-size:" + TextKits.fontSize(fontSize) + "px;" +
  181 + "color:#ffffff;" +
  182 + "}" +
  183 + "span{color:#ffffff;text-indent:2em;}" +
  184 + "</style>");
  185 + } else {
  186 + head.append("<style>" +
  187 + "body{text-indent:0rem!important;" +
  188 + "text-align:justify!important;text-justify:inter-ideograph!important;" +
  189 + "word-break:break-all!important;}" +
  190 + "img{max-width: 100%!important;height:auto}" +
  191 + "p{font-size:" + TextKits.fontSize(fontSize) + "px" +
  192 + ";}" +
  193 + "video{width: 100%;height:260;}" +
  194 + "span{text-indent:2em;}" +
  195 + "</style>");
  196 + }
  197 + List<Element> elements = doc.select("p");
  198 + for (Element element : elements) {
  199 + List<Element> list1 = element.children();
  200 + if (list1 == null || list1.isEmpty()) {
  201 + if (!isTextCenter(element)) {
  202 + updateStyle(element, "text-indent", "2em");
  203 + }
  204 + } else {
  205 + int count = 0;
  206 + for (Element element1 : list1) {
  207 + if (element1.is("img")
  208 + || element1.is("video")
  209 + || element1.is("audio")
  210 + || element1.is("a")
  211 + || (element1.children() != null && !element1.children().isEmpty())
  212 + || isTextCenter(element1)) {
  213 + count++;
  214 + }
  215 + }
  216 + if (count == 0) {
  217 + updateStyle(element, "text-indent", "2em");
  218 + }
  219 + }
  220 + }
  221 + return doc.toString();
  222 + }
  223 +
  224 + private static boolean loadImage(Context context) {
  225 +// return DeviceUtils.isConnectionWifi(context) || !SettingKits.getLoadWifiImg(context);
  226 + return true;
  227 + }
  228 +
  229 + public static boolean isTextCenter(Element element) {
  230 + if (element.hasAttr("text-align")) {
  231 + String text = element.attr("text-align");
  232 + return text.contains("center");
  233 + }
  234 + if (element.hasAttr("style")) {
  235 + String style = element.attr("style");
  236 + return style.contains("center");
  237 + }
  238 + return false;
  239 + }
  240 +
  241 + private static void updateStyle(Element element, String attr, String value) {
  242 + if (element.hasAttr("style")) {
  243 + String styleStr = element.attr("style");
  244 + if (styleStr.contains(attr)) {
  245 + String s1 = styleStr.substring(0, styleStr.indexOf(attr));
  246 + String s2 = styleStr.substring(styleStr.indexOf(attr));
  247 + String s3 = s2.contains(";") ? s2.substring(s2.indexOf(";")) : "";
  248 + element.removeAttr("style");
  249 + element.attr("style", s1 + attr + ":" + value + s3);
  250 + } else {
  251 + element.removeAttr("style");
  252 + element.attr("style", styleStr + attr + ":" + value);
  253 + }
  254 + } else {
  255 + element.attr("style", attr + ":" + value);
  256 + }
  257 + }
  258 +}
  1 +package com.br_technology.securitytrain_master.util;
  2 +
  3 +import android.text.format.Time;
  4 +
  5 +
  6 +public class SettingKits {
  7 +
  8 + public static final int SHOW_MRCODE_GUIDE_VERSION = 30;//扫一扫引导
  9 +
  10 + static final String SP_NAME = "app_setting";
  11 + static boolean isUpdate;
  12 +
  13 + public static boolean isUpdate() {
  14 + return isUpdate;
  15 + }
  16 +
  17 + /**
  18 + * - 判断当前系统时间是否在特定时间的段内
  19 + * <p>
  20 + * <p>
  21 + * - @param beginHour 开始的小时,例如5
  22 + * <p>
  23 + * - @param beginMin 开始小时的分钟数,例如00
  24 + * <p>
  25 + * - @param endHour 结束小时,例如 8
  26 + * <p>
  27 + * - @param endMin 结束小时的分钟数,例如00
  28 + * <p>
  29 + * - @return true表示在范围内,否则false
  30 + */
  31 + public static boolean isCurrentInTimeScope(int beginHour, int beginMin, int endHour, int endMin) {
  32 + boolean result = false;// 结果
  33 + final long aDayInMillis = 1000 * 60 * 60 * 24;// 一天的全部毫秒数
  34 + final long currentTimeMillis = System.currentTimeMillis();// 当前时间
  35 +
  36 + Time now = new Time();// 注意这里导入的时候选择android.text.format.Time类,而不是java.sql.Time类
  37 + now.set(currentTimeMillis);
  38 +
  39 + Time startTime = new Time();
  40 + startTime.set(currentTimeMillis);
  41 + startTime.hour = beginHour;
  42 + startTime.minute = beginMin;
  43 +
  44 + Time endTime = new Time();
  45 + endTime.set(currentTimeMillis);
  46 + endTime.hour = endHour;
  47 + endTime.minute = endMin;
  48 +
  49 + if (!startTime.before(endTime)) {
  50 + // 跨天的特殊情况(比如22:00-8:00)
  51 + startTime.set(startTime.toMillis(true) - aDayInMillis);
  52 + result = !now.before(startTime) && !now.after(endTime); // startTime <= now <= endTime
  53 + Time startTimeInThisDay = new Time();
  54 + startTimeInThisDay.set(startTime.toMillis(true) + aDayInMillis);
  55 + if (!now.before(startTimeInThisDay)) {
  56 + result = true;
  57 + }
  58 + } else {
  59 + // 普通情况(比如 8:00 - 14:00)
  60 + result = !now.before(startTime) && !now.after(endTime); // startTime <= now <= endTime
  61 + }
  62 + return result;
  63 + }
  64 +
  65 +}
  66 +
  1 +package com.br_technology.securitytrain_master.util;
  2 +
  3 +import android.content.Context;
  4 +import android.graphics.Color;
  5 +import android.text.Html;
  6 +import android.text.Spannable;
  7 +import android.text.SpannableStringBuilder;
  8 +import android.text.Spanned;
  9 +import android.text.TextUtils;
  10 +import android.text.style.AbsoluteSizeSpan;
  11 +import android.text.style.CharacterStyle;
  12 +import android.text.style.ForegroundColorSpan;
  13 +import android.util.Base64;
  14 +import android.widget.TextView;
  15 +
  16 +import com.br_technology.securitytrain_master.base.view.BaseApplication;
  17 +
  18 +import java.math.BigDecimal;
  19 +import java.security.MessageDigest;
  20 +import java.security.NoSuchAlgorithmException;
  21 +import java.text.SimpleDateFormat;
  22 +import java.util.Date;
  23 +import java.util.Locale;
  24 +import java.util.regex.Matcher;
  25 +import java.util.regex.Pattern;
  26 +
  27 +import kotlin.text.Charsets;
  28 +
  29 +public class TextKits {
  30 + private static final int WAN = 10000;
  31 +
  32 + /**
  33 + * MD5加密
  34 + *
  35 + * @param info 要加密的字符串
  36 + * @return 加密完成的字符串
  37 + */
  38 + public static String getMD5(String info) {
  39 + String result = "加密失败";
  40 + try {
  41 + MessageDigest md = MessageDigest.getInstance("MD5");
  42 + md.update(info.getBytes());
  43 + byte[] b = md.digest();
  44 + int i;
  45 + StringBuilder buf = new StringBuilder("");
  46 + for (byte value : b) {
  47 + i = value;
  48 + if (i < 0) i += 256;
  49 + if (i < 16) buf.append("0");
  50 + buf.append(Integer.toHexString(i));
  51 + }
  52 + result = buf.toString();
  53 + } catch (NoSuchAlgorithmException e) {
  54 + e.printStackTrace();
  55 + }
  56 + return result;
  57 + }
  58 +
  59 + //中文标点符号
  60 + final static String[] chinesePunctuation = {
  61 +// "!", ",", "。", ";", "《", "》", "(", ")", "?",
  62 +// "{", "}", "“", ":", "【", "】", "”", "‘", "’"
  63 + };
  64 +
  65 +
  66 + //英文标点符号
  67 + final static String[] englishPunctuation = {
  68 +// "!", ",",
  69 +// ".", ";", "<", ">", "(", ")", "?", "{", "}", "\"",
  70 +// ":", "{", "}", "\"", "\'", "\'"
  71 + };
  72 +
  73 + /**
  74 + * 中文标点符号转英文字标点符号
  75 + *
  76 + * @param str 原字符串
  77 + * @return str 新字符串
  78 + */
  79 + public static String chinesePunctuationToEnglish(String str) {
  80 + if (TextUtils.isEmpty(str)) {
  81 + return str;
  82 + }
  83 + //去除空格
  84 +// str = str.replaceAll("\\s+", "");
  85 + for (int i = 0; i < chinesePunctuation.length; i++) {
  86 + str = str.replaceAll(chinesePunctuation[i], englishPunctuation[i]);
  87 + }
  88 + return str;
  89 + }
  90 +
  91 + /**
  92 + * 英文标点符号转中文字标点符号
  93 + *
  94 + * @param str 原字符串
  95 + * @return str 新字符串
  96 + */
  97 + public static String englishPunctuationToChinese(String str) {
  98 + if (TextUtils.isEmpty(str)) {
  99 + return str;
  100 + }
  101 + //去除空格
  102 + str = str.replaceAll("\\s+", " ");
  103 + for (int i = 0; i < chinesePunctuation.length; i++) {
  104 + str = str.replaceAll(chinesePunctuation[i], englishPunctuation[i]);
  105 + }
  106 + return str;
  107 + }
  108 +
  109 + /**
  110 + * 全角转半角
  111 + */
  112 + public static String ToDBC(String input) {
  113 +// char[] c = input.toCharArray();
  114 +// for (int i = 0; i < c.length; i++) {
  115 +// if (c[i] == 12288) {
  116 +// c[i] = (char) 32;
  117 +// continue;
  118 +// }
  119 +// if (c[i] > 65280 && c[i] < 65375)
  120 +// c[i] = (char) (c[i] - 65248);
  121 +// }
  122 +// return new String(c);
  123 + return input;
  124 + }
  125 +
  126 + /**
  127 + *关键字高亮显示
  128 + *@param text 文字
  129 + *@param keyword1 文字中的关键字数组
  130 + *
  131 + */
  132 + public static SpannableStringBuilder matcherSearchContent(String text, String[] keyword1) {
  133 + String[] keyword = new String[keyword1.length];
  134 + System.arraycopy(keyword1, 0, keyword, 0, keyword1.length);
  135 + SpannableStringBuilder spannable = new SpannableStringBuilder(text);
  136 +
  137 + CharacterStyle span;
  138 + String wordReg;
  139 + for (int i = 0; i < keyword.length; i++) {
  140 + StringBuilder key = new StringBuilder();
  141 + // 处理通配符问题
  142 + if (keyword[i].contains("*") || keyword[i].contains("(") || keyword[i].contains(")")) {
  143 + char[] chars = keyword[i].toCharArray();
  144 + for (char aChar : chars) {
  145 + if (aChar == '*' || aChar == '(' || aChar == ')') {
  146 + key.append("\\").append(aChar);
  147 + } else {
  148 + key.append(aChar);
  149 + }
  150 + }
  151 + keyword[i] = key.toString();
  152 + }
  153 +
  154 + wordReg = "(?i)" + keyword[i]; //忽略字母大小写
  155 + Pattern pattern = Pattern.compile(wordReg);
  156 + Matcher matcher = pattern.matcher(text);
  157 + while (matcher.find()) {
  158 + span = new ForegroundColorSpan(Color.parseColor("#ff5656"));
  159 + spannable.setSpan(span, matcher.start(), matcher.end(), Spannable.SPAN_MARK_MARK);
  160 + }
  161 + }
  162 +
  163 + return spannable;
  164 + }
  165 +
  166 + /**
  167 + * 半角转全角
  168 + */
  169 + public static String ToSBC(String input) {
  170 + char[] c = input.toCharArray();
  171 + for (int i = 0; i < c.length; i++) {
  172 + if (c[i] == ' ') {
  173 + c[i] = '\u3000';
  174 + } else if (c[i] < '\177') {
  175 + c[i] = (char) (c[i] + 65248);
  176 + }
  177 + }
  178 + return new String(c);
  179 + }
  180 +
  181 + public static CharSequence getHighLightText(String text, String highText, int highColor) {
  182 + if (TextUtils.isEmpty(highText)) {
  183 + return text;
  184 + }
  185 + text = TextKits.englishPunctuationToChinese(text);
  186 + if (!TextUtils.isEmpty(text) && text.contains(highText)) {
  187 + SpannableStringBuilder builder = new SpannableStringBuilder(text);
  188 +
  189 + int index = text.indexOf(highText);
  190 + while (index >= 0) {
  191 + builder.setSpan(new ForegroundColorSpan(highColor), index, index + highText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
  192 + index = text.indexOf(highText, index + highText.length());
  193 + }
  194 +
  195 + return builder;
  196 + }
  197 + return text;
  198 + }
  199 +
  200 + public static CharSequence getHighLightTextNew(String text, String highText, int highColor) {
  201 + if (TextUtils.isEmpty(highText)) {
  202 + return text;
  203 + }
  204 + if (!TextUtils.isEmpty(text) && text.contains(highText)) {
  205 + SpannableStringBuilder builder = new SpannableStringBuilder(text);
  206 + int index = text.indexOf(highText);
  207 + builder.setSpan(new ForegroundColorSpan(highColor), index, index + highText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
  208 + return builder;
  209 + }
  210 + return text;
  211 + }
  212 +
  213 + public static void setHighLightTextHtml(String text, String highText, int highColor, TextView textView) {
  214 + String color = ColorTransHelp.toHexEncoding(highColor);
  215 + String font = "<font color='" + color + "'>" + highText + "</font>";
  216 + String str = text.replaceAll(highText, font);
  217 + textView.setText(Html.fromHtml(str));
  218 + }
  219 +
  220 + public static CharSequence getLabelText(String text, String big, int sizeBig) {
  221 + if (TextUtils.isEmpty(big)) {
  222 + return text;
  223 + }
  224 + Context context = BaseApplication.Companion.getInstance().getApplicationContext();
  225 + if (!TextUtils.isEmpty(text) && text.contains(big)) {
  226 + SpannableStringBuilder builder = new SpannableStringBuilder(text);
  227 +
  228 + AbsoluteSizeSpan span = new AbsoluteSizeSpan(TranslateUnit.px2dp(context, TranslateUnit.sp2px(context, sizeBig)), true);
  229 + builder.setSpan(span, 0, big.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
  230 + return builder;
  231 + }
  232 + return text;
  233 + }
  234 +
  235 + public static String formatCommentCount(int count) {
  236 + if (count < WAN) {
  237 + return String.valueOf(count);
  238 + } else {
  239 + return new BigDecimal(count * 1.0f / WAN).setScale(1, BigDecimal.ROUND_HALF_UP).toString() + "W";
  240 + }
  241 + }
  242 +
  243 + public static String formatPhone(String phone) {
  244 + if (phone.length() >= 11) {
  245 + return phone.substring(0, 3) + "****" + phone.substring(7);
  246 + } else return "待完善";
  247 + }
  248 +
  249 + public static void setTextNickName(TextView textView, String name, String cell) {
  250 + String cell_str = TextKits.formatPhone(cell);
  251 + name = TextUtils.isEmpty(name) ? (TextUtils.isEmpty(cell_str) ? "待完善" : cell_str) : name;
  252 + textView.setText(name);
  253 + }
  254 +
  255 + public static String limitCount(String text, int length) {
  256 + if (text == null) {
  257 + return "";
  258 + }
  259 + if (text.length() > length) {
  260 + return text.substring(0, length) + "…";
  261 + }
  262 + return text;
  263 + }
  264 +
  265 + public static int fontSize(int font) {
  266 + switch (font) {
  267 + case 0:
  268 + return 13;
  269 + case 1:
  270 + return 15;
  271 + case 2:
  272 + return 16;
  273 + case 3:
  274 + return 17;
  275 + default:
  276 + return 0;
  277 + }
  278 + }
  279 +
  280 + public static String decodeToString(String str) {
  281 + String s = str.replace("\t", "").replace("\n", "")
  282 + .replace("\r", "").trim();
  283 + try {
  284 + if (isBase64(s)) {
  285 + byte[] bytes = Base64.decode(s.getBytes(Charsets.UTF_8), Base64.DEFAULT);
  286 + String decode = new String(bytes).trim();
  287 + return (!decode.isEmpty() && !isMessyCode(decode)) ? decode : s;
  288 + }
  289 + return s;
  290 + } catch (Exception e) {
  291 + e.printStackTrace();
  292 + return s;
  293 + }
  294 + }
  295 +
  296 + /**
  297 + * 判断是否为乱码
  298 + *
  299 + * @param str
  300 + * @return
  301 + */
  302 + private static boolean isMessyCode(String str) {
  303 + return str.contains("�");
  304 + }
  305 +
  306 + public static boolean isComment(String s) {
  307 + return Pattern.matches("^[\\\\w\\u4E00-\\u9FA5\\uF900-\\uFA2D\\\\s]*$", s);
  308 + }
  309 +
  310 + private static boolean isBase64(String str) {
  311 + return Pattern.matches("^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$", str);
  312 + }
  313 +
  314 + public static String fontSizeSpan(int font) {
  315 + switch (font) {
  316 + case -1:
  317 + return "small";
  318 + case 0:
  319 + return "large";
  320 + case 2:
  321 + return "x-large";
  322 + case 3:
  323 + return "xx-large";
  324 + default:
  325 + return "medium";
  326 + }
  327 + }
  328 +
  329 + private static final SimpleDateFormat format = new SimpleDateFormat("MM月dd日", Locale.CHINA);
  330 + private static final SimpleDateFormat formatDay = new SimpleDateFormat("yyyyMMdd", Locale.CHINA);
  331 + private static final SimpleDateFormat formatTime = new SimpleDateFormat("HHmmss", Locale.CHINA);
  332 + private static final SimpleDateFormat formatTimeSub = new SimpleDateFormat("HH:mm", Locale.CHINA);
  333 +
  334 +
  335 + public static String timeSub(String time) {
  336 + try {
  337 + Date date = formatTime.parse(time);
  338 + if (date != null) {
  339 + return formatTimeSub.format(date);
  340 + }
  341 + } catch (Exception e) {
  342 + e.printStackTrace();
  343 + }
  344 + return "";
  345 + }
  346 +}
  1 +package com.br_technology.securitytrain_master.view
  2 +
  3 +import android.app.Dialog
  4 +import android.content.Context
  5 +import android.os.Bundle
  6 +import android.view.LayoutInflater
  7 +import com.br_technology.securitytrain_master.R
  8 +import com.br_technology.securitytrain_master.databinding.DialogMentionBinding
  9 +import com.br_technology.securitytrain_master.expand.dp2px
  10 +import com.br_technology.securitytrain_master.expand.screenWidth
  11 +
  12 +/**
  13 + * Author by YSir
  14 + * Date on 2022/1/25.
  15 + * description
  16 + * PS: Not easy to write code, please indicate.
  17 + */
  18 +class DialogMention(context: Context) : Dialog(context, R.style.UserDefaultDialog) {
  19 + private lateinit var listener: () -> Unit
  20 + private val binding by lazy {
  21 + DialogMentionBinding.inflate(LayoutInflater.from(context))
  22 + }
  23 +
  24 + override fun onCreate(savedInstanceState: Bundle?) {
  25 + super.onCreate(savedInstanceState)
  26 + setContentView(binding.root)
  27 + val attributes = window?.attributes
  28 + attributes?.width = binding.root.screenWidth() - 32.dp2px()
  29 + // 点击区域外取消
  30 + setCanceledOnTouchOutside(true)
  31 + setCancelable(true)
  32 + binding.apply {
  33 + // 确定
  34 + tvCancel.setOnClickListener {
  35 + dismiss()
  36 + }
  37 + tvEnsure.setOnClickListener {
  38 + if (::listener.isInitialized) {
  39 + listener.invoke()
  40 + }
  41 + }
  42 + }
  43 + }
  44 +
  45 + fun setListener(listener: () -> Unit): DialogMention {
  46 + this.listener = listener
  47 + return this
  48 + }
  49 +
  50 + fun setTitle(title: String): DialogMention {
  51 + binding.tvTitle.text = title
  52 + return this
  53 + }
  54 +
  55 + fun setMention(title: String): DialogMention {
  56 + binding.tvMention.text = title
  57 + return this
  58 + }
  59 +
  60 +}
@@ -4,6 +4,8 @@ import android.content.Context @@ -4,6 +4,8 @@ import android.content.Context
4 import android.util.AttributeSet 4 import android.util.AttributeSet
5 import android.webkit.WebView 5 import android.webkit.WebView
6 import com.br_technology.securitytrain_master.expand.dp2px 6 import com.br_technology.securitytrain_master.expand.dp2px
  7 +import com.br_technology.securitytrain_master.util.HtmlConvert
  8 +import org.jsoup.Jsoup
7 9
8 /** 10 /**
9 * createTime:2021/8/4 9:21 11 * createTime:2021/8/4 9:21
@@ -29,6 +31,12 @@ class MyWebView(context: Context, attrs: AttributeSet?) : WebView(context, attrs @@ -29,6 +31,12 @@ class MyWebView(context: Context, attrs: AttributeSet?) : WebView(context, attrs
29 31
30 32
31 fun loadData(data: String) { 33 fun loadData(data: String) {
32 - loadDataWithBaseURL(null, data, "text/html", "utf-8", null) 34 + val html = HtmlConvert.convert(
  35 + Jsoup.parse(data),
  36 + false,
  37 + "",
  38 + ""
  39 + )
  40 + loadDataWithBaseURL(null, html, "text/html", "utf-8", null)
33 } 41 }
34 } 42 }
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<shape xmlns:android="http://schemas.android.com/apk/res/android"
  3 + android:shape="rectangle">
  4 +
  5 + <corners android:radius="16dp" />
  6 + <solid android:color="@color/white"/>
  7 +</shape>
@@ -87,6 +87,7 @@ @@ -87,6 +87,7 @@
87 android:textSize="12sp" /> 87 android:textSize="12sp" />
88 88
89 <TextView 89 <TextView
  90 + android:id="@+id/tvClassName"
90 android:layout_width="wrap_content" 91 android:layout_width="wrap_content"
91 android:layout_height="wrap_content" 92 android:layout_height="wrap_content"
92 android:layout_marginTop="15dp" 93 android:layout_marginTop="15dp"
@@ -116,6 +117,7 @@ @@ -116,6 +117,7 @@
116 android:textSize="12sp" /> 117 android:textSize="12sp" />
117 118
118 <TextView 119 <TextView
  120 + android:id="@+id/tv_class_type"
119 android:layout_width="wrap_content" 121 android:layout_width="wrap_content"
120 android:layout_height="wrap_content" 122 android:layout_height="wrap_content"
121 android:layout_marginTop="15dp" 123 android:layout_marginTop="15dp"
@@ -144,6 +146,7 @@ @@ -144,6 +146,7 @@
144 android:textSize="12sp" /> 146 android:textSize="12sp" />
145 147
146 <TextView 148 <TextView
  149 + android:id="@+id/tv_time"
147 android:layout_width="wrap_content" 150 android:layout_width="wrap_content"
148 android:layout_height="wrap_content" 151 android:layout_height="wrap_content"
149 android:layout_marginTop="15dp" 152 android:layout_marginTop="15dp"
@@ -172,6 +175,7 @@ @@ -172,6 +175,7 @@
172 android:textSize="12sp" /> 175 android:textSize="12sp" />
173 176
174 <TextView 177 <TextView
  178 + android:id="@+id/tv_company"
175 android:layout_width="wrap_content" 179 android:layout_width="wrap_content"
176 android:layout_height="wrap_content" 180 android:layout_height="wrap_content"
177 android:layout_marginTop="15dp" 181 android:layout_marginTop="15dp"
@@ -200,6 +204,7 @@ @@ -200,6 +204,7 @@
200 android:textSize="12sp" /> 204 android:textSize="12sp" />
201 205
202 <TextView 206 <TextView
  207 + android:id="@+id/tv_lesson"
203 android:layout_width="wrap_content" 208 android:layout_width="wrap_content"
204 android:layout_height="wrap_content" 209 android:layout_height="wrap_content"
205 android:layout_marginTop="15dp" 210 android:layout_marginTop="15dp"
@@ -64,17 +64,23 @@ @@ -64,17 +64,23 @@
64 android:layout_marginEnd="16dp" 64 android:layout_marginEnd="16dp"
65 android:text="" 65 android:text=""
66 android:textColor="@color/color_32" 66 android:textColor="@color/color_32"
67 - android:textSize="26sp" /> 67 + android:textSize="18sp" />
68 68
69 <com.br_technology.securitytrain_master.view.MyWebView 69 <com.br_technology.securitytrain_master.view.MyWebView
70 android:id="@+id/content" 70 android:id="@+id/content"
71 android:layout_width="match_parent" 71 android:layout_width="match_parent"
72 - android:layout_height="match_parent" 72 + android:layout_height="wrap_content"
73 android:layout_marginStart="16dp" 73 android:layout_marginStart="16dp"
74 android:layout_marginTop="16dp" 74 android:layout_marginTop="16dp"
75 android:layout_marginEnd="16dp" 75 android:layout_marginEnd="16dp"
76 android:overScrollMode="never" 76 android:overScrollMode="never"
77 android:scrollbars="none" /> 77 android:scrollbars="none" />
  78 +
  79 + <androidx.recyclerview.widget.RecyclerView
  80 + android:id="@+id/pdf"
  81 + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
  82 + android:layout_width="match_parent"
  83 + android:layout_height="wrap_content"/>
78 </LinearLayout> 84 </LinearLayout>
79 </androidx.core.widget.NestedScrollView> 85 </androidx.core.widget.NestedScrollView>
80 86
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3 + android:layout_width="match_parent"
  4 + android:layout_height="match_parent">
  5 +
  6 + <LinearLayout
  7 + android:layout_width="match_parent"
  8 + android:layout_height="wrap_content"
  9 + android:layout_margin="16dp"
  10 + android:background="@drawable/shape_dialog_corner"
  11 + android:gravity="center_vertical"
  12 + android:orientation="vertical">
  13 +
  14 + <TextView
  15 + android:id="@+id/tv_title"
  16 + android:layout_width="match_parent"
  17 + android:layout_height="56dp"
  18 + android:gravity="center_vertical"
  19 + android:padding="12dp"
  20 + android:text="温馨提示"
  21 + android:textColor="@color/black"
  22 + android:textSize="18sp" />
  23 +
  24 + <TextView
  25 + android:id="@+id/tv_mention"
  26 + android:layout_width="match_parent"
  27 + android:layout_height="wrap_content"
  28 + android:padding="12dp"
  29 + android:text="@string/app_name" />
  30 +
  31 + <LinearLayout
  32 + android:layout_width="match_parent"
  33 + android:layout_height="44dp"
  34 + android:orientation="horizontal">
  35 +
  36 + <TextView
  37 + android:id="@+id/tv_cancel"
  38 + android:layout_width="0dp"
  39 + android:layout_height="match_parent"
  40 + android:layout_weight="1"
  41 + android:foreground="?android:attr/selectableItemBackground"
  42 + android:gravity="center"
  43 + android:text="取消"
  44 + android:textColor="@color/black"
  45 + android:textSize="18sp" />
  46 +
  47 + <View
  48 + android:layout_width="1dp"
  49 + android:layout_height="match_parent"
  50 + android:background="@color/text_light_gray" />
  51 +
  52 + <TextView
  53 + android:id="@+id/tv_ensure"
  54 + android:layout_width="0dp"
  55 + android:layout_height="match_parent"
  56 + android:layout_weight="1"
  57 + android:foreground="?android:attr/selectableItemBackground"
  58 + android:gravity="center"
  59 + android:text="确认"
  60 + android:textColor="@color/black"
  61 + android:textSize="18sp" />
  62 + </LinearLayout>
  63 + </LinearLayout>
  64 +</FrameLayout>
@@ -3,6 +3,8 @@ @@ -3,6 +3,8 @@
3 android:layout_width="match_parent" 3 android:layout_width="match_parent"
4 android:layout_height="258dp" 4 android:layout_height="258dp"
5 android:id="@+id/content_course" 5 android:id="@+id/content_course"
  6 + android:layout_marginStart="16dp"
  7 + android:layout_marginEnd="16dp"
6 android:background="@drawable/solid_eff2_4" 8 android:background="@drawable/solid_eff2_4"
7 android:orientation="vertical"> 9 android:orientation="vertical">
8 10
@@ -8,7 +8,6 @@ @@ -8,7 +8,6 @@
8 android:layout_width="match_parent" 8 android:layout_width="match_parent"
9 android:layout_height="136dp" 9 android:layout_height="136dp"
10 android:layout_marginStart="16dp" 10 android:layout_marginStart="16dp"
11 - android:layout_marginTop="16dp"  
12 android:layout_marginEnd="16dp" 11 android:layout_marginEnd="16dp"
13 android:background="@drawable/solid_eff2_4"> 12 android:background="@drawable/solid_eff2_4">
14 13
@@ -4,12 +4,13 @@ @@ -4,12 +4,13 @@
4 android:layout_width="match_parent" 4 android:layout_width="match_parent"
5 android:layout_height="wrap_content" 5 android:layout_height="wrap_content"
6 android:background="@color/white" 6 android:background="@color/white"
  7 + android:layout_marginStart="16dp"
  8 + android:layout_marginEnd="16dp"
7 android:orientation="vertical"> 9 android:orientation="vertical">
8 10
9 <LinearLayout 11 <LinearLayout
10 android:layout_width="match_parent" 12 android:layout_width="match_parent"
11 android:layout_height="83dp" 13 android:layout_height="83dp"
12 - android:layout_marginBottom="16dp"  
13 android:background="@drawable/solid_eff2_4" 14 android:background="@drawable/solid_eff2_4"
14 android:orientation="vertical"> 15 android:orientation="vertical">
15 16