diff --git a/app/src/main/java/com/cniao5/cainiaowo/CnApplication.kt b/app/src/main/java/com/cniao5/cainiaowo/CnApplication.kt index d0b7297..0cd33d3 100644 --- a/app/src/main/java/com/cniao5/cainiaowo/CnApplication.kt +++ b/app/src/main/java/com/cniao5/cainiaowo/CnApplication.kt @@ -4,6 +4,7 @@ import com.alibaba.android.arouter.launcher.ARouter import com.cniao5.common.BaseApplication import com.cniao5.common.ktx.application import com.cniao5.mine.moduleMine +import com.cniao5.study.moduleStudy import com.test.service.assistant.AssistantApp import com.test.service.moduleService import login.moduleLogin @@ -13,7 +14,7 @@ import org.koin.core.module.Module class CnApplication : BaseApplication() { private val modules = arrayListOf( - moduleService,/*moduleHome,*/ moduleLogin, moduleMine + moduleService,/*moduleHome,*/ moduleLogin, moduleMine, moduleStudy ) override fun initConfig() { diff --git a/app/src/main/java/com/cniao5/cainiaowo/MainActivity.kt b/app/src/main/java/com/cniao5/cainiaowo/MainActivity.kt index df0c437..7bd52db 100644 --- a/app/src/main/java/com/cniao5/cainiaowo/MainActivity.kt +++ b/app/src/main/java/com/cniao5/cainiaowo/MainActivity.kt @@ -9,8 +9,7 @@ import com.cniao5.common.widget.BnvVp2Mediator import com.cniao5.course.CourseFragment import com.cniao5.home.HomeFragment import com.cniao5.mine.MineContainerFragment -import com.cniao5.mine.ui.MineFragment -import com.cniao5.study.StudyFragment +import com.cniao5.study.ui.StudyFragment /* * App主工程的入口界面 @@ -69,8 +68,9 @@ class MainViewPagerAdapter(fragmentActivity: FragmentActivity, private val fragm //创建fragment invoke回调函数,让它实例化创建新的对象 override fun createFragment(position: Int) = fragments[position]?.invoke() ?: error("请确保fragment数据源和viewPager2的index匹配设置") - } + + //类型别名定义 传入的是一个代码块,每次都是一个新的Fragment typealias ReFragment = () -> Fragment diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml index 1fa1a72..945cced 100644 --- a/common/src/main/res/values/strings.xml +++ b/common/src/main/res/values/strings.xml @@ -4,4 +4,5 @@ 课程 学习中心 我的 + 未登录 \ No newline at end of file diff --git a/dependencies.gradle b/dependencies.gradle index d77f726..a6ddce9 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -101,8 +101,9 @@ dependencies { kapt "androidx.room:room-compiler:2.2.6" //region jetpack组件分页,上拉加载平滑过渡效果 - implementation "androidx.paging:paging-runtime:2.1.2" - testImplementation "androidx.paging:paging-common:2.1.2" + def paging_version = "3.0.0-beta01" + implementation "androidx.paging:paging-runtime:$paging_version" + testImplementation "androidx.paging:paging-common:$paging_version" //endregion //region Description 注意配置navigetion版本号的时候,project下的build.gradle的class path也需要同步navigation的版本号配置 @@ -133,7 +134,7 @@ dependencies { //region test libs testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.2' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' //endregion } \ No newline at end of file diff --git a/login/src/debug/java/login/repo/LoginRepo.kt b/login/src/debug/java/login/repo/LoginRepo.kt index 54e0034..243eb86 100644 --- a/login/src/debug/java/login/repo/LoginRepo.kt +++ b/login/src/debug/java/login/repo/LoginRepo.kt @@ -4,10 +4,7 @@ import androidx.lifecycle.LiveData import com.blankj.utilcode.util.LogUtils import com.cniao5.common.model.SingleLiveData import com.cniao5.common.network.support.serverData -import com.test.service.net.onBizError -import com.test.service.net.onBizOK -import com.test.service.net.onFailure -import com.test.service.net.onSuccess +import com.test.service.net.* import login.net.LoginService import login.net.LoginReqBody import login.net.LoginRsp @@ -37,7 +34,7 @@ class LoginRepo(private val service: LoginService) : ILoginResource{ } onBizOK { code, data, message -> _registerRsp.value = data - LogUtils.i("是否注册 BizOK $data") + // LogUtils.i("是否注册 BizOK $data") return@onBizOK } } @@ -57,7 +54,6 @@ class LoginRepo(private val service: LoginService) : ILoginResource{ onBizOK { code, data, message -> _loginRsp.value = data //同步到room数据库 登录状态 - LogUtils.i("登录接口 BizOK $data") return@onBizOK } diff --git a/mine/build.gradle b/mine/build.gradle index 2d50ae6..de02c8c 100644 --- a/mine/build.gradle +++ b/mine/build.gradle @@ -12,6 +12,7 @@ android { defaultConfig{ //只要独立运行时候才需要applicationId if (singleModule.toBoolean()){ + //唯一的身份标记 applicationId "com.cniao5.cainiaowo.mine" } } diff --git a/mine/src/main/java/com/cniao5/mine/repo/MineRepo.kt b/mine/src/main/java/com/cniao5/mine/repo/MineRepo.kt index 9018e46..7d871cb 100644 --- a/mine/src/main/java/com/cniao5/mine/repo/MineRepo.kt +++ b/mine/src/main/java/com/cniao5/mine/repo/MineRepo.kt @@ -6,10 +6,7 @@ import com.blankj.utilcode.util.LogUtils import com.cniao5.common.network.support.serverData import com.cniao5.mine.net.MineService import com.cniao5.mine.net.UserInfoRsp -import com.test.service.net.onFailure -import com.test.service.net.onSuccess -import com.test.service.net.onBizError -import com.test.service.net.onBizOK +import com.test.service.net.* /* diff --git a/mine/src/main/java/com/cniao5/mine/ui/MineFragment.kt b/mine/src/main/java/com/cniao5/mine/ui/MineFragment.kt index 3144b3e..bfaab7f 100644 --- a/mine/src/main/java/com/cniao5/mine/ui/MineFragment.kt +++ b/mine/src/main/java/com/cniao5/mine/ui/MineFragment.kt @@ -1,6 +1,7 @@ package com.cniao5.mine.ui import android.os.Bundle +import android.util.Log import android.view.View import androidx.databinding.ViewDataBinding import androidx.navigation.fragment.findNavController @@ -90,7 +91,7 @@ class MineFragment : BaseFragment() { viewModel.apply { //如果Liveinfo数据更新不为空的话就存储到数据库中 - liveInfo.observeKt { + liveInforep.observeKt { it?.let { UserInfoRspDBHelper.insertUserInfoRsp(requireContext(), it) //将数据给到布局的livedata diff --git a/mine/src/main/java/com/cniao5/mine/ui/MineViewModel.kt b/mine/src/main/java/com/cniao5/mine/ui/MineViewModel.kt index 4446929..7ef562e 100644 --- a/mine/src/main/java/com/cniao5/mine/ui/MineViewModel.kt +++ b/mine/src/main/java/com/cniao5/mine/ui/MineViewModel.kt @@ -20,7 +20,7 @@ class MineViewModel(private val repo: IMineResource): BaseViewModel() { // val liveUser = MutableLiveData() //用在userInfoRspFragment中 - val liveInfo: LiveData = repo.liveUserInfo + val liveInforep: LiveData = repo.liveUserInfo //用于布局的Livedata val liveInfoRsp = MutableLiveData() diff --git a/service/src/main/java/com/test/service/net/NetRspKtx.kt b/service/src/main/java/com/test/service/net/NetRspKtx.kt index 8fc77cc..922b094 100644 --- a/service/src/main/java/com/test/service/net/NetRspKtx.kt +++ b/service/src/main/java/com/test/service/net/NetRspKtx.kt @@ -38,19 +38,21 @@ inline fun BaseResponse.toEntity(): T? { } /** - * 接口成功,但是业务返回code不是1的情况 + * 接口成功,但是业务返回code不是1或1001的情况 */ @OptIn(ExperimentalContracts::class) -inline fun BaseResponse.onBizError(crossinline block: (code: Int, message: String) -> Unit): BaseResponse { +inline fun BaseResponse.onBizError(crossinline block: (code: Int, message: String?) -> Unit): BaseResponse { contract { callsInPlace(block, InvocationKind.AT_MOST_ONCE) } - if (code != BaseResponse.SERVER_CODE_SUCCESS || code != BaseResponse.SERVER_CODE_SUCCESS1) { //code == 除了1001和1之外的其他,不成功 + if (code != BaseResponse.SERVER_CODE_SUCCESS1 && code != BaseResponse.SERVER_CODE_SUCCESS) { //同时不等于1和1001的时候执行if block.invoke(code, message ?: "Error Message Null") //返回错误码和错误信息 } return this } + + /** * 接口成功且业务成功code==1的情况 * crossinline关键字 只要标志了就不能进入return true函数快 @@ -60,7 +62,6 @@ inline fun BaseResponse.onBizOK(crossinline action: (code: Int, data contract { callsInPlace(action, InvocationKind.AT_MOST_ONCE) } - com.blankj.utilcode.util.LogUtils.i("获取data成功 $data") if (code == BaseResponse.SERVER_CODE_SUCCESS || code == BaseResponse.SERVER_CODE_SUCCESS1) { //code == 1001或code == 1,成功 action.invoke(code, this.toEntity(), message) //返回成功码和解密之后的序列化对象 } diff --git a/service/src/main/java/com/test/service/utils/BindingApt.kt b/service/src/main/java/com/test/service/utils/BindingApt.kt index 5433775..ac646bd 100644 --- a/service/src/main/java/com/test/service/utils/BindingApt.kt +++ b/service/src/main/java/com/test/service/utils/BindingApt.kt @@ -25,6 +25,7 @@ fun imgSrcCompat(iv: ImageView, src: Any?) { val imgRes = when(src) { is String -> { when { + //如果开头是//img.cniao5.com就添加前缀https: src.startsWith("//img.cniao5.com") -> "https:$src" else -> src } diff --git a/service/src/main/res/values/colors.xml b/service/src/main/res/values/colors.xml index b0a5d71..ef143d7 100644 --- a/service/src/main/res/values/colors.xml +++ b/service/src/main/res/values/colors.xml @@ -10,4 +10,131 @@ #BDBDBD #F6F6F6 #333333 + + #ff000000 + #ffffffff + #ff7fa87f + @android:color/black + @android:color/black + @color/material_deep_teal_200 + @color/material_deep_teal_500 + #1f000000 + #8a000000 + @color/material_grey_800 + @android:color/white + @color/material_grey_850 + @color/material_grey_50 + #ff000000 + #80ffffff + #80000000 + @color/bright_foreground_material_light + + @color/bright_foreground_material_dark + + @android:color/white + @android:color/black + #ff5a595b + #ffd6d7d7 + #ff424242 + #ffffffff + #03000000 + #37000000 + #ff000000 + #fff56c6c + #ff757575 + #ffffffff + #14000000 + #ff121212 + #ffcf6679 + #ffffffff + #ff000000 + #ff000000 + #ff000000 + #ffffffff + #ffba86fc + #ff000000 + #ff3700b3 + #ff03dac6 + #ff03dac6 + #ff121212 + #ffffffff + #ffb00020 + #ff000000 + #ffffffff + #ffffffff + #ff000000 + #ff000000 + #ff6200ee + #ff3700b3 + #ff3700b3 + #ff03dac6 + #ff018786 + #ffffffff + @android:color/transparent + #14000000 + #44000000 + #0a000000 + #0f000000 + #1affffff + #2effffff + #ff323232 + #80bebebe + #80323232 + #ffbebebe + #ff323232 + #ffff7043 + #ffff5722 + @android:color/white + @android:color/black + #6680cbc4 + #66009688 + #ffffffff + #ff37474f + #ff263238 + #ff21272b + #ff80cbc4 + #ff008577 + #fff5f5f5 + #ffe0e0e0 + #fffafafa + #ff757575 + #ff424242 + #ff303030 + #ff212121 + #61000000 + #00ffffff + #52000000 + #6b000000 + #1f000000 + #0a000000 + #00000000 + #de000000 + + @color/androidx_core_secondary_text_default_material_light + + #ff9e9e9e + #ff424242 + @android:color/black + @color/material_grey_600 + @color/material_grey_900 + @color/material_grey_100 + #ffffffff + #de000000 + #4dffffff + #39000000 + #33ffffff + #1f000000 + #b3ffffff + #8a000000 + #36ffffff + #24000000 + #ff2e2e32 + #ff616161 + #ffbdbdbd + #ffbdbdbd + #fff1f1f1 + #e6616161 + #e6ffffff + #ffffffff + \ No newline at end of file diff --git a/study/build.gradle b/study/build.gradle index a260657..c6ea3aa 100644 --- a/study/build.gradle +++ b/study/build.gradle @@ -17,5 +17,8 @@ android { } dependencies { implementation project(path:':service') + //numberprogressbar进度条 + implementation 'com.daimajia.numberprogressbar:library:1.4@aar' + //from dependencies.gradle } \ No newline at end of file diff --git a/study/src/main/AndroidManifest.xml b/study/src/main/AndroidManifest.xml index 083c8ce..e20a763 100644 --- a/study/src/main/AndroidManifest.xml +++ b/study/src/main/AndroidManifest.xml @@ -2,4 +2,7 @@ + + + \ No newline at end of file diff --git a/study/src/main/java/com/cniao5/study/LibStudy.kt b/study/src/main/java/com/cniao5/study/LibStudy.kt new file mode 100644 index 0000000..a9009d1 --- /dev/null +++ b/study/src/main/java/com/cniao5/study/LibStudy.kt @@ -0,0 +1,36 @@ +package com.cniao5.study + +import com.cniao5.common.network.KtRetrofit +import com.cniao5.common.utils.getBaseHost +import com.cniao5.study.net.StudyInfoRsp +import com.cniao5.study.net.StudyService +import com.cniao5.study.repo.IStudyResource +import com.cniao5.study.repo.StudyResource +import com.cniao5.study.ui.StudyViewModel +import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.core.parameter.parametersOf +import org.koin.dsl.bind +import org.koin.dsl.module + +/* +* 依赖注入管理 study的module +* */ +val moduleStudy = module { + + //service retrofit + //single声明单例对象 + // single { + // KtRetrofit.initConfig("http://yapi.54yct.com/mock/24/") //baseurl + // .getService(MineService::class.java) + // } + + single { + get { parametersOf(getBaseHost()) }.getService(StudyService::class.java) + } + + //repo IMineResource + single { StudyResource(get()) } bind IStudyResource::class + + viewModel { StudyViewModel(get()) } + +} \ No newline at end of file diff --git a/study/src/main/java/com/cniao5/study/StudyFragment.kt b/study/src/main/java/com/cniao5/study/StudyFragment.kt deleted file mode 100644 index 4b1bbda..0000000 --- a/study/src/main/java/com/cniao5/study/StudyFragment.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.cniao5.study - -import android.os.Bundle -import android.view.View -import androidx.databinding.ViewDataBinding -import com.cniao5.common.base.BaseFragment -import com.cniao5.study.databinding.FragmentStudyBinding - - -/* -* 学习中心的Fragmennt -* 传入了R.layout.fragment_course之后就不用写onCreateView,因为布局已经被关联到fragment里了 -* */ -class StudyFragment: BaseFragment() { - - //传入布局资源,将布局和view绑定到一起 - override fun getLayoutRes() = R.layout.fragment_study - - //传入view,将view和databinding绑定到一起 - override fun bindView(view: View, savedInstanceState: Bundle?): ViewDataBinding { - return FragmentStudyBinding.bind(view) - } - -} \ No newline at end of file diff --git a/study/src/main/java/com/cniao5/study/net/StudyRsp.kt b/study/src/main/java/com/cniao5/study/net/StudyRsp.kt new file mode 100644 index 0000000..9f1c6bd --- /dev/null +++ b/study/src/main/java/com/cniao5/study/net/StudyRsp.kt @@ -0,0 +1,235 @@ +package com.cniao5.study.net + +import android.content.Context +import androidx.annotation.Keep +import androidx.lifecycle.LiveData +import androidx.room.* + +import com.google.gson.annotations.SerializedName + + +/* +* 学习中心页面相关的数据返回类 +* */ + +/* +* 用户学习详情 +* member/study/info +* */ +@Keep +@Entity(tableName = "tb_studyinforsp") +data class StudyInfoRsp( + @PrimaryKey + val id: Int, //主键 + val rank: Int, + @SerializedName("today_study_time") + val todayStudyTime: Int, + @SerializedName("total_study_time") + val totalStudyTime: Int +) + +/* +* 用户学习详情的Dao层 +* */ +@Dao +interface StudyInfoDao { + @Insert(onConflict = OnConflictStrategy.REPLACE) + fun insertStudyInfo(studyinfo: StudyInfoRsp) + + @Update(onConflict = OnConflictStrategy.REPLACE) + fun updateStudyInfo(studyinfo: StudyInfoRsp) + + @Delete + fun deleteStudyInfo(studyinfo: StudyInfoRsp) + + //= in like + @Query("select * from tb_studyinforsp where id =:id") + fun queryStudyInfo(id: Int = 0): StudyInfoRsp? + + @Query("select * from tb_studyinforsp where id =:id") + fun queryLiveData(id: Int = 0): LiveData +} + +/* +* 用户学习详情的数据库 +* */ +@Database(entities = [StudyInfoRsp::class], version = 1, exportSchema = true) +abstract class StudyInfoDB : RoomDatabase() { + abstract fun studyInfoDao():StudyInfoDao + + companion object { + private const val DB_NAME = "study_data" + + @Volatile + private var instance: StudyInfoDB? = null + + @Synchronized + fun getInstance(context: Context): StudyInfoDB { + return instance ?: Room.databaseBuilder( + context, + StudyInfoDB::class.java, + DB_NAME + ).build().also { instance = it } + } + } +} + +/* +* 已经学习过的课程列表 +* member/courses/studied +* */ +@Keep +data class StudiedRsp( + val datas: List?, + val page: Int, + val size: Int, + val total: Int, + @SerializedName("total_page") + val totalPage: Int +) { + @Keep + data class Data( + val brief: String?, + @SerializedName("comment_count") + val commentCount: Int, + @SerializedName("cost_price") + val costPrice: Int, + val course: Course?, + @SerializedName("course_type") + val courseType: Int, + @SerializedName("current_price") + val currentPrice: Int, + @SerializedName("first_category") + val firstCategory: FirstCategory?, + @SerializedName("get_method") + val getMethod: Int, + val id: Int, + @SerializedName("img_url") + val imgUrl: String?, + @SerializedName("is_distribution") + val isDistribution: Boolean, + @SerializedName("is_free") + val isFree: Int, + @SerializedName("is_live") + val isLive: Int, + @SerializedName("is_pt") + val isPt: Boolean, + @SerializedName("left_expiry_days") + val leftExpiryDays: Int, + @SerializedName("lessons_count") + val lessonsCount: Any?, + @SerializedName("lessons_played_time") + val lessonsPlayedTime: Int, + val name: String?, + @SerializedName("now_price") + val nowPrice: Int, + val number: Int, + @SerializedName("original_price") + val originalPrice: Int, + val progress: Double, + val title: String? + ) { + @Keep + data class Course( + val h5site: String?, + val id: Int, + @SerializedName("img_url") + val imgUrl: String?, + val name: String?, + val website: String? + ) + + @Keep + data class FirstCategory( + val code: String?, + val id: Int, + val title: String? + ) + } +} + + +/* +* 已经购买的课程,班级 信息 +* member/courses/bought +* */ +@Keep +data class BoughtRsp( + val datas: List?, + val page: Int, + val size: Int, + val total: Int, + @SerializedName("total_page") + val totalPage: Int +) { + @Keep + data class Data( + @SerializedName("cancel_time") + val cancelTime: String?, + val course: Course?, + @SerializedName("created_time") + val createdTime: String?, + @SerializedName("get_method") + val getMethod: Int, + val id: Int, + @SerializedName("is_expired") + val isExpired: Boolean, + @SerializedName("left_expiry_days") + val leftExpiryDays: Int, + @SerializedName("order_time") + val orderTime: String?, + @SerializedName("product_id") + val productId: Int, + @SerializedName("product_type") + val productType: Int + ) { + @Keep + data class Course( + val brief: Any?, + @SerializedName("comment_count") + val commentCount: Int, + @SerializedName("cost_price") + val costPrice: Int, + val course: Course?, + @SerializedName("first_category") + val firstCategory: FirstCategory?, + val id: Int, + @SerializedName("img_url") + val imgUrl: String?, + @SerializedName("is_distribution") + val isDistribution: Boolean, + @SerializedName("is_free") + val isFree: Int, + @SerializedName("is_live") + val isLive: Int, + @SerializedName("is_pt") + val isPt: Boolean, + @SerializedName("lessons_played_time") + val lessonsPlayedTime: Int, + val name: String?, + @SerializedName("now_price") + val nowPrice: Int, + val number: Int, + val progress: Int, + val title: String? + ) { + @Keep + data class Course( + val h5site: String?, + val id: Int, + @SerializedName("img_url") + val imgUrl: String?, + val name: String?, + val website: String? + ) + + @Keep + data class FirstCategory( + val code: String?, + val id: Int, + val title: String? + ) + } + } +} + diff --git a/study/src/main/java/com/cniao5/study/net/StudyService.kt b/study/src/main/java/com/cniao5/study/net/StudyService.kt new file mode 100644 index 0000000..b7c7e8b --- /dev/null +++ b/study/src/main/java/com/cniao5/study/net/StudyService.kt @@ -0,0 +1,36 @@ +package com.cniao5.study.net + +import com.test.service.net.BaseResponse +import retrofit2.Call +import retrofit2.http.GET +import retrofit2.http.Query + +/* +* 学习中心模块的接口 +* */ + +interface StudyService { + + //用户学习详情 + @GET("member/study/info") + fun getStudyInfo(): Call + + //用户学习过的课程列表 + @GET("member/courses/studied") + fun getStudyList( + @Query("page")page: Int = 1, + @Query("size")size: Int = 10 + ): Call + + /* + * 用户购买的课程 + * page默认为1 + * size默认为10 + * */ + @GET("member/courses/bought") + fun getBoughtCourse( + @Query("page")page: Int = 1, + @Query("size")size: Int = 10 + ): Call + +} \ No newline at end of file diff --git a/study/src/main/java/com/cniao5/study/repo/IStudyResource.kt b/study/src/main/java/com/cniao5/study/repo/IStudyResource.kt new file mode 100644 index 0000000..3e61694 --- /dev/null +++ b/study/src/main/java/com/cniao5/study/repo/IStudyResource.kt @@ -0,0 +1,28 @@ +package com.cniao5.study.repo + +import androidx.lifecycle.LiveData +import androidx.paging.PagingData +import com.cniao5.study.net.BoughtRsp +import com.cniao5.study.net.StudiedRsp +import com.cniao5.study.net.StudyInfoRsp +import kotlinx.coroutines.flow.Flow + +/* +* 学习中心模块相关的抽象数据接口 +* */ +interface IStudyResource { + + val liveStudyInfo: LiveData + val liveStudyList: LiveData + val liveBoughtList: LiveData + + //用户学习详情 + suspend fun getStudyInfo() + + //用户学习过的课程列表 + suspend fun getStudyList() + + //用户购买的课程 + suspend fun getBoughtCourse() + +} \ No newline at end of file diff --git a/study/src/main/java/com/cniao5/study/repo/StudyInfoDbHelper.kt b/study/src/main/java/com/cniao5/study/repo/StudyInfoDbHelper.kt new file mode 100644 index 0000000..e8d79c6 --- /dev/null +++ b/study/src/main/java/com/cniao5/study/repo/StudyInfoDbHelper.kt @@ -0,0 +1,43 @@ +package com.cniao5.study.repo + +import android.content.Context +import com.cniao5.study.net.StudyInfoDB +import com.cniao5.study.net.StudyInfoRsp +import com.test.service.repo.DbHelper.getUserInfo +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch + +object StudyInfoDbHelper { + /** + * 以普通数据对象的形式,获取数据 + */ + fun getStudyInfo(context: Context) = StudyInfoDB.getInstance(context).studyInfoDao().queryStudyInfo() + + /** + * 获取room数据表中存储的数据 + * return liveData形式 + */ + fun getLiveStudyInfo(context: Context) = StudyInfoDB.getInstance(context).studyInfoDao().queryLiveData() + + /** + * 删除数据表中的信息 + */ + fun deleteStudyInfo(context: Context) { + GlobalScope.launch(Dispatchers.IO) { + getStudyInfo(context)?.apply { + StudyInfoDB.getInstance(context).studyInfoDao().deleteStudyInfo(this) + } + } + } + + /** + * 新增用户数据到数据表 + */ + fun insertStudyInfo(context: Context, info: StudyInfoRsp) { + GlobalScope.launch(Dispatchers.IO) { + StudyInfoDB.getInstance(context).studyInfoDao().insertStudyInfo(info) + } + } + +} \ No newline at end of file diff --git a/study/src/main/java/com/cniao5/study/repo/StudyResource.kt b/study/src/main/java/com/cniao5/study/repo/StudyResource.kt new file mode 100644 index 0000000..6260e6f --- /dev/null +++ b/study/src/main/java/com/cniao5/study/repo/StudyResource.kt @@ -0,0 +1,95 @@ +package com.cniao5.study.repo + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.paging.* +import com.blankj.utilcode.util.LogUtils +import com.cniao5.common.network.support.serverData +import com.cniao5.study.net.BoughtRsp +import com.cniao5.study.net.StudiedRsp +import com.cniao5.study.net.StudyInfoRsp +import com.cniao5.study.net.StudyService +import com.test.service.net.* +import kotlinx.coroutines.flow.Flow + +class StudyResource(private val service: StudyService) : IStudyResource { + + private val _studyInfo = MutableLiveData() + private val _studyList = MutableLiveData() + private val _boughtList = MutableLiveData() + + override val liveStudyInfo: LiveData = _studyInfo + override val liveStudyList: LiveData = _studyList + override val liveBoughtList: LiveData = _boughtList + + /* + * 获取学习信息 + * */ + override suspend fun getStudyInfo() { + service.getStudyInfo() + .serverData() + .onSuccess { + //只要不是接口响应成功 + onBizError { code, message -> + // _studyInfo.value = null + LogUtils.w("获取学习信息 BizError $code,$message") //警告 + } + onBizOK { code, data, message -> + _studyInfo.value = data + LogUtils.i("获取学习信息 BizOK $data") //信息 + return@onBizOK + } + } + .onFailure { + // _studyInfo.value = null + LogUtils.e("获取学习信息 接口异常 ${it.message}") //错误 + } + } + + /* + * 获取学习过的课程列表 + * */ + override suspend fun getStudyList() { + service.getStudyList() + .serverData() + .onSuccess { + onBizError { code, message -> + LogUtils.w("获取学习过的课程列表 BizError $code,$message") + } + onBizOK { code, data, message -> + _studyList.value = data + LogUtils.i("获取学习过的课程列表 BizOK $data") + return@onBizOK + } + } + .onFailure { + LogUtils.e("获取学习过的课程列表 接口异常 ${it.message}") + } + } + + /* + * 获取购买的课程 + * */ + override suspend fun getBoughtCourse() { + service.getBoughtCourse() + .serverData() + .onSuccess { + onBizError { code, message -> + // _boughtList.value = null + LogUtils.w("获取购买的课程 BizError $code,$message") + } + onBizOK { code, data, message -> + _boughtList.value = data + LogUtils.i("获取购买的课程 BizOK $data") + return@onBizOK + } + } + .onFailure { + // _boughtList.value = null + LogUtils.e("获取购买的课程 接口异常 ${it.message}") + } + } + + +} + diff --git a/study/src/main/java/com/cniao5/study/ui/StudiedAdapter.kt b/study/src/main/java/com/cniao5/study/ui/StudiedAdapter.kt new file mode 100644 index 0000000..b590de8 --- /dev/null +++ b/study/src/main/java/com/cniao5/study/ui/StudiedAdapter.kt @@ -0,0 +1,50 @@ +package com.cniao5.study.ui + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.cniao5.study.databinding.ItemCourseStudyBinding +import com.cniao5.study.net.StudiedRsp + +class StudiedAdapter : RecyclerView.Adapter() { + + private val mList = mutableListOf() + + fun submit(list: List) { + mList.clear() + mList.addAll(list) + notifyDataSetChanged() + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = StudiedVH.createVH(parent) + + override fun onBindViewHolder(holder: StudiedVH, position: Int) { + holder.bind(mList[position]) + } + + override fun getItemCount() = mList.size + +} + +class StudiedVH(private val binding: ItemCourseStudyBinding) : RecyclerView.ViewHolder(binding.root){ + + companion object { + fun createVH(parent: ViewGroup): StudiedVH { + return StudiedVH( + ItemCourseStudyBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + } + + fun bind(info: StudiedRsp.Data) { + binding.info = info + //直接把数据给进度条控件 + binding.npbProgressItemStudy.progress = info.progress.toInt() + binding.executePendingBindings() + } + +} diff --git a/study/src/main/java/com/cniao5/study/ui/StudyFragment.kt b/study/src/main/java/com/cniao5/study/ui/StudyFragment.kt new file mode 100644 index 0000000..af5818f --- /dev/null +++ b/study/src/main/java/com/cniao5/study/ui/StudyFragment.kt @@ -0,0 +1,76 @@ +package com.cniao5.study.ui + +import android.os.Bundle +import android.util.Log +import android.view.View +import androidx.databinding.ViewDataBinding +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.LinearLayoutManager +import com.blankj.utilcode.util.LogUtils +import com.cniao5.common.base.BaseFragment +import com.cniao5.study.R +import com.cniao5.study.databinding.FragmentStudyBinding +import com.cniao5.study.repo.StudyInfoDbHelper +import com.test.service.repo.DbHelper +import org.koin.androidx.viewmodel.ext.android.viewModel + + +/* +* 学习中心的Fragmennt +* 传入了R.layout.fragment_course之后就不用写onCreateView,因为布局已经被关联到fragment里了 +* */ +class StudyFragment: BaseFragment() { + + private val viewModel: StudyViewModel by viewModel() + + // private lateinit var adapter : StudiedAdapter + + //传入布局资源,将布局和view绑定到一起 + override fun getLayoutRes() = R.layout.fragment_study + + //传入view,将view和databinding绑定到一起 + override fun bindView(view: View, savedInstanceState: Bundle?): ViewDataBinding { + return FragmentStudyBinding.bind(view).apply { + vm = viewModel //记得加 + } + } + + override fun initData() { + super.initData() + + DbHelper.getLiveUserInfo(requireContext()).observeKt { + // it.let { + // viewModel.obUserInfo.set(it) //数据库发生变化的时候拿到Userinfo的值 + // viewModel.getStudyData() + // } + if (it == null) { + //清空界面上的数据 + StudyInfoDbHelper.deleteStudyInfo(requireContext()) + viewModel.obUserInfo.set(it) //数据库发生变化的时候拿到Userinfo的值 + } else { + viewModel.obUserInfo.set(it) //数据库发生变化的时候拿到Userinfo的值 + viewModel.getStudyData() + } + } + + viewModel.apply { + //用户学习详情 + liveStudyInfo.observeKt { + //将数据保存到数据库 + it?.let { StudyInfoDbHelper.insertStudyInfo(requireContext(), it) } + } + //已经学习过的课程列表 + liveStudyList.observeKt { + adapter.submit(it?.datas?: emptyList()) + } + } + + //studyinfo数据库的数据有变化时触发 + StudyInfoDbHelper.getLiveStudyInfo(requireContext()).observeKt { info -> + info.let { + viewModel.liveStudyInfoR.value = info //清空liveStudyInfoR的数据 + } + } + } + +} diff --git a/study/src/main/java/com/cniao5/study/ui/StudyViewModel.kt b/study/src/main/java/com/cniao5/study/ui/StudyViewModel.kt new file mode 100644 index 0000000..fe8537a --- /dev/null +++ b/study/src/main/java/com/cniao5/study/ui/StudyViewModel.kt @@ -0,0 +1,38 @@ +package com.cniao5.study.ui + +import androidx.databinding.ObservableField +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.asLiveData +import com.cniao5.common.base.BaseViewModel +import com.cniao5.study.net.BoughtRsp +import com.cniao5.study.net.StudiedRsp +import com.cniao5.study.net.StudyInfoRsp +import com.cniao5.study.repo.StudyResource +import com.test.service.repo.UserInfo + +class StudyViewModel(private val resource: StudyResource) : BaseViewModel() { + + //用户学习详情 + val liveStudyInfo: LiveData = resource.liveStudyInfo //这个数据用来观察 + val liveStudyInfoR = MutableLiveData() //这个是界面布局数据 + //已经学习过的课程列表 + val liveStudyList: LiveData = resource.liveStudyList + //已经购买的课程,班级 信息 + val liveBoughtList: LiveData = resource.liveBoughtList + + //用户信息 + val obUserInfo = ObservableField() + + //我的学习列表适配器 + val adapter = StudiedAdapter() + + //请求数据 + fun getStudyData() = serverAwait { + resource.getStudyInfo() + resource.getStudyList() + // resource.getBoughtCourse() + } + + +} \ No newline at end of file diff --git a/study/src/main/java/com/cniao5/study/utils/StudyUtils.kt b/study/src/main/java/com/cniao5/study/utils/StudyUtils.kt new file mode 100644 index 0000000..59a3e05 --- /dev/null +++ b/study/src/main/java/com/cniao5/study/utils/StudyUtils.kt @@ -0,0 +1,56 @@ +package com.cniao5.study.utils + +import androidx.databinding.BindingAdapter +import com.cniao5.study.net.StudiedRsp +import com.daimajia.numberprogressbar.NumberProgressBar + +/** + * + */ +object StudyUtils { + @JvmStatic + fun rankStr(rank: Int): String { + return if (rank > 0) "第${rank}名" else "千里之外" + } + + + @JvmStatic + fun parseStudentComment(info: StudiedRsp.Data?): String { + return "${info?.lessonsPlayedTime} ${info?.commentCount}人评价" + } + + @JvmStatic + fun parseFree(info: StudiedRsp.Data?): String { + return if (info?.isFree == 1) "免费" else "¥${info?.nowPrice}" + } + + /* + * 有两种格式的返回值,判断该用哪种 + * */ + @JvmStatic + fun parseTitle(info: StudiedRsp.Data?): String { + return if (info?.course != null) { + "${info.course.name}" + } else { + "${info?.name}" + } + } + + /* + * 有两种格式的返回值,判断该用哪种 + * */ + @JvmStatic + fun parseImage(info: StudiedRsp.Data?): String { + return if (info?.course != null) { + "${info.course.imgUrl}" + } else { + "${info?.imgUrl}" + } + } + +} + +@BindingAdapter("app:progress_current", requireAll = false) +fun setProgress(pb: NumberProgressBar, progress: Double?) { + pb.progress = ((progress ?: 0.0) * 100).toInt() +} \ No newline at end of file diff --git a/study/src/main/res/drawable/img_course.png b/study/src/main/res/drawable/img_course.png new file mode 100644 index 0000000..a8ddceb Binary files /dev/null and b/study/src/main/res/drawable/img_course.png differ diff --git a/study/src/main/res/layout/fragment_study.xml b/study/src/main/res/layout/fragment_study.xml index c120591..941d4cc 100644 --- a/study/src/main/res/layout/fragment_study.xml +++ b/study/src/main/res/layout/fragment_study.xml @@ -1,27 +1,196 @@ - + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + app:layout_constraintTop_toBottomOf="@id/v_top_bg_study" /> + + + + + + + + + + - \ No newline at end of file + diff --git a/study/src/main/res/layout/item_course_study.xml b/study/src/main/res/layout/item_course_study.xml new file mode 100644 index 0000000..b8f76aa --- /dev/null +++ b/study/src/main/res/layout/item_course_study.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/study/src/main/res/values/strings.xml b/study/src/main/res/values/strings.xml new file mode 100644 index 0000000..3261aff --- /dev/null +++ b/study/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file