作者 杨谦

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

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