diff --git a/app/build.gradle b/app/build.gradle
index 44305da69..303349ad5 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -3,13 +3,14 @@ plugins {
id 'org.jetbrains.kotlin.android'
id 'kotlin-parcelize'
id 'kotlin-kapt'
+ id "com.google.firebase.crashlytics"
}
android {
compileSdk 33
defaultConfig {
- applicationId "com.raccoongang.newedx"
+ applicationId "org.openedx.app"
minSdk 24
targetSdk 33
versionCode 1
@@ -20,7 +21,7 @@ android {
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
- namespace 'com.raccoongang.newedx'
+ namespace 'org.openedx.app'
flavorDimensions "tier"
productFlavors {
@@ -63,6 +64,9 @@ android {
enableSplit = true
}
}
+ testOptions {
+ unitTests.returnDefaultValues = true
+ }
}
dependencies {
@@ -76,9 +80,15 @@ dependencies {
kapt "androidx.room:room-compiler:$room_version"
- implementation 'androidx.core:core-splashscreen:1.0.0'
- testImplementation 'junit:junit:4.13.2'
+ implementation 'androidx.core:core-splashscreen:1.0.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+ androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
+ debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
+ testImplementation "junit:junit:$junit_version"
+ testImplementation "io.mockk:mockk:$mockk_version"
+ testImplementation "io.mockk:mockk-android:$mockk_version"
+ testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version"
+ testImplementation "androidx.arch.core:core-testing:$android_arch_version"
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 196ad73b4..385f39534 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -12,7 +12,7 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/raccoongang/newedx/MainFragment.kt b/app/src/main/java/com/raccoongang/newedx/MainFragment.kt
deleted file mode 100644
index 7d5c91c6f..000000000
--- a/app/src/main/java/com/raccoongang/newedx/MainFragment.kt
+++ /dev/null
@@ -1,48 +0,0 @@
-package com.raccoongang.newedx
-
-import android.os.Bundle
-import android.view.View
-import androidx.viewpager2.widget.ViewPager2
-import androidx.fragment.app.Fragment
-import com.raccoongang.core.presentation.global.viewBinding
-import com.raccoongang.dashboard.presentation.DashboardFragment
-import com.raccoongang.discovery.presentation.DiscoveryFragment
-import com.raccoongang.newedx.adapter.MainNavigationFragmentAdapter
-import com.raccoongang.newedx.databinding.FragmentMainBinding
-import com.raccoongang.profile.presentation.profile.ProfileFragment
-
-class MainFragment : Fragment(R.layout.fragment_main) {
-
- private val binding by viewBinding(FragmentMainBinding::bind)
-
- private lateinit var adapter: MainNavigationFragmentAdapter
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- initViewPager()
-
- binding.bottomNavView.setOnItemSelectedListener {
- when (it.itemId) {
- R.id.fragmentHome -> binding.viewPager.setCurrentItem(0, false)
- R.id.fragmentDashboard -> binding.viewPager.setCurrentItem(1, false)
- R.id.fragmentPrograms -> binding.viewPager.setCurrentItem(2, false)
- R.id.fragmentProfile -> binding.viewPager.setCurrentItem(3, false)
- }
- true
- }
- }
-
- private fun initViewPager() {
- binding.viewPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL
- binding.viewPager.offscreenPageLimit = 4
- adapter = MainNavigationFragmentAdapter(this).apply {
- addFragment(DiscoveryFragment())
- addFragment(DashboardFragment())
- addFragment(InDevelopmentFragment())
- addFragment(ProfileFragment())
- }
- binding.viewPager.adapter = adapter
- binding.viewPager.isUserInputEnabled = false
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/raccoongang/newedx/NewEdXApp.kt b/app/src/main/java/com/raccoongang/newedx/NewEdXApp.kt
deleted file mode 100644
index 3a855ad05..000000000
--- a/app/src/main/java/com/raccoongang/newedx/NewEdXApp.kt
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.raccoongang.newedx
-
-import android.app.Application
-import com.raccoongang.newedx.di.appModule
-import com.raccoongang.newedx.di.networkingModule
-import com.raccoongang.newedx.di.screenModule
-import org.koin.android.ext.koin.androidContext
-import org.koin.core.context.startKoin
-
-class NewEdXApp : Application() {
-
- override fun onCreate() {
- super.onCreate()
- startKoin {
- androidContext(this@NewEdXApp)
- modules(
- appModule,
- networkingModule,
- screenModule
- )
- }
- }
-
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/raccoongang/newedx/di/AppModule.kt b/app/src/main/java/com/raccoongang/newedx/di/AppModule.kt
deleted file mode 100644
index 7dc0a89e5..000000000
--- a/app/src/main/java/com/raccoongang/newedx/di/AppModule.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-package com.raccoongang.newedx.di
-
-import androidx.room.Room
-import androidx.room.RoomDatabase
-import com.google.gson.Gson
-import com.google.gson.GsonBuilder
-import com.raccoongang.auth.presentation.AuthRouter
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.module.DownloadWorkerController
-import com.raccoongang.core.module.TranscriptManager
-import com.raccoongang.core.module.download.FileDownloader
-import com.raccoongang.core.system.AppCookieManager
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.core.system.connection.NetworkConnection
-import com.raccoongang.core.system.notifier.CourseNotifier
-import com.raccoongang.course.presentation.CourseRouter
-import com.raccoongang.dashboard.presentation.DashboardRouter
-import com.raccoongang.discovery.presentation.DiscoveryRouter
-import com.raccoongang.discussion.presentation.DiscussionRouter
-import com.raccoongang.discussion.system.notifier.DiscussionNotifier
-import com.raccoongang.newedx.AppRouter
-import com.raccoongang.newedx.room.AppDatabase
-import com.raccoongang.newedx.room.DATABASE_NAME
-import com.raccoongang.newedx.system.notifier.AppNotifier
-import com.raccoongang.profile.presentation.ProfileRouter
-import com.raccoongang.profile.system.notifier.ProfileNotifier
-import kotlinx.coroutines.Dispatchers
-import org.koin.android.ext.koin.androidApplication
-import org.koin.core.qualifier.named
-import org.koin.dsl.module
-
-val appModule = module {
-
- single { PreferencesManager(get()) }
-
- single { ResourceManager(get()) }
-
- single { AppCookieManager(get()) }
-
- single { GsonBuilder().create() }
-
- single { AppNotifier() }
- single { CourseNotifier() }
- single { DiscussionNotifier() }
- single { ProfileNotifier() }
-
- single { AppRouter() }
- single { get() }
- single { get() }
- single { get() }
- single { get() }
- single { get() }
- single { get() }
-
-
- single { NetworkConnection(get()) }
-
- single(named("IODispatcher")) {
- Dispatchers.IO
- }
-
- single {
- Room.databaseBuilder(
- androidApplication(),
- AppDatabase::class.java,
- DATABASE_NAME
- ).fallbackToDestructiveMigration()
- .fallbackToDestructiveMigrationOnDowngrade()
- .build()
- }
-
- single {
- get()
- }
-
- single {
- val room = get()
- room.discoveryDao()
- }
-
- single {
- val room = get()
- room.courseDao()
- }
-
- single {
- val room = get()
- room.dashboardDao()
- }
-
- single {
- val room = get()
- room.downloadDao()
- }
-
- single {
- FileDownloader()
- }
-
- single {
- DownloadWorkerController(get(), get(), get())
- }
-
- single { TranscriptManager(get()) }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/raccoongang/newedx/di/ScreenModule.kt b/app/src/main/java/com/raccoongang/newedx/di/ScreenModule.kt
deleted file mode 100644
index 924880a76..000000000
--- a/app/src/main/java/com/raccoongang/newedx/di/ScreenModule.kt
+++ /dev/null
@@ -1,101 +0,0 @@
-package com.raccoongang.newedx.di
-
-import com.raccoongang.auth.data.repository.AuthRepository
-import com.raccoongang.auth.domain.interactor.AuthInteractor
-import com.raccoongang.auth.presentation.restore.RestorePasswordViewModel
-import com.raccoongang.auth.presentation.signin.SignInViewModel
-import com.raccoongang.auth.presentation.signup.SignUpViewModel
-import com.raccoongang.core.Validator
-import com.raccoongang.core.domain.model.Account
-import com.raccoongang.core.presentation.dialog.SelectDialogViewModel
-import com.raccoongang.course.data.repository.CourseRepository
-import com.raccoongang.course.domain.interactor.CourseInteractor
-import com.raccoongang.course.presentation.container.CourseContainerViewModel
-import com.raccoongang.course.presentation.detail.CourseDetailsViewModel
-import com.raccoongang.course.presentation.handouts.HandoutsViewModel
-import com.raccoongang.course.presentation.outline.CourseOutlineViewModel
-import com.raccoongang.discovery.presentation.search.CourseSearchViewModel
-import com.raccoongang.course.presentation.section.CourseSectionViewModel
-import com.raccoongang.course.presentation.unit.container.CourseUnitContainerViewModel
-import com.raccoongang.course.presentation.unit.video.VideoUnitViewModel
-import com.raccoongang.course.presentation.unit.video.VideoViewModel
-import com.raccoongang.course.presentation.units.CourseUnitsViewModel
-import com.raccoongang.course.presentation.videos.CourseVideoViewModel
-import com.raccoongang.dashboard.data.repository.DashboardRepository
-import com.raccoongang.dashboard.domain.interactor.DashboardInteractor
-import com.raccoongang.dashboard.presentation.DashboardViewModel
-import com.raccoongang.discovery.data.repository.DiscoveryRepository
-import com.raccoongang.discovery.domain.interactor.DiscoveryInteractor
-import com.raccoongang.discovery.presentation.DiscoveryViewModel
-import com.raccoongang.discussion.data.repository.DiscussionRepository
-import com.raccoongang.discussion.domain.interactor.DiscussionInteractor
-import com.raccoongang.discussion.domain.model.DiscussionComment
-import com.raccoongang.discussion.presentation.comments.DiscussionCommentsViewModel
-import com.raccoongang.discussion.presentation.responses.DiscussionResponsesViewModel
-import com.raccoongang.discussion.presentation.search.DiscussionSearchThreadViewModel
-import com.raccoongang.discussion.presentation.threads.DiscussionAddThreadViewModel
-import com.raccoongang.discussion.presentation.threads.DiscussionThreadsViewModel
-import com.raccoongang.discussion.presentation.topics.DiscussionTopicsViewModel
-import com.raccoongang.newedx.AppViewModel
-import com.raccoongang.profile.data.repository.ProfileRepository
-import com.raccoongang.profile.domain.interactor.ProfileInteractor
-import com.raccoongang.profile.presentation.delete.DeleteProfileViewModel
-import com.raccoongang.profile.presentation.edit.EditProfileViewModel
-import com.raccoongang.profile.presentation.profile.ProfileViewModel
-import com.raccoongang.profile.presentation.settings.video.VideoQualityViewModel
-import com.raccoongang.profile.presentation.settings.video.VideoSettingsViewModel
-import org.koin.androidx.viewmodel.dsl.viewModel
-import org.koin.core.qualifier.named
-import org.koin.dsl.module
-
-val screenModule = module {
-
- viewModel { AppViewModel(get(), get(), get(), get(named("IODispatcher"))) }
-
- factory { AuthRepository(get(), get()) }
- factory { AuthInteractor(get()) }
- factory { Validator() }
- viewModel { SignInViewModel(get(), get(), get()) }
- viewModel { SignUpViewModel(get(), get()) }
- viewModel { RestorePasswordViewModel(get(), get()) }
-
- factory { DashboardRepository(get(), get(),get()) }
- factory { DashboardInteractor(get()) }
- viewModel { DashboardViewModel(get(), get(), get(), get()) }
-
- factory { DiscoveryRepository(get(), get()) }
- factory { DiscoveryInteractor(get()) }
- viewModel { DiscoveryViewModel(get(), get(), get()) }
-
- factory { ProfileRepository(get(), get(), get()) }
- factory { ProfileInteractor(get()) }
- viewModel { ProfileViewModel(get(), get(), get(), get(), get(named("IODispatcher")), get(), get()) }
- viewModel { (account: Account) -> EditProfileViewModel(get(), get(), get(), account) }
- viewModel { VideoSettingsViewModel(get(), get()) }
- viewModel { VideoQualityViewModel(get(), get()) }
- viewModel { DeleteProfileViewModel(get(), get(), get(), get()) }
-
- single { CourseRepository(get(), get(), get(),get()) }
- factory { CourseInteractor(get()) }
- viewModel { (courseId: String) -> CourseDetailsViewModel(courseId, get(), get(), get(), get()) }
- viewModel { (courseId: String) -> CourseContainerViewModel(courseId, get(), get(), get(), get()) }
- viewModel { (courseId: String) -> CourseOutlineViewModel(courseId, get(), get(), get(), get(), get(), get(), get()) }
- viewModel { CourseUnitsViewModel(get(), get(),get(), get(), get()) }
- viewModel { (courseId: String) -> CourseSectionViewModel(get(), get(), get(), get(), get(), get(), courseId) }
- viewModel { (courseId: String) -> CourseUnitContainerViewModel(get(), get(), courseId) }
- viewModel { (courseId: String) -> CourseVideoViewModel(courseId, get(), get(), get(), get(), get(), get(), get()) }
- viewModel { (courseId: String) -> VideoViewModel(courseId, get(), get(), get()) }
- viewModel { (courseId: String) -> VideoUnitViewModel(courseId, get(), get(), get(), get(), get()) }
- viewModel { (courseId:String, handoutsType: String) -> HandoutsViewModel(courseId, handoutsType, get()) }
- viewModel { CourseSearchViewModel(get(), get()) }
- viewModel { SelectDialogViewModel(get()) }
-
- single { DiscussionRepository(get()) }
- factory { DiscussionInteractor(get()) }
- viewModel { (courseId: String) -> DiscussionTopicsViewModel(get(), get(), courseId) }
- viewModel { (courseId: String, threadType: String) -> DiscussionThreadsViewModel(get(), get(), get(), courseId, threadType) }
- viewModel { (thread: com.raccoongang.discussion.domain.model.Thread) -> DiscussionCommentsViewModel(get(), get(), get(), get(), thread) }
- viewModel { (comment: DiscussionComment) -> DiscussionResponsesViewModel(get(), get(), get(), get(), comment) }
- viewModel { (courseId: String) -> DiscussionAddThreadViewModel(get(), get(), get(), courseId) }
- viewModel { (courseId: String) -> DiscussionSearchThreadViewModel(get(), get(), get(), courseId) }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/raccoongang/newedx/room/AppDatabase.kt b/app/src/main/java/com/raccoongang/newedx/room/AppDatabase.kt
deleted file mode 100644
index e8acf7994..000000000
--- a/app/src/main/java/com/raccoongang/newedx/room/AppDatabase.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.raccoongang.newedx.room
-
-import androidx.room.Database
-import androidx.room.RoomDatabase
-import androidx.room.TypeConverters
-import com.raccoongang.core.data.model.room.CourseEntity
-import com.raccoongang.core.data.model.room.CourseStructureEntity
-import com.raccoongang.core.data.model.room.discovery.EnrolledCourseEntity
-import com.raccoongang.core.module.db.DownloadDao
-import com.raccoongang.core.module.db.DownloadModelEntity
-import com.raccoongang.course.data.storage.CourseConverter
-import com.raccoongang.course.data.storage.CourseDao
-import com.raccoongang.dashboard.data.DashboardDao
-import com.raccoongang.discovery.data.converter.DiscoveryConverter
-import com.raccoongang.discovery.data.storage.DiscoveryDao
-
-const val DATABASE_VERSION = 1
-const val DATABASE_NAME = "newEdx_db"
-
-@Database(
- entities = [
- CourseEntity::class,
- EnrolledCourseEntity::class,
- CourseStructureEntity::class,
- DownloadModelEntity::class
- ],
- version = DATABASE_VERSION,
- exportSchema = false
-)
-@TypeConverters(DiscoveryConverter::class, CourseConverter::class)
-abstract class AppDatabase : RoomDatabase() {
- abstract fun discoveryDao(): DiscoveryDao
- abstract fun courseDao(): CourseDao
- abstract fun dashboardDao(): DashboardDao
- abstract fun downloadDao(): DownloadDao
-}
diff --git a/app/src/main/java/com/raccoongang/newedx/system/notifier/AppEvent.kt b/app/src/main/java/com/raccoongang/newedx/system/notifier/AppEvent.kt
deleted file mode 100644
index aa42906b6..000000000
--- a/app/src/main/java/com/raccoongang/newedx/system/notifier/AppEvent.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package com.raccoongang.newedx.system.notifier
-
-interface AppEvent
\ No newline at end of file
diff --git a/app/src/main/java/com/raccoongang/newedx/system/notifier/LogoutEvent.kt b/app/src/main/java/com/raccoongang/newedx/system/notifier/LogoutEvent.kt
deleted file mode 100644
index d498342f7..000000000
--- a/app/src/main/java/com/raccoongang/newedx/system/notifier/LogoutEvent.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package com.raccoongang.newedx.system.notifier
-
-class LogoutEvent : AppEvent
diff --git a/app/src/main/java/org/openedx/app/AnalyticsManager.kt b/app/src/main/java/org/openedx/app/AnalyticsManager.kt
new file mode 100644
index 000000000..0efcb7d54
--- /dev/null
+++ b/app/src/main/java/org/openedx/app/AnalyticsManager.kt
@@ -0,0 +1,424 @@
+package org.openedx.app
+
+import android.content.Context
+import android.os.Bundle
+import androidx.core.os.bundleOf
+import com.google.firebase.analytics.FirebaseAnalytics
+import org.openedx.auth.presentation.AuthAnalytics
+import org.openedx.course.presentation.CourseAnalytics
+import org.openedx.dashboard.presentation.DashboardAnalytics
+import org.openedx.discovery.presentation.DiscoveryAnalytics
+import org.openedx.discussion.presentation.DiscussionAnalytics
+import org.openedx.profile.presentation.ProfileAnalytics
+
+class AnalyticsManager(context: Context) : DashboardAnalytics, AuthAnalytics, AppAnalytics,
+ DiscoveryAnalytics, ProfileAnalytics, CourseAnalytics, DiscussionAnalytics {
+
+ private val analytics = FirebaseAnalytics.getInstance(context)
+
+ private fun logEvent(event: Event, params: Bundle = bundleOf()) {
+ analytics.logEvent(event.eventName, params)
+ }
+
+ private fun setUserId(userId: Long) {
+ analytics.setUserId(userId.toString())
+ }
+
+ override fun dashboardCourseClickedEvent(courseId: String, courseName: String) {
+ logEvent(
+ Event.DASHBOARD_COURSE_CLICKED,
+ bundleOf(
+ Key.COURSE_ID.keyName to courseId,
+ Key.COURSE_NAME.keyName to courseName
+ )
+ )
+ }
+
+ override fun userLoginEvent(method: String) {
+ logEvent(
+ Event.USER_LOGIN,
+ bundleOf(
+ Key.METHOD.keyName to method
+ )
+ )
+ }
+
+ override fun signUpClickedEvent() {
+ logEvent(Event.SIGN_UP_CLICKED)
+ }
+
+ override fun createAccountClickedEvent(provider: String) {
+ logEvent(
+ Event.CREATE_ACCOUNT_CLICKED,
+ bundleOf(Key.PROVIDER.keyName to provider)
+ )
+ }
+
+ override fun registrationSuccessEvent(provider: String) {
+ logEvent(
+ Event.REGISTRATION_SUCCESS,
+ bundleOf(Key.PROVIDER.keyName to provider)
+ )
+ }
+
+ override fun forgotPasswordClickedEvent() {
+ logEvent(Event.FORGOT_PASSWORD_CLICKED)
+ }
+
+ override fun resetPasswordClickedEvent(success: Boolean) {
+ logEvent(
+ Event.RESET_PASSWORD_CLICKED, bundleOf(
+ Key.SUCCESS.keyName to success
+ )
+ )
+ }
+
+ override fun logoutEvent(force: Boolean) {
+ logEvent(
+ Event.USER_LOGOUT, bundleOf(
+ Key.FORCE.keyName to force
+ )
+ )
+ }
+
+ override fun discoveryTabClickedEvent() {
+ logEvent(Event.DISCOVERY_TAB_CLICKED)
+ }
+
+ override fun dashboardTabClickedEvent() {
+ logEvent(Event.DASHBOARD_TAB_CLICKED)
+ }
+
+ override fun programsTabClickedEvent() {
+ logEvent(Event.PROGRAMS_TAB_CLICKED)
+ }
+
+ override fun profileTabClickedEvent() {
+ logEvent(Event.PROFILE_TAB_CLICKED)
+ }
+
+ override fun setUserIdForSession(userId: Long) {
+ setUserId(userId)
+ }
+
+ override fun discoverySearchBarClickedEvent() {
+ logEvent(Event.DISCOVERY_SEARCH_BAR_CLICKED)
+ }
+
+ override fun discoveryCourseSearchEvent(label: String, coursesCount: Int) {
+ logEvent(
+ Event.DISCOVERY_COURSE_SEARCH, bundleOf(
+ Key.LABEL.keyName to label,
+ Key.COURSE_COUNT.keyName to coursesCount
+ )
+ )
+ }
+
+ override fun discoveryCourseClickedEvent(courseId: String, courseName: String) {
+ logEvent(
+ Event.DISCOVERY_COURSE_CLICKED, bundleOf(
+ Key.COURSE_ID.keyName to courseId,
+ Key.COURSE_NAME.keyName to courseName
+ )
+ )
+ }
+
+ override fun profileEditClickedEvent() {
+ logEvent(Event.PROFILE_EDIT_CLICKED)
+ }
+
+ override fun profileEditDoneClickedEvent() {
+ logEvent(Event.PROFILE_EDIT_DONE_CLICKED)
+ }
+
+ override fun profileDeleteAccountClickedEvent() {
+ logEvent(Event.PROFILE_DELETE_ACCOUNT_CLICKED)
+ }
+
+ override fun profileVideoSettingsClickedEvent() {
+ logEvent(Event.PROFILE_VIDEO_SETTINGS_CLICKED)
+ }
+
+ override fun privacyPolicyClickedEvent() {
+ logEvent(Event.PRIVACY_POLICY_CLICKED)
+ }
+
+ override fun cookiePolicyClickedEvent() {
+ logEvent(Event.COOKIE_POLICY_CLICKED)
+ }
+
+ override fun emailSupportClickedEvent() {
+ logEvent(Event.EMAIL_SUPPORT_CLICKED)
+ }
+
+ override fun courseEnrollClickedEvent(courseId: String, courseName: String) {
+ logEvent(
+ Event.COURSE_ENROLL_CLICKED, bundleOf(
+ Key.COURSE_ID.keyName to courseId,
+ Key.COURSE_NAME.keyName to courseName,
+ )
+ )
+ }
+
+ override fun courseEnrollSuccessEvent(courseId: String, courseName: String) {
+ logEvent(
+ Event.COURSE_ENROLL_SUCCESS, bundleOf(
+ Key.COURSE_ID.keyName to courseId,
+ Key.COURSE_NAME.keyName to courseName,
+ )
+ )
+ }
+
+ override fun viewCourseClickedEvent(courseId: String, courseName: String) {
+ logEvent(
+ Event.VIEW_COURSE_CLICKED, bundleOf(
+ Key.COURSE_ID.keyName to courseId,
+ Key.COURSE_NAME.keyName to courseName,
+ )
+ )
+ }
+
+ override fun resumeCourseTappedEvent(courseId: String, courseName: String, blockId: String) {
+ logEvent(
+ Event.RESUME_COURSE_TAPPED, bundleOf(
+ Key.COURSE_ID.keyName to courseId,
+ Key.COURSE_NAME.keyName to courseName,
+ Key.BLOCK_ID.keyName to blockId
+ )
+ )
+ }
+
+ override fun sequentialClickedEvent(
+ courseId: String,
+ courseName: String,
+ blockId: String,
+ blockName: String
+ ) {
+ logEvent(
+ Event.SEQUENTIAL_CLICKED, bundleOf(
+ Key.COURSE_ID.keyName to courseId,
+ Key.COURSE_NAME.keyName to courseName,
+ Key.BLOCK_ID.keyName to blockId,
+ Key.BLOCK_NAME.keyName to blockName,
+ )
+ )
+ }
+
+ override fun verticalClickedEvent(
+ courseId: String,
+ courseName: String,
+ blockId: String,
+ blockName: String
+ ) {
+ logEvent(
+ Event.VERTICAL_CLICKED, bundleOf(
+ Key.COURSE_ID.keyName to courseId,
+ Key.COURSE_NAME.keyName to courseName,
+ Key.BLOCK_ID.keyName to blockId,
+ Key.BLOCK_NAME.keyName to blockName,
+ )
+ )
+ }
+
+ override fun nextBlockClickedEvent(
+ courseId: String,
+ courseName: String,
+ blockId: String,
+ blockName: String
+ ) {
+ logEvent(
+ Event.NEXT_BLOCK_CLICKED, bundleOf(
+ Key.COURSE_ID.keyName to courseId,
+ Key.COURSE_NAME.keyName to courseName,
+ Key.BLOCK_ID.keyName to blockId,
+ Key.BLOCK_NAME.keyName to blockName,
+ )
+ )
+ }
+
+ override fun prevBlockClickedEvent(
+ courseId: String,
+ courseName: String,
+ blockId: String,
+ blockName: String
+ ) {
+ logEvent(
+ Event.PREV_BLOCK_CLICKED, bundleOf(
+ Key.COURSE_ID.keyName to courseId,
+ Key.COURSE_NAME.keyName to courseName,
+ Key.BLOCK_ID.keyName to blockId,
+ Key.BLOCK_NAME.keyName to blockName,
+ )
+ )
+ }
+
+ override fun finishVerticalClickedEvent(
+ courseId: String,
+ courseName: String,
+ blockId: String,
+ blockName: String
+ ) {
+ logEvent(
+ Event.FINISH_VERTICAL_CLICKED, bundleOf(
+ Key.COURSE_ID.keyName to courseId,
+ Key.COURSE_NAME.keyName to courseName,
+ Key.BLOCK_ID.keyName to blockId,
+ Key.BLOCK_NAME.keyName to blockName,
+ )
+ )
+ }
+
+ override fun finishVerticalNextClickedEvent(
+ courseId: String,
+ courseName: String,
+ blockId: String,
+ blockName: String
+ ) {
+ logEvent(
+ Event.FINISH_VERTICAL_NEXT_CLICKED, bundleOf(
+ Key.COURSE_ID.keyName to courseId,
+ Key.COURSE_NAME.keyName to courseName,
+ Key.BLOCK_ID.keyName to blockId,
+ Key.BLOCK_NAME.keyName to blockName,
+ )
+ )
+ }
+
+ override fun finishVerticalBackClickedEvent(courseId: String, courseName: String) {
+ logEvent(
+ Event.FINISH_VERTICAL_BACK_CLICKED, bundleOf(
+ Key.COURSE_ID.keyName to courseId,
+ Key.COURSE_NAME.keyName to courseName
+ )
+ )
+ }
+
+ override fun courseTabClickedEvent(courseId: String, courseName: String) {
+ logEvent(
+ Event.COURSE_TAB_CLICKED, bundleOf(
+ Key.COURSE_ID.keyName to courseId,
+ Key.COURSE_NAME.keyName to courseName
+ )
+ )
+ }
+
+ override fun videoTabClickedEvent(courseId: String, courseName: String) {
+ logEvent(
+ Event.VIDEO_TAB_CLICKED, bundleOf(
+ Key.COURSE_ID.keyName to courseId,
+ Key.COURSE_NAME.keyName to courseName
+ )
+ )
+ }
+
+ override fun discussionTabClickedEvent(courseId: String, courseName: String) {
+ logEvent(
+ Event.DISCUSSION_TAB_CLICKED, bundleOf(
+ Key.COURSE_ID.keyName to courseId,
+ Key.COURSE_NAME.keyName to courseName
+ )
+ )
+ }
+
+ override fun handoutsTabClickedEvent(courseId: String, courseName: String) {
+ logEvent(
+ Event.HANDOUTS_TAB_CLICKED, bundleOf(
+ Key.COURSE_ID.keyName to courseId,
+ Key.COURSE_NAME.keyName to courseName
+ )
+ )
+ }
+
+ override fun discussionAllPostsClickedEvent(courseId: String, courseName: String) {
+ logEvent(
+ Event.DISCUSSION_ALL_POSTS_CLICKED, bundleOf(
+ Key.COURSE_ID.keyName to courseId,
+ Key.COURSE_NAME.keyName to courseName
+ )
+ )
+ }
+
+ override fun discussionFollowingClickedEvent(courseId: String, courseName: String) {
+ logEvent(
+ Event.DISCUSSION_FOLLOWING_CLICKED, bundleOf(
+ Key.COURSE_ID.keyName to courseId,
+ Key.COURSE_NAME.keyName to courseName
+ )
+ )
+ }
+
+ override fun discussionTopicClickedEvent(
+ courseId: String,
+ courseName: String,
+ topicId: String,
+ topicName: String
+ ) {
+ logEvent(
+ Event.DISCUSSION_TOPIC_CLICKED, bundleOf(
+ Key.COURSE_ID.keyName to courseId,
+ Key.COURSE_NAME.keyName to courseName,
+ Key.TOPIC_ID.keyName to topicId,
+ Key.TOPIC_NAME.keyName to topicName
+ )
+ )
+ }
+
+}
+
+private enum class Event(val eventName: String) {
+ USER_LOGIN("User_Login"),
+ SIGN_UP_CLICKED("Sign_up_Clicked"),
+ CREATE_ACCOUNT_CLICKED("Create_Account_Clicked"),
+ REGISTRATION_SUCCESS("Registration_Success"),
+ USER_LOGOUT("User_Logout"),
+ FORGOT_PASSWORD_CLICKED("Forgot_password_Clicked"),
+ RESET_PASSWORD_CLICKED("Reset_password_Clicked"),
+ DISCOVERY_TAB_CLICKED("Main_Discovery_tab_Clicked"),
+ DASHBOARD_TAB_CLICKED("Main_Dashboard_tab_Clicked"),
+ PROGRAMS_TAB_CLICKED("Main_Programs_tab_Clicked"),
+ PROFILE_TAB_CLICKED("Main_Profile_tab_Clicked"),
+ DISCOVERY_SEARCH_BAR_CLICKED("Discovery_Search_Bar_Clicked"),
+ DISCOVERY_COURSE_SEARCH("Discovery_Courses_Search"),
+ DISCOVERY_COURSE_CLICKED("Discovery_Course_Clicked"),
+ DASHBOARD_COURSE_CLICKED("Dashboard_Course_Clicked"),
+ PROFILE_EDIT_CLICKED("Profile_Edit_Clicked"),
+ PROFILE_EDIT_DONE_CLICKED("Profile_Edit_Done_Clicked"),
+ PROFILE_DELETE_ACCOUNT_CLICKED("Profile_Delete_Account_Clicked"),
+ PROFILE_VIDEO_SETTINGS_CLICKED("Profile_Video_settings_Clicked"),
+ PRIVACY_POLICY_CLICKED("Privacy_Policy_Clicked"),
+ COOKIE_POLICY_CLICKED("Cookie_Policy_Clicked"),
+ EMAIL_SUPPORT_CLICKED("Email_Support_Clicked"),
+ COURSE_ENROLL_CLICKED("Course_Enroll_Clicked"),
+ COURSE_ENROLL_SUCCESS("Course_Enroll_Success"),
+ VIEW_COURSE_CLICKED("View_Course_Clicked"),
+ RESUME_COURSE_TAPPED("Resume_Course_Tapped"),
+ SEQUENTIAL_CLICKED("Sequential_Clicked"),
+ VERTICAL_CLICKED("Vertical_Clicked"),
+ NEXT_BLOCK_CLICKED("Next_Block_Clicked"),
+ PREV_BLOCK_CLICKED("Prev_Block_Clicked"),
+ FINISH_VERTICAL_CLICKED("Finish_Vertical_Clicked"),
+ FINISH_VERTICAL_NEXT_CLICKED("Finish_Vertical_Next_section_Clicked"),
+ FINISH_VERTICAL_BACK_CLICKED("Finish_Vertical_Back_to_outline_Clicked"),
+ COURSE_TAB_CLICKED("Course_Outline_Course_tab_Clicked"),
+ VIDEO_TAB_CLICKED("Course_Outline_Videos_tab_Clicked"),
+ DISCUSSION_TAB_CLICKED("Course_Outline_Discussion_tab_Clicked"),
+ HANDOUTS_TAB_CLICKED("Course_Outline_Handouts_tab_Clicked"),
+ DISCUSSION_ALL_POSTS_CLICKED("Discussion_All_Posts_Clicked"),
+ DISCUSSION_FOLLOWING_CLICKED("Discussion_Following_Clicked"),
+ DISCUSSION_TOPIC_CLICKED("Discussion_Topic_Clicked"),
+}
+
+private enum class Key(val keyName: String) {
+ COURSE_ID("course_id"),
+ COURSE_NAME("course_name"),
+ BLOCK_ID("block_id"),
+ BLOCK_NAME("block_name"),
+ TOPIC_ID("topic_id"),
+ TOPIC_NAME("topic_name"),
+ METHOD("method"),
+ SUCCESS("success"),
+ PROVIDER("provider"),
+ FORCE("force"),
+ LABEL("label"),
+ COURSE_COUNT("courses_count"),
+}
diff --git a/app/src/main/java/com/raccoongang/newedx/AppActivity.kt b/app/src/main/java/org/openedx/app/AppActivity.kt
similarity index 89%
rename from app/src/main/java/com/raccoongang/newedx/AppActivity.kt
rename to app/src/main/java/org/openedx/app/AppActivity.kt
index b70455c1e..426a7f4e8 100644
--- a/app/src/main/java/com/raccoongang/newedx/AppActivity.kt
+++ b/app/src/main/java/org/openedx/app/AppActivity.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.newedx
+package org.openedx.app
import android.content.pm.ActivityInfo
import android.content.res.Configuration
@@ -12,19 +12,19 @@ import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.window.layout.WindowMetricsCalculator
-import com.raccoongang.auth.presentation.signin.SignInFragment
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.extension.requestApplyInsetsWhenAttached
-import com.raccoongang.core.presentation.global.AppData
-import com.raccoongang.core.presentation.global.AppDataHolder
-import com.raccoongang.core.presentation.global.InsetHolder
-import com.raccoongang.core.presentation.global.WindowSizeHolder
-import com.raccoongang.core.ui.WindowSize
-import com.raccoongang.core.ui.WindowType
-import com.raccoongang.newedx.databinding.ActivityAppBinding
-import com.raccoongang.profile.presentation.ProfileRouter
+import org.openedx.auth.presentation.signin.SignInFragment
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.extension.requestApplyInsetsWhenAttached
+import org.openedx.core.presentation.global.AppData
+import org.openedx.core.presentation.global.AppDataHolder
+import org.openedx.core.presentation.global.InsetHolder
+import org.openedx.core.presentation.global.WindowSizeHolder
+import org.openedx.core.ui.WindowSize
+import org.openedx.core.ui.WindowType
+import org.openedx.profile.presentation.ProfileRouter
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
+import org.openedx.app.databinding.ActivityAppBinding
class AppActivity : AppCompatActivity(), InsetHolder, WindowSizeHolder, AppDataHolder {
diff --git a/app/src/main/java/org/openedx/app/AppAnalytics.kt b/app/src/main/java/org/openedx/app/AppAnalytics.kt
new file mode 100644
index 000000000..1114ccd8e
--- /dev/null
+++ b/app/src/main/java/org/openedx/app/AppAnalytics.kt
@@ -0,0 +1,10 @@
+package org.openedx.app
+
+interface AppAnalytics {
+ fun logoutEvent(force: Boolean)
+ fun discoveryTabClickedEvent()
+ fun dashboardTabClickedEvent()
+ fun programsTabClickedEvent()
+ fun profileTabClickedEvent()
+ fun setUserIdForSession(userId: Long)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/raccoongang/newedx/AppRouter.kt b/app/src/main/java/org/openedx/app/AppRouter.kt
similarity index 71%
rename from app/src/main/java/com/raccoongang/newedx/AppRouter.kt
rename to app/src/main/java/org/openedx/app/AppRouter.kt
index 756643997..9679abaf2 100644
--- a/app/src/main/java/com/raccoongang/newedx/AppRouter.kt
+++ b/app/src/main/java/org/openedx/app/AppRouter.kt
@@ -1,42 +1,42 @@
-package com.raccoongang.newedx
+package org.openedx.app
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
-import com.raccoongang.auth.presentation.AuthRouter
-import com.raccoongang.auth.presentation.restore.RestorePasswordFragment
-import com.raccoongang.auth.presentation.signin.SignInFragment
-import com.raccoongang.auth.presentation.signup.SignUpFragment
-import com.raccoongang.core.FragmentViewType
-import com.raccoongang.core.domain.model.Account
-import com.raccoongang.core.domain.model.CoursewareAccess
-import com.raccoongang.core.presentation.course.CourseViewMode
-import com.raccoongang.course.presentation.CourseRouter
-import com.raccoongang.course.presentation.container.CourseContainerFragment
-import com.raccoongang.course.presentation.container.NoAccessCourseContainerFragment
-import com.raccoongang.course.presentation.detail.CourseDetailsFragment
-import com.raccoongang.course.presentation.handouts.HandoutsType
-import com.raccoongang.course.presentation.handouts.WebViewFragment
-import com.raccoongang.discovery.presentation.search.CourseSearchFragment
-import com.raccoongang.course.presentation.section.CourseSectionFragment
-import com.raccoongang.course.presentation.unit.container.CourseUnitContainerFragment
-import com.raccoongang.course.presentation.unit.video.VideoFullScreenFragment
-import com.raccoongang.course.presentation.unit.video.YoutubeVideoFullScreenFragment
-import com.raccoongang.course.presentation.units.CourseUnitsFragment
-import com.raccoongang.dashboard.presentation.DashboardRouter
-import com.raccoongang.discovery.presentation.DiscoveryRouter
-import com.raccoongang.discussion.domain.model.DiscussionComment
-import com.raccoongang.discussion.domain.model.Thread
-import com.raccoongang.discussion.presentation.DiscussionRouter
-import com.raccoongang.discussion.presentation.comments.DiscussionCommentsFragment
-import com.raccoongang.discussion.presentation.responses.DiscussionResponsesFragment
-import com.raccoongang.discussion.presentation.search.DiscussionSearchThreadFragment
-import com.raccoongang.discussion.presentation.threads.DiscussionAddThreadFragment
-import com.raccoongang.discussion.presentation.threads.DiscussionThreadsFragment
-import com.raccoongang.profile.presentation.ProfileRouter
-import com.raccoongang.profile.presentation.delete.DeleteProfileFragment
-import com.raccoongang.profile.presentation.edit.EditProfileFragment
-import com.raccoongang.profile.presentation.settings.video.VideoQualityFragment
-import com.raccoongang.profile.presentation.settings.video.VideoSettingsFragment
+import androidx.fragment.app.FragmentTransaction
+import org.openedx.auth.presentation.AuthRouter
+import org.openedx.auth.presentation.restore.RestorePasswordFragment
+import org.openedx.auth.presentation.signin.SignInFragment
+import org.openedx.auth.presentation.signup.SignUpFragment
+import org.openedx.core.FragmentViewType
+import org.openedx.core.domain.model.Account
+import org.openedx.core.domain.model.CoursewareAccess
+import org.openedx.core.presentation.course.CourseViewMode
+import org.openedx.course.presentation.CourseRouter
+import org.openedx.course.presentation.container.CourseContainerFragment
+import org.openedx.course.presentation.container.NoAccessCourseContainerFragment
+import org.openedx.course.presentation.detail.CourseDetailsFragment
+import org.openedx.course.presentation.handouts.HandoutsType
+import org.openedx.course.presentation.handouts.WebViewFragment
+import org.openedx.discovery.presentation.search.CourseSearchFragment
+import org.openedx.course.presentation.section.CourseSectionFragment
+import org.openedx.course.presentation.unit.container.CourseUnitContainerFragment
+import org.openedx.course.presentation.unit.video.VideoFullScreenFragment
+import org.openedx.course.presentation.unit.video.YoutubeVideoFullScreenFragment
+import org.openedx.dashboard.presentation.DashboardRouter
+import org.openedx.discovery.presentation.DiscoveryRouter
+import org.openedx.discussion.domain.model.DiscussionComment
+import org.openedx.discussion.domain.model.Thread
+import org.openedx.discussion.presentation.DiscussionRouter
+import org.openedx.discussion.presentation.comments.DiscussionCommentsFragment
+import org.openedx.discussion.presentation.responses.DiscussionResponsesFragment
+import org.openedx.discussion.presentation.search.DiscussionSearchThreadFragment
+import org.openedx.discussion.presentation.threads.DiscussionAddThreadFragment
+import org.openedx.discussion.presentation.threads.DiscussionThreadsFragment
+import org.openedx.profile.presentation.ProfileRouter
+import org.openedx.profile.presentation.delete.DeleteProfileFragment
+import org.openedx.profile.presentation.edit.EditProfileFragment
+import org.openedx.profile.presentation.settings.video.VideoQualityFragment
+import org.openedx.profile.presentation.settings.video.VideoSettingsFragment
import java.util.*
class AppRouter : AuthRouter, DiscoveryRouter, DashboardRouter, CourseRouter, DiscussionRouter,
@@ -93,42 +93,44 @@ class AppRouter : AuthRouter, DiscoveryRouter, DashboardRouter, CourseRouter, Di
//endregion
//region CourseRouter
- override fun navigateToCourseUnits(
+
+ override fun navigateToCourseSubsections(
fm: FragmentManager,
courseId: String,
blockId: String,
- courseName: String,
+ title: String,
mode: CourseViewMode,
) {
replaceFragmentWithBackStack(
fm,
- CourseUnitsFragment.newInstance(courseId, blockId, courseName, mode)
+ CourseSectionFragment.newInstance(courseId, blockId, title, mode)
)
}
- override fun navigateToCourseSubsections(
+ override fun navigateToCourseContainer(
fm: FragmentManager,
- courseId: String,
blockId: String,
- title: String,
- mode: CourseViewMode,
+ courseId: String,
+ courseName: String,
+ mode: CourseViewMode
) {
replaceFragmentWithBackStack(
fm,
- CourseSectionFragment.newInstance(courseId, blockId, title, mode)
+ CourseUnitContainerFragment.newInstance(blockId, courseId, courseName, mode)
)
}
- override fun navigateToCourseContainer(
+ override fun replaceCourseContainer(
fm: FragmentManager,
blockId: String,
courseId: String,
courseName: String,
mode: CourseViewMode
) {
- replaceFragmentWithBackStack(
+ replaceFragment(
fm,
- CourseUnitContainerFragment.newInstance(blockId, courseId, courseName, mode)
+ CourseUnitContainerFragment.newInstance(blockId, courseId, courseName, mode),
+ FragmentTransaction.TRANSIT_FRAGMENT_FADE
)
}
@@ -259,8 +261,9 @@ class AppRouter : AuthRouter, DiscoveryRouter, DashboardRouter, CourseRouter, Di
.commit()
}
- private fun replaceFragment(fm: FragmentManager, fragment: Fragment) {
+ private fun replaceFragment(fm: FragmentManager, fragment: Fragment, transaction: Int = FragmentTransaction.TRANSIT_NONE) {
fm.beginTransaction()
+ .setTransition(transaction)
.replace(R.id.container, fragment, fragment.javaClass.simpleName)
.commit()
}
diff --git a/app/src/main/java/com/raccoongang/newedx/AppViewModel.kt b/app/src/main/java/org/openedx/app/AppViewModel.kt
similarity index 68%
rename from app/src/main/java/com/raccoongang/newedx/AppViewModel.kt
rename to app/src/main/java/org/openedx/app/AppViewModel.kt
index e57021ffd..ee5a752c3 100644
--- a/app/src/main/java/com/raccoongang/newedx/AppViewModel.kt
+++ b/app/src/main/java/org/openedx/app/AppViewModel.kt
@@ -1,14 +1,14 @@
-package com.raccoongang.newedx
+package org.openedx.app
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.viewModelScope
import androidx.room.RoomDatabase
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.SingleEventLiveData
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.newedx.system.notifier.AppNotifier
-import com.raccoongang.newedx.system.notifier.LogoutEvent
+import org.openedx.core.BaseViewModel
+import org.openedx.core.SingleEventLiveData
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.app.system.notifier.AppNotifier
+import org.openedx.app.system.notifier.LogoutEvent
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -17,7 +17,8 @@ class AppViewModel(
private val notifier: AppNotifier,
private val room: RoomDatabase,
private val preferencesManager: PreferencesManager,
- private val dispatcher: CoroutineDispatcher
+ private val dispatcher: CoroutineDispatcher,
+ private val analytics: AppAnalytics
) : BaseViewModel() {
val logoutUser: LiveData
@@ -28,6 +29,7 @@ class AppViewModel(
override fun onCreate(owner: LifecycleOwner) {
super.onCreate(owner)
+ setUserId()
viewModelScope.launch {
notifier.notifier.collect { event ->
if (event is LogoutEvent && System.currentTimeMillis() - logoutHandledAt > 5000) {
@@ -36,10 +38,17 @@ class AppViewModel(
withContext(dispatcher) {
room.clearAllTables()
}
+ analytics.logoutEvent(true)
_logoutUser.value = Unit
}
}
}
}
+ private fun setUserId() {
+ preferencesManager.user?.let {
+ analytics.setUserIdForSession(it.id)
+ }
+ }
+
}
\ No newline at end of file
diff --git a/app/src/main/java/com/raccoongang/newedx/InDevelopmentFragment.kt b/app/src/main/java/org/openedx/app/InDevelopmentFragment.kt
similarity index 92%
rename from app/src/main/java/com/raccoongang/newedx/InDevelopmentFragment.kt
rename to app/src/main/java/org/openedx/app/InDevelopmentFragment.kt
index e0701c059..d99fef53b 100644
--- a/app/src/main/java/com/raccoongang/newedx/InDevelopmentFragment.kt
+++ b/app/src/main/java/org/openedx/app/InDevelopmentFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.newedx
+package org.openedx.app
import android.os.Bundle
import android.view.LayoutInflater
@@ -15,8 +15,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.fragment.app.Fragment
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appTypography
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appTypography
class InDevelopmentFragment : Fragment() {
diff --git a/app/src/main/java/org/openedx/app/MainFragment.kt b/app/src/main/java/org/openedx/app/MainFragment.kt
new file mode 100644
index 000000000..9152a0023
--- /dev/null
+++ b/app/src/main/java/org/openedx/app/MainFragment.kt
@@ -0,0 +1,62 @@
+package org.openedx.app
+
+import android.os.Bundle
+import android.view.View
+import androidx.viewpager2.widget.ViewPager2
+import androidx.fragment.app.Fragment
+import org.openedx.core.presentation.global.viewBinding
+import org.openedx.dashboard.presentation.DashboardFragment
+import org.openedx.discovery.presentation.DiscoveryFragment
+import org.openedx.app.adapter.MainNavigationFragmentAdapter
+import org.openedx.profile.presentation.profile.ProfileFragment
+import org.koin.android.ext.android.inject
+import org.openedx.app.databinding.FragmentMainBinding
+
+class MainFragment : Fragment(R.layout.fragment_main) {
+
+ private val binding by viewBinding(FragmentMainBinding::bind)
+ private val analytics by inject()
+
+ private lateinit var adapter: MainNavigationFragmentAdapter
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ initViewPager()
+
+ binding.bottomNavView.setOnItemSelectedListener {
+ when (it.itemId) {
+ R.id.fragmentHome -> {
+ analytics.discoveryTabClickedEvent()
+ binding.viewPager.setCurrentItem(0, false)
+ }
+ R.id.fragmentDashboard -> {
+ analytics.dashboardTabClickedEvent()
+ binding.viewPager.setCurrentItem(1, false)
+ }
+ R.id.fragmentPrograms -> {
+ analytics.programsTabClickedEvent()
+ binding.viewPager.setCurrentItem(2, false)
+ }
+ R.id.fragmentProfile -> {
+ analytics.profileTabClickedEvent()
+ binding.viewPager.setCurrentItem(3, false)
+ }
+ }
+ true
+ }
+ }
+
+ private fun initViewPager() {
+ binding.viewPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL
+ binding.viewPager.offscreenPageLimit = 4
+ adapter = MainNavigationFragmentAdapter(this).apply {
+ addFragment(DiscoveryFragment())
+ addFragment(DashboardFragment())
+ addFragment(InDevelopmentFragment())
+ addFragment(ProfileFragment())
+ }
+ binding.viewPager.adapter = adapter
+ binding.viewPager.isUserInputEnabled = false
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/openedx/app/OpenEdXApp.kt b/app/src/main/java/org/openedx/app/OpenEdXApp.kt
new file mode 100644
index 000000000..7cb2b1ad5
--- /dev/null
+++ b/app/src/main/java/org/openedx/app/OpenEdXApp.kt
@@ -0,0 +1,37 @@
+package org.openedx.app
+
+import android.app.Application
+import com.google.firebase.FirebaseOptions
+import com.google.firebase.ktx.Firebase
+import com.google.firebase.ktx.initialize
+import org.openedx.app.di.appModule
+import org.openedx.app.di.networkingModule
+import org.openedx.app.di.screenModule
+import org.koin.android.ext.koin.androidContext
+import org.koin.core.context.startKoin
+
+class OpenEdXApp : Application() {
+
+ override fun onCreate() {
+ super.onCreate()
+ startKoin {
+ androidContext(this@OpenEdXApp)
+ modules(
+ appModule,
+ networkingModule,
+ screenModule
+ )
+ }
+
+ if (org.openedx.core.BuildConfig.FIREBASE_PROJECT_ID.isNotEmpty()) {
+ val options = FirebaseOptions.Builder()
+ .setProjectId(org.openedx.core.BuildConfig.FIREBASE_PROJECT_ID)
+ .setApplicationId(getString(org.openedx.core.R.string.google_app_id))
+ .setApiKey(org.openedx.core.BuildConfig.FIREBASE_API_KEY)
+ .setGcmSenderId(org.openedx.core.BuildConfig.FIREBASE_GCM_SENDER_ID)
+ .build()
+ Firebase.initialize(this, options)
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/raccoongang/newedx/adapter/MainNavigationFragmentAdapter.kt b/app/src/main/java/org/openedx/app/adapter/MainNavigationFragmentAdapter.kt
similarity index 92%
rename from app/src/main/java/com/raccoongang/newedx/adapter/MainNavigationFragmentAdapter.kt
rename to app/src/main/java/org/openedx/app/adapter/MainNavigationFragmentAdapter.kt
index 97c45fcab..ccbe6f715 100644
--- a/app/src/main/java/com/raccoongang/newedx/adapter/MainNavigationFragmentAdapter.kt
+++ b/app/src/main/java/org/openedx/app/adapter/MainNavigationFragmentAdapter.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.newedx.adapter
+package org.openedx.app.adapter
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
diff --git a/app/src/main/java/com/raccoongang/newedx/data/networking/HandleErrorInterceptor.kt b/app/src/main/java/org/openedx/app/data/networking/HandleErrorInterceptor.kt
similarity index 91%
rename from app/src/main/java/com/raccoongang/newedx/data/networking/HandleErrorInterceptor.kt
rename to app/src/main/java/org/openedx/app/data/networking/HandleErrorInterceptor.kt
index 26fbc5015..501208bc3 100644
--- a/app/src/main/java/com/raccoongang/newedx/data/networking/HandleErrorInterceptor.kt
+++ b/app/src/main/java/org/openedx/app/data/networking/HandleErrorInterceptor.kt
@@ -1,9 +1,9 @@
-package com.raccoongang.newedx.data.networking
+package org.openedx.app.data.networking
import com.google.gson.Gson
import com.google.gson.JsonSyntaxException
-import com.raccoongang.core.data.model.ErrorResponse
-import com.raccoongang.core.system.EdxError
+import org.openedx.core.data.model.ErrorResponse
+import org.openedx.core.system.EdxError
import okhttp3.Interceptor
import okhttp3.Response
import okio.IOException
diff --git a/app/src/main/java/com/raccoongang/newedx/data/networking/HeadersInterceptor.kt b/app/src/main/java/org/openedx/app/data/networking/HeadersInterceptor.kt
similarity index 81%
rename from app/src/main/java/com/raccoongang/newedx/data/networking/HeadersInterceptor.kt
rename to app/src/main/java/org/openedx/app/data/networking/HeadersInterceptor.kt
index af0f8837e..ec2622c78 100644
--- a/app/src/main/java/com/raccoongang/newedx/data/networking/HeadersInterceptor.kt
+++ b/app/src/main/java/org/openedx/app/data/networking/HeadersInterceptor.kt
@@ -1,7 +1,7 @@
-package com.raccoongang.newedx.data.networking
+package org.openedx.app.data.networking
-import com.raccoongang.core.ApiConstants
-import com.raccoongang.core.data.storage.PreferencesManager
+import org.openedx.core.ApiConstants
+import org.openedx.core.data.storage.PreferencesManager
import okhttp3.Interceptor
import okhttp3.Response
diff --git a/app/src/main/java/com/raccoongang/newedx/data/networking/OauthRefreshTokenAuthenticator.kt b/app/src/main/java/org/openedx/app/data/networking/OauthRefreshTokenAuthenticator.kt
similarity index 92%
rename from app/src/main/java/com/raccoongang/newedx/data/networking/OauthRefreshTokenAuthenticator.kt
rename to app/src/main/java/org/openedx/app/data/networking/OauthRefreshTokenAuthenticator.kt
index cf4888d88..f0ad5caa3 100644
--- a/app/src/main/java/com/raccoongang/newedx/data/networking/OauthRefreshTokenAuthenticator.kt
+++ b/app/src/main/java/org/openedx/app/data/networking/OauthRefreshTokenAuthenticator.kt
@@ -1,19 +1,19 @@
-package com.raccoongang.newedx.data.networking
+package org.openedx.app.data.networking
import android.util.Log
import com.google.gson.Gson
-import com.raccoongang.auth.data.api.AuthApi
-import com.raccoongang.auth.data.model.AuthResponse
-import com.raccoongang.core.ApiConstants
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.newedx.BuildConfig
-import com.raccoongang.newedx.system.notifier.AppNotifier
-import com.raccoongang.newedx.system.notifier.LogoutEvent
+import org.openedx.auth.data.api.AuthApi
+import org.openedx.auth.data.model.AuthResponse
+import org.openedx.core.ApiConstants
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.app.system.notifier.AppNotifier
+import org.openedx.app.system.notifier.LogoutEvent
import kotlinx.coroutines.runBlocking
import okhttp3.*
import okhttp3.logging.HttpLoggingInterceptor
import org.json.JSONException
import org.json.JSONObject
+import org.openedx.core.BuildConfig
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.io.IOException
@@ -35,7 +35,7 @@ class OauthRefreshTokenAuthenticator(
}
}.build()
authApi = Retrofit.Builder()
- .baseUrl(com.raccoongang.core.BuildConfig.BASE_URL)
+ .baseUrl(org.openedx.core.BuildConfig.BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(Gson()))
.build()
@@ -113,7 +113,7 @@ class OauthRefreshTokenAuthenticator(
private fun refreshAccessToken(refreshToken: String): AuthResponse? {
val response = authApi.refreshAccessToken(
ApiConstants.TOKEN_TYPE_REFRESH,
- com.raccoongang.core.BuildConfig.CLIENT_ID,
+ org.openedx.core.BuildConfig.CLIENT_ID,
refreshToken
).execute()
val authResponse = response.body()
diff --git a/app/src/main/java/org/openedx/app/di/AppModule.kt b/app/src/main/java/org/openedx/app/di/AppModule.kt
new file mode 100644
index 000000000..7118f1c08
--- /dev/null
+++ b/app/src/main/java/org/openedx/app/di/AppModule.kt
@@ -0,0 +1,122 @@
+package org.openedx.app.di
+
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import com.google.gson.Gson
+import com.google.gson.GsonBuilder
+import org.openedx.auth.presentation.AuthAnalytics
+import org.openedx.auth.presentation.AuthRouter
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.module.DownloadWorkerController
+import org.openedx.core.module.TranscriptManager
+import org.openedx.core.module.download.FileDownloader
+import org.openedx.core.system.AppCookieManager
+import org.openedx.core.system.ResourceManager
+import org.openedx.core.system.connection.NetworkConnection
+import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.course.presentation.CourseAnalytics
+import org.openedx.course.presentation.CourseRouter
+import org.openedx.dashboard.presentation.DashboardAnalytics
+import org.openedx.dashboard.presentation.DashboardRouter
+import org.openedx.discovery.presentation.DiscoveryAnalytics
+import org.openedx.discovery.presentation.DiscoveryRouter
+import org.openedx.discussion.presentation.DiscussionAnalytics
+import org.openedx.discussion.presentation.DiscussionRouter
+import org.openedx.discussion.system.notifier.DiscussionNotifier
+import org.openedx.app.AnalyticsManager
+import org.openedx.app.AppAnalytics
+import org.openedx.app.AppRouter
+import org.openedx.app.room.AppDatabase
+import org.openedx.app.room.DATABASE_NAME
+import org.openedx.app.system.notifier.AppNotifier
+import org.openedx.profile.presentation.ProfileAnalytics
+import org.openedx.profile.presentation.ProfileRouter
+import org.openedx.profile.system.notifier.ProfileNotifier
+import kotlinx.coroutines.Dispatchers
+import org.koin.android.ext.koin.androidApplication
+import org.koin.core.qualifier.named
+import org.koin.dsl.module
+
+val appModule = module {
+
+ single { PreferencesManager(get()) }
+
+ single { ResourceManager(get()) }
+
+ single { AppCookieManager(get()) }
+
+ single { GsonBuilder().create() }
+
+ single { AppNotifier() }
+ single { CourseNotifier() }
+ single { DiscussionNotifier() }
+ single { ProfileNotifier() }
+
+ single { AppRouter() }
+ single { get() }
+ single { get() }
+ single { get() }
+ single { get() }
+ single { get() }
+ single { get() }
+
+
+ single { NetworkConnection(get()) }
+
+ single(named("IODispatcher")) {
+ Dispatchers.IO
+ }
+
+ single {
+ Room.databaseBuilder(
+ androidApplication(),
+ AppDatabase::class.java,
+ DATABASE_NAME
+ ).fallbackToDestructiveMigration()
+ .fallbackToDestructiveMigrationOnDowngrade()
+ .build()
+ }
+
+ single {
+ get()
+ }
+
+ single {
+ val room = get()
+ room.discoveryDao()
+ }
+
+ single {
+ val room = get()
+ room.courseDao()
+ }
+
+ single {
+ val room = get()
+ room.dashboardDao()
+ }
+
+ single {
+ val room = get()
+ room.downloadDao()
+ }
+
+ single {
+ FileDownloader()
+ }
+
+ single {
+ DownloadWorkerController(get(), get(), get())
+ }
+
+ single { TranscriptManager(get()) }
+
+ single { AnalyticsManager(get()) }
+ single { get() }
+ single { get() }
+ single { get() }
+ single { get() }
+ single { get() }
+ single { get() }
+ single { get() }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/raccoongang/newedx/di/NetworkingModule.kt b/app/src/main/java/org/openedx/app/di/NetworkingModule.kt
similarity index 68%
rename from app/src/main/java/com/raccoongang/newedx/di/NetworkingModule.kt
rename to app/src/main/java/org/openedx/app/di/NetworkingModule.kt
index 0f1a6fcd9..ea8b645bd 100644
--- a/app/src/main/java/com/raccoongang/newedx/di/NetworkingModule.kt
+++ b/app/src/main/java/org/openedx/app/di/NetworkingModule.kt
@@ -1,17 +1,17 @@
-package com.raccoongang.newedx.di
-
-import com.raccoongang.auth.data.api.AuthApi
-import com.raccoongang.core.data.api.CookiesApi
-import com.raccoongang.core.data.api.CourseApi
-import com.raccoongang.discussion.data.api.DiscussionApi
-import com.raccoongang.newedx.BuildConfig
-import com.raccoongang.newedx.data.networking.HandleErrorInterceptor
-import com.raccoongang.newedx.data.networking.HeadersInterceptor
-import com.raccoongang.newedx.data.networking.OauthRefreshTokenAuthenticator
-import com.raccoongang.profile.data.api.ProfileApi
+package org.openedx.app.di
+
+import org.openedx.auth.data.api.AuthApi
+import org.openedx.core.data.api.CookiesApi
+import org.openedx.core.data.api.CourseApi
+import org.openedx.discussion.data.api.DiscussionApi
+import org.openedx.app.data.networking.HandleErrorInterceptor
+import org.openedx.app.data.networking.HeadersInterceptor
+import org.openedx.app.data.networking.OauthRefreshTokenAuthenticator
+import org.openedx.profile.data.api.ProfileApi
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import org.koin.dsl.module
+import org.openedx.core.BuildConfig
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
@@ -35,7 +35,7 @@ val networkingModule = module {
single {
Retrofit.Builder()
- .baseUrl(com.raccoongang.core.BuildConfig.BASE_URL)
+ .baseUrl(org.openedx.core.BuildConfig.BASE_URL)
.client(get())
.addConverterFactory(GsonConverterFactory.create(get()))
.build()
diff --git a/app/src/main/java/org/openedx/app/di/ScreenModule.kt b/app/src/main/java/org/openedx/app/di/ScreenModule.kt
new file mode 100644
index 000000000..2b92633f3
--- /dev/null
+++ b/app/src/main/java/org/openedx/app/di/ScreenModule.kt
@@ -0,0 +1,99 @@
+package org.openedx.app.di
+
+import org.openedx.auth.data.repository.AuthRepository
+import org.openedx.auth.domain.interactor.AuthInteractor
+import org.openedx.auth.presentation.restore.RestorePasswordViewModel
+import org.openedx.auth.presentation.signin.SignInViewModel
+import org.openedx.auth.presentation.signup.SignUpViewModel
+import org.openedx.core.Validator
+import org.openedx.core.domain.model.Account
+import org.openedx.core.presentation.dialog.SelectDialogViewModel
+import org.openedx.course.data.repository.CourseRepository
+import org.openedx.course.domain.interactor.CourseInteractor
+import org.openedx.course.presentation.container.CourseContainerViewModel
+import org.openedx.course.presentation.detail.CourseDetailsViewModel
+import org.openedx.course.presentation.handouts.HandoutsViewModel
+import org.openedx.course.presentation.outline.CourseOutlineViewModel
+import org.openedx.discovery.presentation.search.CourseSearchViewModel
+import org.openedx.course.presentation.section.CourseSectionViewModel
+import org.openedx.course.presentation.unit.container.CourseUnitContainerViewModel
+import org.openedx.course.presentation.unit.video.VideoUnitViewModel
+import org.openedx.course.presentation.unit.video.VideoViewModel
+import org.openedx.course.presentation.videos.CourseVideoViewModel
+import org.openedx.dashboard.data.repository.DashboardRepository
+import org.openedx.dashboard.domain.interactor.DashboardInteractor
+import org.openedx.dashboard.presentation.DashboardViewModel
+import org.openedx.discovery.data.repository.DiscoveryRepository
+import org.openedx.discovery.domain.interactor.DiscoveryInteractor
+import org.openedx.discovery.presentation.DiscoveryViewModel
+import org.openedx.discussion.data.repository.DiscussionRepository
+import org.openedx.discussion.domain.interactor.DiscussionInteractor
+import org.openedx.discussion.domain.model.DiscussionComment
+import org.openedx.discussion.presentation.comments.DiscussionCommentsViewModel
+import org.openedx.discussion.presentation.responses.DiscussionResponsesViewModel
+import org.openedx.discussion.presentation.search.DiscussionSearchThreadViewModel
+import org.openedx.discussion.presentation.threads.DiscussionAddThreadViewModel
+import org.openedx.discussion.presentation.threads.DiscussionThreadsViewModel
+import org.openedx.discussion.presentation.topics.DiscussionTopicsViewModel
+import org.openedx.app.AppViewModel
+import org.openedx.profile.data.repository.ProfileRepository
+import org.openedx.profile.domain.interactor.ProfileInteractor
+import org.openedx.profile.presentation.delete.DeleteProfileViewModel
+import org.openedx.profile.presentation.edit.EditProfileViewModel
+import org.openedx.profile.presentation.profile.ProfileViewModel
+import org.openedx.profile.presentation.settings.video.VideoQualityViewModel
+import org.openedx.profile.presentation.settings.video.VideoSettingsViewModel
+import org.koin.androidx.viewmodel.dsl.viewModel
+import org.koin.core.qualifier.named
+import org.koin.dsl.module
+
+val screenModule = module {
+
+ viewModel { AppViewModel(get(), get(), get(), get(named("IODispatcher")), get()) }
+
+ factory { AuthRepository(get(), get()) }
+ factory { AuthInteractor(get()) }
+ factory { Validator() }
+ viewModel { SignInViewModel(get(), get(), get(), get(), get()) }
+ viewModel { SignUpViewModel(get(), get(), get(), get()) }
+ viewModel { RestorePasswordViewModel(get(), get(), get()) }
+
+ factory { DashboardRepository(get(), get(),get()) }
+ factory { DashboardInteractor(get()) }
+ viewModel { DashboardViewModel(get(), get(), get(), get(), get()) }
+
+ factory { DiscoveryRepository(get(), get()) }
+ factory { DiscoveryInteractor(get()) }
+ viewModel { DiscoveryViewModel(get(), get(), get(), get()) }
+
+ factory { ProfileRepository(get(), get(), get()) }
+ factory { ProfileInteractor(get()) }
+ viewModel { ProfileViewModel(get(), get(), get(), get(), get(named("IODispatcher")), get(), get(), get()) }
+ viewModel { (account: Account) -> EditProfileViewModel(get(), get(), get(), get(), account) }
+ viewModel { VideoSettingsViewModel(get(), get()) }
+ viewModel { VideoQualityViewModel(get(), get()) }
+ viewModel { DeleteProfileViewModel(get(), get(), get(), get()) }
+
+ single { CourseRepository(get(), get(), get(),get()) }
+ factory { CourseInteractor(get()) }
+ viewModel { (courseId: String) -> CourseDetailsViewModel(courseId, get(), get(), get(), get(), get()) }
+ viewModel { (courseId: String) -> CourseContainerViewModel(courseId, get(), get(), get(), get(), get()) }
+ viewModel { (courseId: String) -> CourseOutlineViewModel(courseId, get(), get(), get(), get(), get(), get(), get(), get()) }
+ viewModel { (courseId: String) -> CourseSectionViewModel(get(), get(), get(), get(), get(), get(), get(), get(), courseId) }
+ viewModel { (courseId: String) -> CourseUnitContainerViewModel(get(), get(), get(), courseId) }
+ viewModel { (courseId: String) -> CourseVideoViewModel(courseId, get(), get(), get(), get(), get(), get(), get()) }
+ viewModel { (courseId: String) -> VideoViewModel(courseId, get(), get(), get()) }
+ viewModel { (courseId: String) -> VideoUnitViewModel(courseId, get(), get(), get(), get()) }
+ viewModel { (courseId:String, handoutsType: String) -> HandoutsViewModel(courseId, handoutsType, get()) }
+ viewModel { CourseSearchViewModel(get(), get(), get()) }
+ viewModel { SelectDialogViewModel(get()) }
+
+ single { DiscussionRepository(get(), get()) }
+ factory { DiscussionInteractor(get()) }
+ viewModel { (courseId: String) -> DiscussionTopicsViewModel(get(), get(), get(), courseId) }
+ viewModel { (courseId: String, topicId: String, threadType: String) -> DiscussionThreadsViewModel(get(), get(), get(), courseId, topicId, threadType) }
+ viewModel { (thread: org.openedx.discussion.domain.model.Thread) -> DiscussionCommentsViewModel(get(), get(), get(), get(), thread) }
+ viewModel { (comment: DiscussionComment) -> DiscussionResponsesViewModel(get(), get(), get(), get(), comment) }
+ viewModel { (courseId: String) -> DiscussionAddThreadViewModel(get(), get(), get(), courseId) }
+ viewModel { (courseId: String) -> DiscussionSearchThreadViewModel(get(), get(), get(), courseId) }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/openedx/app/room/AppDatabase.kt b/app/src/main/java/org/openedx/app/room/AppDatabase.kt
new file mode 100644
index 000000000..f28bd8192
--- /dev/null
+++ b/app/src/main/java/org/openedx/app/room/AppDatabase.kt
@@ -0,0 +1,36 @@
+package org.openedx.app.room
+
+import androidx.room.Database
+import androidx.room.RoomDatabase
+import androidx.room.TypeConverters
+import org.openedx.core.data.model.room.CourseEntity
+import org.openedx.core.data.model.room.CourseStructureEntity
+import org.openedx.core.data.model.room.discovery.EnrolledCourseEntity
+import org.openedx.core.module.db.DownloadDao
+import org.openedx.core.module.db.DownloadModelEntity
+import org.openedx.course.data.storage.CourseConverter
+import org.openedx.course.data.storage.CourseDao
+import org.openedx.dashboard.data.DashboardDao
+import org.openedx.discovery.data.converter.DiscoveryConverter
+import org.openedx.discovery.data.storage.DiscoveryDao
+
+const val DATABASE_VERSION = 1
+const val DATABASE_NAME = "OpenEdX_db"
+
+@Database(
+ entities = [
+ CourseEntity::class,
+ EnrolledCourseEntity::class,
+ CourseStructureEntity::class,
+ DownloadModelEntity::class
+ ],
+ version = DATABASE_VERSION,
+ exportSchema = false
+)
+@TypeConverters(DiscoveryConverter::class, CourseConverter::class)
+abstract class AppDatabase : RoomDatabase() {
+ abstract fun discoveryDao(): DiscoveryDao
+ abstract fun courseDao(): CourseDao
+ abstract fun dashboardDao(): DashboardDao
+ abstract fun downloadDao(): DownloadDao
+}
diff --git a/app/src/main/java/org/openedx/app/system/notifier/AppEvent.kt b/app/src/main/java/org/openedx/app/system/notifier/AppEvent.kt
new file mode 100644
index 000000000..1a6f750f4
--- /dev/null
+++ b/app/src/main/java/org/openedx/app/system/notifier/AppEvent.kt
@@ -0,0 +1,3 @@
+package org.openedx.app.system.notifier
+
+interface AppEvent
\ No newline at end of file
diff --git a/app/src/main/java/com/raccoongang/newedx/system/notifier/AppNotifier.kt b/app/src/main/java/org/openedx/app/system/notifier/AppNotifier.kt
similarity index 88%
rename from app/src/main/java/com/raccoongang/newedx/system/notifier/AppNotifier.kt
rename to app/src/main/java/org/openedx/app/system/notifier/AppNotifier.kt
index da61281b5..d0c579d8f 100644
--- a/app/src/main/java/com/raccoongang/newedx/system/notifier/AppNotifier.kt
+++ b/app/src/main/java/org/openedx/app/system/notifier/AppNotifier.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.newedx.system.notifier
+package org.openedx.app.system.notifier
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
diff --git a/app/src/main/java/org/openedx/app/system/notifier/LogoutEvent.kt b/app/src/main/java/org/openedx/app/system/notifier/LogoutEvent.kt
new file mode 100644
index 000000000..209ac8815
--- /dev/null
+++ b/app/src/main/java/org/openedx/app/system/notifier/LogoutEvent.kt
@@ -0,0 +1,3 @@
+package org.openedx.app.system.notifier
+
+class LogoutEvent : AppEvent
diff --git a/app/src/main/res/values-v31/splash.xml b/app/src/main/res/values-v31/splash.xml
index 30fcb174a..3d2b861bd 100644
--- a/app/src/main/res/values-v31/splash.xml
+++ b/app/src/main/res/values-v31/splash.xml
@@ -13,6 +13,6 @@
- - @style/Theme.NewEdX.NoActionBar
+ - @style/Theme.OpenEdX.NoActionBar
\ No newline at end of file
diff --git a/app/src/main/res/values/splash.xml b/app/src/main/res/values/splash.xml
index ad23b2125..e206c4bc1 100644
--- a/app/src/main/res/values/splash.xml
+++ b/app/src/main/res/values/splash.xml
@@ -14,6 +14,6 @@
- - @style/Theme.NewEdX.NoActionBar
+ - @style/Theme.OpenEdX.NoActionBar
\ No newline at end of file
diff --git a/app/src/test/java/org/openedx/AppViewModelTest.kt b/app/src/test/java/org/openedx/AppViewModelTest.kt
new file mode 100644
index 000000000..fbb8135d0
--- /dev/null
+++ b/app/src/test/java/org/openedx/AppViewModelTest.kt
@@ -0,0 +1,122 @@
+package org.openedx
+
+import androidx.arch.core.executor.testing.InstantTaskExecutorRule
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LifecycleRegistry
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.domain.model.User
+import org.openedx.app.room.AppDatabase
+import org.openedx.app.system.notifier.AppNotifier
+import org.openedx.app.system.notifier.LogoutEvent
+import org.openedx.app.AppAnalytics
+import org.openedx.app.AppViewModel
+import io.mockk.every
+import io.mockk.mockk
+import io.mockk.verify
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.flow
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.rules.TestRule
+
+@ExperimentalCoroutinesApi
+class AppViewModelTest {
+
+ @get:Rule
+ val testInstantTaskExecutorRule: TestRule = InstantTaskExecutorRule()
+
+ private val dispatcher = UnconfinedTestDispatcher()
+
+ private val notifier = mockk()
+ private val room = mockk()
+ private val preferencesManager = mockk()
+ private val dispatcher2 = Dispatchers.IO
+ private val analytics = mockk()
+
+ private val user = User(0, "", "", "")
+
+ @Before
+ fun before() {
+ Dispatchers.setMain(dispatcher)
+ }
+
+ @After
+ fun after() {
+ Dispatchers.resetMain()
+ }
+
+ @Test
+ fun setIdSuccess() = runTest {
+ every { analytics.setUserIdForSession(any()) } returns Unit
+ every { preferencesManager.user } returns user
+ every { notifier.notifier } returns flow { }
+ val viewModel = AppViewModel(notifier, room, preferencesManager, dispatcher2, analytics)
+
+ val mockLifeCycleOwner: LifecycleOwner = mockk()
+ val lifecycleRegistry = LifecycleRegistry(mockLifeCycleOwner)
+ lifecycleRegistry.addObserver(viewModel)
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
+ advanceUntilIdle()
+
+ verify(exactly = 1) { analytics.setUserIdForSession(any()) }
+ }
+
+ @Test
+ fun forceLogout() = runTest {
+ every { notifier.notifier } returns flow {
+ emit(LogoutEvent())
+ }
+ every { preferencesManager.clear() } returns Unit
+ every { analytics.setUserIdForSession(any()) } returns Unit
+ every { preferencesManager.user } returns user
+ every { room.clearAllTables() } returns Unit
+ every { analytics.logoutEvent(true) } returns Unit
+ val viewModel = AppViewModel(notifier, room, preferencesManager, dispatcher2, analytics)
+
+ val mockLifeCycleOwner: LifecycleOwner = mockk()
+ val lifecycleRegistry = LifecycleRegistry(mockLifeCycleOwner)
+ lifecycleRegistry.addObserver(viewModel)
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
+ advanceUntilIdle()
+
+ verify(exactly = 1) { analytics.logoutEvent(true) }
+ assert(viewModel.logoutUser.value != null)
+ }
+
+ @Test
+ fun forceLogoutTwice() = runTest {
+ every { notifier.notifier } returns flow {
+ emit(LogoutEvent())
+ emit(LogoutEvent())
+ }
+ every { preferencesManager.clear() } returns Unit
+ every { analytics.setUserIdForSession(any()) } returns Unit
+ every { preferencesManager.user } returns user
+ every { room.clearAllTables() } returns Unit
+ every { analytics.logoutEvent(true) } returns Unit
+ val viewModel = AppViewModel(notifier, room, preferencesManager, dispatcher2, analytics)
+
+ val mockLifeCycleOwner: LifecycleOwner = mockk()
+ val lifecycleRegistry = LifecycleRegistry(mockLifeCycleOwner)
+ lifecycleRegistry.addObserver(viewModel)
+ lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
+ advanceUntilIdle()
+
+ verify(exactly = 1) { analytics.logoutEvent(true) }
+ verify(exactly = 1) { preferencesManager.clear() }
+ verify(exactly = 1) { analytics.setUserIdForSession(any()) }
+ verify(exactly = 1) { preferencesManager.user }
+ verify(exactly = 1) { room.clearAllTables() }
+ verify(exactly = 1) { analytics.logoutEvent(true) }
+ }
+
+}
\ No newline at end of file
diff --git a/auth/build.gradle b/auth/build.gradle
index 84d747e99..0db08a23f 100644
--- a/auth/build.gradle
+++ b/auth/build.gradle
@@ -15,7 +15,7 @@ android {
consumerProguardFiles "consumer-rules.pro"
}
- namespace 'com.raccoongang.auth'
+ namespace 'org.openedx.auth'
flavorDimensions "tier"
productFlavors {
diff --git a/auth/src/androidTest/java/com/raccoongang/auth/presentation/signup/RegistrationScreenTest.kt b/auth/src/androidTest/java/org/openedx/auth/presentation/signup/RegistrationScreenTest.kt
similarity index 83%
rename from auth/src/androidTest/java/com/raccoongang/auth/presentation/signup/RegistrationScreenTest.kt
rename to auth/src/androidTest/java/org/openedx/auth/presentation/signup/RegistrationScreenTest.kt
index 0416d4908..845e39034 100644
--- a/auth/src/androidTest/java/com/raccoongang/auth/presentation/signup/RegistrationScreenTest.kt
+++ b/auth/src/androidTest/java/org/openedx/auth/presentation/signup/RegistrationScreenTest.kt
@@ -1,14 +1,14 @@
-package com.raccoongang.auth.presentation.signup
+package org.openedx.auth.presentation.signup
import androidx.activity.ComponentActivity
import androidx.compose.ui.semantics.ProgressBarRangeInfo
import androidx.compose.ui.test.*
import androidx.compose.ui.test.junit4.createAndroidComposeRule
-import com.raccoongang.auth.R
-import com.raccoongang.core.domain.model.RegistrationField
-import com.raccoongang.core.domain.model.RegistrationFieldType
-import com.raccoongang.core.ui.WindowSize
-import com.raccoongang.core.ui.WindowType
+import org.openedx.auth.R
+import org.openedx.core.domain.model.RegistrationField
+import org.openedx.core.domain.model.RegistrationFieldType
+import org.openedx.core.ui.WindowSize
+import org.openedx.core.ui.WindowType
import org.junit.Rule
import org.junit.Test
@@ -38,9 +38,9 @@ class RegistrationScreenTest {
@Test
fun signUpLoadingFields() {
composeTestRule.setContent {
- RegistrationScreen(
+ org.openedx.auth.presentation.signup.RegistrationScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
- uiState = SignUpUIState.Loading,
+ uiState = org.openedx.auth.presentation.signup.SignUpUIState.Loading,
uiMessage = null,
isButtonClicked = false,
validationError = false,
@@ -59,9 +59,9 @@ class RegistrationScreenTest {
@Test
fun signUpNoOptionalFields() {
composeTestRule.setContent {
- RegistrationScreen(
+ org.openedx.auth.presentation.signup.RegistrationScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
- uiState = SignUpUIState.Fields(
+ uiState = org.openedx.auth.presentation.signup.SignUpUIState.Fields(
fields = listOf(
mockField,
mockField.copy(name = "Age", label = "Age", errorInstructions = "error")
@@ -89,9 +89,9 @@ class RegistrationScreenTest {
@Test
fun signUpHasOptionalFields() {
composeTestRule.setContent {
- RegistrationScreen(
+ org.openedx.auth.presentation.signup.RegistrationScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
- uiState = SignUpUIState.Fields(
+ uiState = org.openedx.auth.presentation.signup.SignUpUIState.Fields(
fields = listOf(
mockField,
mockField.copy(name = "Age", label = "Age", errorInstructions = "error")
@@ -117,9 +117,9 @@ class RegistrationScreenTest {
@Test
fun signUpFieldsWithError() {
composeTestRule.setContent {
- RegistrationScreen(
+ org.openedx.auth.presentation.signup.RegistrationScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
- uiState = SignUpUIState.Fields(
+ uiState = org.openedx.auth.presentation.signup.SignUpUIState.Fields(
fields = listOf(
mockField,
mockField.copy(name = "Age", label = "Age", errorInstructions = "error")
@@ -141,9 +141,9 @@ class RegistrationScreenTest {
@Test
fun signUpCreateAccountClicked() {
composeTestRule.setContent {
- RegistrationScreen(
+ org.openedx.auth.presentation.signup.RegistrationScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
- uiState = SignUpUIState.Fields(
+ uiState = org.openedx.auth.presentation.signup.SignUpUIState.Fields(
fields = listOf(
mockField,
mockField.copy(name = "Age", label = "Age", errorInstructions = "error")
diff --git a/auth/src/main/java/com/raccoongang/auth/data/api/AuthApi.kt b/auth/src/main/java/org/openedx/auth/data/api/AuthApi.kt
similarity index 78%
rename from auth/src/main/java/com/raccoongang/auth/data/api/AuthApi.kt
rename to auth/src/main/java/org/openedx/auth/data/api/AuthApi.kt
index 5edd2e719..a8ee4bcb4 100644
--- a/auth/src/main/java/com/raccoongang/auth/data/api/AuthApi.kt
+++ b/auth/src/main/java/org/openedx/auth/data/api/AuthApi.kt
@@ -1,11 +1,11 @@
-package com.raccoongang.auth.data.api
-
-import com.raccoongang.auth.data.model.AuthResponse
-import com.raccoongang.auth.data.model.PasswordResetResponse
-import com.raccoongang.auth.data.model.RegistrationFields
-import com.raccoongang.auth.data.model.ValidationFields
-import com.raccoongang.core.ApiConstants
-import com.raccoongang.core.data.model.*
+package org.openedx.auth.data.api
+
+import org.openedx.auth.data.model.AuthResponse
+import org.openedx.auth.data.model.PasswordResetResponse
+import org.openedx.auth.data.model.RegistrationFields
+import org.openedx.auth.data.model.ValidationFields
+import org.openedx.core.ApiConstants
+import org.openedx.core.data.model.*
import retrofit2.Call
import retrofit2.http.*
diff --git a/auth/src/main/java/com/raccoongang/auth/data/model/AuthResponse.kt b/auth/src/main/java/org/openedx/auth/data/model/AuthResponse.kt
similarity index 91%
rename from auth/src/main/java/com/raccoongang/auth/data/model/AuthResponse.kt
rename to auth/src/main/java/org/openedx/auth/data/model/AuthResponse.kt
index 100955103..6890dcfce 100644
--- a/auth/src/main/java/com/raccoongang/auth/data/model/AuthResponse.kt
+++ b/auth/src/main/java/org/openedx/auth/data/model/AuthResponse.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.auth.data.model
+package org.openedx.auth.data.model
import com.google.gson.annotations.SerializedName
diff --git a/auth/src/main/java/com/raccoongang/auth/data/model/PasswordResetResponse.kt b/auth/src/main/java/org/openedx/auth/data/model/PasswordResetResponse.kt
similarity index 78%
rename from auth/src/main/java/com/raccoongang/auth/data/model/PasswordResetResponse.kt
rename to auth/src/main/java/org/openedx/auth/data/model/PasswordResetResponse.kt
index 95238c740..1be96a795 100644
--- a/auth/src/main/java/com/raccoongang/auth/data/model/PasswordResetResponse.kt
+++ b/auth/src/main/java/org/openedx/auth/data/model/PasswordResetResponse.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.auth.data.model
+package org.openedx.auth.data.model
import com.google.gson.annotations.SerializedName
diff --git a/auth/src/main/java/com/raccoongang/auth/data/model/RegistrationFields.kt b/auth/src/main/java/org/openedx/auth/data/model/RegistrationFields.kt
similarity index 93%
rename from auth/src/main/java/com/raccoongang/auth/data/model/RegistrationFields.kt
rename to auth/src/main/java/org/openedx/auth/data/model/RegistrationFields.kt
index a6b36293e..ef300156f 100644
--- a/auth/src/main/java/com/raccoongang/auth/data/model/RegistrationFields.kt
+++ b/auth/src/main/java/org/openedx/auth/data/model/RegistrationFields.kt
@@ -1,8 +1,8 @@
-package com.raccoongang.auth.data.model
+package org.openedx.auth.data.model
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.domain.model.RegistrationField
-import com.raccoongang.core.domain.model.RegistrationFieldType
+import org.openedx.core.domain.model.RegistrationField
+import org.openedx.core.domain.model.RegistrationFieldType
data class RegistrationFields(
@SerializedName("fields")
diff --git a/auth/src/main/java/com/raccoongang/auth/data/model/ValidationFields.kt b/auth/src/main/java/org/openedx/auth/data/model/ValidationFields.kt
similarity index 86%
rename from auth/src/main/java/com/raccoongang/auth/data/model/ValidationFields.kt
rename to auth/src/main/java/org/openedx/auth/data/model/ValidationFields.kt
index 3747bb178..29c97ab33 100644
--- a/auth/src/main/java/com/raccoongang/auth/data/model/ValidationFields.kt
+++ b/auth/src/main/java/org/openedx/auth/data/model/ValidationFields.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.auth.data.model
+package org.openedx.auth.data.model
import com.google.gson.annotations.SerializedName
diff --git a/auth/src/main/java/com/raccoongang/auth/data/repository/AuthRepository.kt b/auth/src/main/java/org/openedx/auth/data/repository/AuthRepository.kt
similarity index 74%
rename from auth/src/main/java/com/raccoongang/auth/data/repository/AuthRepository.kt
rename to auth/src/main/java/org/openedx/auth/data/repository/AuthRepository.kt
index 334976acf..8da6f661b 100644
--- a/auth/src/main/java/com/raccoongang/auth/data/repository/AuthRepository.kt
+++ b/auth/src/main/java/org/openedx/auth/data/repository/AuthRepository.kt
@@ -1,12 +1,12 @@
-package com.raccoongang.auth.data.repository
+package org.openedx.auth.data.repository
-import com.raccoongang.auth.data.api.AuthApi
-import com.raccoongang.auth.data.model.ValidationFields
-import com.raccoongang.core.ApiConstants
-import com.raccoongang.core.BuildConfig
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.domain.model.RegistrationField
-import com.raccoongang.core.system.EdxError
+import org.openedx.auth.data.api.AuthApi
+import org.openedx.auth.data.model.ValidationFields
+import org.openedx.core.ApiConstants
+import org.openedx.core.BuildConfig
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.domain.model.RegistrationField
+import org.openedx.core.system.EdxError
class AuthRepository(
private val api: AuthApi,
@@ -19,7 +19,7 @@ class AuthRepository(
) {
val authResponse = api.getAccessToken(
ApiConstants.GRANT_TYPE_PASSWORD,
- BuildConfig.CLIENT_ID,
+ org.openedx.core.BuildConfig.CLIENT_ID,
username,
password
)
diff --git a/auth/src/main/java/com/raccoongang/auth/domain/interactor/AuthInteractor.kt b/auth/src/main/java/org/openedx/auth/domain/interactor/AuthInteractor.kt
similarity index 76%
rename from auth/src/main/java/com/raccoongang/auth/domain/interactor/AuthInteractor.kt
rename to auth/src/main/java/org/openedx/auth/domain/interactor/AuthInteractor.kt
index 70f70ad4c..53d68fd22 100644
--- a/auth/src/main/java/com/raccoongang/auth/domain/interactor/AuthInteractor.kt
+++ b/auth/src/main/java/org/openedx/auth/domain/interactor/AuthInteractor.kt
@@ -1,8 +1,8 @@
-package com.raccoongang.auth.domain.interactor
+package org.openedx.auth.domain.interactor
-import com.raccoongang.auth.data.model.ValidationFields
-import com.raccoongang.auth.data.repository.AuthRepository
-import com.raccoongang.core.domain.model.RegistrationField
+import org.openedx.auth.data.model.ValidationFields
+import org.openedx.auth.data.repository.AuthRepository
+import org.openedx.core.domain.model.RegistrationField
class AuthInteractor(private val repository: AuthRepository) {
diff --git a/auth/src/main/java/org/openedx/auth/presentation/AuthAnalytics.kt b/auth/src/main/java/org/openedx/auth/presentation/AuthAnalytics.kt
new file mode 100644
index 000000000..669c749b4
--- /dev/null
+++ b/auth/src/main/java/org/openedx/auth/presentation/AuthAnalytics.kt
@@ -0,0 +1,11 @@
+package org.openedx.auth.presentation
+
+interface AuthAnalytics {
+ fun userLoginEvent(method: String)
+ fun signUpClickedEvent()
+ fun createAccountClickedEvent(provider: String)
+ fun registrationSuccessEvent(provider: String)
+ fun forgotPasswordClickedEvent()
+ fun resetPasswordClickedEvent(success: Boolean)
+ fun setUserIdForSession(userId: Long)
+}
\ No newline at end of file
diff --git a/auth/src/main/java/com/raccoongang/auth/presentation/AuthRouter.kt b/auth/src/main/java/org/openedx/auth/presentation/AuthRouter.kt
similarity index 83%
rename from auth/src/main/java/com/raccoongang/auth/presentation/AuthRouter.kt
rename to auth/src/main/java/org/openedx/auth/presentation/AuthRouter.kt
index 851690c72..1db5b98c1 100644
--- a/auth/src/main/java/com/raccoongang/auth/presentation/AuthRouter.kt
+++ b/auth/src/main/java/org/openedx/auth/presentation/AuthRouter.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.auth.presentation
+package org.openedx.auth.presentation
import androidx.fragment.app.FragmentManager
diff --git a/auth/src/main/java/com/raccoongang/auth/presentation/restore/RestorePasswordFragment.kt b/auth/src/main/java/org/openedx/auth/presentation/restore/RestorePasswordFragment.kt
similarity index 95%
rename from auth/src/main/java/com/raccoongang/auth/presentation/restore/RestorePasswordFragment.kt
rename to auth/src/main/java/org/openedx/auth/presentation/restore/RestorePasswordFragment.kt
index e716393fa..67ad8c294 100644
--- a/auth/src/main/java/com/raccoongang/auth/presentation/restore/RestorePasswordFragment.kt
+++ b/auth/src/main/java/org/openedx/auth/presentation/restore/RestorePasswordFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.auth.presentation.restore
+package org.openedx.auth.presentation.restore
import android.content.res.Configuration
import android.os.Bundle
@@ -26,15 +26,15 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.fragment.app.Fragment
-import com.raccoongang.auth.presentation.ui.LoginTextField
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appShapes
-import com.raccoongang.core.ui.theme.appTypography
+import org.openedx.auth.presentation.ui.LoginTextField
+import org.openedx.core.UIMessage
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appShapes
+import org.openedx.core.ui.theme.appTypography
import org.koin.androidx.viewmodel.ext.android.viewModel
-import com.raccoongang.auth.R as authR
+import org.openedx.auth.R as authR
class RestorePasswordFragment : Fragment() {
@@ -47,7 +47,7 @@ class RestorePasswordFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val uiState by viewModel.uiState.observeAsState(RestorePasswordUIState.Initial)
@@ -131,7 +131,7 @@ private fun RestorePasswordScreen(
modifier = Modifier
.fillMaxWidth()
.height(200.dp),
- painter = painterResource(id = com.raccoongang.core.R.drawable.core_top_header),
+ painter = painterResource(id = org.openedx.core.R.drawable.core_top_header),
contentScale = ContentScale.FillBounds,
contentDescription = null
)
@@ -227,7 +227,7 @@ private fun RestorePasswordScreen(
CircularProgressIndicator(color = MaterialTheme.appColors.primary)
}
} else {
- NewEdxButton(
+ OpenEdXButton(
width = buttonWidth,
text = stringResource(id = authR.string.auth_reset_password),
onClick = {
@@ -273,7 +273,7 @@ private fun RestorePasswordScreen(
color = MaterialTheme.appColors.textPrimary
)
Spacer(Modifier.height(48.dp))
- NewEdxButton(
+ OpenEdXButton(
width = buttonWidth,
text = stringResource(id = authR.string.auth_sign_in),
onClick = {
@@ -296,7 +296,7 @@ private fun RestorePasswordScreen(
@Preview(name = "NEXUS_5_Dark", device = Devices.NEXUS_5, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun RestorePasswordPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
RestorePasswordScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
uiState = RestorePasswordUIState.Initial,
@@ -311,7 +311,7 @@ fun RestorePasswordPreview() {
@Preview(name = "NEXUS_9_Dark", device = Devices.NEXUS_9, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun RestorePasswordTabletPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
RestorePasswordScreen(
windowSize = WindowSize(WindowType.Medium, WindowType.Medium),
uiState = RestorePasswordUIState.Initial,
diff --git a/auth/src/main/java/com/raccoongang/auth/presentation/restore/RestorePasswordUIState.kt b/auth/src/main/java/org/openedx/auth/presentation/restore/RestorePasswordUIState.kt
similarity index 79%
rename from auth/src/main/java/com/raccoongang/auth/presentation/restore/RestorePasswordUIState.kt
rename to auth/src/main/java/org/openedx/auth/presentation/restore/RestorePasswordUIState.kt
index 311a54f8a..779adaf12 100644
--- a/auth/src/main/java/com/raccoongang/auth/presentation/restore/RestorePasswordUIState.kt
+++ b/auth/src/main/java/org/openedx/auth/presentation/restore/RestorePasswordUIState.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.auth.presentation.restore
+package org.openedx.auth.presentation.restore
sealed class RestorePasswordUIState {
object Initial : RestorePasswordUIState()
diff --git a/auth/src/main/java/com/raccoongang/auth/presentation/restore/RestorePasswordViewModel.kt b/auth/src/main/java/org/openedx/auth/presentation/restore/RestorePasswordViewModel.kt
similarity index 69%
rename from auth/src/main/java/com/raccoongang/auth/presentation/restore/RestorePasswordViewModel.kt
rename to auth/src/main/java/org/openedx/auth/presentation/restore/RestorePasswordViewModel.kt
index 074e501cd..e8d635e03 100644
--- a/auth/src/main/java/com/raccoongang/auth/presentation/restore/RestorePasswordViewModel.kt
+++ b/auth/src/main/java/org/openedx/auth/presentation/restore/RestorePasswordViewModel.kt
@@ -1,22 +1,24 @@
-package com.raccoongang.auth.presentation.restore
+package org.openedx.auth.presentation.restore
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.auth.domain.interactor.AuthInteractor
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.R
-import com.raccoongang.core.SingleEventLiveData
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.extension.isEmailValid
-import com.raccoongang.core.extension.isInternetError
-import com.raccoongang.core.system.EdxError
-import com.raccoongang.core.system.ResourceManager
+import org.openedx.auth.domain.interactor.AuthInteractor
+import org.openedx.auth.presentation.AuthAnalytics
+import org.openedx.core.BaseViewModel
+import org.openedx.core.R
+import org.openedx.core.SingleEventLiveData
+import org.openedx.core.UIMessage
+import org.openedx.core.extension.isEmailValid
+import org.openedx.core.extension.isInternetError
+import org.openedx.core.system.EdxError
+import org.openedx.core.system.ResourceManager
import kotlinx.coroutines.launch
class RestorePasswordViewModel(
private val interactor: AuthInteractor,
- private val resourceManager: ResourceManager
+ private val resourceManager: ResourceManager,
+ private val analytics: AuthAnalytics
) : BaseViewModel() {
private val _uiState = MutableLiveData()
@@ -34,18 +36,22 @@ class RestorePasswordViewModel(
if (email.isNotEmpty() && email.isEmailValid()) {
if (interactor.passwordReset(email)) {
_uiState.value = RestorePasswordUIState.Success(email)
+ analytics.resetPasswordClickedEvent(true)
} else {
_uiState.value = RestorePasswordUIState.Initial
_uiMessage.value =
UIMessage.SnackBarMessage(resourceManager.getString(R.string.core_error_unknown_error))
+ analytics.resetPasswordClickedEvent(false)
}
} else {
_uiState.value = RestorePasswordUIState.Initial
_uiMessage.value =
- UIMessage.SnackBarMessage(resourceManager.getString(com.raccoongang.auth.R.string.auth_invalid_email))
+ UIMessage.SnackBarMessage(resourceManager.getString(org.openedx.auth.R.string.auth_invalid_email))
+ analytics.resetPasswordClickedEvent(false)
}
} catch (e: Exception) {
_uiState.value = RestorePasswordUIState.Initial
+ analytics.resetPasswordClickedEvent(false)
if (e is EdxError.ValidationException) {
_uiMessage.value = UIMessage.SnackBarMessage(e.error)
} else if (e.isInternetError()) {
diff --git a/auth/src/main/java/com/raccoongang/auth/presentation/signin/SignInFragment.kt b/auth/src/main/java/org/openedx/auth/presentation/signin/SignInFragment.kt
similarity index 93%
rename from auth/src/main/java/com/raccoongang/auth/presentation/signin/SignInFragment.kt
rename to auth/src/main/java/org/openedx/auth/presentation/signin/SignInFragment.kt
index 9f53771ac..1f82823ac 100644
--- a/auth/src/main/java/com/raccoongang/auth/presentation/signin/SignInFragment.kt
+++ b/auth/src/main/java/org/openedx/auth/presentation/signin/SignInFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.auth.presentation.signin
+package org.openedx.auth.presentation.signin
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
@@ -31,15 +31,15 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.fragment.app.Fragment
-import com.raccoongang.auth.R
-import com.raccoongang.auth.presentation.AuthRouter
-import com.raccoongang.auth.presentation.ui.LoginTextField
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appShapes
-import com.raccoongang.core.ui.theme.appTypography
+import org.openedx.auth.R
+import org.openedx.auth.presentation.AuthRouter
+import org.openedx.auth.presentation.ui.LoginTextField
+import org.openedx.core.UIMessage
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appShapes
+import org.openedx.core.ui.theme.appTypography
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
@@ -55,7 +55,7 @@ class SignInFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val showProgress by viewModel.showProgress.observeAsState(initial = false)
@@ -70,9 +70,11 @@ class SignInFragment : Fragment() {
viewModel.login(login, password)
},
onRegisterClick = {
+ viewModel.signUpClickedEvent()
router.navigateToSignUp(parentFragmentManager)
},
onForgotPasswordClick = {
+ viewModel.forgotPasswordClickedEvent()
router.navigateToRestorePassword(parentFragmentManager)
}
)
@@ -134,7 +136,7 @@ private fun LoginScreen(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(0.3f),
- painter = painterResource(id = com.raccoongang.core.R.drawable.core_top_header),
+ painter = painterResource(id = org.openedx.core.R.drawable.core_top_header),
contentScale = ContentScale.FillBounds,
contentDescription = null
)
@@ -154,7 +156,7 @@ private fun LoginScreen(
contentAlignment = Alignment.Center
) {
Image(
- painter = painterResource(id = com.raccoongang.core.R.drawable.core_ic_logo),
+ painter = painterResource(id = org.openedx.core.R.drawable.core_ic_logo),
contentDescription = null,
modifier = Modifier
.padding(top = 20.dp)
@@ -257,7 +259,7 @@ private fun AuthForm(
if (isLoading) {
CircularProgressIndicator(color = MaterialTheme.appColors.primary)
} else {
- NewEdxButton(
+ OpenEdXButton(
width = buttonWidth,
text = stringResource(id = R.string.auth_sign_in),
onClick = {
@@ -283,7 +285,7 @@ private fun PasswordTextField(
val focusManager = LocalFocusManager.current
Text(
modifier = Modifier.fillMaxWidth(),
- text = stringResource(id = com.raccoongang.core.R.string.core_password),
+ text = stringResource(id = org.openedx.core.R.string.core_password),
color = MaterialTheme.appColors.textPrimary,
style = MaterialTheme.appTypography.labelLarge
)
@@ -327,7 +329,7 @@ private fun PasswordTextField(
@Preview(name = "NEXUS_5_Dark", device = Devices.NEXUS_5, uiMode = UI_MODE_NIGHT_YES)
@Composable
private fun SignInScreenPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
LoginScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
showProgress = false,
@@ -346,7 +348,7 @@ private fun SignInScreenPreview() {
@Preview(name = "NEXUS_9_Night", device = Devices.NEXUS_9, uiMode = UI_MODE_NIGHT_YES)
@Composable
private fun SignInScreenTabletPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
LoginScreen(
windowSize = WindowSize(WindowType.Expanded, WindowType.Expanded),
showProgress = false,
diff --git a/auth/src/main/java/com/raccoongang/auth/presentation/signin/SignInViewModel.kt b/auth/src/main/java/org/openedx/auth/presentation/signin/SignInViewModel.kt
similarity index 63%
rename from auth/src/main/java/com/raccoongang/auth/presentation/signin/SignInViewModel.kt
rename to auth/src/main/java/org/openedx/auth/presentation/signin/SignInViewModel.kt
index dd357840c..3d155c0a6 100644
--- a/auth/src/main/java/com/raccoongang/auth/presentation/signin/SignInViewModel.kt
+++ b/auth/src/main/java/org/openedx/auth/presentation/signin/SignInViewModel.kt
@@ -1,24 +1,28 @@
-package com.raccoongang.auth.presentation.signin
+package org.openedx.auth.presentation.signin
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.auth.R
-import com.raccoongang.auth.domain.interactor.AuthInteractor
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.SingleEventLiveData
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.Validator
-import com.raccoongang.core.extension.isInternetError
-import com.raccoongang.core.system.EdxError
-import com.raccoongang.core.system.ResourceManager
+import org.openedx.auth.R
+import org.openedx.auth.domain.interactor.AuthInteractor
+import org.openedx.auth.presentation.AuthAnalytics
+import org.openedx.core.BaseViewModel
+import org.openedx.core.SingleEventLiveData
+import org.openedx.core.UIMessage
+import org.openedx.core.Validator
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.extension.isInternetError
+import org.openedx.core.system.EdxError
+import org.openedx.core.system.ResourceManager
import kotlinx.coroutines.launch
-import com.raccoongang.core.R as CoreRes
+import org.openedx.core.R as CoreRes
class SignInViewModel(
private val interactor: AuthInteractor,
private val resourceManager: ResourceManager,
+ private val preferencesManager: PreferencesManager,
private val validator: Validator,
+ private val analytics: AuthAnalytics
) : BaseViewModel() {
private val _showProgress = MutableLiveData()
@@ -50,6 +54,8 @@ class SignInViewModel(
try {
interactor.login(username, password)
_loginSuccess.value = true
+ setUserId()
+ analytics.userLoginEvent(LoginMethod.PASSWORD.methodName)
} catch (e: Exception) {
if (e is EdxError.InvalidGrantException) {
_uiMessage.value =
@@ -65,5 +71,26 @@ class SignInViewModel(
_showProgress.value = false
}
}
+
+ fun signUpClickedEvent() {
+ analytics.signUpClickedEvent()
+ }
+
+ fun forgotPasswordClickedEvent() {
+ analytics.forgotPasswordClickedEvent()
+ }
+
+ private fun setUserId() {
+ preferencesManager.user?.let {
+ analytics.setUserIdForSession(it.id)
+ }
+ }
+}
+
+private enum class LoginMethod(val methodName: String) {
+ PASSWORD("Password"),
+ FACEBOOK("Facebook"),
+ GOOGLE("Google"),
+ MICROSOFT("Microsoft")
}
diff --git a/auth/src/main/java/com/raccoongang/auth/presentation/signup/SignUpFragment.kt b/auth/src/main/java/org/openedx/auth/presentation/signup/SignUpFragment.kt
similarity index 89%
rename from auth/src/main/java/com/raccoongang/auth/presentation/signup/SignUpFragment.kt
rename to auth/src/main/java/org/openedx/auth/presentation/signup/SignUpFragment.kt
index 80ef362ab..59a25fca0 100644
--- a/auth/src/main/java/com/raccoongang/auth/presentation/signup/SignUpFragment.kt
+++ b/auth/src/main/java/org/openedx/auth/presentation/signup/SignUpFragment.kt
@@ -1,6 +1,6 @@
@file:OptIn(ExperimentalComposeUiApi::class, ExperimentalComposeUiApi::class)
-package com.raccoongang.auth.presentation.signup
+package org.openedx.auth.presentation.signup
import android.content.res.Configuration
import android.content.res.Configuration.ORIENTATION_PORTRAIT
@@ -28,22 +28,23 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.*
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.fragment.app.Fragment
-import com.raccoongang.auth.presentation.AuthRouter
-import com.raccoongang.auth.presentation.ui.ExpandableText
-import com.raccoongang.auth.presentation.ui.OptionalFields
-import com.raccoongang.auth.presentation.ui.RequiredFields
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.RegistrationField
-import com.raccoongang.core.domain.model.RegistrationFieldType
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.*
+import org.openedx.auth.presentation.AuthRouter
+import org.openedx.auth.presentation.ui.ExpandableText
+import org.openedx.auth.presentation.ui.OptionalFields
+import org.openedx.auth.presentation.ui.RequiredFields
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.RegistrationField
+import org.openedx.core.domain.model.RegistrationFieldType
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.*
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
@@ -65,7 +66,7 @@ class SignUpFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val uiState by viewModel.uiState.observeAsState()
@@ -111,8 +112,10 @@ internal fun RegistrationScreen(
) {
val scaffoldState = rememberScaffoldState()
val configuration = LocalConfiguration.current
+ val focusManager = LocalFocusManager.current
val bottomSheetScaffoldState = rememberModalBottomSheetState(
- initialValue = ModalBottomSheetValue.Hidden
+ initialValue = ModalBottomSheetValue.Hidden,
+ skipHalfExpanded = true
)
val coroutine = rememberCoroutineScope()
val keyboardController = LocalSoftwareKeyboardController.current
@@ -140,6 +143,16 @@ internal fun RegistrationScreen(
val listState = rememberLazyListState()
+ var bottomDialogTitle by rememberSaveable {
+ mutableStateOf("")
+ }
+
+ var searchValue by rememberSaveable(stateSaver = TextFieldValue.Saver) {
+ mutableStateOf(TextFieldValue())
+ }
+
+ val isImeVisible by isImeVisibleState()
+
LaunchedEffect(validationError) {
if (validationError) {
coroutine.launch {
@@ -149,6 +162,13 @@ internal fun RegistrationScreen(
}
}
+ LaunchedEffect(bottomSheetScaffoldState.isVisible) {
+ if (!bottomSheetScaffoldState.isVisible) {
+ focusManager.clearFocus()
+ searchValue = TextFieldValue("")
+ }
+ }
+
Scaffold(
scaffoldState = scaffoldState,
modifier = Modifier
@@ -193,17 +213,9 @@ internal fun RegistrationScreen(
)
}
- val bottomSheetWeight by remember(key1 = windowSize) {
- mutableStateOf(
- windowSize.windowSizeValue(
- expanded = if (configuration.orientation == ORIENTATION_PORTRAIT) 0.8f else 0.6f,
- compact = 1f
- )
- )
- }
-
ModalBottomSheetLayout(
modifier = Modifier
+ .padding(bottom = if (isImeVisible && bottomSheetScaffoldState.isVisible) 120.dp else 0.dp)
.noRippleClickable {
if (bottomSheetScaffoldState.isVisible) {
coroutine.launch {
@@ -212,16 +224,15 @@ internal fun RegistrationScreen(
}
},
sheetState = bottomSheetScaffoldState,
- sheetShape = BottomSheetShape(
- width = configuration.screenWidthDp.px,
- height = configuration.screenHeightDp.px,
- weight = bottomSheetWeight
- ),
+ sheetShape = MaterialTheme.appShapes.screenBackgroundShape,
scrimColor = Color.Black.copy(alpha = 0.4f),
sheetBackgroundColor = MaterialTheme.appColors.background,
sheetContent = {
SheetContent(
+ title = bottomDialogTitle,
+ searchValue = searchValue,
expandedList = expandedList,
+ listState = listState,
onItemClick = { item ->
mapFields[serverFieldName.value] = item.value
selectableNamesMap[serverFieldName.value] = item.name
@@ -229,7 +240,9 @@ internal fun RegistrationScreen(
bottomSheetScaffoldState.hide()
}
},
- listState = listState
+ searchValueChanged = {
+ searchValue = TextFieldValue(it)
+ }
)
}
) {
@@ -260,7 +273,7 @@ internal fun RegistrationScreen(
Text(
modifier = Modifier
.fillMaxWidth(),
- text = stringResource(id = com.raccoongang.auth.R.string.auth_register),
+ text = stringResource(id = org.openedx.auth.R.string.auth_register),
color = Color.White,
textAlign = TextAlign.Center,
style = MaterialTheme.appTypography.titleMedium
@@ -311,7 +324,7 @@ internal fun RegistrationScreen(
Column() {
Text(
modifier = Modifier.fillMaxWidth(),
- text = stringResource(id = com.raccoongang.auth.R.string.auth_sign_up),
+ text = stringResource(id = org.openedx.auth.R.string.auth_sign_up),
color = MaterialTheme.appColors.textPrimary,
style = MaterialTheme.appTypography.displaySmall
)
@@ -319,7 +332,7 @@ internal fun RegistrationScreen(
modifier = Modifier
.fillMaxWidth()
.padding(top = 4.dp),
- text = stringResource(id = com.raccoongang.auth.R.string.auth_create_new_account),
+ text = stringResource(id = org.openedx.auth.R.string.auth_create_new_account),
color = MaterialTheme.appColors.textPrimary,
style = MaterialTheme.appTypography.titleSmall
)
@@ -337,6 +350,7 @@ internal fun RegistrationScreen(
if (bottomSheetScaffoldState.isVisible) {
bottomSheetScaffoldState.hide()
} else {
+ bottomDialogTitle = field.label
showErrorMap[field.name] = false
bottomSheetScaffoldState.show()
}
@@ -363,6 +377,7 @@ internal fun RegistrationScreen(
if (bottomSheetScaffoldState.isVisible) {
bottomSheetScaffoldState.hide()
} else {
+ bottomDialogTitle = field.label
showErrorMap[field.name] = false
bottomSheetScaffoldState.show()
}
@@ -383,9 +398,9 @@ internal fun RegistrationScreen(
CircularProgressIndicator(color = MaterialTheme.appColors.primary)
}
} else {
- NewEdxButton(
+ OpenEdXButton(
width = buttonWidth,
- text = stringResource(id = com.raccoongang.auth.R.string.auth_create_account),
+ text = stringResource(id = org.openedx.auth.R.string.auth_create_account),
onClick = {
showErrorMap.clear()
onRegisterClick(mapFields.toMap())
@@ -410,7 +425,7 @@ internal fun RegistrationScreen(
@Preview(name = "NEXUS_5_Dark", device = Devices.NEXUS_5, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun RegistrationScreenPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
RegistrationScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
uiState = SignUpUIState.Fields(
@@ -432,7 +447,7 @@ private fun RegistrationScreenPreview() {
@Preview(name = "NEXUS_9_Dark", device = Devices.NEXUS_9, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun RegistrationScreenTabletPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
RegistrationScreen(
windowSize = WindowSize(WindowType.Medium, WindowType.Medium),
uiState = SignUpUIState.Fields(
diff --git a/auth/src/main/java/com/raccoongang/auth/presentation/signup/SignUpUIState.kt b/auth/src/main/java/org/openedx/auth/presentation/signup/SignUpUIState.kt
similarity index 66%
rename from auth/src/main/java/com/raccoongang/auth/presentation/signup/SignUpUIState.kt
rename to auth/src/main/java/org/openedx/auth/presentation/signup/SignUpUIState.kt
index 8aef57c5c..a6af275f5 100644
--- a/auth/src/main/java/com/raccoongang/auth/presentation/signup/SignUpUIState.kt
+++ b/auth/src/main/java/org/openedx/auth/presentation/signup/SignUpUIState.kt
@@ -1,6 +1,6 @@
-package com.raccoongang.auth.presentation.signup
+package org.openedx.auth.presentation.signup
-import com.raccoongang.core.domain.model.RegistrationField
+import org.openedx.core.domain.model.RegistrationField
sealed class SignUpUIState {
data class Fields(
diff --git a/auth/src/main/java/com/raccoongang/auth/presentation/signup/SignUpViewModel.kt b/auth/src/main/java/org/openedx/auth/presentation/signup/SignUpViewModel.kt
similarity index 79%
rename from auth/src/main/java/com/raccoongang/auth/presentation/signup/SignUpViewModel.kt
rename to auth/src/main/java/org/openedx/auth/presentation/signup/SignUpViewModel.kt
index 07f8340f8..89c263f8a 100644
--- a/auth/src/main/java/com/raccoongang/auth/presentation/signup/SignUpViewModel.kt
+++ b/auth/src/main/java/org/openedx/auth/presentation/signup/SignUpViewModel.kt
@@ -1,18 +1,26 @@
-package com.raccoongang.auth.presentation.signup
+package org.openedx.auth.presentation.signup
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.auth.domain.interactor.AuthInteractor
-import com.raccoongang.core.*
-import com.raccoongang.core.domain.model.RegistrationField
-import com.raccoongang.core.extension.isInternetError
-import com.raccoongang.core.system.ResourceManager
+import org.openedx.auth.domain.interactor.AuthInteractor
+import org.openedx.auth.presentation.AuthAnalytics
+import org.openedx.core.ApiConstants
+import org.openedx.core.BaseViewModel
+import org.openedx.core.R
+import org.openedx.core.SingleEventLiveData
+import org.openedx.core.UIMessage
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.domain.model.RegistrationField
+import org.openedx.core.extension.isInternetError
+import org.openedx.core.system.ResourceManager
import kotlinx.coroutines.launch
class SignUpViewModel(
private val interactor: AuthInteractor,
- private val resourceManager: ResourceManager
+ private val resourceManager: ResourceManager,
+ private val analytics: AuthAnalytics,
+ private val preferencesManager: PreferencesManager
) : BaseViewModel() {
private val _uiState = MutableLiveData(SignUpUIState.Loading)
@@ -64,6 +72,7 @@ class SignUpViewModel(
}
fun register(mapFields: Map) {
+ analytics.createAccountClickedEvent("")
val resultMap = mapFields.toMutableMap()
optionalFields.forEach { (k, v) ->
if (mapFields[k].isNullOrEmpty()) {
@@ -84,6 +93,8 @@ class SignUpViewModel(
resultMap.getValue(ApiConstants.EMAIL),
resultMap.getValue(ApiConstants.PASSWORD)
)
+ setUserId()
+ analytics.registrationSuccessEvent("")
_successLogin.value = true
}
_isButtonLoading.value = false
@@ -117,4 +128,17 @@ class SignUpViewModel(
)
}
+
+ private fun setUserId() {
+ preferencesManager.user?.let {
+ analytics.setUserIdForSession(it.id)
+ }
+ }
+
+}
+
+private enum class RegisterProvider(val keyName: String) {
+ GOOGLE("google-oauth2"),
+ AZURE("azuread-oauth2"),
+ FACEBOOK("facebook")
}
\ No newline at end of file
diff --git a/auth/src/main/java/com/raccoongang/auth/presentation/ui/AuthUI.kt b/auth/src/main/java/org/openedx/auth/presentation/ui/AuthUI.kt
similarity index 91%
rename from auth/src/main/java/com/raccoongang/auth/presentation/ui/AuthUI.kt
rename to auth/src/main/java/org/openedx/auth/presentation/ui/AuthUI.kt
index 415fd7ad2..e00b56e6c 100644
--- a/auth/src/main/java/com/raccoongang/auth/presentation/ui/AuthUI.kt
+++ b/auth/src/main/java/org/openedx/auth/presentation/ui/AuthUI.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.auth.presentation.ui
+package org.openedx.auth.presentation.ui
import android.content.res.Configuration
import androidx.compose.animation.core.MutableTransitionState
@@ -6,16 +6,29 @@ import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.tween
import androidx.compose.animation.core.updateTransition
import androidx.compose.foundation.background
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
-import androidx.compose.material.*
+import androidx.compose.material.Icon
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.OutlinedTextField
+import androidx.compose.material.Text
+import androidx.compose.material.TextFieldDefaults
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ChevronRight
import androidx.compose.material.icons.filled.ExpandMore
-import androidx.compose.runtime.*
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshots.SnapshotStateMap
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
@@ -25,20 +38,24 @@ import androidx.compose.ui.focus.focusTarget
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.input.*
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.input.PasswordVisualTransformation
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
-import com.raccoongang.auth.R
-import com.raccoongang.core.domain.model.RegistrationField
-import com.raccoongang.core.domain.model.RegistrationFieldType
-import com.raccoongang.core.extension.TextConverter
-import com.raccoongang.core.ui.HyperlinkText
-import com.raccoongang.core.ui.SheetContent
-import com.raccoongang.core.ui.noRippleClickable
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appShapes
-import com.raccoongang.core.ui.theme.appTypography
+import org.openedx.auth.R
+import org.openedx.core.domain.model.RegistrationField
+import org.openedx.core.domain.model.RegistrationFieldType
+import org.openedx.core.extension.TextConverter
+import org.openedx.core.ui.HyperlinkText
+import org.openedx.core.ui.SheetContent
+import org.openedx.core.ui.noRippleClickable
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appShapes
+import org.openedx.core.ui.theme.appTypography
@Composable
fun RequiredFields(
@@ -63,6 +80,7 @@ fun RequiredFields(
}
)
}
+
RegistrationFieldType.PLAINTEXT -> {
val linkedText =
TextConverter.htmlTextToLinkedText(field.label)
@@ -72,9 +90,11 @@ fun RequiredFields(
linkTextColor = MaterialTheme.appColors.primary
)
}
+
RegistrationFieldType.CHECKBOX -> {
//Text("checkbox")
}
+
RegistrationFieldType.SELECT -> {
SelectableRegisterField(
registrationField = field,
@@ -85,6 +105,7 @@ fun RequiredFields(
}
)
}
+
RegistrationFieldType.TEXTAREA -> {
InputRegistrationField(
modifier = Modifier
@@ -100,6 +121,7 @@ fun RequiredFields(
}
)
}
+
RegistrationFieldType.UNKNOWN -> {
}
@@ -134,6 +156,7 @@ fun OptionalFields(
}
)
}
+
RegistrationFieldType.PLAINTEXT -> {
val linkedText =
TextConverter.htmlTextToLinkedText(
@@ -145,9 +168,11 @@ fun OptionalFields(
linkTextColor = MaterialTheme.appColors.primary
)
}
+
RegistrationFieldType.CHECKBOX -> {
//Text("checkbox")
}
+
RegistrationFieldType.SELECT -> {
SelectableRegisterField(
registrationField = field,
@@ -159,6 +184,7 @@ fun OptionalFields(
onSelectClick(serverName, field, list)
})
}
+
RegistrationFieldType.TEXTAREA -> {
InputRegistrationField(
modifier = Modifier
@@ -177,6 +203,7 @@ fun OptionalFields(
}
)
}
+
RegistrationFieldType.UNKNOWN -> {
}
}
@@ -457,7 +484,7 @@ fun ExpandableText(
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun SelectRegistrationFieldPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
Column(Modifier.background(MaterialTheme.appColors.background)) {
SelectableRegisterField(
field,
@@ -475,7 +502,7 @@ fun SelectRegistrationFieldPreview() {
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun InputRegistrationFieldPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
Column(Modifier.background(MaterialTheme.appColors.background)) {
InputRegistrationField(
modifier = Modifier.fillMaxWidth(),
@@ -493,7 +520,7 @@ fun InputRegistrationFieldPreview() {
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun OptionalFieldsPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
Column(Modifier.background(MaterialTheme.appColors.background)) {
OptionalFields(
fields = listOf(field, field, field),
@@ -513,7 +540,7 @@ private fun OptionalFieldsPreview() {
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun RequiredFieldsPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
Column(Modifier.background(MaterialTheme.appColors.background)) {
RequiredFields(
fields = listOf(field, field, field),
@@ -532,12 +559,14 @@ private fun RequiredFieldsPreview() {
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun SheetContentPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
Column(Modifier.background(MaterialTheme.appColors.background)) {
SheetContent(
+ searchValue = TextFieldValue(),
expandedList = listOf(option, option, option),
onItemClick = {},
- listState = rememberLazyListState()
+ listState = rememberLazyListState(),
+ searchValueChanged = {}
)
}
}
diff --git a/auth/src/test/java/com/raccoongang/auth/presentation/restore/RestorePasswordViewModelTest.kt b/auth/src/test/java/org/openedx/auth/presentation/restore/RestorePasswordViewModelTest.kt
similarity index 74%
rename from auth/src/test/java/com/raccoongang/auth/presentation/restore/RestorePasswordViewModelTest.kt
rename to auth/src/test/java/org/openedx/auth/presentation/restore/RestorePasswordViewModelTest.kt
index 58034ddd5..895a87739 100644
--- a/auth/src/test/java/com/raccoongang/auth/presentation/restore/RestorePasswordViewModelTest.kt
+++ b/auth/src/test/java/org/openedx/auth/presentation/restore/RestorePasswordViewModelTest.kt
@@ -1,16 +1,17 @@
-package com.raccoongang.auth.presentation.restore
+package org.openedx.auth.presentation.restore
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import com.raccoongang.auth.domain.interactor.AuthInteractor
-import com.raccoongang.auth.presentation.signup.SignUpViewModel
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.system.EdxError
-import com.raccoongang.core.system.ResourceManager
+import org.openedx.auth.domain.interactor.AuthInteractor
+import org.openedx.auth.presentation.AuthAnalytics
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.system.EdxError
+import org.openedx.core.system.ResourceManager
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
+import io.mockk.verify
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.*
@@ -31,6 +32,7 @@ class RestorePasswordViewModelTest {
private val resourceManager = mockk()
private val interactor = mockk()
+ private val analytics = mockk()
//region parameters
@@ -49,8 +51,8 @@ class RestorePasswordViewModelTest {
Dispatchers.setMain(dispatcher)
every { resourceManager.getString(R.string.core_error_no_connection) } returns noInternet
every { resourceManager.getString(R.string.core_error_unknown_error) } returns somethingWrong
- every { resourceManager.getString(com.raccoongang.auth.R.string.auth_invalid_email) } returns invalidEmail
- every { resourceManager.getString(com.raccoongang.auth.R.string.auth_invalid_password) } returns invalidPassword
+ every { resourceManager.getString(org.openedx.auth.R.string.auth_invalid_email) } returns invalidEmail
+ every { resourceManager.getString(org.openedx.auth.R.string.auth_invalid_password) } returns invalidPassword
}
@After
@@ -60,11 +62,13 @@ class RestorePasswordViewModelTest {
@Test
fun `passwordReset empty email validation error`() = runTest {
- val viewModel = RestorePasswordViewModel(interactor, resourceManager)
+ val viewModel = RestorePasswordViewModel(interactor, resourceManager, analytics)
coEvery { interactor.passwordReset(emptyEmail) } returns true
+ every { analytics.resetPasswordClickedEvent(false) } returns Unit
viewModel.passwordReset(emptyEmail)
advanceUntilIdle()
coVerify(exactly = 0) { interactor.passwordReset(any()) }
+ verify(exactly = 1) { analytics.resetPasswordClickedEvent(false) }
val message = viewModel.uiMessage.value as? UIMessage.SnackBarMessage
@@ -74,11 +78,13 @@ class RestorePasswordViewModelTest {
@Test
fun `passwordReset invalid email validation error`() = runTest {
- val viewModel = RestorePasswordViewModel(interactor, resourceManager)
+ val viewModel = RestorePasswordViewModel(interactor, resourceManager, analytics)
coEvery { interactor.passwordReset(invalidEmail) } returns true
+ every { analytics.resetPasswordClickedEvent(false) } returns Unit
viewModel.passwordReset(invalidEmail)
advanceUntilIdle()
coVerify(exactly = 0) { interactor.passwordReset(any()) }
+ verify(exactly = 1) { analytics.resetPasswordClickedEvent(false) }
val message = viewModel.uiMessage.value as? UIMessage.SnackBarMessage
@@ -88,11 +94,13 @@ class RestorePasswordViewModelTest {
@Test
fun `passwordReset validation error`() = runTest {
- val viewModel = RestorePasswordViewModel(interactor, resourceManager)
- coEvery { interactor.passwordReset(correctEmail) } throws EdxError.ValidationException("error")
+ val viewModel = RestorePasswordViewModel(interactor, resourceManager, analytics)
+ coEvery { interactor.passwordReset(correctEmail) } throws EdxError.ValidationException("error")
+ every { analytics.resetPasswordClickedEvent(false) } returns Unit
viewModel.passwordReset(correctEmail)
advanceUntilIdle()
coVerify(exactly = 1) { interactor.passwordReset(any()) }
+ verify(exactly = 1) { analytics.resetPasswordClickedEvent(false) }
val message = viewModel.uiMessage.value as? UIMessage.SnackBarMessage
@@ -102,11 +110,13 @@ class RestorePasswordViewModelTest {
@Test
fun `passwordReset no internet error`() = runTest {
- val viewModel = RestorePasswordViewModel(interactor, resourceManager)
- coEvery { interactor.passwordReset(correctEmail) } throws UnknownHostException()
+ val viewModel = RestorePasswordViewModel(interactor, resourceManager, analytics)
+ coEvery { interactor.passwordReset(correctEmail) } throws UnknownHostException()
+ every { analytics.resetPasswordClickedEvent(false) } returns Unit
viewModel.passwordReset(correctEmail)
advanceUntilIdle()
coVerify(exactly = 1) { interactor.passwordReset(any()) }
+ verify(exactly = 1) { analytics.resetPasswordClickedEvent(false) }
val message = viewModel.uiMessage.value as? UIMessage.SnackBarMessage
@@ -116,11 +126,13 @@ class RestorePasswordViewModelTest {
@Test
fun `passwordReset unknown error`() = runTest {
- val viewModel = RestorePasswordViewModel(interactor, resourceManager)
- coEvery { interactor.passwordReset(correctEmail) } throws Exception()
+ val viewModel = RestorePasswordViewModel(interactor, resourceManager, analytics)
+ coEvery { interactor.passwordReset(correctEmail) } throws Exception()
+ every { analytics.resetPasswordClickedEvent(false) } returns Unit
viewModel.passwordReset(correctEmail)
advanceUntilIdle()
coVerify(exactly = 1) { interactor.passwordReset(any()) }
+ verify(exactly = 1) { analytics.resetPasswordClickedEvent(false) }
val message = viewModel.uiMessage.value as? UIMessage.SnackBarMessage
@@ -130,11 +142,13 @@ class RestorePasswordViewModelTest {
@Test
fun `unSuccess restore password`() = runTest {
- val viewModel = RestorePasswordViewModel(interactor, resourceManager)
+ val viewModel = RestorePasswordViewModel(interactor, resourceManager, analytics)
coEvery { interactor.passwordReset(correctEmail) } returns false
+ every { analytics.resetPasswordClickedEvent(false) } returns Unit
viewModel.passwordReset(correctEmail)
advanceUntilIdle()
coVerify(exactly = 1) { interactor.passwordReset(any()) }
+ verify(exactly = 1) { analytics.resetPasswordClickedEvent(false) }
val message = viewModel.uiMessage.value as? UIMessage.SnackBarMessage
@@ -145,11 +159,13 @@ class RestorePasswordViewModelTest {
@Test
fun `success restore password`() = runTest {
- val viewModel = RestorePasswordViewModel(interactor, resourceManager)
+ val viewModel = RestorePasswordViewModel(interactor, resourceManager, analytics)
coEvery { interactor.passwordReset(correctEmail) } returns true
+ every { analytics.resetPasswordClickedEvent(true) } returns Unit
viewModel.passwordReset(correctEmail)
advanceUntilIdle()
coVerify(exactly = 1) { interactor.passwordReset(any()) }
+ verify(exactly = 1) { analytics.resetPasswordClickedEvent(true) }
val state = viewModel.uiState.value as? RestorePasswordUIState.Success
val message = viewModel.uiMessage.value as? UIMessage.SnackBarMessage
diff --git a/auth/src/test/java/com/raccoongang/auth/presentation/signin/SignInViewModelTest.kt b/auth/src/test/java/org/openedx/auth/presentation/signin/SignInViewModelTest.kt
similarity index 64%
rename from auth/src/test/java/com/raccoongang/auth/presentation/signin/SignInViewModelTest.kt
rename to auth/src/test/java/org/openedx/auth/presentation/signin/SignInViewModelTest.kt
index c44cb96f7..9b2b61a66 100644
--- a/auth/src/test/java/com/raccoongang/auth/presentation/signin/SignInViewModelTest.kt
+++ b/auth/src/test/java/org/openedx/auth/presentation/signin/SignInViewModelTest.kt
@@ -1,19 +1,27 @@
-package com.raccoongang.auth.presentation.signin
+package org.openedx.auth.presentation.signin
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import com.raccoongang.auth.R
-import com.raccoongang.auth.domain.interactor.AuthInteractor
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.Validator
-import com.raccoongang.core.system.EdxError
-import com.raccoongang.core.system.ResourceManager
+import org.openedx.auth.R
+import org.openedx.auth.domain.interactor.AuthInteractor
+import org.openedx.auth.presentation.AuthAnalytics
+import org.openedx.core.UIMessage
+import org.openedx.core.Validator
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.domain.model.User
+import org.openedx.core.system.EdxError
+import org.openedx.core.system.ResourceManager
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
+import io.mockk.verify
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.*
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
@@ -21,7 +29,7 @@ import org.junit.Rule
import org.junit.Test
import org.junit.rules.TestRule
import java.net.UnknownHostException
-import com.raccoongang.core.R as CoreRes
+import org.openedx.core.R as CoreRes
@ExperimentalCoroutinesApi
class SignInViewModelTest {
@@ -33,7 +41,9 @@ class SignInViewModelTest {
private val validator = mockk()
private val resourceManager = mockk()
+ private val preferencesManager = mockk()
private val interactor = mockk()
+ private val analytics = mockk()
private val invalidCredential = "Invalid credentials"
private val noInternet = "Slow or no internet connection"
@@ -41,6 +51,8 @@ class SignInViewModelTest {
private val invalidEmail = "Invalid email"
private val invalidPassword = "Password too short"
+ private val user = User(0, "", "", "")
+
@Before
fun before() {
Dispatchers.setMain(dispatcher)
@@ -59,9 +71,13 @@ class SignInViewModelTest {
@Test
fun `login empty credentials validation error`() = runTest {
every { validator.isEmailValid(any()) } returns false
- val viewModel = SignInViewModel(interactor, resourceManager, validator)
+ every { preferencesManager.user } returns user
+ every { analytics.setUserIdForSession(any()) } returns Unit
+ val viewModel =
+ SignInViewModel(interactor, resourceManager, preferencesManager, validator, analytics)
viewModel.login("", "")
coVerify(exactly = 0) { interactor.login(any(), any()) }
+ verify(exactly = 0) { analytics.setUserIdForSession(any()) }
val message = viewModel.uiMessage.value as UIMessage.SnackBarMessage
@@ -73,9 +89,13 @@ class SignInViewModelTest {
@Test
fun `login invalid email validation error`() = runTest {
every { validator.isEmailValid(any()) } returns false
- val viewModel = SignInViewModel(interactor, resourceManager, validator)
+ every { preferencesManager.user } returns user
+ every { analytics.setUserIdForSession(any()) } returns Unit
+ val viewModel =
+ SignInViewModel(interactor, resourceManager, preferencesManager, validator, analytics)
viewModel.login("acc@test.o", "")
coVerify(exactly = 0) { interactor.login(any(), any()) }
+ verify(exactly = 0) { analytics.setUserIdForSession(any()) }
val message = viewModel.uiMessage.value as UIMessage.SnackBarMessage
@@ -88,10 +108,14 @@ class SignInViewModelTest {
fun `login empty password validation error`() = runTest {
every { validator.isEmailValid(any()) } returns true
every { validator.isPasswordValid(any()) } returns false
+ every { preferencesManager.user } returns user
+ every { analytics.setUserIdForSession(any()) } returns Unit
coVerify(exactly = 0) { interactor.login(any(), any()) }
- val viewModel = SignInViewModel(interactor, resourceManager, validator)
+ val viewModel =
+ SignInViewModel(interactor, resourceManager, preferencesManager, validator, analytics)
viewModel.login("acc@test.org", "")
+ verify(exactly = 0) { analytics.setUserIdForSession(any()) }
val message = viewModel.uiMessage.value as UIMessage.SnackBarMessage
assertEquals(invalidPassword, message.message)
@@ -103,10 +127,14 @@ class SignInViewModelTest {
fun `login invalid password validation error`() = runTest {
every { validator.isEmailValid(any()) } returns true
every { validator.isPasswordValid(any()) } returns false
- val viewModel = SignInViewModel(interactor, resourceManager, validator)
+ every { preferencesManager.user } returns user
+ every { analytics.setUserIdForSession(any()) } returns Unit
+ val viewModel =
+ SignInViewModel(interactor, resourceManager, preferencesManager, validator, analytics)
viewModel.login("acc@test.org", "ed")
coVerify(exactly = 0) { interactor.login(any(), any()) }
+ verify(exactly = 0) { analytics.setUserIdForSession(any()) }
val message = viewModel.uiMessage.value as UIMessage.SnackBarMessage
@@ -119,12 +147,18 @@ class SignInViewModelTest {
fun `login success`() = runTest {
every { validator.isEmailValid(any()) } returns true
every { validator.isPasswordValid(any()) } returns true
- val viewModel = SignInViewModel(interactor, resourceManager, validator)
+ every { analytics.userLoginEvent(any()) } returns Unit
+ every { preferencesManager.user } returns user
+ every { analytics.setUserIdForSession(any()) } returns Unit
+ val viewModel =
+ SignInViewModel(interactor, resourceManager, preferencesManager, validator, analytics)
coEvery { interactor.login("acc@test.org", "edx") } returns Unit
viewModel.login("acc@test.org", "edx")
advanceUntilIdle()
coVerify(exactly = 1) { interactor.login(any(), any()) }
+ verify(exactly = 1) { analytics.userLoginEvent(any()) }
+ verify(exactly = 1) { analytics.setUserIdForSession(any()) }
assertEquals(false, viewModel.showProgress.value)
assertEquals(true, viewModel.loginSuccess.value)
@@ -135,12 +169,16 @@ class SignInViewModelTest {
fun `login network error`() = runTest {
every { validator.isEmailValid(any()) } returns true
every { validator.isPasswordValid(any()) } returns true
- val viewModel = SignInViewModel(interactor, resourceManager, validator)
+ every { preferencesManager.user } returns user
+ every { analytics.setUserIdForSession(any()) } returns Unit
+ val viewModel =
+ SignInViewModel(interactor, resourceManager, preferencesManager, validator, analytics)
coEvery { interactor.login("acc@test.org", "edx") } throws UnknownHostException()
viewModel.login("acc@test.org", "edx")
advanceUntilIdle()
coVerify(exactly = 1) { interactor.login(any(), any()) }
+ verify(exactly = 0) { analytics.setUserIdForSession(any()) }
val message = viewModel.uiMessage.value as? UIMessage.SnackBarMessage
@@ -153,12 +191,16 @@ class SignInViewModelTest {
fun `login invalid grant error`() = runTest {
every { validator.isEmailValid(any()) } returns true
every { validator.isPasswordValid(any()) } returns true
- val viewModel = SignInViewModel(interactor, resourceManager, validator)
+ every { preferencesManager.user } returns user
+ every { analytics.setUserIdForSession(any()) } returns Unit
+ val viewModel =
+ SignInViewModel(interactor, resourceManager, preferencesManager, validator, analytics)
coEvery { interactor.login("acc@test.org", "edx") } throws EdxError.InvalidGrantException()
viewModel.login("acc@test.org", "edx")
advanceUntilIdle()
coVerify(exactly = 1) { interactor.login(any(), any()) }
+ verify(exactly = 0) { analytics.setUserIdForSession(any()) }
val message = viewModel.uiMessage.value as UIMessage.SnackBarMessage
@@ -171,12 +213,16 @@ class SignInViewModelTest {
fun `login unknown exception`() = runTest {
every { validator.isEmailValid(any()) } returns true
every { validator.isPasswordValid(any()) } returns true
- val viewModel = SignInViewModel(interactor, resourceManager, validator)
+ every { preferencesManager.user } returns user
+ every { analytics.setUserIdForSession(any()) } returns Unit
+ val viewModel =
+ SignInViewModel(interactor, resourceManager, preferencesManager, validator, analytics)
coEvery { interactor.login("acc@test.org", "edx") } throws IllegalStateException()
viewModel.login("acc@test.org", "edx")
advanceUntilIdle()
coVerify(exactly = 1) { interactor.login(any(), any()) }
+ verify(exactly = 0) { analytics.setUserIdForSession(any()) }
val message = viewModel.uiMessage.value as UIMessage.SnackBarMessage
diff --git a/auth/src/test/java/com/raccoongang/auth/presentation/signup/SignUpViewModelTest.kt b/auth/src/test/java/org/openedx/auth/presentation/signup/SignUpViewModelTest.kt
similarity index 73%
rename from auth/src/test/java/com/raccoongang/auth/presentation/signup/SignUpViewModelTest.kt
rename to auth/src/test/java/org/openedx/auth/presentation/signup/SignUpViewModelTest.kt
index 0702652cb..fbd2bebed 100644
--- a/auth/src/test/java/com/raccoongang/auth/presentation/signup/SignUpViewModelTest.kt
+++ b/auth/src/test/java/org/openedx/auth/presentation/signup/SignUpViewModelTest.kt
@@ -1,25 +1,31 @@
-package com.raccoongang.auth.presentation.signup
+package org.openedx.auth.presentation.signup
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import com.raccoongang.auth.data.model.RegistrationFields
-import com.raccoongang.auth.data.model.ValidationFields
-import com.raccoongang.auth.domain.interactor.AuthInteractor
-import com.raccoongang.core.ApiConstants
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.RegistrationField
-import com.raccoongang.core.domain.model.RegistrationFieldType
-import com.raccoongang.core.system.ResourceManager
+import org.openedx.auth.data.model.ValidationFields
+import org.openedx.auth.domain.interactor.AuthInteractor
+import org.openedx.auth.presentation.AuthAnalytics
+import org.openedx.core.ApiConstants
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.domain.model.RegistrationField
+import org.openedx.core.domain.model.RegistrationFieldType
+import org.openedx.core.domain.model.User
+import org.openedx.core.system.ResourceManager
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
+import io.mockk.verify
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.test.*
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.advanceUntilIdle
+import kotlinx.coroutines.test.resetMain
+import kotlinx.coroutines.test.runTest
+import kotlinx.coroutines.test.setMain
import org.junit.After
-import org.junit.Assert.*
+import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -35,7 +41,9 @@ class SignUpViewModelTest {
private val dispatcher = StandardTestDispatcher()
private val resourceManager = mockk()
+ private val preferencesManager = mockk()
private val interactor = mockk()
+ private val analytics = mockk()
//region parameters
@@ -70,6 +78,8 @@ class SignUpViewModelTest {
)
)
+ private val user = User(0, "", "", "")
+
//endregion
private val noInternet = "Slow or no internet connection"
@@ -91,17 +101,22 @@ class SignUpViewModelTest {
@Test
fun `register has validation errors`() = runTest {
- val viewModel = SignUpViewModel(interactor, resourceManager)
+ val viewModel = SignUpViewModel(interactor, resourceManager, analytics, preferencesManager)
coEvery { interactor.validateRegistrationFields(parametersMap) } returns ValidationFields(
parametersMap
)
+ every { analytics.createAccountClickedEvent(any()) } returns Unit
coEvery { interactor.register(parametersMap) } returns Unit
coEvery { interactor.login("", "") } returns Unit
+ every { preferencesManager.user } returns user
+ every { analytics.setUserIdForSession(any()) } returns Unit
viewModel.register(parametersMap)
advanceUntilIdle()
coVerify(exactly = 1) { interactor.validateRegistrationFields(any()) }
+ verify(exactly = 1) { analytics.createAccountClickedEvent(any()) }
coVerify(exactly = 0) { interactor.register(any()) }
coVerify(exactly = 0) { interactor.login(any(), any()) }
+ verify(exactly = 0) { analytics.setUserIdForSession(any()) }
assertEquals(true, viewModel.validationError.value)
assert(viewModel.successLogin.value != true)
@@ -111,7 +126,7 @@ class SignUpViewModelTest {
@Test
fun `register no internet error`() = runTest {
- val viewModel = SignUpViewModel(interactor, resourceManager)
+ val viewModel = SignUpViewModel(interactor, resourceManager, analytics, preferencesManager)
coEvery { interactor.validateRegistrationFields(parametersMap) } throws UnknownHostException()
coEvery { interactor.register(parametersMap) } returns Unit
coEvery {
@@ -120,8 +135,13 @@ class SignUpViewModelTest {
parametersMap.getValue(ApiConstants.PASSWORD)
)
} returns Unit
+ every { analytics.createAccountClickedEvent(any()) } returns Unit
+ every { preferencesManager.user } returns user
+ every { analytics.setUserIdForSession(any()) } returns Unit
viewModel.register(parametersMap)
advanceUntilIdle()
+ verify(exactly = 1) { analytics.createAccountClickedEvent(any()) }
+ verify(exactly = 0) { analytics.setUserIdForSession(any()) }
coVerify(exactly = 1) { interactor.validateRegistrationFields(any()) }
coVerify(exactly = 0) { interactor.register(any()) }
coVerify(exactly = 0) { interactor.login(any(), any()) }
@@ -136,12 +156,17 @@ class SignUpViewModelTest {
@Test
fun `something went wrong error`() = runTest {
- val viewModel = SignUpViewModel(interactor, resourceManager)
+ val viewModel = SignUpViewModel(interactor, resourceManager, analytics, preferencesManager)
coEvery { interactor.validateRegistrationFields(parametersMap) } throws Exception()
coEvery { interactor.register(parametersMap) } returns Unit
coEvery { interactor.login("", "") } returns Unit
+ every { analytics.createAccountClickedEvent(any()) } returns Unit
+ every { preferencesManager.user } returns user
+ every { analytics.setUserIdForSession(any()) } returns Unit
viewModel.register(parametersMap)
advanceUntilIdle()
+ verify(exactly = 0) { analytics.setUserIdForSession(any()) }
+ verify(exactly = 1) { analytics.createAccountClickedEvent(any()) }
coVerify(exactly = 1) { interactor.validateRegistrationFields(any()) }
coVerify(exactly = 0) { interactor.register(any()) }
coVerify(exactly = 0) { interactor.login(any(), any()) }
@@ -157,10 +182,12 @@ class SignUpViewModelTest {
@Test
fun `success register`() = runTest {
- val viewModel = SignUpViewModel(interactor, resourceManager)
+ val viewModel = SignUpViewModel(interactor, resourceManager, analytics, preferencesManager)
coEvery { interactor.validateRegistrationFields(parametersMap) } returns ValidationFields(
emptyMap()
)
+ every { analytics.createAccountClickedEvent(any()) } returns Unit
+ every { analytics.registrationSuccessEvent(any()) } returns Unit
coEvery { interactor.register(parametersMap) } returns Unit
coEvery {
interactor.login(
@@ -168,11 +195,16 @@ class SignUpViewModelTest {
parametersMap.getValue(ApiConstants.PASSWORD)
)
} returns Unit
+ every { preferencesManager.user } returns user
+ every { analytics.setUserIdForSession(any()) } returns Unit
viewModel.register(parametersMap)
advanceUntilIdle()
+ verify(exactly = 1) { analytics.setUserIdForSession(any()) }
coVerify(exactly = 1) { interactor.validateRegistrationFields(any()) }
coVerify(exactly = 1) { interactor.register(any()) }
coVerify(exactly = 1) { interactor.login(any(), any()) }
+ verify(exactly = 1) { analytics.createAccountClickedEvent(any()) }
+ verify(exactly = 1) { analytics.registrationSuccessEvent(any()) }
assertEquals(false, viewModel.validationError.value)
assertEquals(false, viewModel.isButtonLoading.value)
@@ -182,7 +214,7 @@ class SignUpViewModelTest {
@Test
fun `getRegistrationFields no internet error`() = runTest {
- val viewModel = SignUpViewModel(interactor, resourceManager)
+ val viewModel = SignUpViewModel(interactor, resourceManager, analytics, preferencesManager)
coEvery { interactor.getRegistrationFields() } throws UnknownHostException()
viewModel.getRegistrationFields()
advanceUntilIdle()
@@ -196,7 +228,7 @@ class SignUpViewModelTest {
@Test
fun `getRegistrationFields unknown error`() = runTest {
- val viewModel = SignUpViewModel(interactor, resourceManager)
+ val viewModel = SignUpViewModel(interactor, resourceManager, analytics, preferencesManager)
coEvery { interactor.getRegistrationFields() } throws Exception()
viewModel.getRegistrationFields()
advanceUntilIdle()
@@ -210,7 +242,7 @@ class SignUpViewModelTest {
@Test
fun `getRegistrationFields success`() = runTest {
- val viewModel = SignUpViewModel(interactor, resourceManager)
+ val viewModel = SignUpViewModel(interactor, resourceManager, analytics, preferencesManager)
coEvery { interactor.getRegistrationFields() } returns listOfFields
viewModel.getRegistrationFields()
advanceUntilIdle()
diff --git a/build.gradle b/build.gradle
index 2831e9883..047a0e1bd 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,9 +1,9 @@
buildscript {
ext {
kotlin_version = '1.8.10'
- coroutines_version = '1.6.4'
- compose_version = '1.3.1'
- compose_compiler_version = '1.4.2'
+ coroutines_version = '1.7.1'
+ compose_version = '1.4.3'
+ compose_compiler_version = '1.4.4'
}
}
@@ -11,6 +11,8 @@ plugins {
id 'com.android.application' version '7.3.1' apply false
id 'com.android.library' version '7.3.1' apply false
id 'org.jetbrains.kotlin.android' version "$kotlin_version" apply false
+ id 'com.google.gms.google-services' version '4.3.15' apply false
+ id "com.google.firebase.crashlytics" version "2.9.6" apply false
}
task clean(type: Delete) {
@@ -18,30 +20,32 @@ task clean(type: Delete) {
}
ext {
- core_version = "1.9.0"
- appcompat_version = "1.5.1"
- material_version = "1.7.0"
- lifecycle_version = "2.5.1"
- fragment_version = "1.5.5"
+ core_version = "1.10.1"
+ appcompat_version = "1.6.1"
+ material_version = "1.9.0"
+ lifecycle_version = "2.6.1"
+ fragment_version = "1.6.0"
constraintlayout_version = "2.1.4"
viewpager2_version = "1.0.0"
- exoplayer_version = "2.18.2"
+ exoplayer_version = "2.18.7"
youtubeplayer_version = "11.1.0"
+ firebase_version = "32.1.0"
+
retrofit_version = '2.9.0'
logginginterceptor_version = '4.9.1'
koin_version = '3.2.0'
- coil_version = '2.1.0'
+ coil_version = '2.3.0'
- jsoup_version = '1.11.2'
+ jsoup_version = '1.13.1'
- room_version = '2.5.0'
+ room_version = '2.5.1'
- work_version = '2.8.0'
+ work_version = '2.8.1'
- window_version = '1.0.0'
+ window_version = '1.1.0'
//testing
mockk_version = '1.13.3'
diff --git a/config.yaml b/config.yaml
index 5cbc1b61b..09972a3b6 100644
--- a/config.yaml
+++ b/config.yaml
@@ -7,6 +7,11 @@ environments:
contactUs: "https://dev-example.com/contact"
FEEDBACK_EMAIL_ADDRESS: "support@example.com"
OAUTH_CLIENT_ID: "DEV_OAUTH_CLIENT_ID"
+ FIREBASE:
+ PROJECT_ID: ""
+ APPLICATION_ID: ""
+ API_KEY: ""
+ GCM_SENDER_ID: ""
STAGE:
URLS:
API_HOST_URL: "http://stage-example.com/"
@@ -15,6 +20,11 @@ environments:
contactUs: "http://stage-example.com/contact"
FEEDBACK_EMAIL_ADDRESS: "support@example.com"
OAUTH_CLIENT_ID: "STAGE_OAUTH_CLIENT_ID"
+ FIREBASE:
+ PROJECT_ID: ""
+ APPLICATION_ID: ""
+ API_KEY: ""
+ GCM_SENDER_ID: ""
PROD:
URLS:
API_HOST_URL: "https://example.com/"
@@ -23,5 +33,10 @@ environments:
contactUs: "https://example.com/contact"
FEEDBACK_EMAIL_ADDRESS: "support@example.com"
OAUTH_CLIENT_ID: "PROD_OAUTH_CLIENT_ID"
-platformName: "New Open edX"
-platformFullName: "New Open edX"
+ FIREBASE:
+ PROJECT_ID: ""
+ APPLICATION_ID: ""
+ API_KEY: ""
+ GCM_SENDER_ID: ""
+platformName: "OpenEdX"
+platformFullName: "OpenEdX"
\ No newline at end of file
diff --git a/core/build.gradle b/core/build.gradle
index c3e03c43a..52350bdff 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -31,7 +31,7 @@ android {
consumerProguardFiles "consumer-rules.pro"
}
- namespace 'com.raccoongang.core'
+ namespace 'org.openedx.core'
flavorDimensions "tier"
productFlavors {
@@ -39,9 +39,14 @@ android {
def envMap = config.environments.find { it.key == "PROD" }
def clientId = envMap.value.OAUTH_CLIENT_ID
def envUrls = envMap.value.URLS
+ def firebase = getFirebaseConfig(envMap)
buildConfigField "String", "BASE_URL", "\"${envUrls.API_HOST_URL}\""
buildConfigField "String", "CLIENT_ID", "\"${clientId}\""
+ buildConfigField "String", "FIREBASE_PROJECT_ID", "\"${firebase.projectId}\""
+ buildConfigField "String", "FIREBASE_API_KEY", "\"${firebase.apiKey}\""
+ buildConfigField "String", "FIREBASE_GCM_SENDER_ID", "\"${firebase.gcmSenderId}\""
+ resValue "string", "google_app_id", firebase.appId
resValue "string", "platform_name", config.platformName
resValue "string", "platform_full_name", config.platformFullName
resValue "string", "privacy_policy_link", envUrls.privacyPolicy
@@ -53,9 +58,14 @@ android {
def envMap = config.environments.find { it.key == "DEV" }
def clientId = envMap.value.OAUTH_CLIENT_ID
def envUrls = envMap.value.URLS
+ def firebase = getFirebaseConfig(envMap)
buildConfigField "String", "BASE_URL", "\"${envUrls.API_HOST_URL}\""
buildConfigField "String", "CLIENT_ID", "\"${clientId}\""
+ buildConfigField "String", "FIREBASE_PROJECT_ID", "\"${firebase.projectId}\""
+ buildConfigField "String", "FIREBASE_API_KEY", "\"${firebase.apiKey}\""
+ buildConfigField "String", "FIREBASE_GCM_SENDER_ID", "\"${firebase.gcmSenderId}\""
+ resValue "string", "google_app_id", firebase.appId
resValue "string", "platform_name", config.platformName
resValue "string", "platform_full_name", config.platformFullName
resValue "string", "privacy_policy_link", envUrls.privacyPolicy
@@ -67,9 +77,14 @@ android {
def envMap = config.environments.find { it.key == "STAGE" }
def clientId = envMap.value.OAUTH_CLIENT_ID
def envUrls = envMap.value.URLS
+ def firebase = getFirebaseConfig(envMap)
buildConfigField "String", "BASE_URL", "\"${envUrls.API_HOST_URL}\""
buildConfigField "String", "CLIENT_ID", "\"${clientId}\""
+ buildConfigField "String", "FIREBASE_PROJECT_ID", "\"${firebase.projectId}\""
+ buildConfigField "String", "FIREBASE_API_KEY", "\"${firebase.apiKey}\""
+ buildConfigField "String", "FIREBASE_GCM_SENDER_ID", "\"${firebase.gcmSenderId}\""
+ resValue "string", "google_app_id", firebase.appId
resValue "string", "platform_name", config.platformName
resValue "string", "platform_full_name", config.platformFullName
resValue "string", "privacy_policy_link", envUrls.privacyPolicy
@@ -129,10 +144,11 @@ dependencies {
api "androidx.compose.runtime:runtime-livedata:$compose_version"
api "androidx.compose.ui:ui:$compose_version"
api "androidx.compose.material:material:$compose_version"
+ api "androidx.compose.foundation:foundation:$compose_version"
debugApi "androidx.compose.ui:ui-tooling:$compose_version"
api "androidx.compose.ui:ui-tooling-preview:$compose_version"
api "androidx.compose.material:material-icons-extended:$compose_version"
- debugApi "androidx.customview:customview:1.2.0-alpha01"
+ debugApi "androidx.customview:customview:1.2.0-alpha02"
debugApi "androidx.customview:customview-poolingcontainer:1.0.0"
//Networking
@@ -156,7 +172,41 @@ dependencies {
api "androidx.work:work-runtime-ktx:$work_version"
+
+ api platform("com.google.firebase:firebase-bom:$firebase_version")
+ api 'com.google.firebase:firebase-common-ktx'
+ api "com.google.firebase:firebase-crashlytics-ktx"
+ api "com.google.firebase:firebase-analytics-ktx"
+
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
-}
\ No newline at end of file
+}
+
+class FirebaseConfig {
+ public String projectId = ""
+ public String appId = ""
+ public String apiKey = ""
+ public String gcmSenderId = ""
+}
+
+def getFirebaseConfig(envMap) {
+ FirebaseConfig config = new FirebaseConfig()
+ if (envMap.value["FIREBASE"] != null) {
+ config.projectId = setValue(envMap.value["FIREBASE"]["PROJECT_ID"])
+ config.appId = setValue(envMap.value["FIREBASE"]["APPLICATION_ID"])
+ config.apiKey = setValue(envMap.value["FIREBASE"]["API_KEY"])
+ config.gcmSenderId = setValue(envMap.value["FIREBASE"]["GCM_SENDER_ID"])
+ }
+ return config
+}
+
+def setValue(value) {
+ def result
+ if (value == null) {
+ result = ""
+ } else {
+ result = value
+ }
+ return result
+}
diff --git a/core/src/androidTest/java/com/raccoongang/core/ExampleInstrumentedTest.kt b/core/src/androidTest/java/org/openedx/core/ExampleInstrumentedTest.kt
similarity index 84%
rename from core/src/androidTest/java/com/raccoongang/core/ExampleInstrumentedTest.kt
rename to core/src/androidTest/java/org/openedx/core/ExampleInstrumentedTest.kt
index d5e05e9e0..0c5df88c3 100644
--- a/core/src/androidTest/java/com/raccoongang/core/ExampleInstrumentedTest.kt
+++ b/core/src/androidTest/java/org/openedx/core/ExampleInstrumentedTest.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core
+package org.openedx.core
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -19,6 +19,6 @@ class ExampleInstrumentedTest {
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("com.raccoongang.core.test", appContext.packageName)
+ assertEquals("org.openedx.core.test", appContext.packageName)
}
}
\ No newline at end of file
diff --git a/core/src/main/java/com/raccoongang/core/presentation/dialog/SelectBottomDialogFragment.kt b/core/src/main/java/com/raccoongang/core/presentation/dialog/SelectBottomDialogFragment.kt
deleted file mode 100644
index 7de379ad9..000000000
--- a/core/src/main/java/com/raccoongang/core/presentation/dialog/SelectBottomDialogFragment.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-package com.raccoongang.core.presentation.dialog
-
-import android.content.res.Configuration
-import android.graphics.Color
-import android.graphics.drawable.ColorDrawable
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.ViewGroup
-import androidx.compose.foundation.lazy.rememberLazyListState
-import androidx.compose.material.MaterialTheme
-import androidx.compose.material.Surface
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.ui.platform.ComposeView
-import androidx.compose.ui.platform.LocalConfiguration
-import androidx.compose.ui.platform.ViewCompositionStrategy
-import androidx.fragment.app.DialogFragment
-import com.google.android.material.bottomsheet.BottomSheetBehavior
-import com.google.android.material.bottomsheet.BottomSheetDialog
-import com.google.android.material.bottomsheet.BottomSheetDialogFragment
-import com.raccoongang.core.R
-import com.raccoongang.core.domain.model.RegistrationField
-import com.raccoongang.core.extension.parcelableArrayList
-import com.raccoongang.core.ui.SheetContent
-import com.raccoongang.core.ui.px
-import com.raccoongang.core.ui.rememberWindowSize
-import com.raccoongang.core.ui.theme.BottomSheetShape
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.windowSizeValue
-import org.koin.androidx.viewmodel.ext.android.viewModel
-
-class SelectBottomDialogFragment() : BottomSheetDialogFragment() {
-
- private val viewModel by viewModel()
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- viewModel.values = requireArguments().parcelableArrayList(ARG_LIST_VALUES)!!
- setStyle(DialogFragment.STYLE_NORMAL, R.style.BottomSheetDialog)
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ) = ComposeView(requireContext()).apply {
- if (dialog != null && dialog!!.window != null) {
- dialog!!.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
- (dialog as? BottomSheetDialog)?.behavior?.apply {
- state = BottomSheetBehavior.STATE_EXPANDED
- }
- }
- setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
- setContent {
- NewEdxTheme {
- val windowSize = rememberWindowSize()
- val configuration = LocalConfiguration.current
- val listState = rememberLazyListState()
- val bottomSheetWeight by remember(key1 = windowSize) {
- mutableStateOf(
- windowSize.windowSizeValue(
- expanded = if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) 0.8f else 0.6f,
- compact = 1f
- )
- )
- }
-
- Surface(
- shape = BottomSheetShape(
- width = configuration.screenWidthDp.px,
- height = configuration.screenHeightDp.px,
- weight = bottomSheetWeight
- ),
- color = MaterialTheme.appColors.background
- ) {
- SheetContent(
- expandedList = viewModel.values,
- onItemClick = { item ->
- viewModel.sendCourseEventChanged(item.value)
- dismiss()
- },
- listState = listState
- )
- }
- }
- }
- }
-
- companion object {
- private const val ARG_LIST_VALUES = "argListValues"
-
- fun newInstance(
- values: List
- ): SelectBottomDialogFragment {
- val dialog = SelectBottomDialogFragment()
- dialog.arguments = Bundle().apply {
- putParcelableArrayList(ARG_LIST_VALUES, ArrayList(values))
- }
- return dialog
- }
- }
-
-}
\ No newline at end of file
diff --git a/core/src/main/java/com/raccoongang/core/presentation/global/WindowSizeHolder.kt b/core/src/main/java/com/raccoongang/core/presentation/global/WindowSizeHolder.kt
deleted file mode 100644
index fe9c5910b..000000000
--- a/core/src/main/java/com/raccoongang/core/presentation/global/WindowSizeHolder.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.raccoongang.core.presentation.global
-
-import com.raccoongang.core.ui.WindowSize
-
-interface WindowSizeHolder {
- val windowSize: WindowSize
-}
\ No newline at end of file
diff --git a/core/src/main/java/com/raccoongang/core/system/notifier/CourseDashboardUpdate.kt b/core/src/main/java/com/raccoongang/core/system/notifier/CourseDashboardUpdate.kt
deleted file mode 100644
index 5e58678e1..000000000
--- a/core/src/main/java/com/raccoongang/core/system/notifier/CourseDashboardUpdate.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package com.raccoongang.core.system.notifier
-
-class CourseDashboardUpdate : CourseEvent
\ No newline at end of file
diff --git a/core/src/main/java/com/raccoongang/core/system/notifier/CourseEvent.kt b/core/src/main/java/com/raccoongang/core/system/notifier/CourseEvent.kt
deleted file mode 100644
index 1bfaaaf64..000000000
--- a/core/src/main/java/com/raccoongang/core/system/notifier/CourseEvent.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package com.raccoongang.core.system.notifier
-
-interface CourseEvent
\ No newline at end of file
diff --git a/core/src/main/java/com/raccoongang/core/system/notifier/CoursePauseVideo.kt b/core/src/main/java/com/raccoongang/core/system/notifier/CoursePauseVideo.kt
deleted file mode 100644
index b47475da9..000000000
--- a/core/src/main/java/com/raccoongang/core/system/notifier/CoursePauseVideo.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package com.raccoongang.core.system.notifier
-
-class CoursePauseVideo: CourseEvent
\ No newline at end of file
diff --git a/core/src/main/java/com/raccoongang/core/ui/theme/Shape.kt b/core/src/main/java/com/raccoongang/core/ui/theme/Shape.kt
deleted file mode 100644
index 9b28aa2e4..000000000
--- a/core/src/main/java/com/raccoongang/core/ui/theme/Shape.kt
+++ /dev/null
@@ -1,103 +0,0 @@
-package com.raccoongang.core.ui.theme
-
-import androidx.compose.foundation.shape.CornerBasedShape
-import androidx.compose.foundation.shape.CornerSize
-import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material.MaterialTheme
-import androidx.compose.material.Shapes
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.ReadOnlyComposable
-import androidx.compose.runtime.staticCompositionLocalOf
-import androidx.compose.ui.geometry.*
-import androidx.compose.ui.graphics.Outline
-import androidx.compose.ui.unit.LayoutDirection
-import androidx.compose.ui.unit.dp
-
-data class AppShapes(
- val material: Shapes,
- val buttonShape: CornerBasedShape,
- val textFieldShape: CornerBasedShape,
- val screenBackgroundShape: CornerBasedShape,
- val cardShape: CornerBasedShape,
- val screenBackgroundShapeFull: CornerBasedShape,
- val courseImageShape: CornerBasedShape,
- val dialogShape: CornerBasedShape
-)
-
-internal val LocalShapes = staticCompositionLocalOf {
- AppShapes(
- material = Shapes(
- small = RoundedCornerShape(4.dp),
- medium = RoundedCornerShape(8.dp),
- large = RoundedCornerShape(0.dp)
- ),
- buttonShape = RoundedCornerShape(8.dp),
- textFieldShape = RoundedCornerShape(CornerSize(8.dp)),
- screenBackgroundShape = RoundedCornerShape(topStart = 30.dp, topEnd = 30.dp),
- cardShape = RoundedCornerShape(12.dp),
- screenBackgroundShapeFull = RoundedCornerShape(24.dp),
- courseImageShape = RoundedCornerShape(8.dp),
- dialogShape = RoundedCornerShape(24.dp)
- )
-}
-
-val MaterialTheme.appShapes: AppShapes
- @Composable
- @ReadOnlyComposable
- get() = LocalShapes.current
-
-
-class BottomSheetShape(
- topStart: CornerSize = CornerSize(30.dp),
- topEnd: CornerSize = CornerSize(30.dp),
- bottomEnd: CornerSize = CornerSize(0.dp),
- bottomStart: CornerSize = CornerSize(0.dp),
- val width: Float,
- val height: Float,
- val weight: Float,
-) : CornerBasedShape(topStart, topEnd, bottomEnd, bottomStart) {
-
- override fun copy(
- topStart: CornerSize,
- topEnd: CornerSize,
- bottomEnd: CornerSize,
- bottomStart: CornerSize,
- ): CornerBasedShape {
- return BottomSheetShape(
- topStart,
- topEnd,
- bottomEnd,
- bottomStart,
- width,
- height,
- weight
- )
- }
-
- override fun createOutline(
- size: Size,
- topStart: Float,
- topEnd: Float,
- bottomEnd: Float,
- bottomStart: Float,
- layoutDirection: LayoutDirection,
- ): Outline {
- if (weight < 0 || weight > 1) {
- throw IllegalArgumentException("Weight must be in 0..1 range")
- }
- val desiredWidth = width * weight
- val offset = (width - desiredWidth) / 2
- return Outline.Rounded(
- RoundRect(
- rect = Rect(
- topLeft = Offset(offset, 0f),
- bottomRight = Offset(width - offset, height)
- ),
- topLeft = CornerRadius(if (layoutDirection == LayoutDirection.Ltr) topStart else topEnd),
- topRight = CornerRadius(if (layoutDirection == LayoutDirection.Ltr) topEnd else topStart),
- bottomRight = CornerRadius(if (layoutDirection == LayoutDirection.Ltr) bottomEnd else bottomStart),
- bottomLeft = CornerRadius(if (layoutDirection == LayoutDirection.Ltr) bottomStart else bottomEnd)
- )
- )
- }
-}
\ No newline at end of file
diff --git a/core/src/main/java/com/raccoongang/core/ApiConstants.kt b/core/src/main/java/org/openedx/core/ApiConstants.kt
similarity index 96%
rename from core/src/main/java/com/raccoongang/core/ApiConstants.kt
rename to core/src/main/java/org/openedx/core/ApiConstants.kt
index d1e8fb382..82486f593 100644
--- a/core/src/main/java/com/raccoongang/core/ApiConstants.kt
+++ b/core/src/main/java/org/openedx/core/ApiConstants.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core
+package org.openedx.core
object ApiConstants {
const val URL_LOGIN = "/oauth2/login/"
diff --git a/core/src/main/java/com/raccoongang/core/AppDataConstants.kt b/core/src/main/java/org/openedx/core/AppDataConstants.kt
similarity index 90%
rename from core/src/main/java/com/raccoongang/core/AppDataConstants.kt
rename to core/src/main/java/org/openedx/core/AppDataConstants.kt
index d41752b3a..0bb5a95d0 100644
--- a/core/src/main/java/com/raccoongang/core/AppDataConstants.kt
+++ b/core/src/main/java/org/openedx/core/AppDataConstants.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core
+package org.openedx.core
import java.util.*
diff --git a/core/src/main/java/com/raccoongang/core/BaseViewModel.kt b/core/src/main/java/org/openedx/core/BaseViewModel.kt
similarity index 67%
rename from core/src/main/java/com/raccoongang/core/BaseViewModel.kt
rename to core/src/main/java/org/openedx/core/BaseViewModel.kt
index 86a4e5cea..ac0578624 100644
--- a/core/src/main/java/com/raccoongang/core/BaseViewModel.kt
+++ b/core/src/main/java/org/openedx/core/BaseViewModel.kt
@@ -1,7 +1,6 @@
-package com.raccoongang.core
+package org.openedx.core
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.ViewModel
-import org.koin.java.KoinJavaComponent.inject
open class BaseViewModel : ViewModel(), DefaultLifecycleObserver
\ No newline at end of file
diff --git a/core/src/main/java/com/raccoongang/core/BlockType.kt b/core/src/main/java/org/openedx/core/BlockType.kt
similarity index 97%
rename from core/src/main/java/com/raccoongang/core/BlockType.kt
rename to core/src/main/java/org/openedx/core/BlockType.kt
index 4e72bf83b..dc1862395 100644
--- a/core/src/main/java/com/raccoongang/core/BlockType.kt
+++ b/core/src/main/java/org/openedx/core/BlockType.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core
+package org.openedx.core
enum class BlockType {
CHAPTER{ override fun isContainer() = true },
diff --git a/core/src/main/java/com/raccoongang/core/FragmentViewType.kt b/core/src/main/java/org/openedx/core/FragmentViewType.kt
similarity index 68%
rename from core/src/main/java/com/raccoongang/core/FragmentViewType.kt
rename to core/src/main/java/org/openedx/core/FragmentViewType.kt
index 1a916fd9b..97ebfeed5 100644
--- a/core/src/main/java/com/raccoongang/core/FragmentViewType.kt
+++ b/core/src/main/java/org/openedx/core/FragmentViewType.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core
+package org.openedx.core
enum class FragmentViewType {
MAIN_CONTENT, FULL_CONTENT;
diff --git a/core/src/main/java/com/raccoongang/core/SingleEventLiveData.kt b/core/src/main/java/org/openedx/core/SingleEventLiveData.kt
similarity index 98%
rename from core/src/main/java/com/raccoongang/core/SingleEventLiveData.kt
rename to core/src/main/java/org/openedx/core/SingleEventLiveData.kt
index f30be1bd8..dfa53c6dd 100644
--- a/core/src/main/java/com/raccoongang/core/SingleEventLiveData.kt
+++ b/core/src/main/java/org/openedx/core/SingleEventLiveData.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core
+package org.openedx.core
import androidx.annotation.MainThread
import androidx.lifecycle.LifecycleOwner
diff --git a/core/src/main/java/com/raccoongang/core/UIMessage.kt b/core/src/main/java/org/openedx/core/UIMessage.kt
similarity index 90%
rename from core/src/main/java/com/raccoongang/core/UIMessage.kt
rename to core/src/main/java/org/openedx/core/UIMessage.kt
index 4f13239b5..6f26d6f65 100644
--- a/core/src/main/java/com/raccoongang/core/UIMessage.kt
+++ b/core/src/main/java/org/openedx/core/UIMessage.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core
+package org.openedx.core
import androidx.compose.material.SnackbarDuration
diff --git a/core/src/main/java/com/raccoongang/core/Validator.kt b/core/src/main/java/org/openedx/core/Validator.kt
similarity index 93%
rename from core/src/main/java/com/raccoongang/core/Validator.kt
rename to core/src/main/java/org/openedx/core/Validator.kt
index 6b5e05a95..ca758a071 100644
--- a/core/src/main/java/com/raccoongang/core/Validator.kt
+++ b/core/src/main/java/org/openedx/core/Validator.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core
+package org.openedx.core
import java.util.regex.Pattern
diff --git a/core/src/main/java/com/raccoongang/core/data/api/CookiesApi.kt b/core/src/main/java/org/openedx/core/data/api/CookiesApi.kt
similarity index 71%
rename from core/src/main/java/com/raccoongang/core/data/api/CookiesApi.kt
rename to core/src/main/java/org/openedx/core/data/api/CookiesApi.kt
index 491cea40a..c25cad2c0 100644
--- a/core/src/main/java/com/raccoongang/core/data/api/CookiesApi.kt
+++ b/core/src/main/java/org/openedx/core/data/api/CookiesApi.kt
@@ -1,6 +1,6 @@
-package com.raccoongang.core.data.api
+package org.openedx.core.data.api
-import com.raccoongang.core.ApiConstants
+import org.openedx.core.ApiConstants
import okhttp3.RequestBody
import retrofit2.Response
import retrofit2.http.POST
diff --git a/core/src/main/java/com/raccoongang/core/data/api/CourseApi.kt b/core/src/main/java/org/openedx/core/data/api/CourseApi.kt
similarity index 97%
rename from core/src/main/java/com/raccoongang/core/data/api/CourseApi.kt
rename to core/src/main/java/org/openedx/core/data/api/CourseApi.kt
index 2dfe736d6..89538252d 100644
--- a/core/src/main/java/com/raccoongang/core/data/api/CourseApi.kt
+++ b/core/src/main/java/org/openedx/core/data/api/CourseApi.kt
@@ -1,6 +1,6 @@
-package com.raccoongang.core.data.api
+package org.openedx.core.data.api
-import com.raccoongang.core.data.model.*
+import org.openedx.core.data.model.*
import okhttp3.ResponseBody
import retrofit2.http.*
diff --git a/core/src/main/java/com/raccoongang/core/data/model/AnnouncementModel.kt b/core/src/main/java/org/openedx/core/data/model/AnnouncementModel.kt
similarity index 65%
rename from core/src/main/java/com/raccoongang/core/data/model/AnnouncementModel.kt
rename to core/src/main/java/org/openedx/core/data/model/AnnouncementModel.kt
index e4f9c1091..99dc6b92c 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/AnnouncementModel.kt
+++ b/core/src/main/java/org/openedx/core/data/model/AnnouncementModel.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.data.model
+package org.openedx.core.data.model
import com.google.gson.annotations.SerializedName
@@ -8,7 +8,7 @@ data class AnnouncementModel(
@SerializedName("content")
val content: String
) {
- fun mapToDomain() = com.raccoongang.core.domain.model.AnnouncementModel(
+ fun mapToDomain() = org.openedx.core.domain.model.AnnouncementModel(
date, content
)
}
\ No newline at end of file
diff --git a/core/src/main/java/com/raccoongang/core/data/model/Block.kt b/core/src/main/java/org/openedx/core/data/model/Block.kt
similarity index 82%
rename from core/src/main/java/com/raccoongang/core/data/model/Block.kt
rename to core/src/main/java/org/openedx/core/data/model/Block.kt
index fac04a00b..4ddc549a0 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/Block.kt
+++ b/core/src/main/java/org/openedx/core/data/model/Block.kt
@@ -1,8 +1,8 @@
-package com.raccoongang.core.data.model
+package org.openedx.core.data.model
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.domain.model.Block
+import org.openedx.core.BlockType
+import org.openedx.core.domain.model.Block
data class Block(
@SerializedName("id")
@@ -33,7 +33,7 @@ data class Block(
val completion: Double?
) {
fun mapToDomain(): Block {
- return com.raccoongang.core.domain.model.Block(
+ return org.openedx.core.domain.model.Block(
id = id ?: "",
blockId = blockId ?: "",
lmsWebUrl = lmsWebUrl ?: "",
@@ -65,8 +65,8 @@ data class StudentViewData(
@SerializedName("topic_id")
val topicId: String?
) {
- fun mapToDomain(): com.raccoongang.core.domain.model.StudentViewData {
- return com.raccoongang.core.domain.model.StudentViewData(
+ fun mapToDomain(): org.openedx.core.domain.model.StudentViewData {
+ return org.openedx.core.domain.model.StudentViewData(
onlyOnWeb = onlyOnWeb ?: false,
duration = duration ?: "",
transcripts = transcripts,
@@ -91,8 +91,8 @@ data class EncodedVideos(
var mobileLow: VideoInfo?
) {
- fun mapToDomain(): com.raccoongang.core.domain.model.EncodedVideos {
- return com.raccoongang.core.domain.model.EncodedVideos(
+ fun mapToDomain(): org.openedx.core.domain.model.EncodedVideos {
+ return org.openedx.core.domain.model.EncodedVideos(
youtube = videoInfo?.mapToDomain(),
hls = hls?.mapToDomain(),
fallback = fallback?.mapToDomain(),
@@ -109,8 +109,8 @@ data class VideoInfo(
@SerializedName("file_size")
var fileSize: Int?
) {
- fun mapToDomain(): com.raccoongang.core.domain.model.VideoInfo {
- return com.raccoongang.core.domain.model.VideoInfo(
+ fun mapToDomain(): org.openedx.core.domain.model.VideoInfo {
+ return org.openedx.core.domain.model.VideoInfo(
url = url ?: "",
fileSize = fileSize ?: 0
)
@@ -121,8 +121,8 @@ data class BlockCounts(
@SerializedName("video")
var video: Int?
) {
- fun mapToDomain(): com.raccoongang.core.domain.model.BlockCounts {
- return com.raccoongang.core.domain.model.BlockCounts(
+ fun mapToDomain(): org.openedx.core.domain.model.BlockCounts {
+ return org.openedx.core.domain.model.BlockCounts(
video = video ?: 0
)
}
diff --git a/core/src/main/java/com/raccoongang/core/data/model/BlocksCompletionBody.kt b/core/src/main/java/org/openedx/core/data/model/BlocksCompletionBody.kt
similarity index 87%
rename from core/src/main/java/com/raccoongang/core/data/model/BlocksCompletionBody.kt
rename to core/src/main/java/org/openedx/core/data/model/BlocksCompletionBody.kt
index 447cf2cc0..fa373a2ea 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/BlocksCompletionBody.kt
+++ b/core/src/main/java/org/openedx/core/data/model/BlocksCompletionBody.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.data.model
+package org.openedx.core.data.model
import com.google.gson.annotations.SerializedName
diff --git a/core/src/main/java/com/raccoongang/core/data/model/Certificate.kt b/core/src/main/java/org/openedx/core/data/model/Certificate.kt
similarity index 66%
rename from core/src/main/java/com/raccoongang/core/data/model/Certificate.kt
rename to core/src/main/java/org/openedx/core/data/model/Certificate.kt
index 7c03eb1ad..8324e4cc7 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/Certificate.kt
+++ b/core/src/main/java/org/openedx/core/data/model/Certificate.kt
@@ -1,8 +1,8 @@
-package com.raccoongang.core.data.model
+package org.openedx.core.data.model
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.data.model.room.discovery.CertificateDb
-import com.raccoongang.core.domain.model.Certificate
+import org.openedx.core.data.model.room.discovery.CertificateDb
+import org.openedx.core.domain.model.Certificate
data class Certificate(
@SerializedName("url")
diff --git a/core/src/main/java/com/raccoongang/core/data/model/CourseComponentStatus.kt b/core/src/main/java/org/openedx/core/data/model/CourseComponentStatus.kt
similarity index 76%
rename from core/src/main/java/com/raccoongang/core/data/model/CourseComponentStatus.kt
rename to core/src/main/java/org/openedx/core/data/model/CourseComponentStatus.kt
index dfdf4db1f..c907f932a 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/CourseComponentStatus.kt
+++ b/core/src/main/java/org/openedx/core/data/model/CourseComponentStatus.kt
@@ -1,7 +1,7 @@
-package com.raccoongang.core.data.model
+package org.openedx.core.data.model
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.domain.model.CourseComponentStatus
+import org.openedx.core.domain.model.CourseComponentStatus
data class CourseComponentStatus(
@SerializedName("last_visited_block_id")
diff --git a/core/src/main/java/com/raccoongang/core/data/model/CourseDetails.kt b/core/src/main/java/org/openedx/core/data/model/CourseDetails.kt
similarity index 89%
rename from core/src/main/java/com/raccoongang/core/data/model/CourseDetails.kt
rename to core/src/main/java/org/openedx/core/data/model/CourseDetails.kt
index b8de035a6..c525d8e43 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/CourseDetails.kt
+++ b/core/src/main/java/org/openedx/core/data/model/CourseDetails.kt
@@ -1,8 +1,8 @@
-package com.raccoongang.core.data.model
+package org.openedx.core.data.model
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.domain.model.Course
-import com.raccoongang.core.utils.TimeUtils
+import org.openedx.core.domain.model.Course
+import org.openedx.core.utils.TimeUtils
import java.util.*
data class CourseDetails(
@@ -33,7 +33,7 @@ data class CourseDetails(
@SerializedName("number")
val number: String?,
@SerializedName("org")
- val org: String?,
+ val organization: String?,
@SerializedName("pacing")
val pacing: String?,
@SerializedName("short_description")
@@ -63,7 +63,7 @@ data class CourseDetails(
mobileAvailable = mobileAvailable ?: false,
name = name ?: "",
number = number ?: "",
- org = org ?: "",
+ org = organization ?: "",
shortDescription = shortDescription ?: "",
start = start ?: "",
end = end ?: "",
@@ -72,7 +72,7 @@ data class CourseDetails(
pacing = pacing ?: "",
overview = overview ?: "",
isEnrolled = isEnrolled ?: false,
- media = media?.mapToDomain() ?: com.raccoongang.core.domain.model.Media()
+ media = media?.mapToDomain() ?: org.openedx.core.domain.model.Media()
)
}
}
diff --git a/core/src/main/java/com/raccoongang/core/data/model/CourseList.kt b/core/src/main/java/org/openedx/core/data/model/CourseList.kt
similarity index 84%
rename from core/src/main/java/com/raccoongang/core/data/model/CourseList.kt
rename to core/src/main/java/org/openedx/core/data/model/CourseList.kt
index 4276a62dd..1150c847f 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/CourseList.kt
+++ b/core/src/main/java/org/openedx/core/data/model/CourseList.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.data.model
+package org.openedx.core.data.model
import com.google.gson.annotations.SerializedName
diff --git a/core/src/main/java/com/raccoongang/core/data/model/CourseSharingUtmParameters.kt b/core/src/main/java/org/openedx/core/data/model/CourseSharingUtmParameters.kt
similarity index 73%
rename from core/src/main/java/com/raccoongang/core/data/model/CourseSharingUtmParameters.kt
rename to core/src/main/java/org/openedx/core/data/model/CourseSharingUtmParameters.kt
index 7ad63e5e6..8b2651ddd 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/CourseSharingUtmParameters.kt
+++ b/core/src/main/java/org/openedx/core/data/model/CourseSharingUtmParameters.kt
@@ -1,8 +1,8 @@
-package com.raccoongang.core.data.model
+package org.openedx.core.data.model
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.data.model.room.discovery.CourseSharingUtmParametersDb
-import com.raccoongang.core.domain.model.CourseSharingUtmParameters
+import org.openedx.core.data.model.room.discovery.CourseSharingUtmParametersDb
+import org.openedx.core.domain.model.CourseSharingUtmParameters
data class CourseSharingUtmParameters(
@SerializedName("facebook")
diff --git a/core/src/main/java/com/raccoongang/core/data/model/CourseStructureModel.kt b/core/src/main/java/org/openedx/core/data/model/CourseStructureModel.kt
similarity index 88%
rename from core/src/main/java/com/raccoongang/core/data/model/CourseStructureModel.kt
rename to core/src/main/java/org/openedx/core/data/model/CourseStructureModel.kt
index 3471ae731..ac2552c26 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/CourseStructureModel.kt
+++ b/core/src/main/java/org/openedx/core/data/model/CourseStructureModel.kt
@@ -1,11 +1,11 @@
-package com.raccoongang.core.data.model
+package org.openedx.core.data.model
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.data.model.room.BlockDb
-import com.raccoongang.core.data.model.room.CourseStructureEntity
-import com.raccoongang.core.data.model.room.MediaDb
-import com.raccoongang.core.domain.model.CourseStructure
-import com.raccoongang.core.utils.TimeUtils
+import org.openedx.core.data.model.room.BlockDb
+import org.openedx.core.data.model.room.CourseStructureEntity
+import org.openedx.core.data.model.room.MediaDb
+import org.openedx.core.domain.model.CourseStructure
+import org.openedx.core.utils.TimeUtils
data class CourseStructureModel(
@SerializedName("root")
diff --git a/core/src/main/java/com/raccoongang/core/data/model/CoursewareAccess.kt b/core/src/main/java/org/openedx/core/data/model/CoursewareAccess.kt
similarity index 88%
rename from core/src/main/java/com/raccoongang/core/data/model/CoursewareAccess.kt
rename to core/src/main/java/org/openedx/core/data/model/CoursewareAccess.kt
index 5893f61ab..d92ffc336 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/CoursewareAccess.kt
+++ b/core/src/main/java/org/openedx/core/data/model/CoursewareAccess.kt
@@ -1,8 +1,8 @@
-package com.raccoongang.core.data.model
+package org.openedx.core.data.model
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.data.model.room.discovery.CoursewareAccessDb
-import com.raccoongang.core.domain.model.CoursewareAccess
+import org.openedx.core.data.model.room.discovery.CoursewareAccessDb
+import org.openedx.core.domain.model.CoursewareAccess
data class CoursewareAccess(
@SerializedName("has_access")
diff --git a/core/src/main/java/com/raccoongang/core/data/model/DashboardCourseList.kt b/core/src/main/java/org/openedx/core/data/model/DashboardCourseList.kt
similarity index 81%
rename from core/src/main/java/com/raccoongang/core/data/model/DashboardCourseList.kt
rename to core/src/main/java/org/openedx/core/data/model/DashboardCourseList.kt
index eb1e64785..c75eeef33 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/DashboardCourseList.kt
+++ b/core/src/main/java/org/openedx/core/data/model/DashboardCourseList.kt
@@ -1,7 +1,7 @@
-package com.raccoongang.core.data.model
+package org.openedx.core.data.model
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.domain.model.DashboardCourseList
+import org.openedx.core.domain.model.DashboardCourseList
data class DashboardCourseList(
@SerializedName("next")
@@ -20,7 +20,7 @@ data class DashboardCourseList(
fun mapToDomain(): DashboardCourseList {
return DashboardCourseList(
- com.raccoongang.core.domain.model.Pagination(
+ org.openedx.core.domain.model.Pagination(
count,
next ?: "",
numPages,
diff --git a/core/src/main/java/com/raccoongang/core/data/model/EnrollBody.kt b/core/src/main/java/org/openedx/core/data/model/EnrollBody.kt
similarity index 89%
rename from core/src/main/java/com/raccoongang/core/data/model/EnrollBody.kt
rename to core/src/main/java/org/openedx/core/data/model/EnrollBody.kt
index fcfb62127..f6909a514 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/EnrollBody.kt
+++ b/core/src/main/java/org/openedx/core/data/model/EnrollBody.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.data.model
+package org.openedx.core.data.model
import com.google.gson.annotations.SerializedName
diff --git a/core/src/main/java/com/raccoongang/core/data/model/EnrolledCourse.kt b/core/src/main/java/org/openedx/core/data/model/EnrolledCourse.kt
similarity index 85%
rename from core/src/main/java/com/raccoongang/core/data/model/EnrolledCourse.kt
rename to core/src/main/java/org/openedx/core/data/model/EnrolledCourse.kt
index 550182c2a..984794698 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/EnrolledCourse.kt
+++ b/core/src/main/java/org/openedx/core/data/model/EnrolledCourse.kt
@@ -1,9 +1,9 @@
-package com.raccoongang.core.data.model
+package org.openedx.core.data.model
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.data.model.room.discovery.EnrolledCourseEntity
-import com.raccoongang.core.domain.model.EnrolledCourse
-import com.raccoongang.core.utils.TimeUtils
+import org.openedx.core.data.model.room.discovery.EnrolledCourseEntity
+import org.openedx.core.domain.model.EnrolledCourse
+import org.openedx.core.utils.TimeUtils
data class EnrolledCourse(
@SerializedName("audit_access_expires")
diff --git a/core/src/main/java/com/raccoongang/core/data/model/EnrolledCourseData.kt b/core/src/main/java/org/openedx/core/data/model/EnrolledCourseData.kt
similarity index 92%
rename from core/src/main/java/com/raccoongang/core/data/model/EnrolledCourseData.kt
rename to core/src/main/java/org/openedx/core/data/model/EnrolledCourseData.kt
index 7dc8c310e..74aba5073 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/EnrolledCourseData.kt
+++ b/core/src/main/java/org/openedx/core/data/model/EnrolledCourseData.kt
@@ -1,10 +1,10 @@
-package com.raccoongang.core.data.model
+package org.openedx.core.data.model
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.data.model.room.MediaDb
-import com.raccoongang.core.data.model.room.discovery.EnrolledCourseDataDb
-import com.raccoongang.core.domain.model.EnrolledCourseData
-import com.raccoongang.core.utils.TimeUtils
+import org.openedx.core.data.model.room.MediaDb
+import org.openedx.core.data.model.room.discovery.EnrolledCourseDataDb
+import org.openedx.core.domain.model.EnrolledCourseData
+import org.openedx.core.utils.TimeUtils
data class EnrolledCourseData(
@SerializedName("id")
diff --git a/core/src/main/java/com/raccoongang/core/data/model/ErrorResponse.kt b/core/src/main/java/org/openedx/core/data/model/ErrorResponse.kt
similarity index 87%
rename from core/src/main/java/com/raccoongang/core/data/model/ErrorResponse.kt
rename to core/src/main/java/org/openedx/core/data/model/ErrorResponse.kt
index e02d0c039..f76ea2edb 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/ErrorResponse.kt
+++ b/core/src/main/java/org/openedx/core/data/model/ErrorResponse.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.data.model
+package org.openedx.core.data.model
import com.google.gson.annotations.SerializedName
diff --git a/core/src/main/java/com/raccoongang/core/data/model/HandoutsModel.kt b/core/src/main/java/org/openedx/core/data/model/HandoutsModel.kt
similarity index 68%
rename from core/src/main/java/com/raccoongang/core/data/model/HandoutsModel.kt
rename to core/src/main/java/org/openedx/core/data/model/HandoutsModel.kt
index 640c7d418..5dc2a8e38 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/HandoutsModel.kt
+++ b/core/src/main/java/org/openedx/core/data/model/HandoutsModel.kt
@@ -1,7 +1,7 @@
-package com.raccoongang.core.data.model
+package org.openedx.core.data.model
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.domain.model.HandoutsModel
+import org.openedx.core.domain.model.HandoutsModel
data class HandoutsModel(
@SerializedName("handouts_html")
diff --git a/core/src/main/java/com/raccoongang/core/data/model/Media.kt b/core/src/main/java/org/openedx/core/data/model/Media.kt
similarity index 66%
rename from core/src/main/java/com/raccoongang/core/data/model/Media.kt
rename to core/src/main/java/org/openedx/core/data/model/Media.kt
index 839c74b93..2a7a05000 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/Media.kt
+++ b/core/src/main/java/org/openedx/core/data/model/Media.kt
@@ -1,8 +1,8 @@
-package com.raccoongang.core.data.model
+package org.openedx.core.data.model
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.data.model.room.discovery.*
-import com.raccoongang.core.domain.model.Media
+import org.openedx.core.data.model.room.discovery.*
+import org.openedx.core.domain.model.Media
data class Media(
@SerializedName("banner_image")
@@ -15,7 +15,7 @@ data class Media(
val image: Image?,
) {
- fun mapToDomain(): com.raccoongang.core.domain.model.Media {
+ fun mapToDomain(): org.openedx.core.domain.model.Media {
return Media(
bannerImage = bannerImage?.mapToDomain(),
courseImage = courseImage?.mapToDomain(),
@@ -34,8 +34,8 @@ data class Image(
@SerializedName("small")
val small: String?,
) {
- fun mapToDomain(): com.raccoongang.core.domain.model.Image {
- return com.raccoongang.core.domain.model.Image(
+ fun mapToDomain(): org.openedx.core.domain.model.Image {
+ return org.openedx.core.domain.model.Image(
large = large ?: "",
raw = raw ?: "",
small = small ?: ""
@@ -47,8 +47,8 @@ data class CourseVideo(
@SerializedName("uri")
val uri: String?,
) {
- fun mapToDomain(): com.raccoongang.core.domain.model.CourseVideo {
- return com.raccoongang.core.domain.model.CourseVideo(
+ fun mapToDomain(): org.openedx.core.domain.model.CourseVideo {
+ return org.openedx.core.domain.model.CourseVideo(
uri = uri ?: ""
)
}
@@ -60,8 +60,8 @@ data class CourseImage(
@SerializedName("name")
val name: String?
) {
- fun mapToDomain(): com.raccoongang.core.domain.model.CourseImage {
- return com.raccoongang.core.domain.model.CourseImage(
+ fun mapToDomain(): org.openedx.core.domain.model.CourseImage {
+ return org.openedx.core.domain.model.CourseImage(
uri = uri ?: "",
name = name ?: ""
)
@@ -74,8 +74,8 @@ data class BannerImage(
@SerializedName("uri_absolute")
val uriAbsolute: String?,
) {
- fun mapToDomain(): com.raccoongang.core.domain.model.BannerImage {
- return com.raccoongang.core.domain.model.BannerImage(
+ fun mapToDomain(): org.openedx.core.domain.model.BannerImage {
+ return org.openedx.core.domain.model.BannerImage(
uri = uri ?: "",
uriAbsolute = uriAbsolute ?: ""
)
diff --git a/core/src/main/java/com/raccoongang/core/data/model/Pagination.kt b/core/src/main/java/org/openedx/core/data/model/Pagination.kt
similarity index 80%
rename from core/src/main/java/com/raccoongang/core/data/model/Pagination.kt
rename to core/src/main/java/org/openedx/core/data/model/Pagination.kt
index 0396fa418..e375ac6fe 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/Pagination.kt
+++ b/core/src/main/java/org/openedx/core/data/model/Pagination.kt
@@ -1,7 +1,7 @@
-package com.raccoongang.core.data.model
+package org.openedx.core.data.model
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.domain.model.Pagination as domainPagination
+import org.openedx.core.domain.model.Pagination as domainPagination
data class Pagination(
@SerializedName("count")
diff --git a/core/src/main/java/com/raccoongang/core/data/model/ProfileImage.kt b/core/src/main/java/org/openedx/core/data/model/ProfileImage.kt
similarity index 88%
rename from core/src/main/java/com/raccoongang/core/data/model/ProfileImage.kt
rename to core/src/main/java/org/openedx/core/data/model/ProfileImage.kt
index 61ea13c4b..86396554f 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/ProfileImage.kt
+++ b/core/src/main/java/org/openedx/core/data/model/ProfileImage.kt
@@ -1,7 +1,7 @@
-package com.raccoongang.core.data.model
+package org.openedx.core.data.model
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.domain.model.ProfileImage
+import org.openedx.core.domain.model.ProfileImage
data class ProfileImage(
@SerializedName("image_url_full")
diff --git a/core/src/main/java/com/raccoongang/core/data/model/User.kt b/core/src/main/java/org/openedx/core/data/model/User.kt
similarity index 81%
rename from core/src/main/java/com/raccoongang/core/data/model/User.kt
rename to core/src/main/java/org/openedx/core/data/model/User.kt
index d869994d1..99194624b 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/User.kt
+++ b/core/src/main/java/org/openedx/core/data/model/User.kt
@@ -1,7 +1,7 @@
-package com.raccoongang.core.data.model
+package org.openedx.core.data.model
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.domain.model.User
+import org.openedx.core.domain.model.User
data class User(
@SerializedName("id")
diff --git a/core/src/main/java/com/raccoongang/core/data/model/room/BlockDb.kt b/core/src/main/java/org/openedx/core/data/model/room/BlockDb.kt
similarity index 89%
rename from core/src/main/java/com/raccoongang/core/data/model/room/BlockDb.kt
rename to core/src/main/java/org/openedx/core/data/model/room/BlockDb.kt
index 00aa62d93..26d83e81e 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/room/BlockDb.kt
+++ b/core/src/main/java/org/openedx/core/data/model/room/BlockDb.kt
@@ -1,9 +1,9 @@
-package com.raccoongang.core.data.model.room
+package org.openedx.core.data.model.room
import androidx.room.ColumnInfo
import androidx.room.Embedded
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.domain.model.*
+import org.openedx.core.BlockType
+import org.openedx.core.domain.model.*
data class BlockDb(
@ColumnInfo("id")
@@ -54,7 +54,7 @@ data class BlockDb(
companion object {
fun createFrom(
- block: com.raccoongang.core.data.model.Block
+ block: org.openedx.core.data.model.Block
): BlockDb {
with(block) {
return BlockDb(
@@ -101,7 +101,7 @@ data class StudentViewDataDb(
companion object {
- fun createFrom(studentViewData: com.raccoongang.core.data.model.StudentViewData?): StudentViewDataDb {
+ fun createFrom(studentViewData: org.openedx.core.data.model.StudentViewData?): StudentViewDataDb {
return StudentViewDataDb(
onlyOnWeb = studentViewData?.onlyOnWeb ?: false,
duration = studentViewData?.duration.toString(),
@@ -140,7 +140,7 @@ data class EncodedVideosDb(
}
companion object {
- fun createFrom(encodedVideos: com.raccoongang.core.data.model.EncodedVideos?): EncodedVideosDb {
+ fun createFrom(encodedVideos: org.openedx.core.data.model.EncodedVideos?): EncodedVideosDb {
return EncodedVideosDb(
youtube = VideoInfoDb.createFrom(encodedVideos?.videoInfo),
hls = VideoInfoDb.createFrom(encodedVideos?.hls),
@@ -163,7 +163,7 @@ data class VideoInfoDb(
fun mapToDomain() = VideoInfo(url, fileSize)
companion object {
- fun createFrom(videoInfo: com.raccoongang.core.data.model.VideoInfo?): VideoInfoDb? {
+ fun createFrom(videoInfo: org.openedx.core.data.model.VideoInfo?): VideoInfoDb? {
if (videoInfo == null) return null
return VideoInfoDb(
videoInfo.url ?: "",
@@ -180,7 +180,7 @@ data class BlockCountsDb(
fun mapToDomain() = BlockCounts(video)
companion object {
- fun createFrom(blocksCounts: com.raccoongang.core.data.model.BlockCounts?): BlockCountsDb {
+ fun createFrom(blocksCounts: org.openedx.core.data.model.BlockCounts?): BlockCountsDb {
return BlockCountsDb(blocksCounts?.video ?: 0)
}
}
diff --git a/core/src/main/java/com/raccoongang/core/data/model/room/CourseEntity.kt b/core/src/main/java/org/openedx/core/data/model/room/CourseEntity.kt
similarity index 93%
rename from core/src/main/java/com/raccoongang/core/data/model/room/CourseEntity.kt
rename to core/src/main/java/org/openedx/core/data/model/room/CourseEntity.kt
index 821370eb7..8ddca2bb4 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/room/CourseEntity.kt
+++ b/core/src/main/java/org/openedx/core/data/model/room/CourseEntity.kt
@@ -1,12 +1,12 @@
-package com.raccoongang.core.data.model.room
+package org.openedx.core.data.model.room
import androidx.room.ColumnInfo
import androidx.room.Embedded
import androidx.room.Entity
import androidx.room.PrimaryKey
-import com.raccoongang.core.data.model.CourseDetails
-import com.raccoongang.core.domain.model.Course
-import com.raccoongang.core.utils.TimeUtils
+import org.openedx.core.data.model.CourseDetails
+import org.openedx.core.domain.model.Course
+import org.openedx.core.utils.TimeUtils
@Entity(tableName = "course_discovery_table")
data class CourseEntity(
@@ -96,7 +96,7 @@ data class CourseEntity(
mobileAvailable = mobileAvailable ?: false,
name = name ?: "",
number = number ?: "",
- org = org ?: "",
+ org = organization ?: "",
shortDescription = shortDescription ?: "",
start = start ?: "",
end = end ?: "",
diff --git a/core/src/main/java/com/raccoongang/core/data/model/room/CourseStructureEntity.kt b/core/src/main/java/org/openedx/core/data/model/room/CourseStructureEntity.kt
similarity index 83%
rename from core/src/main/java/com/raccoongang/core/data/model/room/CourseStructureEntity.kt
rename to core/src/main/java/org/openedx/core/data/model/room/CourseStructureEntity.kt
index 1af4d2e7c..3c0ac045b 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/room/CourseStructureEntity.kt
+++ b/core/src/main/java/org/openedx/core/data/model/room/CourseStructureEntity.kt
@@ -1,13 +1,13 @@
-package com.raccoongang.core.data.model.room
+package org.openedx.core.data.model.room
import androidx.room.ColumnInfo
import androidx.room.Embedded
import androidx.room.Entity
import androidx.room.PrimaryKey
-import com.raccoongang.core.data.model.room.discovery.CertificateDb
-import com.raccoongang.core.data.model.room.discovery.CoursewareAccessDb
-import com.raccoongang.core.domain.model.CourseStructure
-import com.raccoongang.core.utils.TimeUtils
+import org.openedx.core.data.model.room.discovery.CertificateDb
+import org.openedx.core.data.model.room.discovery.CoursewareAccessDb
+import org.openedx.core.domain.model.CourseStructure
+import org.openedx.core.utils.TimeUtils
@Entity(tableName = "course_structure_table")
data class CourseStructureEntity(
diff --git a/core/src/main/java/com/raccoongang/core/data/model/room/MediaDb.kt b/core/src/main/java/org/openedx/core/data/model/room/MediaDb.kt
similarity index 82%
rename from core/src/main/java/com/raccoongang/core/data/model/room/MediaDb.kt
rename to core/src/main/java/org/openedx/core/data/model/room/MediaDb.kt
index 1a23c8d40..be5bf45b4 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/room/MediaDb.kt
+++ b/core/src/main/java/org/openedx/core/data/model/room/MediaDb.kt
@@ -1,7 +1,7 @@
-package com.raccoongang.core.data.model.room
+package org.openedx.core.data.model.room
import androidx.room.ColumnInfo
-import com.raccoongang.core.domain.model.*
+import org.openedx.core.domain.model.*
data class MediaDb(
@ColumnInfo("bannerImage")
@@ -21,7 +21,7 @@ data class MediaDb(
)
companion object {
- fun createFrom(media: com.raccoongang.core.data.model.Media?) =
+ fun createFrom(media: org.openedx.core.data.model.Media?) =
if (media == null) {
MediaDb()
} else {
@@ -48,7 +48,7 @@ data class ImageDb(
)
companion object {
- fun createFrom(image: com.raccoongang.core.data.model.Image?) =
+ fun createFrom(image: org.openedx.core.data.model.Image?) =
ImageDb(
large = image?.large ?: "",
raw = image?.raw ?: "",
@@ -65,7 +65,7 @@ data class CourseVideoDb(
fun mapToDomain() = CourseVideo(uri)
companion object {
- fun createFrom(courseVideo: com.raccoongang.core.data.model.CourseVideo?) =
+ fun createFrom(courseVideo: org.openedx.core.data.model.CourseVideo?) =
CourseVideoDb(uri = courseVideo?.uri ?: "")
}
}
@@ -79,7 +79,7 @@ data class CourseImageDb(
fun mapToDomain() = CourseImage(uri, name)
companion object {
- fun createFrom(courseImage: com.raccoongang.core.data.model.CourseImage?) =
+ fun createFrom(courseImage: org.openedx.core.data.model.CourseImage?) =
CourseImageDb(
uri = courseImage?.uri ?: "",
name = courseImage?.name ?: ""
@@ -97,7 +97,7 @@ data class BannerImageDb(
companion object {
- fun createFrom(bannerImage: com.raccoongang.core.data.model.BannerImage?) =
+ fun createFrom(bannerImage: org.openedx.core.data.model.BannerImage?) =
BannerImageDb(
uri = bannerImage?.uri ?: "",
uriAbsolute = bannerImage?.uriAbsolute ?: ""
diff --git a/core/src/main/java/com/raccoongang/core/data/model/room/discovery/EnrolledCourseEntity.kt b/core/src/main/java/org/openedx/core/data/model/room/discovery/EnrolledCourseEntity.kt
similarity index 95%
rename from core/src/main/java/com/raccoongang/core/data/model/room/discovery/EnrolledCourseEntity.kt
rename to core/src/main/java/org/openedx/core/data/model/room/discovery/EnrolledCourseEntity.kt
index a2ece4064..31eaf463f 100644
--- a/core/src/main/java/com/raccoongang/core/data/model/room/discovery/EnrolledCourseEntity.kt
+++ b/core/src/main/java/org/openedx/core/data/model/room/discovery/EnrolledCourseEntity.kt
@@ -1,12 +1,12 @@
-package com.raccoongang.core.data.model.room.discovery
+package org.openedx.core.data.model.room.discovery
import androidx.room.ColumnInfo
import androidx.room.Embedded
import androidx.room.Entity
import androidx.room.PrimaryKey
-import com.raccoongang.core.data.model.room.MediaDb
-import com.raccoongang.core.domain.model.*
-import com.raccoongang.core.utils.TimeUtils
+import org.openedx.core.data.model.room.MediaDb
+import org.openedx.core.domain.model.*
+import org.openedx.core.utils.TimeUtils
@Entity(tableName = "course_enrolled_table")
data class EnrolledCourseEntity(
diff --git a/core/src/main/java/com/raccoongang/core/data/storage/PreferencesManager.kt b/core/src/main/java/org/openedx/core/data/storage/PreferencesManager.kt
similarity index 88%
rename from core/src/main/java/com/raccoongang/core/data/storage/PreferencesManager.kt
rename to core/src/main/java/org/openedx/core/data/storage/PreferencesManager.kt
index 9bb150f87..3e6766ce0 100644
--- a/core/src/main/java/com/raccoongang/core/data/storage/PreferencesManager.kt
+++ b/core/src/main/java/org/openedx/core/data/storage/PreferencesManager.kt
@@ -1,16 +1,16 @@
-package com.raccoongang.core.data.storage
+package org.openedx.core.data.storage
import android.content.Context
import com.google.gson.Gson
-import com.raccoongang.core.domain.model.Account
-import com.raccoongang.core.domain.model.User
-import com.raccoongang.core.domain.model.VideoSettings
+import org.openedx.core.domain.model.Account
+import org.openedx.core.domain.model.User
+import org.openedx.core.domain.model.VideoSettings
class PreferencesManager(private val context: Context) {
private val sharedPreferences =
- context.getSharedPreferences("com.raccoongang.newedx", Context.MODE_PRIVATE)
+ context.getSharedPreferences("org.openedx.app", Context.MODE_PRIVATE)
private fun saveString(key: String, value: String) {
sharedPreferences.edit().apply {
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/Account.kt b/core/src/main/java/org/openedx/core/domain/model/Account.kt
similarity index 94%
rename from core/src/main/java/com/raccoongang/core/domain/model/Account.kt
rename to core/src/main/java/org/openedx/core/domain/model/Account.kt
index 944b88a80..947b0c9d2 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/Account.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/Account.kt
@@ -1,8 +1,8 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.AppDataConstants.USER_MIN_YEAR
+import org.openedx.core.AppDataConstants.USER_MIN_YEAR
import kotlinx.parcelize.Parcelize
import java.util.*
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/AnnouncementModel.kt b/core/src/main/java/org/openedx/core/domain/model/AnnouncementModel.kt
similarity index 65%
rename from core/src/main/java/com/raccoongang/core/domain/model/AnnouncementModel.kt
rename to core/src/main/java/org/openedx/core/domain/model/AnnouncementModel.kt
index 9368f7078..de29e23c7 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/AnnouncementModel.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/AnnouncementModel.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
data class AnnouncementModel(
val date: String,
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/Block.kt b/core/src/main/java/org/openedx/core/domain/model/Block.kt
similarity index 91%
rename from core/src/main/java/com/raccoongang/core/domain/model/Block.kt
rename to core/src/main/java/org/openedx/core/domain/model/Block.kt
index 501947f67..4e384ff5b 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/Block.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/Block.kt
@@ -1,12 +1,12 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
import android.webkit.URLUtil
-import com.raccoongang.core.AppDataConstants
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.module.db.DownloadModel
-import com.raccoongang.core.module.db.DownloadedState
-import com.raccoongang.core.module.db.FileType
-import com.raccoongang.core.utils.VideoUtil
+import org.openedx.core.AppDataConstants
+import org.openedx.core.BlockType
+import org.openedx.core.module.db.DownloadModel
+import org.openedx.core.module.db.DownloadedState
+import org.openedx.core.module.db.FileType
+import org.openedx.core.utils.VideoUtil
data class Block(
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/Certificate.kt b/core/src/main/java/org/openedx/core/domain/model/Certificate.kt
similarity index 84%
rename from core/src/main/java/com/raccoongang/core/domain/model/Certificate.kt
rename to core/src/main/java/org/openedx/core/domain/model/Certificate.kt
index 3c608e492..83430d697 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/Certificate.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/Certificate.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/Course.kt b/core/src/main/java/org/openedx/core/domain/model/Course.kt
similarity index 93%
rename from core/src/main/java/com/raccoongang/core/domain/model/Course.kt
rename to core/src/main/java/org/openedx/core/domain/model/Course.kt
index 3d41341f3..56de3282b 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/Course.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/Course.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
import java.util.Date
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/CourseComponentStatus.kt b/core/src/main/java/org/openedx/core/domain/model/CourseComponentStatus.kt
similarity index 63%
rename from core/src/main/java/com/raccoongang/core/domain/model/CourseComponentStatus.kt
rename to core/src/main/java/org/openedx/core/domain/model/CourseComponentStatus.kt
index 3dd0ba19e..00ed2a44b 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/CourseComponentStatus.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/CourseComponentStatus.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
data class CourseComponentStatus(
var lastVisitedBlockId: String,
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/CourseList.kt b/core/src/main/java/org/openedx/core/domain/model/CourseList.kt
similarity index 67%
rename from core/src/main/java/com/raccoongang/core/domain/model/CourseList.kt
rename to core/src/main/java/org/openedx/core/domain/model/CourseList.kt
index b423673a0..6c38ef924 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/CourseList.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/CourseList.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
data class CourseList(
val pagination: Pagination,
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/CourseSharingUtmParameters.kt b/core/src/main/java/org/openedx/core/domain/model/CourseSharingUtmParameters.kt
similarity index 81%
rename from core/src/main/java/com/raccoongang/core/domain/model/CourseSharingUtmParameters.kt
rename to core/src/main/java/org/openedx/core/domain/model/CourseSharingUtmParameters.kt
index 7b537cac1..f09f057db 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/CourseSharingUtmParameters.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/CourseSharingUtmParameters.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/CourseStructure.kt b/core/src/main/java/org/openedx/core/domain/model/CourseStructure.kt
similarity index 90%
rename from core/src/main/java/com/raccoongang/core/domain/model/CourseStructure.kt
rename to core/src/main/java/org/openedx/core/domain/model/CourseStructure.kt
index e6391dc09..8ac5b6e41 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/CourseStructure.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/CourseStructure.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
import java.util.*
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/CoursewareAccess.kt b/core/src/main/java/org/openedx/core/domain/model/CoursewareAccess.kt
similarity index 88%
rename from core/src/main/java/com/raccoongang/core/domain/model/CoursewareAccess.kt
rename to core/src/main/java/org/openedx/core/domain/model/CoursewareAccess.kt
index afe8b6b14..187c995b6 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/CoursewareAccess.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/CoursewareAccess.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/DashboardCourseList.kt b/core/src/main/java/org/openedx/core/domain/model/DashboardCourseList.kt
similarity index 71%
rename from core/src/main/java/com/raccoongang/core/domain/model/DashboardCourseList.kt
rename to core/src/main/java/org/openedx/core/domain/model/DashboardCourseList.kt
index ed3c2c38e..f5d7eb7c7 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/DashboardCourseList.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/DashboardCourseList.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
data class DashboardCourseList(
val pagination: Pagination,
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/EnrolledCourse.kt b/core/src/main/java/org/openedx/core/domain/model/EnrolledCourse.kt
similarity index 88%
rename from core/src/main/java/com/raccoongang/core/domain/model/EnrolledCourse.kt
rename to core/src/main/java/org/openedx/core/domain/model/EnrolledCourse.kt
index d0e6db1ff..8e339b3f6 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/EnrolledCourse.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/EnrolledCourse.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/EnrolledCourseData.kt b/core/src/main/java/org/openedx/core/domain/model/EnrolledCourseData.kt
similarity index 94%
rename from core/src/main/java/com/raccoongang/core/domain/model/EnrolledCourseData.kt
rename to core/src/main/java/org/openedx/core/domain/model/EnrolledCourseData.kt
index 84419805c..f5bb23d41 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/EnrolledCourseData.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/EnrolledCourseData.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/HandoutsModel.kt b/core/src/main/java/org/openedx/core/domain/model/HandoutsModel.kt
similarity index 57%
rename from core/src/main/java/com/raccoongang/core/domain/model/HandoutsModel.kt
rename to core/src/main/java/org/openedx/core/domain/model/HandoutsModel.kt
index 11e0ae08b..80a84d4b7 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/HandoutsModel.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/HandoutsModel.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
data class HandoutsModel(
val handoutsHtml: String
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/LanguageProficiency.kt b/core/src/main/java/org/openedx/core/domain/model/LanguageProficiency.kt
similarity index 84%
rename from core/src/main/java/com/raccoongang/core/domain/model/LanguageProficiency.kt
rename to core/src/main/java/org/openedx/core/domain/model/LanguageProficiency.kt
index 04f38e405..342b9d675 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/LanguageProficiency.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/LanguageProficiency.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/Media.kt b/core/src/main/java/org/openedx/core/domain/model/Media.kt
similarity index 93%
rename from core/src/main/java/com/raccoongang/core/domain/model/Media.kt
rename to core/src/main/java/org/openedx/core/domain/model/Media.kt
index 8a20307c0..16d6f66c3 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/Media.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/Media.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/Pagination.kt b/core/src/main/java/org/openedx/core/domain/model/Pagination.kt
similarity index 73%
rename from core/src/main/java/com/raccoongang/core/domain/model/Pagination.kt
rename to core/src/main/java/org/openedx/core/domain/model/Pagination.kt
index 31f2fe492..267eb6392 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/Pagination.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/Pagination.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
data class Pagination(
val count: Int,
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/ProfileImage.kt b/core/src/main/java/org/openedx/core/domain/model/ProfileImage.kt
similarity index 86%
rename from core/src/main/java/com/raccoongang/core/domain/model/ProfileImage.kt
rename to core/src/main/java/org/openedx/core/domain/model/ProfileImage.kt
index ea1c76f6e..ef07f5b33 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/ProfileImage.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/ProfileImage.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/RegistrationField.kt b/core/src/main/java/org/openedx/core/domain/model/RegistrationField.kt
similarity index 96%
rename from core/src/main/java/com/raccoongang/core/domain/model/RegistrationField.kt
rename to core/src/main/java/org/openedx/core/domain/model/RegistrationField.kt
index 245c1bd23..20b1af6eb 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/RegistrationField.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/RegistrationField.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
import android.os.Parcelable
import kotlinx.parcelize.Parcelize
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/StartType.kt b/core/src/main/java/org/openedx/core/domain/model/StartType.kt
similarity index 91%
rename from core/src/main/java/com/raccoongang/core/domain/model/StartType.kt
rename to core/src/main/java/org/openedx/core/domain/model/StartType.kt
index 6c3a51f07..38ace6fff 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/StartType.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/StartType.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
import com.google.gson.annotations.SerializedName
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/User.kt b/core/src/main/java/org/openedx/core/domain/model/User.kt
similarity index 72%
rename from core/src/main/java/com/raccoongang/core/domain/model/User.kt
rename to core/src/main/java/org/openedx/core/domain/model/User.kt
index 89d1a2847..a14d19951 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/User.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/User.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
data class User(
diff --git a/core/src/main/java/com/raccoongang/core/domain/model/VideoSettings.kt b/core/src/main/java/org/openedx/core/domain/model/VideoSettings.kt
similarity index 87%
rename from core/src/main/java/com/raccoongang/core/domain/model/VideoSettings.kt
rename to core/src/main/java/org/openedx/core/domain/model/VideoSettings.kt
index 8a7f8b578..9e2b84ddb 100644
--- a/core/src/main/java/com/raccoongang/core/domain/model/VideoSettings.kt
+++ b/core/src/main/java/org/openedx/core/domain/model/VideoSettings.kt
@@ -1,6 +1,6 @@
-package com.raccoongang.core.domain.model
+package org.openedx.core.domain.model
-import com.raccoongang.core.R
+import org.openedx.core.R
data class VideoSettings(
val wifiDownloadOnly: Boolean,
diff --git a/core/src/main/java/com/raccoongang/core/exception/NoCachedDataException.kt b/core/src/main/java/org/openedx/core/exception/NoCachedDataException.kt
similarity index 51%
rename from core/src/main/java/com/raccoongang/core/exception/NoCachedDataException.kt
rename to core/src/main/java/org/openedx/core/exception/NoCachedDataException.kt
index 7379700e1..d917c5c49 100644
--- a/core/src/main/java/com/raccoongang/core/exception/NoCachedDataException.kt
+++ b/core/src/main/java/org/openedx/core/exception/NoCachedDataException.kt
@@ -1,3 +1,3 @@
-package com.raccoongang.core.exception
+package org.openedx.core.exception
class NoCachedDataException : Exception()
\ No newline at end of file
diff --git a/core/src/main/java/com/raccoongang/core/extension/BundleExt.kt b/core/src/main/java/org/openedx/core/extension/BundleExt.kt
similarity index 96%
rename from core/src/main/java/com/raccoongang/core/extension/BundleExt.kt
rename to core/src/main/java/org/openedx/core/extension/BundleExt.kt
index 186c72c72..59c0b9f93 100644
--- a/core/src/main/java/com/raccoongang/core/extension/BundleExt.kt
+++ b/core/src/main/java/org/openedx/core/extension/BundleExt.kt
@@ -1,6 +1,6 @@
@file:Suppress("NOTHING_TO_INLINE")
-package com.raccoongang.core.extension
+package org.openedx.core.extension
import android.os.Build.VERSION.SDK_INT
import android.os.Bundle
diff --git a/core/src/main/java/com/raccoongang/core/extension/FlowExtension.kt b/core/src/main/java/org/openedx/core/extension/FlowExtension.kt
similarity index 93%
rename from core/src/main/java/com/raccoongang/core/extension/FlowExtension.kt
rename to core/src/main/java/org/openedx/core/extension/FlowExtension.kt
index c16555a13..e88aff9ac 100644
--- a/core/src/main/java/com/raccoongang/core/extension/FlowExtension.kt
+++ b/core/src/main/java/org/openedx/core/extension/FlowExtension.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.extension
+package org.openedx.core.extension
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
diff --git a/core/src/main/java/com/raccoongang/core/extension/FragmentExt.kt b/core/src/main/java/org/openedx/core/extension/FragmentExt.kt
similarity index 87%
rename from core/src/main/java/com/raccoongang/core/extension/FragmentExt.kt
rename to core/src/main/java/org/openedx/core/extension/FragmentExt.kt
index adc559577..5d340b55d 100644
--- a/core/src/main/java/com/raccoongang/core/extension/FragmentExt.kt
+++ b/core/src/main/java/org/openedx/core/extension/FragmentExt.kt
@@ -1,9 +1,9 @@
-package com.raccoongang.core.extension
+package org.openedx.core.extension
import androidx.fragment.app.Fragment
import androidx.window.layout.WindowMetricsCalculator
-import com.raccoongang.core.ui.WindowSize
-import com.raccoongang.core.ui.WindowType
+import org.openedx.core.ui.WindowSize
+import org.openedx.core.ui.WindowType
fun Fragment.computeWindowSizeClasses(): WindowSize {
val metrics = WindowMetricsCalculator.getOrCreate()
diff --git a/core/src/main/java/com/raccoongang/core/extension/GsonExt.kt b/core/src/main/java/org/openedx/core/extension/GsonExt.kt
similarity index 86%
rename from core/src/main/java/com/raccoongang/core/extension/GsonExt.kt
rename to core/src/main/java/org/openedx/core/extension/GsonExt.kt
index 4a32f7105..579a5ee6d 100644
--- a/core/src/main/java/com/raccoongang/core/extension/GsonExt.kt
+++ b/core/src/main/java/org/openedx/core/extension/GsonExt.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.extension
+package org.openedx.core.extension
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
diff --git a/core/src/main/java/com/raccoongang/core/extension/ImageUploaderExtension.kt b/core/src/main/java/org/openedx/core/extension/ImageUploaderExtension.kt
similarity index 92%
rename from core/src/main/java/com/raccoongang/core/extension/ImageUploaderExtension.kt
rename to core/src/main/java/org/openedx/core/extension/ImageUploaderExtension.kt
index 6d54dd025..a716544ca 100644
--- a/core/src/main/java/com/raccoongang/core/extension/ImageUploaderExtension.kt
+++ b/core/src/main/java/org/openedx/core/extension/ImageUploaderExtension.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.extension
+package org.openedx.core.extension
import android.content.ContentResolver
import android.net.Uri
diff --git a/core/src/main/java/org/openedx/core/extension/ListExt.kt b/core/src/main/java/org/openedx/core/extension/ListExt.kt
new file mode 100644
index 000000000..2831f917a
--- /dev/null
+++ b/core/src/main/java/org/openedx/core/extension/ListExt.kt
@@ -0,0 +1,25 @@
+package org.openedx.core.extension
+
+inline fun List.indexOfFirstFromIndex(startIndex: Int, predicate: (T) -> Boolean): Int {
+ var index = 0
+ for ((i, item) in this.withIndex()) {
+ if (i > startIndex) {
+ if (predicate(item))
+ return index
+ }
+ index++
+ }
+ return -1
+}
+
+fun ArrayList.clearAndAddAll(collection: Collection): ArrayList {
+ this.clear()
+ this.addAll(collection)
+ return this
+}
+
+fun MutableList.clearAndAddAll(collection: Collection): MutableList {
+ this.clear()
+ this.addAll(collection)
+ return this
+}
diff --git a/core/src/main/java/com/raccoongang/core/extension/StringExt.kt b/core/src/main/java/org/openedx/core/extension/StringExt.kt
similarity index 96%
rename from core/src/main/java/com/raccoongang/core/extension/StringExt.kt
rename to core/src/main/java/org/openedx/core/extension/StringExt.kt
index 6cd69752d..0d7281320 100644
--- a/core/src/main/java/com/raccoongang/core/extension/StringExt.kt
+++ b/core/src/main/java/org/openedx/core/extension/StringExt.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.extension
+package org.openedx.core.extension
import android.util.Patterns
import java.util.regex.Pattern
diff --git a/core/src/main/java/com/raccoongang/core/extension/TextConverter.kt b/core/src/main/java/org/openedx/core/extension/TextConverter.kt
similarity index 87%
rename from core/src/main/java/com/raccoongang/core/extension/TextConverter.kt
rename to core/src/main/java/org/openedx/core/extension/TextConverter.kt
index 5cdaff733..3c7f04676 100644
--- a/core/src/main/java/com/raccoongang/core/extension/TextConverter.kt
+++ b/core/src/main/java/org/openedx/core/extension/TextConverter.kt
@@ -1,8 +1,8 @@
-package com.raccoongang.core.extension
+package org.openedx.core.extension
import android.os.Parcelable
import android.util.Patterns
-import com.raccoongang.core.BuildConfig
+import org.openedx.core.BuildConfig
import kotlinx.parcelize.Parcelize
import org.jsoup.Jsoup
import org.jsoup.nodes.Document
@@ -22,8 +22,8 @@ object TextConverter {
} else {
link.attr("href")
}
- if (resultLink.isNotEmpty() && isLinkValid(BuildConfig.BASE_URL + resultLink)) {
- linksMap[link.text()] = BuildConfig.BASE_URL + resultLink
+ if (resultLink.isNotEmpty() && isLinkValid(org.openedx.core.BuildConfig.BASE_URL + resultLink)) {
+ linksMap[link.text()] = org.openedx.core.BuildConfig.BASE_URL + resultLink
}
}
return LinkedText(text, linksMap.toMap())
@@ -47,8 +47,8 @@ object TextConverter {
} else {
link.attr("href")
}
- if (resultLink.isNotEmpty() && isLinkValid(BuildConfig.BASE_URL + resultLink)) {
- linksMap[link.text()] = BuildConfig.BASE_URL + resultLink
+ if (resultLink.isNotEmpty() && isLinkValid(org.openedx.core.BuildConfig.BASE_URL + resultLink)) {
+ linksMap[link.text()] = org.openedx.core.BuildConfig.BASE_URL + resultLink
}
}
}
diff --git a/core/src/main/java/com/raccoongang/core/extension/ThrowableExt.kt b/core/src/main/java/org/openedx/core/extension/ThrowableExt.kt
similarity index 83%
rename from core/src/main/java/com/raccoongang/core/extension/ThrowableExt.kt
rename to core/src/main/java/org/openedx/core/extension/ThrowableExt.kt
index 79df71e30..511da670a 100644
--- a/core/src/main/java/com/raccoongang/core/extension/ThrowableExt.kt
+++ b/core/src/main/java/org/openedx/core/extension/ThrowableExt.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.extension
+package org.openedx.core.extension
import java.net.SocketTimeoutException
import java.net.UnknownHostException
diff --git a/core/src/main/java/com/raccoongang/core/extension/ViewExt.kt b/core/src/main/java/org/openedx/core/extension/ViewExt.kt
similarity index 96%
rename from core/src/main/java/com/raccoongang/core/extension/ViewExt.kt
rename to core/src/main/java/org/openedx/core/extension/ViewExt.kt
index 3b01d7f3b..4fb9bad85 100644
--- a/core/src/main/java/com/raccoongang/core/extension/ViewExt.kt
+++ b/core/src/main/java/org/openedx/core/extension/ViewExt.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.extension
+package org.openedx.core.extension
import android.content.Context
import android.util.DisplayMetrics
diff --git a/core/src/main/java/com/raccoongang/core/module/DownloadWorker.kt b/core/src/main/java/org/openedx/core/module/DownloadWorker.kt
similarity index 92%
rename from core/src/main/java/com/raccoongang/core/module/DownloadWorker.kt
rename to core/src/main/java/org/openedx/core/module/DownloadWorker.kt
index 42ab16d54..e7a63db38 100644
--- a/core/src/main/java/com/raccoongang/core/module/DownloadWorker.kt
+++ b/core/src/main/java/org/openedx/core/module/DownloadWorker.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.module
+package org.openedx.core.module
import android.app.NotificationChannel
import android.app.NotificationManager
@@ -9,13 +9,13 @@ import androidx.core.app.NotificationCompat
import androidx.work.CoroutineWorker
import androidx.work.ForegroundInfo
import androidx.work.WorkerParameters
-import com.raccoongang.core.R
-import com.raccoongang.core.module.db.DownloadDao
-import com.raccoongang.core.module.db.DownloadModel
-import com.raccoongang.core.module.db.DownloadModelEntity
-import com.raccoongang.core.module.db.DownloadedState
-import com.raccoongang.core.module.download.CurrentProgress
-import com.raccoongang.core.module.download.FileDownloader
+import org.openedx.core.R
+import org.openedx.core.module.db.DownloadDao
+import org.openedx.core.module.db.DownloadModel
+import org.openedx.core.module.db.DownloadModelEntity
+import org.openedx.core.module.db.DownloadedState
+import org.openedx.core.module.download.CurrentProgress
+import org.openedx.core.module.download.FileDownloader
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.first
import org.koin.java.KoinJavaComponent.inject
diff --git a/core/src/main/java/com/raccoongang/core/module/DownloadWorkerController.kt b/core/src/main/java/org/openedx/core/module/DownloadWorkerController.kt
similarity index 92%
rename from core/src/main/java/com/raccoongang/core/module/DownloadWorkerController.kt
rename to core/src/main/java/org/openedx/core/module/DownloadWorkerController.kt
index d92b8c32c..54bdd6dde 100644
--- a/core/src/main/java/com/raccoongang/core/module/DownloadWorkerController.kt
+++ b/core/src/main/java/org/openedx/core/module/DownloadWorkerController.kt
@@ -1,15 +1,15 @@
-package com.raccoongang.core.module
+package org.openedx.core.module
import android.content.Context
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkInfo
import androidx.work.WorkManager
import com.google.common.util.concurrent.ListenableFuture
-import com.raccoongang.core.module.db.DownloadDao
-import com.raccoongang.core.module.db.DownloadModel
-import com.raccoongang.core.module.db.DownloadModelEntity
-import com.raccoongang.core.module.db.DownloadedState
-import com.raccoongang.core.module.download.FileDownloader
+import org.openedx.core.module.db.DownloadDao
+import org.openedx.core.module.db.DownloadModel
+import org.openedx.core.module.db.DownloadModelEntity
+import org.openedx.core.module.db.DownloadedState
+import org.openedx.core.module.download.FileDownloader
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
diff --git a/core/src/main/java/com/raccoongang/core/module/TranscriptManager.kt b/core/src/main/java/org/openedx/core/module/TranscriptManager.kt
similarity index 96%
rename from core/src/main/java/com/raccoongang/core/module/TranscriptManager.kt
rename to core/src/main/java/org/openedx/core/module/TranscriptManager.kt
index 8b9621782..f57f1a948 100644
--- a/core/src/main/java/com/raccoongang/core/module/TranscriptManager.kt
+++ b/core/src/main/java/org/openedx/core/module/TranscriptManager.kt
@@ -1,8 +1,8 @@
-package com.raccoongang.core.module
+package org.openedx.core.module
import android.content.Context
-import com.raccoongang.core.module.download.AbstractDownloader
-import com.raccoongang.core.utils.*
+import org.openedx.core.module.download.AbstractDownloader
+import org.openedx.core.utils.*
import okhttp3.OkHttpClient
import subtitleFile.FormatSRT
import subtitleFile.TimedTextObject
diff --git a/core/src/main/java/com/raccoongang/core/module/db/DownloadDao.kt b/core/src/main/java/org/openedx/core/module/db/DownloadDao.kt
similarity index 93%
rename from core/src/main/java/com/raccoongang/core/module/db/DownloadDao.kt
rename to core/src/main/java/org/openedx/core/module/db/DownloadDao.kt
index 5e232a41a..d3e9d84b7 100644
--- a/core/src/main/java/com/raccoongang/core/module/db/DownloadDao.kt
+++ b/core/src/main/java/org/openedx/core/module/db/DownloadDao.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.module.db
+package org.openedx.core.module.db
import androidx.room.*
import kotlinx.coroutines.flow.Flow
diff --git a/core/src/main/java/com/raccoongang/core/module/db/DownloadModel.kt b/core/src/main/java/org/openedx/core/module/db/DownloadModel.kt
similarity index 93%
rename from core/src/main/java/com/raccoongang/core/module/db/DownloadModel.kt
rename to core/src/main/java/org/openedx/core/module/db/DownloadModel.kt
index 263491eb9..86bc31540 100644
--- a/core/src/main/java/com/raccoongang/core/module/db/DownloadModel.kt
+++ b/core/src/main/java/org/openedx/core/module/db/DownloadModel.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.module.db
+package org.openedx.core.module.db
data class DownloadModel(
val id: String,
diff --git a/core/src/main/java/com/raccoongang/core/module/db/DownloadModelEntity.kt b/core/src/main/java/org/openedx/core/module/db/DownloadModelEntity.kt
similarity index 96%
rename from core/src/main/java/com/raccoongang/core/module/db/DownloadModelEntity.kt
rename to core/src/main/java/org/openedx/core/module/db/DownloadModelEntity.kt
index 7b6668300..cd12a4eea 100644
--- a/core/src/main/java/com/raccoongang/core/module/db/DownloadModelEntity.kt
+++ b/core/src/main/java/org/openedx/core/module/db/DownloadModelEntity.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.module.db
+package org.openedx.core.module.db
import androidx.room.ColumnInfo
import androidx.room.Entity
diff --git a/core/src/main/java/com/raccoongang/core/module/download/AbstractDownloader.kt b/core/src/main/java/org/openedx/core/module/download/AbstractDownloader.kt
similarity index 79%
rename from core/src/main/java/com/raccoongang/core/module/download/AbstractDownloader.kt
rename to core/src/main/java/org/openedx/core/module/download/AbstractDownloader.kt
index 54a614fa2..08f6b2704 100644
--- a/core/src/main/java/com/raccoongang/core/module/download/AbstractDownloader.kt
+++ b/core/src/main/java/org/openedx/core/module/download/AbstractDownloader.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.module.download
+package org.openedx.core.module.download
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
@@ -14,7 +14,7 @@ abstract class AbstractDownloader {
private val downloadApi: DownloadApi by lazy {
Retrofit.Builder()
- .baseUrl(com.raccoongang.core.BuildConfig.BASE_URL)
+ .baseUrl(org.openedx.core.BuildConfig.BASE_URL)
.client(client)
.build()
.create(DownloadApi::class.java)
@@ -25,6 +25,7 @@ abstract class AbstractDownloader {
var isCanceled = false
private var input: InputStream? = null
+ private var fos: FileOutputStream? = null
open suspend fun download(
url: String,
@@ -41,14 +42,14 @@ abstract class AbstractDownloader {
file.createNewFile()
input = response.byteStream()
currentDownloadingFilePath = path
- val fos = FileOutputStream(file)
+ fos = FileOutputStream(file)
fos.use { output ->
val buffer = ByteArray(4 * 1024)
var read: Int
while (input!!.read(buffer).also { read = it } != -1) {
- output.write(buffer, 0, read)
+ output?.write(buffer, 0, read)
}
- output.flush()
+ output?.flush()
}
true
} else {
@@ -58,6 +59,7 @@ abstract class AbstractDownloader {
e.printStackTrace()
false
} finally {
+ fos?.close()
input?.close()
}
}
@@ -66,7 +68,12 @@ abstract class AbstractDownloader {
suspend fun cancelDownloading() {
isCanceled = true
withContext(Dispatchers.IO) {
- input?.close()
+ try {
+ fos?.close()
+ input?.close()
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
}
currentDownloadingFilePath?.let {
val file = File(it)
diff --git a/core/src/main/java/com/raccoongang/core/module/download/BaseDownloadViewModel.kt b/core/src/main/java/org/openedx/core/module/download/BaseDownloadViewModel.kt
similarity index 93%
rename from core/src/main/java/com/raccoongang/core/module/download/BaseDownloadViewModel.kt
rename to core/src/main/java/org/openedx/core/module/download/BaseDownloadViewModel.kt
index 0e998284c..a2471161a 100644
--- a/core/src/main/java/com/raccoongang/core/module/download/BaseDownloadViewModel.kt
+++ b/core/src/main/java/org/openedx/core/module/download/BaseDownloadViewModel.kt
@@ -1,16 +1,16 @@
-package com.raccoongang.core.module.download
+package org.openedx.core.module.download
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.domain.model.Block
-import com.raccoongang.core.module.DownloadWorkerController
-import com.raccoongang.core.module.db.DownloadDao
-import com.raccoongang.core.module.db.DownloadModel
-import com.raccoongang.core.module.db.DownloadedState
-import com.raccoongang.core.utils.Sha1Util
+import org.openedx.core.BaseViewModel
+import org.openedx.core.BlockType
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.domain.model.Block
+import org.openedx.core.module.DownloadWorkerController
+import org.openedx.core.module.db.DownloadDao
+import org.openedx.core.module.db.DownloadModel
+import org.openedx.core.module.db.DownloadedState
+import org.openedx.core.utils.Sha1Util
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.first
diff --git a/core/src/main/java/com/raccoongang/core/module/download/DownloadType.kt b/core/src/main/java/org/openedx/core/module/download/DownloadType.kt
similarity index 53%
rename from core/src/main/java/com/raccoongang/core/module/download/DownloadType.kt
rename to core/src/main/java/org/openedx/core/module/download/DownloadType.kt
index 0ed0b9a1f..1810ee23d 100644
--- a/core/src/main/java/com/raccoongang/core/module/download/DownloadType.kt
+++ b/core/src/main/java/org/openedx/core/module/download/DownloadType.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.module.download
+package org.openedx.core.module.download
enum class DownloadType {
VIDEO, SCORM, HTML
diff --git a/core/src/main/java/com/raccoongang/core/module/download/FileDownloader.kt b/core/src/main/java/org/openedx/core/module/download/FileDownloader.kt
similarity index 97%
rename from core/src/main/java/com/raccoongang/core/module/download/FileDownloader.kt
rename to core/src/main/java/org/openedx/core/module/download/FileDownloader.kt
index d77fe3fa0..018c4c14f 100644
--- a/core/src/main/java/com/raccoongang/core/module/download/FileDownloader.kt
+++ b/core/src/main/java/org/openedx/core/module/download/FileDownloader.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.module.download
+package org.openedx.core.module.download
import android.util.Log
import okhttp3.Interceptor
diff --git a/core/src/main/java/com/raccoongang/core/module/download/ProgressListener.kt b/core/src/main/java/org/openedx/core/module/download/ProgressListener.kt
similarity index 96%
rename from core/src/main/java/com/raccoongang/core/module/download/ProgressListener.kt
rename to core/src/main/java/org/openedx/core/module/download/ProgressListener.kt
index 8e7b4faf5..0acf4f320 100644
--- a/core/src/main/java/com/raccoongang/core/module/download/ProgressListener.kt
+++ b/core/src/main/java/org/openedx/core/module/download/ProgressListener.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.module.download
+package org.openedx.core.module.download
import okhttp3.MediaType
import okhttp3.ResponseBody
diff --git a/core/src/main/java/com/raccoongang/core/presentation/course/CourseViewMode.kt b/core/src/main/java/org/openedx/core/presentation/course/CourseViewMode.kt
similarity index 51%
rename from core/src/main/java/com/raccoongang/core/presentation/course/CourseViewMode.kt
rename to core/src/main/java/org/openedx/core/presentation/course/CourseViewMode.kt
index 53c28c11e..c2aaf97cb 100644
--- a/core/src/main/java/com/raccoongang/core/presentation/course/CourseViewMode.kt
+++ b/core/src/main/java/org/openedx/core/presentation/course/CourseViewMode.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.presentation.course
+package org.openedx.core.presentation.course
enum class CourseViewMode {
FULL,
diff --git a/core/src/main/java/org/openedx/core/presentation/dialog/SelectBottomDialogFragment.kt b/core/src/main/java/org/openedx/core/presentation/dialog/SelectBottomDialogFragment.kt
new file mode 100644
index 000000000..8d15d6973
--- /dev/null
+++ b/core/src/main/java/org/openedx/core/presentation/dialog/SelectBottomDialogFragment.kt
@@ -0,0 +1,134 @@
+package org.openedx.core.presentation.dialog
+
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.widthIn
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Surface
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.platform.ComposeView
+import androidx.compose.ui.platform.ViewCompositionStrategy
+import androidx.compose.ui.text.input.TextFieldValue
+import androidx.compose.ui.unit.dp
+import androidx.fragment.app.DialogFragment
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.bottomsheet.BottomSheetDialog
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment
+import org.openedx.core.R
+import org.openedx.core.domain.model.RegistrationField
+import org.openedx.core.extension.parcelableArrayList
+import org.openedx.core.ui.SheetContent
+import org.openedx.core.ui.rememberWindowSize
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.koin.androidx.viewmodel.ext.android.viewModel
+import org.openedx.core.ui.isImeVisibleState
+import org.openedx.core.ui.noRippleClickable
+import org.openedx.core.ui.theme.appShapes
+
+class SelectBottomDialogFragment : BottomSheetDialogFragment() {
+
+ private val viewModel by viewModel()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ viewModel.values = requireArguments().parcelableArrayList(ARG_LIST_VALUES)!!
+ setStyle(DialogFragment.STYLE_NORMAL, R.style.BottomSheetDialog)
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?,
+ ) = ComposeView(requireContext()).apply {
+ if (dialog != null && dialog!!.window != null) {
+ dialog!!.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
+ (dialog as? BottomSheetDialog)?.behavior?.apply {
+ state = BottomSheetBehavior.STATE_EXPANDED
+ skipCollapsed = true
+ }
+ }
+ setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
+ setContent {
+ OpenEdXTheme {
+ val listState = rememberLazyListState()
+
+ var searchValue by rememberSaveable(stateSaver = TextFieldValue.Saver) {
+ mutableStateOf(TextFieldValue())
+ }
+
+ val isImeVisible by isImeVisibleState()
+
+ Surface(
+ modifier = Modifier,
+ color = androidx.compose.ui.graphics.Color.Transparent
+ ) {
+ Box(
+ modifier = Modifier
+ .fillMaxSize()
+ .noRippleClickable {
+ dismiss()
+ },
+ contentAlignment = Alignment.BottomCenter
+ ) {
+ Box(
+ modifier = Modifier
+ .widthIn(max = 640.dp)
+ .fillMaxWidth()
+ .background(
+ color = MaterialTheme.appColors.background,
+ shape = MaterialTheme.appShapes.screenBackgroundShape
+ )
+ .clip(MaterialTheme.appShapes.screenBackgroundShape)
+ .padding(bottom = if (isImeVisible) 120.dp else 0.dp)
+ .noRippleClickable { }
+ ) {
+ SheetContent(
+ searchValue = searchValue,
+ expandedList = viewModel.values,
+ onItemClick = { item ->
+ viewModel.sendCourseEventChanged(item.value)
+ dismiss()
+ },
+ listState = listState,
+ searchValueChanged = {
+ searchValue = TextFieldValue(it)
+ }
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+
+ companion object {
+ private const val ARG_LIST_VALUES = "argListValues"
+
+ fun newInstance(
+ values: List,
+ ): SelectBottomDialogFragment {
+ val dialog = SelectBottomDialogFragment()
+ dialog.arguments = Bundle().apply {
+ putParcelableArrayList(ARG_LIST_VALUES, ArrayList(values))
+ }
+ return dialog
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/raccoongang/core/presentation/dialog/SelectDialogViewModel.kt b/core/src/main/java/org/openedx/core/presentation/dialog/SelectDialogViewModel.kt
similarity index 58%
rename from core/src/main/java/com/raccoongang/core/presentation/dialog/SelectDialogViewModel.kt
rename to core/src/main/java/org/openedx/core/presentation/dialog/SelectDialogViewModel.kt
index 07c2050f5..07a2f1b74 100644
--- a/core/src/main/java/com/raccoongang/core/presentation/dialog/SelectDialogViewModel.kt
+++ b/core/src/main/java/org/openedx/core/presentation/dialog/SelectDialogViewModel.kt
@@ -1,10 +1,10 @@
-package com.raccoongang.core.presentation.dialog
+package org.openedx.core.presentation.dialog
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.domain.model.RegistrationField
-import com.raccoongang.core.system.notifier.CourseNotifier
-import com.raccoongang.core.system.notifier.CourseSubtitleLanguageChanged
+import org.openedx.core.BaseViewModel
+import org.openedx.core.domain.model.RegistrationField
+import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.core.system.notifier.CourseSubtitleLanguageChanged
import kotlinx.coroutines.launch
class SelectDialogViewModel(
diff --git a/core/src/main/java/com/raccoongang/core/presentation/global/AppDataHolder.kt b/core/src/main/java/org/openedx/core/presentation/global/AppDataHolder.kt
similarity index 67%
rename from core/src/main/java/com/raccoongang/core/presentation/global/AppDataHolder.kt
rename to core/src/main/java/org/openedx/core/presentation/global/AppDataHolder.kt
index 4a82a928f..d10024135 100644
--- a/core/src/main/java/com/raccoongang/core/presentation/global/AppDataHolder.kt
+++ b/core/src/main/java/org/openedx/core/presentation/global/AppDataHolder.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.presentation.global
+package org.openedx.core.presentation.global
interface AppDataHolder {
val appData: AppData
diff --git a/core/src/main/java/com/raccoongang/core/presentation/global/FragmentViewBindingDelegate.kt b/core/src/main/java/org/openedx/core/presentation/global/FragmentViewBindingDelegate.kt
similarity index 97%
rename from core/src/main/java/com/raccoongang/core/presentation/global/FragmentViewBindingDelegate.kt
rename to core/src/main/java/org/openedx/core/presentation/global/FragmentViewBindingDelegate.kt
index 4cac852dc..35163150c 100644
--- a/core/src/main/java/com/raccoongang/core/presentation/global/FragmentViewBindingDelegate.kt
+++ b/core/src/main/java/org/openedx/core/presentation/global/FragmentViewBindingDelegate.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.presentation.global
+package org.openedx.core.presentation.global
import android.view.LayoutInflater
import android.view.View
diff --git a/core/src/main/java/com/raccoongang/core/presentation/global/InsetHolder.kt b/core/src/main/java/org/openedx/core/presentation/global/InsetHolder.kt
similarity index 60%
rename from core/src/main/java/com/raccoongang/core/presentation/global/InsetHolder.kt
rename to core/src/main/java/org/openedx/core/presentation/global/InsetHolder.kt
index fabbfbc2f..8dd5551de 100644
--- a/core/src/main/java/com/raccoongang/core/presentation/global/InsetHolder.kt
+++ b/core/src/main/java/org/openedx/core/presentation/global/InsetHolder.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.presentation.global
+package org.openedx.core.presentation.global
interface InsetHolder {
diff --git a/core/src/main/java/org/openedx/core/presentation/global/WindowSizeHolder.kt b/core/src/main/java/org/openedx/core/presentation/global/WindowSizeHolder.kt
new file mode 100644
index 000000000..463f27ef2
--- /dev/null
+++ b/core/src/main/java/org/openedx/core/presentation/global/WindowSizeHolder.kt
@@ -0,0 +1,7 @@
+package org.openedx.core.presentation.global
+
+import org.openedx.core.ui.WindowSize
+
+interface WindowSizeHolder {
+ val windowSize: WindowSize
+}
\ No newline at end of file
diff --git a/core/src/main/java/com/raccoongang/core/system/AppCookieManager.kt b/core/src/main/java/org/openedx/core/system/AppCookieManager.kt
similarity index 81%
rename from core/src/main/java/com/raccoongang/core/system/AppCookieManager.kt
rename to core/src/main/java/org/openedx/core/system/AppCookieManager.kt
index 066f9cf94..40f4ec015 100644
--- a/core/src/main/java/com/raccoongang/core/system/AppCookieManager.kt
+++ b/core/src/main/java/org/openedx/core/system/AppCookieManager.kt
@@ -1,8 +1,8 @@
-package com.raccoongang.core.system
+package org.openedx.core.system
import android.webkit.CookieManager
-import com.raccoongang.core.BuildConfig
-import com.raccoongang.core.data.api.CookiesApi
+import org.openedx.core.BuildConfig
+import org.openedx.core.data.api.CookiesApi
import okhttp3.Cookie
import okhttp3.RequestBody
import retrofit2.Response
@@ -25,7 +25,7 @@ class AppCookieManager(private val api: CookiesApi) {
clearWebViewCookie()
val cookieManager = CookieManager.getInstance()
for (cookie in Cookie.parseAll(response!!.raw().request.url, response!!.headers())) {
- cookieManager.setCookie(BuildConfig.BASE_URL,cookie.toString())
+ cookieManager.setCookie(org.openedx.core.BuildConfig.BASE_URL,cookie.toString())
}
authSessionCookieExpiration = System.currentTimeMillis() + FRESHNESS_INTERVAL
} catch (e: Exception) {
@@ -46,7 +46,7 @@ class AppCookieManager(private val api: CookiesApi) {
}
fun setMobileCookie() {
- CookieManager.getInstance().setCookie(BuildConfig.BASE_URL, REV_934_COOKIE)
+ CookieManager.getInstance().setCookie(org.openedx.core.BuildConfig.BASE_URL, REV_934_COOKIE)
}
}
\ No newline at end of file
diff --git a/core/src/main/java/com/raccoongang/core/system/EdxError.kt b/core/src/main/java/org/openedx/core/system/EdxError.kt
similarity index 88%
rename from core/src/main/java/com/raccoongang/core/system/EdxError.kt
rename to core/src/main/java/org/openedx/core/system/EdxError.kt
index c062fef3a..f9ea93d56 100644
--- a/core/src/main/java/com/raccoongang/core/system/EdxError.kt
+++ b/core/src/main/java/org/openedx/core/system/EdxError.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.system
+package org.openedx.core.system
import java.io.IOException
diff --git a/core/src/main/java/com/raccoongang/core/system/ResourceManager.kt b/core/src/main/java/org/openedx/core/system/ResourceManager.kt
similarity index 97%
rename from core/src/main/java/com/raccoongang/core/system/ResourceManager.kt
rename to core/src/main/java/org/openedx/core/system/ResourceManager.kt
index 221ac32b0..541eae56f 100644
--- a/core/src/main/java/com/raccoongang/core/system/ResourceManager.kt
+++ b/core/src/main/java/org/openedx/core/system/ResourceManager.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.system
+package org.openedx.core.system
import android.content.Context
import android.graphics.Typeface
diff --git a/core/src/main/java/com/raccoongang/core/system/connection/NetworkConnection.kt b/core/src/main/java/org/openedx/core/system/connection/NetworkConnection.kt
similarity index 96%
rename from core/src/main/java/com/raccoongang/core/system/connection/NetworkConnection.kt
rename to core/src/main/java/org/openedx/core/system/connection/NetworkConnection.kt
index dae16cf0a..570ce2c71 100644
--- a/core/src/main/java/com/raccoongang/core/system/connection/NetworkConnection.kt
+++ b/core/src/main/java/org/openedx/core/system/connection/NetworkConnection.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.system.connection
+package org.openedx.core.system.connection
import android.content.Context
import android.net.ConnectivityManager
diff --git a/core/src/main/java/org/openedx/core/system/notifier/CourseDashboardUpdate.kt b/core/src/main/java/org/openedx/core/system/notifier/CourseDashboardUpdate.kt
new file mode 100644
index 000000000..289b9b8c5
--- /dev/null
+++ b/core/src/main/java/org/openedx/core/system/notifier/CourseDashboardUpdate.kt
@@ -0,0 +1,3 @@
+package org.openedx.core.system.notifier
+
+class CourseDashboardUpdate : CourseEvent
\ No newline at end of file
diff --git a/core/src/main/java/org/openedx/core/system/notifier/CourseEvent.kt b/core/src/main/java/org/openedx/core/system/notifier/CourseEvent.kt
new file mode 100644
index 000000000..a79fe7e70
--- /dev/null
+++ b/core/src/main/java/org/openedx/core/system/notifier/CourseEvent.kt
@@ -0,0 +1,3 @@
+package org.openedx.core.system.notifier
+
+interface CourseEvent
\ No newline at end of file
diff --git a/core/src/main/java/com/raccoongang/core/system/notifier/CourseNotifier.kt b/core/src/main/java/org/openedx/core/system/notifier/CourseNotifier.kt
similarity index 84%
rename from core/src/main/java/com/raccoongang/core/system/notifier/CourseNotifier.kt
rename to core/src/main/java/org/openedx/core/system/notifier/CourseNotifier.kt
index a2a002672..93b00e722 100644
--- a/core/src/main/java/com/raccoongang/core/system/notifier/CourseNotifier.kt
+++ b/core/src/main/java/org/openedx/core/system/notifier/CourseNotifier.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.system.notifier
+package org.openedx.core.system.notifier
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
@@ -13,7 +13,7 @@ class CourseNotifier {
suspend fun send(event: CourseVideoPositionChanged) = channel.emit(event)
suspend fun send(event: CourseStructureUpdated) = channel.emit(event)
suspend fun send(event: CourseDashboardUpdate) = channel.emit(event)
- suspend fun send(event: CoursePauseVideo) = channel.emit(event)
suspend fun send(event: CourseSubtitleLanguageChanged) = channel.emit(event)
+ suspend fun send(event: CourseSectionChanged) = channel.emit(event)
}
\ No newline at end of file
diff --git a/core/src/main/java/org/openedx/core/system/notifier/CourseSectionChanged.kt b/core/src/main/java/org/openedx/core/system/notifier/CourseSectionChanged.kt
new file mode 100644
index 000000000..2e6034920
--- /dev/null
+++ b/core/src/main/java/org/openedx/core/system/notifier/CourseSectionChanged.kt
@@ -0,0 +1,5 @@
+package org.openedx.core.system.notifier
+
+data class CourseSectionChanged(
+ val blockId: String
+) : CourseEvent
diff --git a/core/src/main/java/com/raccoongang/core/system/notifier/CourseStructureUpdated.kt b/core/src/main/java/org/openedx/core/system/notifier/CourseStructureUpdated.kt
similarity index 70%
rename from core/src/main/java/com/raccoongang/core/system/notifier/CourseStructureUpdated.kt
rename to core/src/main/java/org/openedx/core/system/notifier/CourseStructureUpdated.kt
index f3bb09450..e89956d0e 100644
--- a/core/src/main/java/com/raccoongang/core/system/notifier/CourseStructureUpdated.kt
+++ b/core/src/main/java/org/openedx/core/system/notifier/CourseStructureUpdated.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.system.notifier
+package org.openedx.core.system.notifier
class CourseStructureUpdated(
val courseId: String,
diff --git a/core/src/main/java/com/raccoongang/core/system/notifier/CourseSubtitleLanguageChanged.kt b/core/src/main/java/org/openedx/core/system/notifier/CourseSubtitleLanguageChanged.kt
similarity index 64%
rename from core/src/main/java/com/raccoongang/core/system/notifier/CourseSubtitleLanguageChanged.kt
rename to core/src/main/java/org/openedx/core/system/notifier/CourseSubtitleLanguageChanged.kt
index 43a3bc243..1a1d1fa0c 100644
--- a/core/src/main/java/com/raccoongang/core/system/notifier/CourseSubtitleLanguageChanged.kt
+++ b/core/src/main/java/org/openedx/core/system/notifier/CourseSubtitleLanguageChanged.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.system.notifier
+package org.openedx.core.system.notifier
data class CourseSubtitleLanguageChanged(
val value: String
diff --git a/core/src/main/java/com/raccoongang/core/system/notifier/CourseVideoPositionChanged.kt b/core/src/main/java/org/openedx/core/system/notifier/CourseVideoPositionChanged.kt
similarity index 70%
rename from core/src/main/java/com/raccoongang/core/system/notifier/CourseVideoPositionChanged.kt
rename to core/src/main/java/org/openedx/core/system/notifier/CourseVideoPositionChanged.kt
index 0702aa72b..a06a6b7e2 100644
--- a/core/src/main/java/com/raccoongang/core/system/notifier/CourseVideoPositionChanged.kt
+++ b/core/src/main/java/org/openedx/core/system/notifier/CourseVideoPositionChanged.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.system.notifier
+package org.openedx.core.system.notifier
data class CourseVideoPositionChanged(
val videoUrl: String,
diff --git a/core/src/main/java/com/raccoongang/core/ui/ComposeCommon.kt b/core/src/main/java/org/openedx/core/ui/ComposeCommon.kt
similarity index 83%
rename from core/src/main/java/com/raccoongang/core/ui/ComposeCommon.kt
rename to core/src/main/java/org/openedx/core/ui/ComposeCommon.kt
index a058d7b2b..af884aef7 100644
--- a/core/src/main/java/com/raccoongang/core/ui/ComposeCommon.kt
+++ b/core/src/main/java/org/openedx/core/ui/ComposeCommon.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.ui
+package org.openedx.core.ui
import android.os.Build.VERSION.SDK_INT
import android.widget.Toast
@@ -49,15 +49,14 @@ import coil.compose.AsyncImage
import coil.decode.GifDecoder
import coil.decode.ImageDecoderDecoder
import coil.request.ImageRequest
-import com.raccoongang.core.BuildConfig
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.Course
-import com.raccoongang.core.domain.model.RegistrationField
-import com.raccoongang.core.extension.LinkedImageText
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appShapes
-import com.raccoongang.core.ui.theme.appTypography
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.Course
+import org.openedx.core.domain.model.RegistrationField
+import org.openedx.core.extension.LinkedImageText
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appShapes
+import org.openedx.core.ui.theme.appTypography
@Composable
fun StaticSearchBar(
@@ -190,6 +189,91 @@ fun SearchBar(
)
}
+@OptIn(ExperimentalComposeUiApi::class)
+@Composable
+fun SearchBarStateless(
+ modifier: Modifier,
+ searchValue: String,
+ requestFocus: Boolean = false,
+ label: String = stringResource(id = R.string.core_search),
+ keyboardActions: () -> Unit,
+ onValueChanged: (String) -> Unit = {},
+ onClearValue: () -> Unit,
+) {
+ val keyboardController = LocalSoftwareKeyboardController.current
+ val focusRequester = remember { FocusRequester() }
+ LaunchedEffect(key1 = Unit) {
+ if (requestFocus) {
+ focusRequester.requestFocus()
+ keyboardController?.show()
+ }
+ }
+ var isFocused by rememberSaveable {
+ mutableStateOf(false)
+ }
+ OutlinedTextField(
+ modifier = Modifier
+ .focusRequester(focusRequester)
+ .onFocusChanged {
+ isFocused = it.hasFocus
+ }
+ .clip(MaterialTheme.appShapes.textFieldShape)
+ .then(modifier),
+ shape = MaterialTheme.appShapes.textFieldShape,
+ value = searchValue,
+ onValueChange = {
+ if (it != searchValue) {
+ onValueChanged(it)
+ }
+ },
+ colors = TextFieldDefaults.outlinedTextFieldColors(
+ textColor = MaterialTheme.appColors.textPrimary,
+ backgroundColor = if (isFocused) MaterialTheme.appColors.background else MaterialTheme.appColors.textFieldBackground,
+ focusedBorderColor = MaterialTheme.appColors.primary,
+ unfocusedBorderColor = MaterialTheme.appColors.textFieldBorder,
+ cursorColor = MaterialTheme.appColors.primary,
+ leadingIconColor = MaterialTheme.appColors.textPrimary
+ ),
+ placeholder = {
+ Text(
+ modifier = Modifier.fillMaxWidth(),
+ text = label,
+ color = MaterialTheme.appColors.textSecondary,
+ style = MaterialTheme.appTypography.bodyMedium
+ )
+ },
+ leadingIcon = {
+ Icon(
+ modifier = Modifier.padding(start = 16.dp),
+ imageVector = Icons.Filled.Search,
+ contentDescription = null,
+ tint = if (isFocused) MaterialTheme.appColors.primary else MaterialTheme.appColors.onSurface
+ )
+ },
+ trailingIcon = {
+ if (searchValue.isNotEmpty()) {
+ IconButton(onClick = {
+ onClearValue()
+ }) {
+ Icon(
+ modifier = Modifier.padding(end = 16.dp),
+ imageVector = Icons.Filled.Close,
+ contentDescription = null,
+ tint = MaterialTheme.appColors.onSurface
+ )
+ }
+ }
+ },
+ keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
+ keyboardActions = KeyboardActions {
+ keyboardController?.hide()
+ keyboardActions()
+ },
+ textStyle = MaterialTheme.appTypography.bodyMedium,
+ maxLines = 1
+ )
+}
+
@Composable
@NonRestartableComposable
fun HandleUIMessage(
@@ -205,10 +289,12 @@ fun HandleUIMessage(
duration = uiMessage.duration
)
}
+
is UIMessage.ToastMessage -> {
val message = uiMessage.message
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
+
else -> {}
}
}
@@ -296,7 +382,7 @@ fun HyperlinkImageText(
val fullText = imageText.text
val hyperLinks = imageText.links
val annotatedString = buildAnnotatedString {
- if(title.isNotEmpty()) {
+ if (title.isNotEmpty()) {
append(title)
append("\n\n")
}
@@ -408,13 +494,17 @@ fun HyperlinkImageText(
@Composable
fun SheetContent(
+ searchValue: TextFieldValue,
+ title: String = stringResource(id = R.string.core_select_value),
expandedList: List,
onItemClick: (RegistrationField.Option) -> Unit,
listState: LazyListState,
+ searchValueChanged: (String) -> Unit
) {
+ val focusManager = LocalFocusManager.current
Column(
Modifier
- .height(300.dp)
+ .height(400.dp)
.padding(top = 16.dp)
.background(MaterialTheme.appColors.background)
) {
@@ -424,10 +514,28 @@ fun SheetContent(
.padding(10.dp),
textAlign = TextAlign.Center,
style = MaterialTheme.appTypography.titleMedium,
- text = stringResource(id = R.string.core_select_value)
+ text = title
)
- LazyColumn(Modifier.fillMaxWidth(), listState) {
- items(expandedList) { item ->
+ SearchBarStateless(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(48.dp)
+ .padding(horizontal = 16.dp),
+ searchValue = searchValue.text,
+ keyboardActions = {
+ focusManager.clearFocus()
+ },
+ onValueChanged = { textField ->
+ searchValueChanged(textField)
+ }, onClearValue = {
+ searchValueChanged("")
+ }
+ )
+ Spacer(Modifier.height(10.dp))
+ LazyColumn(Modifier.fillMaxSize(), listState) {
+ items(expandedList.filter {
+ it.name.startsWith(searchValue.text, true)
+ }) { item ->
Text(
modifier = Modifier
.fillMaxWidth()
@@ -448,12 +556,16 @@ fun SheetContent(
@Composable
fun SheetContent(
+ searchValue: TextFieldValue,
+ title: String = stringResource(id = R.string.core_select_value),
expandedList: List>,
onItemClick: (Pair) -> Unit,
+ searchValueChanged: (String) -> Unit
) {
+ val focusManager = LocalFocusManager.current
Column(
Modifier
- .height(300.dp)
+ .height(400.dp)
.padding(top = 16.dp)
.background(MaterialTheme.appColors.background)
) {
@@ -463,10 +575,28 @@ fun SheetContent(
.padding(10.dp),
textAlign = TextAlign.Center,
style = MaterialTheme.appTypography.titleMedium,
- text = stringResource(id = R.string.core_select_value)
+ text = title
+ )
+ SearchBarStateless(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(48.dp)
+ .padding(horizontal = 16.dp),
+ searchValue = searchValue.text,
+ keyboardActions = {
+ focusManager.clearFocus()
+ },
+ onValueChanged = { textField ->
+ searchValueChanged(textField)
+ }, onClearValue = {
+ searchValueChanged("")
+ }
)
+ Spacer(Modifier.height(10.dp))
LazyColumn(Modifier.fillMaxWidth()) {
- items(expandedList) { item ->
+ items(expandedList.filter {
+ it.first.startsWith(searchValue.text, true)
+ }) { item ->
Text(
modifier = Modifier
.fillMaxWidth()
@@ -486,7 +616,7 @@ fun SheetContent(
}
@Composable
-fun NewEdxOutlinedTextField(
+fun OpenEdXOutlinedTextField(
modifier: Modifier,
title: String,
isSingleLine: Boolean = true,
@@ -610,7 +740,7 @@ fun DiscoveryCourseItem(course: Course, windowSize: WindowSize, onClick: (String
)
}
- val imageUrl = BuildConfig.BASE_URL.dropLast(1) + course.media.courseImage?.uri
+ val imageUrl = org.openedx.core.BuildConfig.BASE_URL.dropLast(1) + course.media.courseImage?.uri
Surface(
modifier = Modifier
.fillMaxWidth()
@@ -819,7 +949,7 @@ fun OfflineModeDialog(
}
@Composable
-fun NewEdxButton(
+fun OpenEdXButton(
width: Modifier = Modifier.fillMaxWidth(),
text: String,
onClick: () -> Unit,
@@ -851,7 +981,7 @@ fun NewEdxButton(
}
@Composable
-fun NewEdxOutlinedButton(
+fun OpenEdXOutlinedButton(
modifier: Modifier = Modifier.fillMaxWidth(),
backgroundColor: Color = Color.Transparent,
borderColor: Color,
diff --git a/core/src/main/java/com/raccoongang/core/ui/ComposeExtensions.kt b/core/src/main/java/org/openedx/core/ui/ComposeExtensions.kt
similarity index 74%
rename from core/src/main/java/com/raccoongang/core/ui/ComposeExtensions.kt
rename to core/src/main/java/org/openedx/core/ui/ComposeExtensions.kt
index be94fea6c..424bf6066 100644
--- a/core/src/main/java/com/raccoongang/core/ui/ComposeExtensions.kt
+++ b/core/src/main/java/org/openedx/core/ui/ComposeExtensions.kt
@@ -1,5 +1,7 @@
-package com.raccoongang.core.ui
+package org.openedx.core.ui
+import android.graphics.Rect
+import android.view.ViewTreeObserver
import androidx.compose.foundation.MutatePriority
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
@@ -13,7 +15,8 @@ import androidx.compose.ui.composed
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalInspectionMode
-import com.raccoongang.core.presentation.global.InsetHolder
+import androidx.compose.ui.platform.LocalView
+import org.openedx.core.presentation.global.InsetHolder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.launch
@@ -80,6 +83,28 @@ fun rememberSaveableMap(init: () -> MutableMap): MutableMa
}
}
+@Composable
+fun isImeVisibleState(): State {
+ val keyboardState = remember { mutableStateOf(false) }
+ val view = LocalView.current
+ DisposableEffect(view) {
+ val onGlobalListener = ViewTreeObserver.OnGlobalLayoutListener {
+ val rect = Rect()
+ view.getWindowVisibleDisplayFrame(rect)
+ val screenHeight = view.rootView.height
+ val keypadHeight = screenHeight - rect.bottom
+ keyboardState.value = keypadHeight > screenHeight * 0.15
+ }
+ view.viewTreeObserver.addOnGlobalLayoutListener(onGlobalListener)
+
+ onDispose {
+ view.viewTreeObserver.removeOnGlobalLayoutListener(onGlobalListener)
+ }
+ }
+
+ return keyboardState
+}
+
fun LazyListState.disableScrolling(scope: CoroutineScope) {
scope.launch {
scroll(scrollPriority = MutatePriority.PreventUserInput) {
diff --git a/core/src/main/java/com/raccoongang/core/ui/LinkifyText.kt b/core/src/main/java/org/openedx/core/ui/LinkifyText.kt
similarity index 99%
rename from core/src/main/java/com/raccoongang/core/ui/LinkifyText.kt
rename to core/src/main/java/org/openedx/core/ui/LinkifyText.kt
index e85a556ca..ed482bb59 100644
--- a/core/src/main/java/com/raccoongang/core/ui/LinkifyText.kt
+++ b/core/src/main/java/org/openedx/core/ui/LinkifyText.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.ui
+package org.openedx.core.ui
import android.os.Build
import android.text.SpannableString
diff --git a/core/src/main/java/com/raccoongang/core/ui/WindowSize.kt b/core/src/main/java/org/openedx/core/ui/WindowSize.kt
similarity index 97%
rename from core/src/main/java/com/raccoongang/core/ui/WindowSize.kt
rename to core/src/main/java/org/openedx/core/ui/WindowSize.kt
index 2ce484bfa..735dfc209 100644
--- a/core/src/main/java/com/raccoongang/core/ui/WindowSize.kt
+++ b/core/src/main/java/org/openedx/core/ui/WindowSize.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.ui
+package org.openedx.core.ui
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
diff --git a/core/src/main/java/com/raccoongang/core/ui/theme/Color.kt b/core/src/main/java/org/openedx/core/ui/theme/Color.kt
similarity index 94%
rename from core/src/main/java/com/raccoongang/core/ui/theme/Color.kt
rename to core/src/main/java/org/openedx/core/ui/theme/Color.kt
index d183f5159..4dedf421c 100644
--- a/core/src/main/java/com/raccoongang/core/ui/theme/Color.kt
+++ b/core/src/main/java/org/openedx/core/ui/theme/Color.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.ui.theme
+package org.openedx.core.ui.theme
import androidx.compose.material.Colors
import androidx.compose.ui.graphics.Color
@@ -19,6 +19,7 @@ data class AppColors(
val textFieldHint: Color,
val buttonBackground: Color,
+ val buttonSecondaryBackground: Color,
val buttonText: Color,
val cardViewBackground: Color,
diff --git a/core/src/main/java/org/openedx/core/ui/theme/Shape.kt b/core/src/main/java/org/openedx/core/ui/theme/Shape.kt
new file mode 100644
index 000000000..0cb58604f
--- /dev/null
+++ b/core/src/main/java/org/openedx/core/ui/theme/Shape.kt
@@ -0,0 +1,49 @@
+package org.openedx.core.ui.theme
+
+import androidx.compose.foundation.shape.CornerBasedShape
+import androidx.compose.foundation.shape.CornerSize
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Shapes
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.ReadOnlyComposable
+import androidx.compose.runtime.staticCompositionLocalOf
+import androidx.compose.ui.geometry.*
+import androidx.compose.ui.graphics.Outline
+import androidx.compose.ui.unit.LayoutDirection
+import androidx.compose.ui.unit.dp
+
+data class AppShapes(
+ val material: Shapes,
+ val buttonShape: CornerBasedShape,
+ val navigationButtonShape: CornerBasedShape,
+ val textFieldShape: CornerBasedShape,
+ val screenBackgroundShape: CornerBasedShape,
+ val cardShape: CornerBasedShape,
+ val screenBackgroundShapeFull: CornerBasedShape,
+ val courseImageShape: CornerBasedShape,
+ val dialogShape: CornerBasedShape
+)
+
+internal val LocalShapes = staticCompositionLocalOf {
+ AppShapes(
+ material = Shapes(
+ small = RoundedCornerShape(4.dp),
+ medium = RoundedCornerShape(8.dp),
+ large = RoundedCornerShape(0.dp)
+ ),
+ buttonShape = RoundedCornerShape(8.dp),
+ navigationButtonShape = RoundedCornerShape(8.dp),
+ textFieldShape = RoundedCornerShape(CornerSize(8.dp)),
+ screenBackgroundShape = RoundedCornerShape(topStart = 30.dp, topEnd = 30.dp),
+ cardShape = RoundedCornerShape(12.dp),
+ screenBackgroundShapeFull = RoundedCornerShape(24.dp),
+ courseImageShape = RoundedCornerShape(8.dp),
+ dialogShape = RoundedCornerShape(24.dp)
+ )
+}
+
+val MaterialTheme.appShapes: AppShapes
+ @Composable
+ @ReadOnlyComposable
+ get() = LocalShapes.current
diff --git a/core/src/main/java/com/raccoongang/core/ui/theme/Theme.kt b/core/src/main/java/org/openedx/core/ui/theme/Theme.kt
similarity index 94%
rename from core/src/main/java/com/raccoongang/core/ui/theme/Theme.kt
rename to core/src/main/java/org/openedx/core/ui/theme/Theme.kt
index 0967ebac3..a468ebb72 100644
--- a/core/src/main/java/com/raccoongang/core/ui/theme/Theme.kt
+++ b/core/src/main/java/org/openedx/core/ui/theme/Theme.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.ui.theme
+package org.openedx.core.ui.theme
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.LocalOverscrollConfiguration
@@ -39,6 +39,7 @@ private val DarkColorPalette = AppColors(
textFieldHint = Color(0xFF79889F),
buttonBackground = Color(0xFF5478F9),
+ buttonSecondaryBackground = Color(0xFF79889F),
buttonText = Color.White,
cardViewBackground = Color(0xFF273346),
@@ -80,6 +81,7 @@ private val LightColorPalette = AppColors(
textFieldHint = Color(0xFF97A5BB),
buttonBackground = Color(0xFF3C68FF),
+ buttonSecondaryBackground = Color(0xFF79889F),
buttonText = Color.White,
cardViewBackground = Color(0xFFF9FAFB),
@@ -100,7 +102,7 @@ val MaterialTheme.appColors: AppColors
@OptIn(ExperimentalFoundationApi::class)
@Composable
-fun NewEdxTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
+fun OpenEdXTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
val colors = if (darkTheme) {
DarkColorPalette
} else {
diff --git a/core/src/main/java/com/raccoongang/core/ui/theme/Type.kt b/core/src/main/java/org/openedx/core/ui/theme/Type.kt
similarity index 99%
rename from core/src/main/java/com/raccoongang/core/ui/theme/Type.kt
rename to core/src/main/java/org/openedx/core/ui/theme/Type.kt
index 6fbe320fd..42721e5de 100644
--- a/core/src/main/java/com/raccoongang/core/ui/theme/Type.kt
+++ b/core/src/main/java/org/openedx/core/ui/theme/Type.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.ui.theme
+package org.openedx.core.ui.theme
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
diff --git a/core/src/main/java/com/raccoongang/core/utils/EmailUtil.kt b/core/src/main/java/org/openedx/core/utils/EmailUtil.kt
similarity index 97%
rename from core/src/main/java/com/raccoongang/core/utils/EmailUtil.kt
rename to core/src/main/java/org/openedx/core/utils/EmailUtil.kt
index e11360e80..2e78934bb 100644
--- a/core/src/main/java/com/raccoongang/core/utils/EmailUtil.kt
+++ b/core/src/main/java/org/openedx/core/utils/EmailUtil.kt
@@ -1,11 +1,11 @@
-package com.raccoongang.core.utils
+package org.openedx.core.utils
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.os.Build
import android.widget.Toast
-import com.raccoongang.core.R
+import org.openedx.core.R
object EmailUtil {
diff --git a/core/src/main/java/com/raccoongang/core/utils/FileUtil.kt b/core/src/main/java/org/openedx/core/utils/FileUtil.kt
similarity index 70%
rename from core/src/main/java/com/raccoongang/core/utils/FileUtil.kt
rename to core/src/main/java/org/openedx/core/utils/FileUtil.kt
index 7f8b83dad..001d03f4f 100644
--- a/core/src/main/java/com/raccoongang/core/utils/FileUtil.kt
+++ b/core/src/main/java/org/openedx/core/utils/FileUtil.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.utils
+package org.openedx.core.utils
import android.content.Context
import java.io.File
@@ -7,7 +7,7 @@ object FileUtil {
fun getExternalAppDir(context: Context): File {
val dir = context.externalCacheDir.toString() + File.separator +
- context.getString(com.raccoongang.core.R.string.app_name).replace(Regex("\\s"), "_")
+ context.getString(org.openedx.core.R.string.app_name).replace(Regex("\\s"), "_")
val file = File(dir)
file.mkdirs()
return file
diff --git a/core/src/main/java/com/raccoongang/core/utils/IOUtils.kt b/core/src/main/java/org/openedx/core/utils/IOUtils.kt
similarity index 92%
rename from core/src/main/java/com/raccoongang/core/utils/IOUtils.kt
rename to core/src/main/java/org/openedx/core/utils/IOUtils.kt
index 33e660b79..0405168d4 100644
--- a/core/src/main/java/com/raccoongang/core/utils/IOUtils.kt
+++ b/core/src/main/java/org/openedx/core/utils/IOUtils.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.utils
+package org.openedx.core.utils
import okio.buffer
import okio.sink
diff --git a/core/src/main/java/com/raccoongang/core/utils/LocaleUtils.kt b/core/src/main/java/org/openedx/core/utils/LocaleUtils.kt
similarity index 90%
rename from core/src/main/java/com/raccoongang/core/utils/LocaleUtils.kt
rename to core/src/main/java/org/openedx/core/utils/LocaleUtils.kt
index c60517c55..dd2e4531c 100644
--- a/core/src/main/java/com/raccoongang/core/utils/LocaleUtils.kt
+++ b/core/src/main/java/org/openedx/core/utils/LocaleUtils.kt
@@ -1,8 +1,8 @@
-package com.raccoongang.core.utils
+package org.openedx.core.utils
-import com.raccoongang.core.AppDataConstants.USER_MAX_YEAR
-import com.raccoongang.core.AppDataConstants.defaultLocale
-import com.raccoongang.core.domain.model.RegistrationField
+import org.openedx.core.AppDataConstants.USER_MAX_YEAR
+import org.openedx.core.AppDataConstants.defaultLocale
+import org.openedx.core.domain.model.RegistrationField
import java.util.*
object LocaleUtils {
diff --git a/core/src/main/java/com/raccoongang/core/utils/Sha1Util.kt b/core/src/main/java/org/openedx/core/utils/Sha1Util.kt
similarity index 96%
rename from core/src/main/java/com/raccoongang/core/utils/Sha1Util.kt
rename to core/src/main/java/org/openedx/core/utils/Sha1Util.kt
index e61f16eca..13a877e68 100644
--- a/core/src/main/java/com/raccoongang/core/utils/Sha1Util.kt
+++ b/core/src/main/java/org/openedx/core/utils/Sha1Util.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.core.utils
+package org.openedx.core.utils
import java.io.UnsupportedEncodingException
import java.security.MessageDigest
diff --git a/core/src/main/java/com/raccoongang/core/utils/TimeUtils.kt b/core/src/main/java/org/openedx/core/utils/TimeUtils.kt
similarity index 97%
rename from core/src/main/java/com/raccoongang/core/utils/TimeUtils.kt
rename to core/src/main/java/org/openedx/core/utils/TimeUtils.kt
index f5faee11c..bf30e40e2 100644
--- a/core/src/main/java/com/raccoongang/core/utils/TimeUtils.kt
+++ b/core/src/main/java/org/openedx/core/utils/TimeUtils.kt
@@ -1,10 +1,10 @@
-package com.raccoongang.core.utils
+package org.openedx.core.utils
import android.content.Context
import android.text.format.DateUtils
-import com.raccoongang.core.R
-import com.raccoongang.core.domain.model.StartType
-import com.raccoongang.core.system.ResourceManager
+import org.openedx.core.R
+import org.openedx.core.domain.model.StartType
+import org.openedx.core.system.ResourceManager
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.*
diff --git a/core/src/main/java/com/raccoongang/core/utils/VideoUtil.kt b/core/src/main/java/org/openedx/core/utils/VideoUtil.kt
similarity index 89%
rename from core/src/main/java/com/raccoongang/core/utils/VideoUtil.kt
rename to core/src/main/java/org/openedx/core/utils/VideoUtil.kt
index 4038dd1ff..cb24868af 100644
--- a/core/src/main/java/com/raccoongang/core/utils/VideoUtil.kt
+++ b/core/src/main/java/org/openedx/core/utils/VideoUtil.kt
@@ -1,7 +1,7 @@
-package com.raccoongang.core.utils
+package org.openedx.core.utils
-import com.raccoongang.core.AppDataConstants.VIDEO_FORMAT_M3U8
-import com.raccoongang.core.AppDataConstants.VIDEO_FORMAT_MP4
+import org.openedx.core.AppDataConstants.VIDEO_FORMAT_M3U8
+import org.openedx.core.AppDataConstants.VIDEO_FORMAT_MP4
object VideoUtil {
diff --git a/core/src/main/res/drawable/core_ic_down.xml b/core/src/main/res/drawable/core_ic_down.xml
new file mode 100644
index 000000000..d619a24aa
--- /dev/null
+++ b/core/src/main/res/drawable/core_ic_down.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
diff --git a/core/src/main/res/drawable/core_ic_up.xml b/core/src/main/res/drawable/core_ic_up.xml
new file mode 100644
index 000000000..93083d43f
--- /dev/null
+++ b/core/src/main/res/drawable/core_ic_up.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index 7ce2d1303..6c2620495 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -1,6 +1,6 @@
- New Open edX
+ OpenEdX
Results
Invalid credentials
diff --git a/core/src/main/res/values/themes.xml b/core/src/main/res/values/themes.xml
index b5cec9172..f0e209956 100644
--- a/core/src/main/res/values/themes.xml
+++ b/core/src/main/res/values/themes.xml
@@ -1,6 +1,6 @@
-
-
-
+
-
+
\ No newline at end of file
diff --git a/course/build.gradle b/course/build.gradle
index aad937d7e..2f5c4e660 100644
--- a/course/build.gradle
+++ b/course/build.gradle
@@ -15,7 +15,7 @@ android {
consumerProguardFiles "consumer-rules.pro"
}
- namespace 'com.raccoongang.course'
+ namespace 'org.openedx.course'
buildTypes {
release {
diff --git a/course/src/androidTest/java/com/raccoongang/course/presentation/detail/CourseDetailsScreenTest.kt b/course/src/androidTest/java/org/openedx/course/presentation/detail/CourseDetailsScreenTest.kt
similarity index 96%
rename from course/src/androidTest/java/com/raccoongang/course/presentation/detail/CourseDetailsScreenTest.kt
rename to course/src/androidTest/java/org/openedx/course/presentation/detail/CourseDetailsScreenTest.kt
index 361ea6747..5d79f2074 100644
--- a/course/src/androidTest/java/com/raccoongang/course/presentation/detail/CourseDetailsScreenTest.kt
+++ b/course/src/androidTest/java/org/openedx/course/presentation/detail/CourseDetailsScreenTest.kt
@@ -1,11 +1,11 @@
-package com.raccoongang.course.presentation.detail
+package org.openedx.course.presentation.detail
import androidx.activity.ComponentActivity
import androidx.compose.ui.semantics.ProgressBarRangeInfo
import androidx.compose.ui.test.*
import androidx.compose.ui.test.junit4.createAndroidComposeRule
-import com.raccoongang.core.domain.model.*
-import com.raccoongang.course.R
+import org.openedx.core.domain.model.*
+import org.openedx.course.R
import org.junit.Rule
import org.junit.Test
diff --git a/course/src/androidTest/java/com/raccoongang/course/presentation/outline/CourseOutlineScreenTest.kt b/course/src/androidTest/java/org/openedx/course/presentation/outline/CourseOutlineScreenTest.kt
similarity index 91%
rename from course/src/androidTest/java/com/raccoongang/course/presentation/outline/CourseOutlineScreenTest.kt
rename to course/src/androidTest/java/org/openedx/course/presentation/outline/CourseOutlineScreenTest.kt
index 692e07bf4..dd1ecce8f 100644
--- a/course/src/androidTest/java/com/raccoongang/course/presentation/outline/CourseOutlineScreenTest.kt
+++ b/course/src/androidTest/java/org/openedx/course/presentation/outline/CourseOutlineScreenTest.kt
@@ -1,14 +1,14 @@
-package com.raccoongang.course.presentation.outline
+package org.openedx.course.presentation.outline
import androidx.activity.ComponentActivity
import androidx.compose.ui.semantics.ProgressBarRangeInfo
import androidx.compose.ui.test.*
import androidx.compose.ui.test.junit4.createAndroidComposeRule
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.domain.model.Block
-import com.raccoongang.core.domain.model.BlockCounts
-import com.raccoongang.core.domain.model.Certificate
-import com.raccoongang.course.R
+import org.openedx.core.BlockType
+import org.openedx.core.domain.model.Block
+import org.openedx.core.domain.model.BlockCounts
+import org.openedx.core.domain.model.Certificate
+import org.openedx.course.R
import org.junit.Rule
import org.junit.Test
diff --git a/course/src/main/java/com/raccoongang/course/presentation/section/CourseSectionUIState.kt b/course/src/main/java/com/raccoongang/course/presentation/section/CourseSectionUIState.kt
deleted file mode 100644
index 2ebf65c5e..000000000
--- a/course/src/main/java/com/raccoongang/course/presentation/section/CourseSectionUIState.kt
+++ /dev/null
@@ -1,12 +0,0 @@
-package com.raccoongang.course.presentation.section
-
-import com.raccoongang.core.domain.model.Block
-import com.raccoongang.core.module.db.DownloadedState
-
-sealed class CourseSectionUIState {
- data class Blocks(
- val blocks: List,
- val downloadedState: Map
- ) : CourseSectionUIState()
- object Loading : CourseSectionUIState()
-}
\ No newline at end of file
diff --git a/course/src/main/java/com/raccoongang/course/presentation/unit/container/CourseUnitContainerFragment.kt b/course/src/main/java/com/raccoongang/course/presentation/unit/container/CourseUnitContainerFragment.kt
deleted file mode 100644
index 130b5d419..000000000
--- a/course/src/main/java/com/raccoongang/course/presentation/unit/container/CourseUnitContainerFragment.kt
+++ /dev/null
@@ -1,258 +0,0 @@
-package com.raccoongang.course.presentation.unit.container
-
-import android.os.Bundle
-import android.view.View
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
-import androidx.constraintlayout.widget.ConstraintLayout
-import androidx.core.os.bundleOf
-import androidx.fragment.app.Fragment
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.FragmentViewType
-import com.raccoongang.core.domain.model.Block
-import com.raccoongang.core.extension.serializable
-import com.raccoongang.core.presentation.course.CourseViewMode
-import com.raccoongang.core.presentation.global.InsetHolder
-import com.raccoongang.core.presentation.global.viewBinding
-import com.raccoongang.core.ui.BackBtn
-import com.raccoongang.core.ui.rememberWindowSize
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.course.R
-import com.raccoongang.course.databinding.FragmentCourseUnitContainerBinding
-import com.raccoongang.course.presentation.ChapterEndFragmentDialog
-import com.raccoongang.course.presentation.ui.NavigationUnitsButtons
-import com.raccoongang.course.presentation.unit.NotSupportedUnitFragment
-import com.raccoongang.course.presentation.unit.html.HtmlUnitFragment
-import com.raccoongang.course.presentation.unit.video.VideoUnitFragment
-import com.raccoongang.course.presentation.unit.video.YoutubeVideoUnitFragment
-import com.raccoongang.discussion.presentation.threads.DiscussionThreadsFragment
-import com.raccoongang.discussion.presentation.topics.DiscussionTopicsFragment
-import org.koin.androidx.viewmodel.ext.android.viewModel
-import org.koin.core.parameter.parametersOf
-
-class CourseUnitContainerFragment : Fragment(R.layout.fragment_course_unit_container) {
-
- private val binding by viewBinding(FragmentCourseUnitContainerBinding::bind)
-
- private val viewModel by viewModel {
- parametersOf(requireArguments().getString(ARG_COURSE_ID, ""))
- }
-
- private var blockId: String = ""
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- lifecycle.addObserver(viewModel)
- blockId = requireArguments().getString(ARG_BLOCK_ID, "")
- viewModel.loadBlocks(requireArguments().serializable(ARG_MODE)!!)
- viewModel.setupCurrentIndex(blockId)
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- super.onViewCreated(view, savedInstanceState)
-
- val insetHolder = requireActivity() as InsetHolder
- val statusBarParams = binding.statusBarInset.layoutParams as ConstraintLayout.LayoutParams
- statusBarParams.topMargin = insetHolder.topInset
- binding.statusBarInset.layoutParams = statusBarParams
- val bottomNavigationParams =
- binding.cvNavigationBar.layoutParams as ConstraintLayout.LayoutParams
- bottomNavigationParams.bottomMargin = insetHolder.bottomInset
- binding.cvNavigationBar.layoutParams = bottomNavigationParams
-
- if (savedInstanceState == null && childFragmentManager.findFragmentById(R.id.unitContainer) == null) {
- val fragment = unitBlockFragment(viewModel.getCurrentBlock())
- childFragmentManager.beginTransaction()
- .replace(R.id.unitContainer, fragment)
- .commit()
- }
-
- binding.cvNavigationBar.setContent {
- NewEdxTheme {
- var prevButtonText by rememberSaveable {
- mutableStateOf(viewModel.prevButtonText)
- }
- var nextButtonText by rememberSaveable {
- mutableStateOf(viewModel.nextButtonText)
- }
- var hasNextBlock by rememberSaveable {
- mutableStateOf(viewModel.hasNextBlock)
- }
-
- val windowSize = rememberWindowSize()
-
- updateNavigationButtons { prev, next, bool ->
- prevButtonText = prev
- nextButtonText = next
- hasNextBlock = bool
- }
-
- NavigationUnitsButtons(
- windowSize = windowSize,
- prevButtonText = prevButtonText,
- nextButtonText = nextButtonText,
- hasNextBlock = hasNextBlock,
- onPrevClick = {
- val block = viewModel.moveToPrevBlock()
- if (block != null) {
- if (!block.type.isContainer()) {
- navigateToUnit(block, R.id.unitContainer)
- updateNavigationButtons { prev, next, bool ->
- prevButtonText = prev
- nextButtonText = next
- hasNextBlock = bool
- }
- }
- }
- },
- onNextClick = {
- val block = viewModel.moveToNextBlock()
- if (block != null) {
- if (!block.type.isContainer()) {
- navigateToUnit(block, R.id.unitContainer)
- updateNavigationButtons { prev, next, bool ->
- prevButtonText = prev
- nextButtonText = next
- hasNextBlock = bool
- }
- } else {
- viewModel.sendEventPauseVideo()
- val dialog = ChapterEndFragmentDialog.newInstance(block.displayName)
- dialog.show(
- requireActivity().supportFragmentManager,
- ChapterEndFragmentDialog::class.simpleName
- )
- }
- }
- }
- )
- }
- }
-
- binding.btnBack.setContent {
- NewEdxTheme {
- BackBtn {
- requireActivity().supportFragmentManager.popBackStack()
- }
- }
- }
-
- }
-
- private fun updateNavigationButtons(updatedData: (String?, String, Boolean) -> Unit) {
- val prevButtonText = if (viewModel.isFirstIndexInContainer) {
- null
- } else {
- getString(R.string.course_navigation_prev)
- }
- val hasNextBlock: Boolean
- val nextButtonText = if (viewModel.isLastIndexInContainer) {
- hasNextBlock = false
- getString(R.string.course_navigation_finish)
- } else {
- hasNextBlock = true
- getString(R.string.course_navigation_next)
- }
- updatedData(prevButtonText, nextButtonText, hasNextBlock)
- }
-
- private fun navigateToUnit(block: Block, containerId: Int) {
- childFragmentManager.beginTransaction()
- .replace(containerId, unitBlockFragment(block))
- .commit()
- }
-
- private fun unitBlockFragment(block: Block): Fragment {
- return when (block.type) {
- BlockType.HTML,
- BlockType.PROBLEM,
- BlockType.OPENASSESSMENT,
- BlockType.DRAG_AND_DROP_V2,
- BlockType.WORD_CLOUD,
- BlockType.LTI_CONSUMER,
- -> {
- HtmlUnitFragment.newInstance(block.id, block.studentViewUrl)
- }
- BlockType.VIDEO -> {
- val encodedVideos = block.studentViewData!!.encodedVideos!!
- val transcripts = block.studentViewData!!.transcripts
- with(encodedVideos) {
- var isDownloaded = false
- val videoUrl = if (viewModel.getDownloadModelById(block.id) != null) {
- isDownloaded = true
- viewModel.getDownloadModelById(block.id)!!.path
- } else if (fallback != null) {
- fallback!!.url
- } else if (hls != null) {
- hls!!.url
- } else if (desktopMp4 != null) {
- desktopMp4!!.url
- } else if (mobileHigh != null) {
- mobileHigh!!.url
- } else if (mobileLow != null) {
- mobileLow!!.url
- } else {
- ""
- }
- if (videoUrl.isNotEmpty()) {
- VideoUnitFragment.newInstance(
- block.id,
- viewModel.courseId,
- videoUrl,
- transcripts?.toMap() ?: emptyMap(),
- block.displayName,
- isDownloaded
- )
- } else {
- YoutubeVideoUnitFragment.newInstance(
- block.id,
- viewModel.courseId,
- encodedVideos.youtube?.url!!,
- transcripts?.toMap() ?: emptyMap(),
- block.displayName
- )
- }
- }
- }
- BlockType.DISCUSSION -> {
- DiscussionThreadsFragment.newInstance(
- DiscussionTopicsFragment.TOPIC,
- viewModel.courseId,
- block.studentViewData?.topicId ?: "",
- block.displayName,
- FragmentViewType.MAIN_CONTENT.name
- )
- }
- else -> {
- NotSupportedUnitFragment.newInstance(
- block.id,
- block.lmsWebUrl
- )
- }
- }
- }
-
- companion object {
- private const val ARG_BLOCK_ID = "blockId"
- private const val ARG_COURSE_ID = "courseId"
- private const val ARG_COURSE_NAME = "courseName"
- private const val ARG_MODE = "mode"
- fun newInstance(
- blockId: String,
- courseId: String,
- courseName: String,
- mode: CourseViewMode,
- ): CourseUnitContainerFragment {
- val fragment = CourseUnitContainerFragment()
- fragment.arguments = bundleOf(
- ARG_BLOCK_ID to blockId,
- ARG_COURSE_ID to courseId,
- ARG_COURSE_NAME to courseName,
- ARG_MODE to mode
- )
- return fragment
- }
- }
-}
\ No newline at end of file
diff --git a/course/src/main/java/com/raccoongang/course/presentation/unit/container/CourseUnitContainerViewModel.kt b/course/src/main/java/com/raccoongang/course/presentation/unit/container/CourseUnitContainerViewModel.kt
deleted file mode 100644
index afa83fe7a..000000000
--- a/course/src/main/java/com/raccoongang/course/presentation/unit/container/CourseUnitContainerViewModel.kt
+++ /dev/null
@@ -1,108 +0,0 @@
-package com.raccoongang.course.presentation.unit.container
-
-import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.domain.model.Block
-import com.raccoongang.core.module.db.DownloadModel
-import com.raccoongang.core.module.db.DownloadedState
-import com.raccoongang.core.presentation.course.CourseViewMode
-import com.raccoongang.core.system.notifier.CourseNotifier
-import com.raccoongang.core.system.notifier.CoursePauseVideo
-import com.raccoongang.course.domain.interactor.CourseInteractor
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.runBlocking
-
-class CourseUnitContainerViewModel(
- private val interactor: CourseInteractor,
- private val notifier: CourseNotifier,
- val courseId: String
-) : BaseViewModel() {
-
- private val blocks = ArrayList()
-
- var currentIndex = 0
- private set
- var currentVerticalIndex = 0
- private set
-
- val isFirstIndexInContainer: Boolean
- get() {
- return blocks[currentVerticalIndex].descendants.first() == blocks[currentIndex].id
- }
-
- val isLastIndexInContainer: Boolean
- get() {
- return blocks[currentVerticalIndex].descendants.last() == blocks[currentIndex].id
- }
-
- var prevButtonText: String? = null
- var nextButtonText = ""
- var hasNextBlock = false
-
- fun loadBlocks(mode: CourseViewMode) {
- try {
- val courseStructure = when (mode) {
- CourseViewMode.FULL -> interactor.getCourseStructureFromCache()
- CourseViewMode.VIDEOS -> interactor.getCourseStructureForVideos()
- }
- val blocks = courseStructure.blockData
- this.blocks.clear()
- this.blocks.addAll(blocks)
- } catch (e: Exception) {
- e.printStackTrace()
- }
- }
-
- fun setupCurrentIndex(blockId: String) {
- blocks.forEachIndexed { index, block ->
- if (block.id == blockId) {
- currentIndex = index
- updateVerticalIndex(blockId)
- return
- }
- }
- }
-
- fun getDownloadModelById(id: String): DownloadModel? = runBlocking(Dispatchers.IO) {
- return@runBlocking interactor.getDownloadModels().first()
- .find { it.id == id && it.downloadedState == DownloadedState.DOWNLOADED }
- }
-
- fun getCurrentBlock(): Block {
- return blocks[currentIndex]
- }
-
- fun moveToNextBlock(): Block? {
- for (i in currentIndex + 1 until blocks.size) {
- val block = blocks[i]
- currentIndex = i
- updateVerticalIndex(block.id)
- return block
- }
- return null
- }
-
- fun moveToPrevBlock(): Block? {
- for (i in currentIndex - 1 downTo 0) {
- val block = blocks[i]
- currentIndex = i
- updateVerticalIndex(block.id)
- return block
- }
- return null
- }
-
- fun sendEventPauseVideo() {
- viewModelScope.launch {
- notifier.send(CoursePauseVideo())
- }
- }
-
- private fun updateVerticalIndex(blockId: String) {
- currentVerticalIndex =
- blocks.indexOfFirst { it.type == BlockType.VERTICAL && it.descendants.contains(blockId) }
- }
-}
\ No newline at end of file
diff --git a/course/src/main/java/com/raccoongang/course/presentation/unit/video/VideoViewModel.kt b/course/src/main/java/com/raccoongang/course/presentation/unit/video/VideoViewModel.kt
deleted file mode 100644
index 559a7e1a1..000000000
--- a/course/src/main/java/com/raccoongang/course/presentation/unit/video/VideoViewModel.kt
+++ /dev/null
@@ -1,43 +0,0 @@
-package com.raccoongang.course.presentation.unit.video
-
-import androidx.lifecycle.viewModelScope
-import com.google.android.exoplayer2.C
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.course.data.repository.CourseRepository
-import com.raccoongang.core.system.notifier.CourseNotifier
-import com.raccoongang.core.system.notifier.CourseVideoPositionChanged
-import kotlinx.coroutines.launch
-
-class VideoViewModel(
- private val courseId: String,
- private val courseRepository: CourseRepository,
- private val preferencesManager: PreferencesManager,
- private val notifier: CourseNotifier
-) : BaseViewModel() {
-
- var videoUrl = ""
- var currentVideoTime = 0L
-
- fun sendTime() {
- if (currentVideoTime != C.TIME_UNSET) {
- viewModelScope.launch {
- notifier.send(CourseVideoPositionChanged(videoUrl, currentVideoTime))
- }
- }
- }
-
- fun markBlockCompleted(blockId: String) {
- viewModelScope.launch {
- try {
- courseRepository.markBlocksCompletion(
- courseId,
- listOf(blockId)
- )
- } catch (e: Exception) {
- e.printStackTrace()
- }
- }
- }
-
-}
\ No newline at end of file
diff --git a/course/src/main/java/com/raccoongang/course/presentation/units/CourseUnitsFragment.kt b/course/src/main/java/com/raccoongang/course/presentation/units/CourseUnitsFragment.kt
deleted file mode 100644
index e1410c8c5..000000000
--- a/course/src/main/java/com/raccoongang/course/presentation/units/CourseUnitsFragment.kt
+++ /dev/null
@@ -1,415 +0,0 @@
-package com.raccoongang.course.presentation.units
-
-import android.content.res.Configuration.UI_MODE_NIGHT_NO
-import android.content.res.Configuration.UI_MODE_NIGHT_YES
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.ViewGroup
-import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.*
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.items
-import androidx.compose.material.*
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Close
-import androidx.compose.material.icons.filled.TaskAlt
-import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.livedata.observeAsState
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.vector.rememberVectorPainter
-import androidx.compose.ui.platform.ComposeView
-import androidx.compose.ui.platform.ViewCompositionStrategy
-import androidx.compose.ui.res.painterResource
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.text.style.TextOverflow
-import androidx.compose.ui.tooling.preview.Devices
-import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.dp
-import androidx.compose.ui.zIndex
-import androidx.core.os.bundleOf
-import androidx.fragment.app.Fragment
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.Block
-import com.raccoongang.core.domain.model.BlockCounts
-import com.raccoongang.core.extension.serializable
-import com.raccoongang.core.presentation.course.CourseViewMode
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appShapes
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.course.R
-import com.raccoongang.course.presentation.CourseRouter
-import com.raccoongang.course.presentation.ui.CardArrow
-import com.raccoongang.course.presentation.units.CourseUnitsFragment.Companion.getUnitBlockIcon
-import org.koin.android.ext.android.inject
-import org.koin.androidx.viewmodel.ext.android.viewModel
-import java.io.File
-
-
-class CourseUnitsFragment : Fragment() {
-
- private val viewModel by viewModel()
- private val router by inject()
-
- private var courseName: String = ""
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- lifecycle.addObserver(viewModel)
- val blockId = requireArguments().getString(ARG_BLOCK_ID, "")
- val mode = requireArguments().serializable(ARG_MODE)!!
- courseName = requireArguments().getString(ARG_COURSE_NAME, "")
- viewModel.getBlocks(blockId, mode)
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?,
- ) = ComposeView(requireContext()).apply {
- setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
- setContent {
- NewEdxTheme {
- val windowSize = rememberWindowSize()
-
- val uiState by viewModel.uiState.observeAsState()
- val uiMessage by viewModel.uiMessage.observeAsState()
-
- CourseUnitsScreen(
- windowSize = windowSize,
- uiState = uiState!!,
- courseName = courseName,
- uiMessage = uiMessage,
- onBackClick = {
- requireActivity().supportFragmentManager.popBackStack()
- },
- onItemClick = {
- router.navigateToCourseContainer(
- requireActivity().supportFragmentManager,
- it.id,
- requireArguments().getString(ARG_COURSE_ID, ""),
- requireArguments().getString(ARG_COURSE_NAME, ""),
- requireArguments().serializable(ARG_MODE)!!
- )
- },
- onDownloadClick = {
- if (it.isDownloading()) {
- viewModel.cancelWork(it.id)
- } else if (it.isDownloaded()) {
- viewModel.removeDownloadedModel(it.id)
- } else {
- viewModel.saveDownloadModel(
- requireContext().externalCacheDir.toString() +
- File.separator +
- requireContext()
- .getString(com.raccoongang.core.R.string.app_name)
- .replace(Regex("\\s"), "_"),
- it
- )
- }
- }
- )
- }
- }
- }
-
- companion object {
- private const val ARG_COURSE_ID = "courseId"
- private const val ARG_BLOCK_ID = "blockId"
- private const val ARG_COURSE_NAME = "courseName"
- private const val ARG_MODE = "mode"
- fun newInstance(
- courseId: String,
- blockId: String,
- courseName: String,
- mode: CourseViewMode,
- ): CourseUnitsFragment {
- val fragment = CourseUnitsFragment()
- fragment.arguments = bundleOf(
- ARG_COURSE_ID to courseId,
- ARG_BLOCK_ID to blockId,
- ARG_COURSE_NAME to courseName,
- ARG_MODE to mode
- )
- return fragment
- }
-
- fun getUnitBlockIcon(block: Block): Int {
- return when (block.type) {
- BlockType.VIDEO -> R.drawable.ic_course_video
- BlockType.PROBLEM -> R.drawable.ic_course_pen
- BlockType.DISCUSSION -> R.drawable.ic_course_discussion
- else -> R.drawable.ic_course_block
- }
- }
- }
-}
-
-
-@Composable
-private fun CourseUnitsScreen(
- windowSize: WindowSize,
- uiState: CourseUnitsUIState,
- courseName: String,
- uiMessage: UIMessage?,
- onBackClick: () -> Unit,
- onItemClick: (Block) -> Unit,
- onDownloadClick: (Block) -> Unit
-) {
- val scaffoldState = rememberScaffoldState()
- Scaffold(
- modifier = Modifier
- .fillMaxSize()
- .navigationBarsPadding(),
- scaffoldState = scaffoldState,
- backgroundColor = MaterialTheme.appColors.background
- ) { paddingValues ->
-
- val contentWidth by remember(key1 = windowSize) {
- mutableStateOf(
- windowSize.windowSizeValue(
- expanded = Modifier.widthIn(Dp.Unspecified, 560.dp),
- compact = Modifier.fillMaxWidth()
- )
- )
- }
-
- val listPadding by remember(key1 = windowSize) {
- mutableStateOf(
- windowSize.windowSizeValue(
- expanded = PaddingValues(vertical = 24.dp),
- compact = PaddingValues(24.dp)
- )
- )
- }
-
- HandleUIMessage(uiMessage = uiMessage, scaffoldState = scaffoldState)
-
-
- Box(
- modifier = Modifier
- .fillMaxSize()
- .padding(paddingValues)
- .statusBarsInset(),
- contentAlignment = Alignment.TopCenter
- ) {
- Column(contentWidth) {
- Box(
- Modifier
- .fillMaxWidth()
- .zIndex(1f),
- contentAlignment = Alignment.CenterStart
- ) {
- BackBtn {
- onBackClick()
- }
- Text(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 56.dp),
- text = courseName,
- color = MaterialTheme.appColors.textPrimary,
- style = MaterialTheme.appTypography.titleMedium,
- maxLines = 1,
- overflow = TextOverflow.Ellipsis,
- textAlign = TextAlign.Center
- )
- }
- Spacer(Modifier.height(6.dp))
- Surface(
- color = MaterialTheme.appColors.background,
- shape = MaterialTheme.appShapes.screenBackgroundShape
- ) {
- when (uiState) {
- is CourseUnitsUIState.Loading -> {
- Box(
- modifier = Modifier.fillMaxSize(),
- contentAlignment = Alignment.Center
- ) {
- CircularProgressIndicator(color = MaterialTheme.appColors.primary)
- }
- }
- is CourseUnitsUIState.Blocks -> {
- Column(Modifier.fillMaxSize()) {
- LazyColumn(
- contentPadding = listPadding,
- ) {
- items(uiState.blocks) { block ->
- CourseUnitItem(
- block = block,
- onClick = {
- onItemClick(it)
- },
- onDownloadClick = onDownloadClick
- )
- Divider()
- }
- }
- }
- }
- }
- }
- }
- }
- }
-}
-
-
-@Composable
-private fun CourseUnitItem(
- block: Block,
- onClick: (Block) -> Unit,
- onDownloadClick: (Block) -> Unit
-) {
- val icon =
- if (block.completion == 1.0) painterResource(R.drawable.course_ic_task_alt) else painterResource(
- id = getUnitBlockIcon(block)
- )
- val iconColor =
- if (block.completion == 1.0) MaterialTheme.appColors.primary else MaterialTheme.appColors.textPrimary
- val iconModifier = Modifier.size(24.dp)
- Column(Modifier.clickable { onClick(block) }) {
- Row(
- Modifier
- .fillMaxWidth()
- .height(80.dp)
- .padding(
- horizontal = 20.dp,
- vertical = 24.dp
- ),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.SpaceBetween
- ) {
- Icon(
- painter = icon,
- contentDescription = null,
- tint = iconColor
- )
- Spacer(Modifier.width(16.dp))
- Text(
- modifier = Modifier.weight(1f),
- text = block.displayName,
- style = MaterialTheme.appTypography.titleSmall,
- color = MaterialTheme.appColors.textPrimary,
- overflow = TextOverflow.Ellipsis,
- maxLines = 1
- )
- Spacer(Modifier.width(16.dp))
- Row(
- modifier = Modifier.fillMaxHeight(),
- horizontalArrangement = Arrangement.spacedBy(24.dp),
- verticalAlignment = Alignment.CenterVertically
- ) {
- if (block.isDownloaded()) {
- IconButton(modifier = iconModifier,
- onClick = { onDownloadClick(block) }) {
- Icon(
- painter = painterResource(id = R.drawable.course_ic_remove_download),
- contentDescription = null,
- tint = MaterialTheme.appColors.textPrimary
- )
- }
- } else if (block.isDownloadable && !block.isDownloading()) {
- IconButton(modifier = iconModifier,
- onClick = { onDownloadClick(block) }) {
- Icon(
- painter = painterResource(id = R.drawable.course_ic_start_download),
- contentDescription = null,
- tint = MaterialTheme.appColors.textPrimary
- )
- }
- } else if (block.isDownloadable) {
- Box(contentAlignment = Alignment.Center) {
- CircularProgressIndicator(
- modifier = Modifier.size(34.dp),
- color = MaterialTheme.appColors.primary
- )
- IconButton(modifier = iconModifier,
- onClick = { onDownloadClick(block) }) {
- Icon(
- imageVector = Icons.Filled.Close,
- contentDescription = null,
- tint = MaterialTheme.appColors.error
- )
- }
- }
- }
- CardArrow(
- degrees = 0f
- )
- }
- }
- }
-}
-
-
-@Preview(uiMode = UI_MODE_NIGHT_NO)
-@Preview(uiMode = UI_MODE_NIGHT_YES)
-@Composable
-private fun CourseUnitsScreenPreview() {
- NewEdxTheme {
- CourseUnitsScreen(
- windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
- uiState = CourseUnitsUIState.Blocks(
- listOf(
- mockBlock,
- mockBlock,
- mockBlock,
- mockBlock
- )
- ),
- "Course default",
- uiMessage = null,
- onBackClick = {},
- onItemClick = {},
- onDownloadClick = {}
- )
- }
-}
-
-@Preview(uiMode = UI_MODE_NIGHT_NO, device = Devices.NEXUS_9)
-@Preview(uiMode = UI_MODE_NIGHT_YES, device = Devices.NEXUS_9)
-@Composable
-private fun CourseUnitsScreenTabletPreview() {
- NewEdxTheme {
- CourseUnitsScreen(
- windowSize = WindowSize(WindowType.Medium, WindowType.Medium),
- uiState = CourseUnitsUIState.Blocks(
- listOf(
- mockBlock,
- mockBlock,
- mockBlock,
- mockBlock
- )
- ),
- "Course default",
- uiMessage = null,
- onBackClick = {},
- onItemClick = {},
- onDownloadClick = {}
- )
- }
-}
-
-private val mockBlock = Block(
- id = "id",
- blockId = "blockId",
- lmsWebUrl = "lmsWebUrl",
- legacyWebUrl = "legacyWebUrl",
- studentViewUrl = "studentViewUrl",
- type = BlockType.HTML,
- displayName = "Block",
- graded = false,
- studentViewData = null,
- studentViewMultiDevice = false,
- blockCounts = BlockCounts(0),
- descendants = emptyList(),
- completion = 0.0
-)
\ No newline at end of file
diff --git a/course/src/main/java/com/raccoongang/course/presentation/units/CourseUnitsUIState.kt b/course/src/main/java/com/raccoongang/course/presentation/units/CourseUnitsUIState.kt
deleted file mode 100644
index e9bddd66e..000000000
--- a/course/src/main/java/com/raccoongang/course/presentation/units/CourseUnitsUIState.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.raccoongang.course.presentation.units
-
-import com.raccoongang.core.domain.model.Block
-
-sealed class CourseUnitsUIState {
- data class Blocks(val blocks: List) : CourseUnitsUIState()
- object Loading : CourseUnitsUIState()
-}
\ No newline at end of file
diff --git a/course/src/main/java/com/raccoongang/course/presentation/units/CourseUnitsViewModel.kt b/course/src/main/java/com/raccoongang/course/presentation/units/CourseUnitsViewModel.kt
deleted file mode 100644
index b17db9456..000000000
--- a/course/src/main/java/com/raccoongang/course/presentation/units/CourseUnitsViewModel.kt
+++ /dev/null
@@ -1,148 +0,0 @@
-package com.raccoongang.course.presentation.units
-
-import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.LiveData
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.R
-import com.raccoongang.core.SingleEventLiveData
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.domain.model.Block
-import com.raccoongang.core.extension.isInternetError
-import com.raccoongang.core.module.DownloadWorkerController
-import com.raccoongang.core.module.db.DownloadModel
-import com.raccoongang.core.module.db.DownloadedState
-import com.raccoongang.core.presentation.course.CourseViewMode
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.core.system.connection.NetworkConnection
-import com.raccoongang.core.utils.Sha1Util
-import com.raccoongang.course.domain.interactor.CourseInteractor
-import kotlinx.coroutines.flow.first
-import kotlinx.coroutines.launch
-import java.io.File
-import com.raccoongang.course.R as courseR
-
-class CourseUnitsViewModel(
- private val interactor: CourseInteractor,
- private val resourceManager: ResourceManager,
- private val preferencesManager: PreferencesManager,
- private val networkConnection: NetworkConnection,
- private val workerController: DownloadWorkerController
-) : BaseViewModel() {
-
- private val _uiState = MutableLiveData(CourseUnitsUIState.Loading)
- val uiState: LiveData
- get() = _uiState
-
- private val _uiMessage = SingleEventLiveData()
- val uiMessage: LiveData
- get() = _uiMessage
-
-
- override fun onCreate(owner: LifecycleOwner) {
- super.onCreate(owner)
- viewModelScope.launch {
- getDownloadModels().collect { list ->
- if (uiState.value is CourseUnitsUIState.Blocks) {
- val uiList = (uiState.value as CourseUnitsUIState.Blocks).blocks
- _uiState.value = CourseUnitsUIState.Blocks(uiList.map { block ->
- val downloadingBlock =
- getDownloadModels().first().find { it.id == block.id }
- block.copy(downloadModel = downloadingBlock)
- })
- }
- }
- }
- }
-
- fun getBlocks(blockId: String, mode: CourseViewMode) {
- _uiState.value = CourseUnitsUIState.Loading
- viewModelScope.launch {
- try {
- val courseStructure = when (mode) {
- CourseViewMode.FULL -> interactor.getCourseStructureFromCache()
- CourseViewMode.VIDEOS -> interactor.getCourseStructureForVideos()
- }
- val blocks = courseStructure.blockData
- val newList = getDescendantBlocks(blocks, blockId)
- _uiState.value = CourseUnitsUIState.Blocks(newList.map { block ->
- val downloadingBlock = getDownloadModels().first().find { it.id == block.id }
- block.copy(downloadModel = downloadingBlock)
- })
- } catch (e: Exception) {
- if (e.isInternetError()) {
- _uiMessage.value =
- UIMessage.SnackBarMessage(resourceManager.getString(R.string.core_error_no_connection))
- } else {
- _uiMessage.value =
- UIMessage.SnackBarMessage(resourceManager.getString(R.string.core_error_unknown_error))
- }
- }
- }
- }
-
- fun cancelWork(id: String) {
- viewModelScope.launch {
- workerController.cancelWork(id)
- }
- }
-
- fun removeDownloadedModel(id: String) {
- viewModelScope.launch {
- interactor.removeDownloadModel(id)
- }
- }
-
- fun saveDownloadModel(folder: String, block: Block) {
- val videoInfo = block.studentViewData?.encodedVideos?.getPreferredVideoInfoForDownloading(
- preferencesManager.videoSettings.videoQuality
- )
- val size = videoInfo?.fileSize ?: 0
- val url = videoInfo?.url ?: ""
- val extension = url.split('.').lastOrNull() ?: "mp4"
- val path =
- folder + File.separator + "${Sha1Util.SHA1(block.displayName)}.$extension"
- val downloadModel =
- DownloadModel(
- block.id,
- block.displayName,
- size,
- path,
- url,
- block.downloadableType,
- DownloadedState.WAITING,
- null
- )
- viewModelScope.launch {
- if (preferencesManager.videoSettings.wifiDownloadOnly) {
- if (networkConnection.isWifiConnected()) {
- workerController.saveModels(downloadModel)
- } else {
- _uiMessage.value =
- UIMessage.ToastMessage(resourceManager.getString(courseR.string.course_can_download_only_with_wifi))
- }
- } else {
- workerController.saveModels(downloadModel)
- }
- }
- }
-
- private fun getDownloadModels() = interactor.getDownloadModels()
-
- private fun getDescendantBlocks(blocks: List, id: String): List {
- val resultList = mutableListOf()
- if (blocks.isEmpty()) return emptyList()
- val selectedBlock = blocks.first {
- it.id == id
- }
- selectedBlock.descendants.forEach { descendantId ->
- blocks.firstOrNull { b -> b.id == descendantId }?.let {
- resultList.add(it)
- }
- }
- return resultList
- }
-
-}
\ No newline at end of file
diff --git a/course/src/main/java/com/raccoongang/course/data/repository/CourseRepository.kt b/course/src/main/java/org/openedx/course/data/repository/CourseRepository.kt
similarity index 84%
rename from course/src/main/java/com/raccoongang/course/data/repository/CourseRepository.kt
rename to course/src/main/java/org/openedx/course/data/repository/CourseRepository.kt
index 9ee3b5766..afd82627c 100644
--- a/course/src/main/java/com/raccoongang/course/data/repository/CourseRepository.kt
+++ b/course/src/main/java/org/openedx/course/data/repository/CourseRepository.kt
@@ -1,14 +1,14 @@
-package com.raccoongang.course.data.repository
-
-import com.raccoongang.core.data.api.CourseApi
-import com.raccoongang.core.data.model.BlocksCompletionBody
-import com.raccoongang.core.data.model.EnrollBody
-import com.raccoongang.core.data.model.room.CourseEntity
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.domain.model.*
-import com.raccoongang.core.exception.NoCachedDataException
-import com.raccoongang.core.module.db.DownloadDao
-import com.raccoongang.course.data.storage.CourseDao
+package org.openedx.course.data.repository
+
+import org.openedx.core.data.api.CourseApi
+import org.openedx.core.data.model.BlocksCompletionBody
+import org.openedx.core.data.model.EnrollBody
+import org.openedx.core.data.model.room.CourseEntity
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.domain.model.*
+import org.openedx.core.exception.NoCachedDataException
+import org.openedx.core.module.db.DownloadDao
+import org.openedx.course.data.storage.CourseDao
import kotlinx.coroutines.flow.map
import okhttp3.ResponseBody
@@ -22,7 +22,7 @@ class CourseRepository(
suspend fun getCourseDetail(id: String): Course {
val course = api.getCourseDetail(id)
- courseDao.insertCourseEntity(CourseEntity.createFrom(course))
+ courseDao.updateCourseEntity(CourseEntity.createFrom(course))
return course.mapToDomain()
}
diff --git a/course/src/main/java/com/raccoongang/course/data/storage/CourseConverter.kt b/course/src/main/java/org/openedx/course/data/storage/CourseConverter.kt
similarity index 87%
rename from course/src/main/java/com/raccoongang/course/data/storage/CourseConverter.kt
rename to course/src/main/java/org/openedx/course/data/storage/CourseConverter.kt
index 504742ee2..91ac5a610 100644
--- a/course/src/main/java/com/raccoongang/course/data/storage/CourseConverter.kt
+++ b/course/src/main/java/org/openedx/course/data/storage/CourseConverter.kt
@@ -1,10 +1,10 @@
-package com.raccoongang.course.data.storage
+package org.openedx.course.data.storage
import androidx.room.TypeConverter
import com.google.gson.Gson
-import com.raccoongang.core.data.model.room.BlockDb
-import com.raccoongang.core.data.model.room.VideoInfoDb
-import com.raccoongang.core.extension.genericType
+import org.openedx.core.data.model.room.BlockDb
+import org.openedx.core.data.model.room.VideoInfoDb
+import org.openedx.core.extension.genericType
class CourseConverter {
diff --git a/course/src/main/java/com/raccoongang/course/data/storage/CourseDao.kt b/course/src/main/java/org/openedx/course/data/storage/CourseDao.kt
similarity index 65%
rename from course/src/main/java/com/raccoongang/course/data/storage/CourseDao.kt
rename to course/src/main/java/org/openedx/course/data/storage/CourseDao.kt
index cbb9a7fbd..ca344c8ef 100644
--- a/course/src/main/java/com/raccoongang/course/data/storage/CourseDao.kt
+++ b/course/src/main/java/org/openedx/course/data/storage/CourseDao.kt
@@ -1,12 +1,13 @@
-package com.raccoongang.course.data.storage
+package org.openedx.course.data.storage
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
-import com.raccoongang.core.data.model.room.CourseEntity
-import com.raccoongang.core.data.model.room.CourseStructureEntity
-import com.raccoongang.core.data.model.room.discovery.EnrolledCourseEntity
+import androidx.room.Update
+import org.openedx.core.data.model.room.CourseEntity
+import org.openedx.core.data.model.room.CourseStructureEntity
+import org.openedx.core.data.model.room.discovery.EnrolledCourseEntity
@Dao
interface CourseDao {
@@ -20,8 +21,8 @@ interface CourseDao {
@Query("SELECT * FROM course_structure_table WHERE id=:id")
suspend fun getCourseStructureById(id: String): CourseStructureEntity?
- @Insert(onConflict = OnConflictStrategy.REPLACE)
- suspend fun insertCourseEntity(vararg courseEntity: CourseEntity)
+ @Update
+ suspend fun updateCourseEntity(courseEntity: CourseEntity)
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertCourseStructureEntity(vararg courseStructureEntity: CourseStructureEntity)
diff --git a/course/src/main/java/com/raccoongang/course/domain/interactor/CourseInteractor.kt b/course/src/main/java/org/openedx/course/domain/interactor/CourseInteractor.kt
similarity index 92%
rename from course/src/main/java/com/raccoongang/course/domain/interactor/CourseInteractor.kt
rename to course/src/main/java/org/openedx/course/domain/interactor/CourseInteractor.kt
index b901fc78b..ac8d163ec 100644
--- a/course/src/main/java/com/raccoongang/course/domain/interactor/CourseInteractor.kt
+++ b/course/src/main/java/org/openedx/course/domain/interactor/CourseInteractor.kt
@@ -1,9 +1,9 @@
-package com.raccoongang.course.domain.interactor
+package org.openedx.course.domain.interactor
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.domain.model.Block
-import com.raccoongang.core.domain.model.CourseStructure
-import com.raccoongang.course.data.repository.CourseRepository
+import org.openedx.core.BlockType
+import org.openedx.core.domain.model.Block
+import org.openedx.core.domain.model.CourseStructure
+import org.openedx.course.data.repository.CourseRepository
class CourseInteractor(
private val repository: CourseRepository
diff --git a/course/src/main/java/com/raccoongang/course/presentation/ChapterEndFragmentDialog.kt b/course/src/main/java/org/openedx/course/presentation/ChapterEndFragmentDialog.kt
similarity index 52%
rename from course/src/main/java/com/raccoongang/course/presentation/ChapterEndFragmentDialog.kt
rename to course/src/main/java/org/openedx/course/presentation/ChapterEndFragmentDialog.kt
index 7fa736786..ccf2c11fa 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/ChapterEndFragmentDialog.kt
+++ b/course/src/main/java/org/openedx/course/presentation/ChapterEndFragmentDialog.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.course.presentation
+package org.openedx.course.presentation
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
@@ -7,8 +7,16 @@ import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
-import androidx.compose.foundation.layout.*
-import androidx.compose.material.*
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.Card
+import androidx.compose.material.Icon
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -22,16 +30,20 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.core.os.bundleOf
import androidx.fragment.app.DialogFragment
-import com.raccoongang.core.ui.NewEdxButton
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appShapes
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.course.R
-import com.raccoongang.course.presentation.section.CourseSectionFragment
+import org.openedx.core.ui.OpenEdXButton
+import org.openedx.core.ui.OpenEdXOutlinedButton
+import org.openedx.core.ui.TextIcon
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appShapes
+import org.openedx.core.ui.theme.appTypography
+import org.openedx.course.R
+import org.openedx.course.presentation.section.CourseSectionFragment
class ChapterEndFragmentDialog : DialogFragment() {
+ var listener: DialogListener? = null
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -42,15 +54,21 @@ class ChapterEndFragmentDialog : DialogFragment() {
}
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
ChapterEndDialogScreen(
sectionName = requireArguments().getString(ARG_SECTION_NAME) ?: "",
- onButtonClick = {
+ nextSectionName = requireArguments().getString(ARG_NEXT_SECTION_NAME) ?: "",
+ onBackButtonClick = {
dismiss()
+ listener?.onDismiss()
requireActivity().supportFragmentManager.popBackStack(
CourseSectionFragment::class.java.simpleName,
0
)
+ },
+ onProceedButtonClick = {
+ dismiss()
+ listener?.onClick(true)
}
)
}
@@ -59,27 +77,36 @@ class ChapterEndFragmentDialog : DialogFragment() {
companion object {
private const val ARG_SECTION_NAME = "sectionName"
+ private const val ARG_NEXT_SECTION_NAME = "nexSectionName"
fun newInstance(
- sectionName: String
+ sectionName: String,
+ nextSectionName: String
): ChapterEndFragmentDialog {
val dialog = ChapterEndFragmentDialog()
dialog.arguments = bundleOf(
- ARG_SECTION_NAME to sectionName
+ ARG_SECTION_NAME to sectionName,
+ ARG_NEXT_SECTION_NAME to nextSectionName
)
- dialog.isCancelable = false
return dialog
}
}
}
+interface DialogListener {
+ fun onClick(value: T)
+ fun onDismiss()
+}
+
@Composable
private fun ChapterEndDialogScreen(
sectionName: String,
- onButtonClick: () -> Unit
+ nextSectionName: String,
+ onBackButtonClick: () -> Unit,
+ onProceedButtonClick: () -> Unit
) {
Card(
modifier = Modifier
- .fillMaxWidth()
+ .fillMaxWidth(0.95f)
.clip(MaterialTheme.appShapes.courseImageShape),
backgroundColor = MaterialTheme.appColors.background,
shape = MaterialTheme.appShapes.courseImageShape
@@ -108,15 +135,42 @@ private fun ChapterEndDialogScreen(
Text(
modifier = Modifier.fillMaxWidth(),
text = stringResource(id = R.string.course_section_finished, sectionName),
- color = MaterialTheme.appColors.textSecondary,
+ color = MaterialTheme.appColors.textFieldText,
style = MaterialTheme.appTypography.titleSmall,
textAlign = TextAlign.Center
)
Spacer(Modifier.height(42.dp))
- NewEdxButton(
+ if (nextSectionName.isNotEmpty()) {
+ OpenEdXButton(
+ text = stringResource(id = R.string.course_next_section),
+ content = {
+ TextIcon(
+ text = stringResource(id = R.string.course_next_section),
+ painter = painterResource(org.openedx.core.R.drawable.core_ic_forward),
+ color = MaterialTheme.appColors.buttonText,
+ textStyle = MaterialTheme.appTypography.labelLarge
+ )
+ },
+ onClick = onProceedButtonClick
+ )
+ Spacer(Modifier.height(16.dp))
+ }
+ OpenEdXOutlinedButton(
+ borderColor = MaterialTheme.appColors.buttonBackground,
+ textColor = MaterialTheme.appColors.buttonBackground,
text = stringResource(id = R.string.course_back_to_outline),
- onClick = onButtonClick
+ onClick = onBackButtonClick
)
+ if (nextSectionName.isNotEmpty()) {
+ Spacer(Modifier.height(24.dp))
+ Text(
+ modifier = Modifier.fillMaxWidth(),
+ text = stringResource(id = R.string.course_to_proceed, nextSectionName),
+ color = MaterialTheme.appColors.textPrimaryVariant,
+ style = MaterialTheme.appTypography.labelSmall,
+ textAlign = TextAlign.Center
+ )
+ }
}
}
}
@@ -125,7 +179,12 @@ private fun ChapterEndDialogScreen(
@Preview(uiMode = UI_MODE_NIGHT_YES)
@Composable
fun ChapterEndDialogScreenPreview() {
- NewEdxTheme {
- ChapterEndDialogScreen(sectionName = "Section", onButtonClick = {})
+ OpenEdXTheme {
+ ChapterEndDialogScreen(
+ sectionName = "Section",
+ nextSectionName = "Section2",
+ onBackButtonClick = {},
+ onProceedButtonClick = {}
+ )
}
}
\ No newline at end of file
diff --git a/course/src/main/java/org/openedx/course/presentation/CourseAnalytics.kt b/course/src/main/java/org/openedx/course/presentation/CourseAnalytics.kt
new file mode 100644
index 000000000..5cea3d63f
--- /dev/null
+++ b/course/src/main/java/org/openedx/course/presentation/CourseAnalytics.kt
@@ -0,0 +1,19 @@
+package org.openedx.course.presentation
+
+interface CourseAnalytics {
+ fun courseEnrollClickedEvent(courseId: String, courseName: String)
+ fun courseEnrollSuccessEvent(courseId: String, courseName: String)
+ fun viewCourseClickedEvent(courseId: String, courseName: String)
+ fun resumeCourseTappedEvent(courseId: String, courseName: String, blockId: String)
+ fun sequentialClickedEvent(courseId: String, courseName: String, blockId: String, blockName: String)
+ fun verticalClickedEvent(courseId: String, courseName: String, blockId: String, blockName: String)
+ fun nextBlockClickedEvent(courseId: String, courseName: String, blockId: String, blockName: String)
+ fun prevBlockClickedEvent(courseId: String, courseName: String, blockId: String, blockName: String)
+ fun finishVerticalClickedEvent(courseId: String, courseName: String, blockId: String, blockName: String)
+ fun finishVerticalNextClickedEvent(courseId: String, courseName: String, blockId: String, blockName: String)
+ fun finishVerticalBackClickedEvent(courseId: String, courseName: String)
+ fun courseTabClickedEvent(courseId: String, courseName: String)
+ fun videoTabClickedEvent(courseId: String, courseName: String)
+ fun discussionTabClickedEvent(courseId: String, courseName: String)
+ fun handoutsTabClickedEvent(courseId: String, courseName: String)
+}
\ No newline at end of file
diff --git a/course/src/main/java/com/raccoongang/course/presentation/CourseRouter.kt b/course/src/main/java/org/openedx/course/presentation/CourseRouter.kt
similarity index 82%
rename from course/src/main/java/com/raccoongang/course/presentation/CourseRouter.kt
rename to course/src/main/java/org/openedx/course/presentation/CourseRouter.kt
index b0c61b932..11c4420c0 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/CourseRouter.kt
+++ b/course/src/main/java/org/openedx/course/presentation/CourseRouter.kt
@@ -1,9 +1,9 @@
-package com.raccoongang.course.presentation
+package org.openedx.course.presentation
import androidx.fragment.app.FragmentManager
-import com.raccoongang.core.domain.model.CoursewareAccess
-import com.raccoongang.core.presentation.course.CourseViewMode
-import com.raccoongang.course.presentation.handouts.HandoutsType
+import org.openedx.core.domain.model.CoursewareAccess
+import org.openedx.core.presentation.course.CourseViewMode
+import org.openedx.course.presentation.handouts.HandoutsType
import java.util.Date
interface CourseRouter {
@@ -21,23 +21,23 @@ interface CourseRouter {
auditAccessExpires: Date?
)
- fun navigateToCourseUnits(
+ fun navigateToCourseSubsections(
fm: FragmentManager,
courseId: String,
blockId: String,
- courseName: String,
+ title: String,
mode: CourseViewMode,
)
- fun navigateToCourseSubsections(
+ fun navigateToCourseContainer(
fm: FragmentManager,
- courseId: String,
blockId: String,
- title: String,
- mode: CourseViewMode,
+ courseId: String,
+ courseName: String,
+ mode: CourseViewMode
)
- fun navigateToCourseContainer(
+ fun replaceCourseContainer(
fm: FragmentManager,
blockId: String,
courseId: String,
diff --git a/course/src/main/java/com/raccoongang/course/presentation/container/CourseNavigationFragmentAdapter.kt b/course/src/main/java/org/openedx/course/presentation/container/CourseContainerAdapter.kt
similarity index 71%
rename from course/src/main/java/com/raccoongang/course/presentation/container/CourseNavigationFragmentAdapter.kt
rename to course/src/main/java/org/openedx/course/presentation/container/CourseContainerAdapter.kt
index afec43f1d..80a383143 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/container/CourseNavigationFragmentAdapter.kt
+++ b/course/src/main/java/org/openedx/course/presentation/container/CourseContainerAdapter.kt
@@ -1,9 +1,9 @@
-package com.raccoongang.course.presentation.container
+package org.openedx.course.presentation.container
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
-class CourseNavigationFragmentAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
+class CourseContainerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
private val fragments = ArrayList()
diff --git a/course/src/main/java/com/raccoongang/course/presentation/container/CourseContainerFragment.kt b/course/src/main/java/org/openedx/course/presentation/container/CourseContainerFragment.kt
similarity index 72%
rename from course/src/main/java/com/raccoongang/course/presentation/container/CourseContainerFragment.kt
rename to course/src/main/java/org/openedx/course/presentation/container/CourseContainerFragment.kt
index 1ae1a62b9..17363a01b 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/container/CourseContainerFragment.kt
+++ b/course/src/main/java/org/openedx/course/presentation/container/CourseContainerFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.course.presentation.container
+package org.openedx.course.presentation.container
import android.os.Bundle
import android.view.View
@@ -7,14 +7,14 @@ import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.snackbar.Snackbar
-import com.raccoongang.core.presentation.global.viewBinding
-import com.raccoongang.course.R
-import com.raccoongang.course.databinding.FragmentCourseContainerBinding
-import com.raccoongang.course.presentation.CourseRouter
-import com.raccoongang.course.presentation.handouts.HandoutsFragment
-import com.raccoongang.course.presentation.outline.CourseOutlineFragment
-import com.raccoongang.course.presentation.videos.CourseVideosFragment
-import com.raccoongang.discussion.presentation.topics.DiscussionTopicsFragment
+import org.openedx.core.presentation.global.viewBinding
+import org.openedx.course.R
+import org.openedx.course.databinding.FragmentCourseContainerBinding
+import org.openedx.course.presentation.CourseRouter
+import org.openedx.course.presentation.handouts.HandoutsFragment
+import org.openedx.course.presentation.outline.CourseOutlineFragment
+import org.openedx.course.presentation.videos.CourseVideosFragment
+import org.openedx.discussion.presentation.topics.DiscussionTopicsFragment
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
@@ -28,7 +28,7 @@ class CourseContainerFragment : Fragment(R.layout.fragment_course_container) {
private val router by inject()
- private var adapter: CourseNavigationFragmentAdapter? = null
+ private var adapter: CourseContainerAdapter? = null
private var courseTitle = ""
@@ -49,10 +49,25 @@ class CourseContainerFragment : Fragment(R.layout.fragment_course_container) {
binding.bottomNavView.setOnItemSelectedListener {
when (it.itemId) {
- R.id.outline -> binding.viewPager.setCurrentItem(0, false)
- R.id.videos -> binding.viewPager.setCurrentItem(1, false)
- R.id.discussions -> binding.viewPager.setCurrentItem(2, false)
- R.id.resources -> binding.viewPager.setCurrentItem(3, false)
+ R.id.outline -> {
+ viewModel.courseTabClickedEvent()
+ binding.viewPager.setCurrentItem(0, false)
+ }
+
+ R.id.videos -> {
+ viewModel.videoTabClickedEvent()
+ binding.viewPager.setCurrentItem(1, false)
+ }
+
+ R.id.discussions -> {
+ viewModel.discussionTabClickedEvent()
+ binding.viewPager.setCurrentItem(2, false)
+ }
+
+ R.id.resources -> {
+ viewModel.handoutsTabClickedEvent()
+ binding.viewPager.setCurrentItem(3, false)
+ }
}
true
}
@@ -82,7 +97,7 @@ class CourseContainerFragment : Fragment(R.layout.fragment_course_container) {
}
viewModel.errorMessage.observe(viewLifecycleOwner) {
snackBar = Snackbar.make(binding.root, it, Snackbar.LENGTH_INDEFINITE)
- .setAction(com.raccoongang.core.R.string.core_error_try_again) {
+ .setAction(org.openedx.core.R.string.core_error_try_again) {
viewModel.preloadCourseStructure()
}
snackBar?.show()
@@ -96,10 +111,10 @@ class CourseContainerFragment : Fragment(R.layout.fragment_course_container) {
private fun initViewPager() {
binding.viewPager.orientation = ViewPager2.ORIENTATION_HORIZONTAL
binding.viewPager.offscreenPageLimit = 4
- adapter = CourseNavigationFragmentAdapter(this).apply {
+ adapter = CourseContainerAdapter(this).apply {
addFragment(CourseOutlineFragment.newInstance(viewModel.courseId, courseTitle))
addFragment(CourseVideosFragment.newInstance(viewModel.courseId, courseTitle))
- addFragment(DiscussionTopicsFragment.newInstance(viewModel.courseId))
+ addFragment(DiscussionTopicsFragment.newInstance(viewModel.courseId, courseTitle))
addFragment(HandoutsFragment.newInstance(viewModel.courseId))
}
binding.viewPager.adapter = adapter
diff --git a/course/src/main/java/com/raccoongang/course/presentation/container/CourseContainerViewModel.kt b/course/src/main/java/org/openedx/course/presentation/container/CourseContainerViewModel.kt
similarity index 66%
rename from course/src/main/java/com/raccoongang/course/presentation/container/CourseContainerViewModel.kt
rename to course/src/main/java/org/openedx/course/presentation/container/CourseContainerViewModel.kt
index ebf3c24a6..1fb6e9aa5 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/container/CourseContainerViewModel.kt
+++ b/course/src/main/java/org/openedx/course/presentation/container/CourseContainerViewModel.kt
@@ -1,19 +1,20 @@
-package com.raccoongang.course.presentation.container
+package org.openedx.course.presentation.container
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.system.connection.NetworkConnection
-import com.raccoongang.core.R
-import com.raccoongang.core.SingleEventLiveData
-import com.raccoongang.core.domain.model.CoursewareAccess
-import com.raccoongang.core.exception.NoCachedDataException
-import com.raccoongang.core.extension.isInternetError
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.core.system.notifier.CourseNotifier
-import com.raccoongang.core.system.notifier.CourseStructureUpdated
-import com.raccoongang.course.domain.interactor.CourseInteractor
+import org.openedx.core.BaseViewModel
+import org.openedx.core.R
+import org.openedx.core.SingleEventLiveData
+import org.openedx.core.domain.model.CoursewareAccess
+import org.openedx.core.exception.NoCachedDataException
+import org.openedx.core.extension.isInternetError
+import org.openedx.core.system.ResourceManager
+import org.openedx.core.system.connection.NetworkConnection
+import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.core.system.notifier.CourseStructureUpdated
+import org.openedx.course.domain.interactor.CourseInteractor
+import org.openedx.course.presentation.CourseAnalytics
import kotlinx.coroutines.launch
class CourseContainerViewModel(
@@ -21,7 +22,8 @@ class CourseContainerViewModel(
private val interactor: CourseInteractor,
private val resourceManager: ResourceManager,
private val notifier: CourseNotifier,
- private val networkConnection: NetworkConnection
+ private val networkConnection: NetworkConnection,
+ private val analytics: CourseAnalytics
) : BaseViewModel() {
private val _dataReady = MutableLiveData()
@@ -36,6 +38,8 @@ class CourseContainerViewModel(
val showProgress: LiveData
get() = _showProgress
+ private var courseName = ""
+
fun preloadCourseStructure() {
if (_dataReady.value != null) {
return
@@ -50,6 +54,7 @@ class CourseContainerViewModel(
interactor.preloadCourseStructureFromCache(courseId)
}
val courseStructure = interactor.getCourseStructureFromCache()
+ courseName = courseStructure.name
_dataReady.value = courseStructure.coursewareAccess
} catch (e: Exception) {
if (e.isInternetError() || e is NoCachedDataException) {
@@ -83,5 +88,20 @@ class CourseContainerViewModel(
}
}
+ fun courseTabClickedEvent() {
+ analytics.courseTabClickedEvent(courseId, courseName)
+ }
+
+ fun videoTabClickedEvent() {
+ analytics.videoTabClickedEvent(courseId, courseName)
+ }
+
+ fun discussionTabClickedEvent() {
+ analytics.discussionTabClickedEvent(courseId, courseName)
+ }
+
+ fun handoutsTabClickedEvent() {
+ analytics.handoutsTabClickedEvent(courseId, courseName)
+ }
}
\ No newline at end of file
diff --git a/course/src/main/java/com/raccoongang/course/presentation/container/NoAccessCourseContainerFragment.kt b/course/src/main/java/org/openedx/course/presentation/container/NoAccessCourseContainerFragment.kt
similarity index 93%
rename from course/src/main/java/com/raccoongang/course/presentation/container/NoAccessCourseContainerFragment.kt
rename to course/src/main/java/org/openedx/course/presentation/container/NoAccessCourseContainerFragment.kt
index 69206075c..c23c3c71b 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/container/NoAccessCourseContainerFragment.kt
+++ b/course/src/main/java/org/openedx/course/presentation/container/NoAccessCourseContainerFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.course.presentation.container
+package org.openedx.course.presentation.container
import android.content.res.Configuration
import android.os.Bundle
@@ -25,14 +25,14 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
-import com.raccoongang.core.domain.model.CoursewareAccess
-import com.raccoongang.core.extension.parcelable
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appTypography
+import org.openedx.core.domain.model.CoursewareAccess
+import org.openedx.core.extension.parcelable
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appTypography
import java.util.*
-import com.raccoongang.course.R as courseR
+import org.openedx.course.R as courseR
class NoAccessCourseContainerFragment : Fragment() {
@@ -56,7 +56,7 @@ class NoAccessCourseContainerFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val auditAccessExpired =
auditAccessExpires != null && Date().after(auditAccessExpires)
@@ -186,7 +186,7 @@ private fun NoAccessCourseContainerScreen(
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun NoAccessCourseContainerScreenPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
NoAccessCourseContainerScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
title = "Example title",
diff --git a/course/src/main/java/com/raccoongang/course/presentation/detail/CourseDetailsFragment.kt b/course/src/main/java/org/openedx/course/presentation/detail/CourseDetailsFragment.kt
similarity index 95%
rename from course/src/main/java/com/raccoongang/course/presentation/detail/CourseDetailsFragment.kt
rename to course/src/main/java/org/openedx/course/presentation/detail/CourseDetailsFragment.kt
index 9780acc05..e08332d66 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/detail/CourseDetailsFragment.kt
+++ b/course/src/main/java/org/openedx/course/presentation/detail/CourseDetailsFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.course.presentation.detail
+package org.openedx.course.presentation.detail
import android.annotation.SuppressLint
import android.content.Intent
@@ -36,26 +36,26 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.zIndex
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
-import com.raccoongang.core.BuildConfig
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.Course
-import com.raccoongang.core.domain.model.Media
-import com.raccoongang.core.extension.isEmailValid
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appShapes
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.core.utils.EmailUtil
-import com.raccoongang.course.R
-import com.raccoongang.course.presentation.CourseRouter
-import com.raccoongang.course.presentation.ui.CourseImageHeader
+import org.openedx.core.BuildConfig
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.Course
+import org.openedx.core.domain.model.Media
+import org.openedx.core.extension.isEmailValid
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appShapes
+import org.openedx.core.ui.theme.appTypography
+import org.openedx.core.utils.EmailUtil
+import org.openedx.course.R
+import org.openedx.course.presentation.CourseRouter
+import org.openedx.course.presentation.ui.CourseImageHeader
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import java.nio.charset.StandardCharsets
import java.util.*
-import com.raccoongang.course.R as courseR
+import org.openedx.course.R as courseR
class CourseDetailsFragment : Fragment() {
@@ -71,7 +71,7 @@ class CourseDetailsFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val uiState by viewModel.uiState.observeAsState()
@@ -99,6 +99,7 @@ class CourseDetailsFragment : Fragment() {
val currentState = uiState
if (currentState is CourseDetailsUIState.CourseData) {
if (currentState.course.isEnrolled) {
+ viewModel.viewCourseClickedEvent(currentState.course.courseId, currentState.course.name)
router.navigateToCourseOutline(
requireActivity().supportFragmentManager,
currentState.course.courseId,
@@ -383,7 +384,7 @@ private fun CourseDetailNativeContent(
)
if (!(enrollmentEnd != null && Date() > enrollmentEnd)) {
Spacer(Modifier.height(32.dp))
- NewEdxButton(
+ OpenEdXButton(
width = buttonWidth,
text = buttonText,
onClick = onButtonClick
@@ -451,7 +452,7 @@ private fun CourseDetailNativeContentLandscape(
Spacer(Modifier.height(4.dp))
EnrollOverLabel()
} else {
- NewEdxButton(
+ OpenEdXButton(
width = buttonWidth,
text = buttonText,
onClick = onButtonClick
@@ -584,7 +585,7 @@ private fun CourseDescription(
isVerticalScrollBarEnabled = false
isHorizontalScrollBarEnabled = false
loadDataWithBaseURL(
- BuildConfig.BASE_URL, body, "text/html", StandardCharsets.UTF_8.name(), null
+ org.openedx.core.BuildConfig.BASE_URL, body, "text/html", StandardCharsets.UTF_8.name(), null
)
}
})
@@ -594,7 +595,7 @@ private fun CourseDescription(
@Preview(uiMode = UI_MODE_NIGHT_YES)
@Composable
private fun CourseDetailNativeContentPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
CourseDetailsScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
uiState = CourseDetailsUIState.CourseData(mockCourse),
@@ -612,7 +613,7 @@ private fun CourseDetailNativeContentPreview() {
@Preview(uiMode = UI_MODE_NIGHT_YES, device = Devices.NEXUS_9)
@Composable
private fun CourseDetailNativeContentTabletPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
CourseDetailsScreen(
windowSize = WindowSize(WindowType.Medium, WindowType.Medium),
uiState = CourseDetailsUIState.CourseData(mockCourse),
diff --git a/course/src/main/java/com/raccoongang/course/presentation/detail/CourseDetailsUIState.kt b/course/src/main/java/org/openedx/course/presentation/detail/CourseDetailsUIState.kt
similarity index 60%
rename from course/src/main/java/com/raccoongang/course/presentation/detail/CourseDetailsUIState.kt
rename to course/src/main/java/org/openedx/course/presentation/detail/CourseDetailsUIState.kt
index 1a2148f53..73257badd 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/detail/CourseDetailsUIState.kt
+++ b/course/src/main/java/org/openedx/course/presentation/detail/CourseDetailsUIState.kt
@@ -1,6 +1,6 @@
-package com.raccoongang.course.presentation.detail
+package org.openedx.course.presentation.detail
-import com.raccoongang.core.domain.model.Course
+import org.openedx.core.domain.model.Course
sealed class CourseDetailsUIState {
data class CourseData(val course: Course) : CourseDetailsUIState()
diff --git a/course/src/main/java/com/raccoongang/course/presentation/detail/CourseDetailsViewModel.kt b/course/src/main/java/org/openedx/course/presentation/detail/CourseDetailsViewModel.kt
similarity index 72%
rename from course/src/main/java/com/raccoongang/course/presentation/detail/CourseDetailsViewModel.kt
rename to course/src/main/java/org/openedx/course/presentation/detail/CourseDetailsViewModel.kt
index ede39fe73..1b94c2464 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/detail/CourseDetailsViewModel.kt
+++ b/course/src/main/java/org/openedx/course/presentation/detail/CourseDetailsViewModel.kt
@@ -1,19 +1,20 @@
-package com.raccoongang.course.presentation.detail
+package org.openedx.course.presentation.detail
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.R
-import com.raccoongang.core.SingleEventLiveData
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.Course
-import com.raccoongang.core.extension.isInternetError
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.core.system.connection.NetworkConnection
-import com.raccoongang.core.system.notifier.CourseDashboardUpdate
-import com.raccoongang.core.system.notifier.CourseNotifier
-import com.raccoongang.course.domain.interactor.CourseInteractor
+import org.openedx.core.BaseViewModel
+import org.openedx.core.R
+import org.openedx.core.SingleEventLiveData
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.Course
+import org.openedx.core.extension.isInternetError
+import org.openedx.core.system.ResourceManager
+import org.openedx.core.system.connection.NetworkConnection
+import org.openedx.core.system.notifier.CourseDashboardUpdate
+import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.course.domain.interactor.CourseInteractor
+import org.openedx.course.presentation.CourseAnalytics
import kotlinx.coroutines.launch
class CourseDetailsViewModel(
@@ -21,7 +22,8 @@ class CourseDetailsViewModel(
private val networkConnection: NetworkConnection,
private val interactor: CourseInteractor,
private val resourceManager: ResourceManager,
- private val notifier: CourseNotifier
+ private val notifier: CourseNotifier,
+ private val analytics: CourseAnalytics
) : BaseViewModel() {
private val _uiState = MutableLiveData(CourseDetailsUIState.Loading)
@@ -65,11 +67,15 @@ class CourseDetailsViewModel(
fun enrollInACourse(id: String) {
viewModelScope.launch {
try {
+ val courseData = _uiState.value
+ if (courseData is CourseDetailsUIState.CourseData) {
+ courseEnrollClickedEvent(id, courseData.course.name)
+ }
interactor.enrollInACourse(id)
val course = interactor.getCourseDetails(id)
- val courseData = _uiState.value
if (courseData is CourseDetailsUIState.CourseData) {
_uiState.value = courseData.copy(course = course)
+ courseEnrollSuccessEvent(id, course.name)
notifier.send(CourseDashboardUpdate())
}
} catch (e: Exception) {
@@ -106,4 +112,16 @@ class CourseDetailsViewModel(
if (color == ULong.MIN_VALUE) return "black"
return java.lang.Long.toHexString(color.toLong()).substring(2, 8)
}
+
+ private fun courseEnrollClickedEvent(courseId: String, courseName: String) {
+ analytics.courseEnrollClickedEvent(courseId, courseName)
+ }
+
+ private fun courseEnrollSuccessEvent(courseId: String, courseName: String) {
+ analytics.courseEnrollSuccessEvent(courseId, courseName)
+ }
+
+ fun viewCourseClickedEvent(courseId: String, courseName: String) {
+ analytics.viewCourseClickedEvent(courseId, courseName)
+ }
}
\ No newline at end of file
diff --git a/course/src/main/java/com/raccoongang/course/presentation/handouts/HandoutsFragment.kt b/course/src/main/java/org/openedx/course/presentation/handouts/HandoutsFragment.kt
similarity index 94%
rename from course/src/main/java/com/raccoongang/course/presentation/handouts/HandoutsFragment.kt
rename to course/src/main/java/org/openedx/course/presentation/handouts/HandoutsFragment.kt
index 4cb2e798d..c5fd806e8 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/handouts/HandoutsFragment.kt
+++ b/course/src/main/java/org/openedx/course/presentation/handouts/HandoutsFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.course.presentation.handouts
+package org.openedx.course.presentation.handouts
import android.content.res.Configuration
import android.os.Bundle
@@ -28,14 +28,14 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.course.presentation.CourseRouter
-import com.raccoongang.course.presentation.ui.CardArrow
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appTypography
+import org.openedx.course.presentation.CourseRouter
+import org.openedx.course.presentation.ui.CardArrow
import org.koin.android.ext.android.inject
-import com.raccoongang.course.R as courseR
+import org.openedx.course.R as courseR
class HandoutsFragment : Fragment() {
@@ -48,7 +48,7 @@ class HandoutsFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
HandoutsScreen(
windowSize = windowSize,
@@ -218,7 +218,7 @@ private fun HandoutsItem(
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun HandoutsScreenPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
HandoutsScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
onBackClick = {}, onHandoutsClick = {}, onAnnouncementsClick = {})
@@ -229,7 +229,7 @@ private fun HandoutsScreenPreview() {
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, device = Devices.NEXUS_9)
@Composable
private fun HandoutsScreenTabletPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
HandoutsScreen(
windowSize = WindowSize(WindowType.Medium, WindowType.Medium),
onBackClick = {}, onHandoutsClick = {}, onAnnouncementsClick = {})
diff --git a/course/src/main/java/com/raccoongang/course/presentation/handouts/HandoutsType.kt b/course/src/main/java/org/openedx/course/presentation/handouts/HandoutsType.kt
similarity index 51%
rename from course/src/main/java/com/raccoongang/course/presentation/handouts/HandoutsType.kt
rename to course/src/main/java/org/openedx/course/presentation/handouts/HandoutsType.kt
index 3007df8c7..da36feda0 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/handouts/HandoutsType.kt
+++ b/course/src/main/java/org/openedx/course/presentation/handouts/HandoutsType.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.course.presentation.handouts
+package org.openedx.course.presentation.handouts
enum class HandoutsType {
Handouts, Announcements
diff --git a/course/src/main/java/com/raccoongang/course/presentation/handouts/HandoutsViewModel.kt b/course/src/main/java/org/openedx/course/presentation/handouts/HandoutsViewModel.kt
similarity index 91%
rename from course/src/main/java/com/raccoongang/course/presentation/handouts/HandoutsViewModel.kt
rename to course/src/main/java/org/openedx/course/presentation/handouts/HandoutsViewModel.kt
index 9a9d8f2c9..61691d88d 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/handouts/HandoutsViewModel.kt
+++ b/course/src/main/java/org/openedx/course/presentation/handouts/HandoutsViewModel.kt
@@ -1,12 +1,12 @@
-package com.raccoongang.course.presentation.handouts
+package org.openedx.course.presentation.handouts
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.domain.model.AnnouncementModel
-import com.raccoongang.core.domain.model.HandoutsModel
-import com.raccoongang.course.domain.interactor.CourseInteractor
+import org.openedx.core.BaseViewModel
+import org.openedx.core.domain.model.AnnouncementModel
+import org.openedx.core.domain.model.HandoutsModel
+import org.openedx.course.domain.interactor.CourseInteractor
import kotlinx.coroutines.launch
class HandoutsViewModel(
diff --git a/course/src/main/java/com/raccoongang/course/presentation/handouts/WebViewFragment.kt b/course/src/main/java/org/openedx/course/presentation/handouts/WebViewFragment.kt
similarity index 94%
rename from course/src/main/java/com/raccoongang/course/presentation/handouts/WebViewFragment.kt
rename to course/src/main/java/org/openedx/course/presentation/handouts/WebViewFragment.kt
index c2e4d5538..d342b2f40 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/handouts/WebViewFragment.kt
+++ b/course/src/main/java/org/openedx/course/presentation/handouts/WebViewFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.course.presentation.handouts
+package org.openedx.course.presentation.handouts
import android.annotation.SuppressLint
import android.content.Intent
@@ -33,14 +33,14 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.zIndex
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
-import com.raccoongang.core.BuildConfig
-import com.raccoongang.core.extension.isEmailValid
-import com.raccoongang.core.extension.replaceLinkTags
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.core.utils.EmailUtil
+import org.openedx.core.BuildConfig
+import org.openedx.core.extension.isEmailValid
+import org.openedx.core.extension.replaceLinkTags
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appTypography
+import org.openedx.core.utils.EmailUtil
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
import java.nio.charset.StandardCharsets
@@ -61,7 +61,7 @@ class WebViewFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val htmlBody by viewModel.htmlContent.observeAsState("")
@@ -240,7 +240,7 @@ private fun HandoutsContent(body: String, onWebPageLoaded: () -> Unit) {
isVerticalScrollBarEnabled = false
isHorizontalScrollBarEnabled = false
loadDataWithBaseURL(
- BuildConfig.BASE_URL,
+ org.openedx.core.BuildConfig.BASE_URL,
body.replaceLinkTags(isDarkTheme),
"text/html",
StandardCharsets.UTF_8.name(),
@@ -249,7 +249,7 @@ private fun HandoutsContent(body: String, onWebPageLoaded: () -> Unit) {
}
}, update = {
it.loadDataWithBaseURL(
- BuildConfig.BASE_URL,
+ org.openedx.core.BuildConfig.BASE_URL,
body.replaceLinkTags(isDarkTheme),
"text/html",
StandardCharsets.UTF_8.name(),
diff --git a/course/src/main/java/com/raccoongang/course/presentation/outline/CourseOutlineFragment.kt b/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineFragment.kt
similarity index 82%
rename from course/src/main/java/com/raccoongang/course/presentation/outline/CourseOutlineFragment.kt
rename to course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineFragment.kt
index 10abb8254..b0e2586a5 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/outline/CourseOutlineFragment.kt
+++ b/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.course.presentation.outline
+package org.openedx.course.presentation.outline
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
@@ -30,20 +30,20 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.*
-import com.raccoongang.core.presentation.course.CourseViewMode
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.course.presentation.CourseRouter
-import com.raccoongang.course.presentation.container.CourseContainerFragment
-import com.raccoongang.course.presentation.ui.CourseImageHeader
-import com.raccoongang.course.presentation.ui.CourseSectionCard
-import com.raccoongang.course.presentation.units.CourseUnitsFragment
+import org.openedx.core.BlockType
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.*
+import org.openedx.core.presentation.course.CourseViewMode
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appTypography
+import org.openedx.course.presentation.CourseRouter
+import org.openedx.course.presentation.container.CourseContainerFragment
+import org.openedx.course.presentation.outline.CourseOutlineFragment.Companion.getUnitBlockIcon
+import org.openedx.course.presentation.ui.CourseImageHeader
+import org.openedx.course.presentation.ui.CourseSectionCard
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
@@ -72,7 +72,7 @@ class CourseOutlineFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val uiState by viewModel.uiState.observeAsState()
@@ -94,6 +94,7 @@ class CourseOutlineFragment : Fragment() {
(parentFragment as CourseContainerFragment).updateCourseStructure(false)
},
onItemClick = { block ->
+ viewModel.sequentialClickedEvent(block.blockId, block.displayName)
router.navigateToCourseSubsections(
requireActivity().supportFragmentManager,
courseId = viewModel.courseId,
@@ -104,6 +105,7 @@ class CourseOutlineFragment : Fragment() {
},
onResumeClick = { blockId ->
viewModel.resumeSectionBlock?.let { sequential ->
+ viewModel.resumeCourseTappedEvent(sequential.blockId)
router.navigateToCourseSubsections(
requireActivity().supportFragmentManager,
viewModel.courseId,
@@ -111,15 +113,6 @@ class CourseOutlineFragment : Fragment() {
sequential.displayName,
CourseViewMode.FULL
)
- viewModel.resumeVerticalBlock?.let { vertical ->
- router.navigateToCourseUnits(
- requireActivity().supportFragmentManager,
- viewModel.courseId,
- vertical.id,
- vertical.displayName,
- CourseViewMode.FULL
- )
- }
}
},
onBackClick = {
@@ -160,6 +153,15 @@ class CourseOutlineFragment : Fragment() {
)
return fragment
}
+
+ fun getUnitBlockIcon(block: Block): Int {
+ return when (block.type) {
+ BlockType.VIDEO -> org.openedx.course.R.drawable.ic_course_video
+ BlockType.PROBLEM -> org.openedx.course.R.drawable.ic_course_pen
+ BlockType.DISCUSSION -> org.openedx.course.R.drawable.ic_course_discussion
+ else -> org.openedx.course.R.drawable.ic_course_block
+ }
+ }
}
}
@@ -204,11 +206,11 @@ internal fun CourseOutlineScreen(
)
}
- val imageHeight by remember(key1 = windowSize) {
+ val listBottomPadding by remember(key1 = windowSize) {
mutableStateOf(
windowSize.windowSizeValue(
- expanded = 300.dp,
- compact = 250.dp
+ expanded = PaddingValues(bottom = 24.dp),
+ compact = PaddingValues(bottom = 24.dp)
)
)
}
@@ -216,16 +218,8 @@ internal fun CourseOutlineScreen(
val listPadding by remember(key1 = windowSize) {
mutableStateOf(
windowSize.windowSizeValue(
- expanded = PaddingValues(
- start = 6.dp,
- end = 6.dp,
- bottom = 24.dp
- ),
- compact = PaddingValues(
- start = 24.dp,
- end = 24.dp,
- bottom = 24.dp
- )
+ expanded = Modifier.padding(horizontal = 6.dp),
+ compact = Modifier.padding(horizontal = 24.dp)
)
)
}
@@ -277,26 +271,26 @@ internal fun CourseOutlineScreen(
CircularProgressIndicator(color = MaterialTheme.appColors.primary)
}
}
+
is CourseOutlineUIState.CourseData -> {
- Column(
- Modifier
- .fillMaxSize()
+ LazyColumn(
+ modifier = Modifier.fillMaxSize(),
+ contentPadding = listBottomPadding
) {
- CourseImageHeader(
- modifier = Modifier
- .aspectRatio(1.86f)
- .padding(6.dp),
- courseImage = uiState.courseStructure.media?.image?.large
- ?: "",
- courseCertificate = uiState.courseStructure.certificate
- )
- LazyColumn(
- modifier = Modifier.fillMaxWidth(),
- contentPadding = listPadding
- ) {
- if (uiState.resumeBlock != null) {
- item {
- Spacer(Modifier.height(28.dp))
+ item {
+ CourseImageHeader(
+ modifier = Modifier
+ .aspectRatio(1.86f)
+ .padding(6.dp),
+ courseImage = uiState.courseStructure.media?.image?.large
+ ?: "",
+ courseCertificate = uiState.courseStructure.certificate
+ )
+ }
+ if (uiState.resumeBlock != null) {
+ item {
+ Spacer(Modifier.height(28.dp))
+ Box(listPadding) {
if (windowSize.isTablet) {
ResumeCourseTablet(
block = uiState.resumeBlock,
@@ -310,7 +304,9 @@ internal fun CourseOutlineScreen(
}
}
}
- items(uiState.courseStructure.blockData) { block ->
+ }
+ items(uiState.courseStructure.blockData) { block ->
+ Column(listPadding) {
if (block.type == BlockType.CHAPTER) {
Text(
modifier = Modifier.padding(
@@ -372,7 +368,7 @@ private fun ResumeCourse(
modifier = Modifier.fillMaxWidth()
) {
Text(
- text = stringResource(id = com.raccoongang.course.R.string.course_continue_with),
+ text = stringResource(id = org.openedx.course.R.string.course_continue_with),
style = MaterialTheme.appTypography.labelMedium,
color = MaterialTheme.appColors.textPrimaryVariant
)
@@ -383,7 +379,7 @@ private fun ResumeCourse(
) {
Icon(
modifier = Modifier.size(24.dp),
- painter = painterResource(id = CourseUnitsFragment.getUnitBlockIcon(block)),
+ painter = painterResource(id = getUnitBlockIcon(block)),
contentDescription = null,
tint = MaterialTheme.appColors.textPrimary
)
@@ -396,14 +392,14 @@ private fun ResumeCourse(
)
}
Spacer(Modifier.height(24.dp))
- NewEdxButton(
- text = stringResource(id = com.raccoongang.course.R.string.course_continue),
+ OpenEdXButton(
+ text = stringResource(id = org.openedx.course.R.string.course_continue),
onClick = {
onResumeClick(block.id)
},
content = {
TextIcon(
- text = stringResource(id = com.raccoongang.course.R.string.course_continue),
+ text = stringResource(id = org.openedx.course.R.string.course_continue),
painter = painterResource(id = R.drawable.core_ic_forward),
color = MaterialTheme.appColors.buttonText,
textStyle = MaterialTheme.appTypography.labelLarge
@@ -423,9 +419,13 @@ private fun ResumeCourseTablet(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
- Column(Modifier.weight(1f).padding(end = 35.dp)) {
+ Column(
+ Modifier
+ .weight(1f)
+ .padding(end = 35.dp)
+ ) {
Text(
- text = stringResource(id = com.raccoongang.course.R.string.course_continue_with),
+ text = stringResource(id = org.openedx.course.R.string.course_continue_with),
style = MaterialTheme.appTypography.labelMedium,
color = MaterialTheme.appColors.textPrimaryVariant
)
@@ -436,7 +436,7 @@ private fun ResumeCourseTablet(
) {
Icon(
modifier = Modifier.size((MaterialTheme.appTypography.titleMedium.fontSize.value + 4).dp),
- painter = painterResource(id = CourseUnitsFragment.getUnitBlockIcon(block)),
+ painter = painterResource(id = getUnitBlockIcon(block)),
contentDescription = null,
tint = MaterialTheme.appColors.textPrimary
)
@@ -449,15 +449,15 @@ private fun ResumeCourseTablet(
)
}
}
- NewEdxButton(
+ OpenEdXButton(
width = Modifier.width(194.dp),
- text = stringResource(id = com.raccoongang.course.R.string.course_continue),
+ text = stringResource(id = org.openedx.course.R.string.course_continue),
onClick = {
onResumeClick(block.id)
},
content = {
TextIcon(
- text = stringResource(id = com.raccoongang.course.R.string.course_continue),
+ text = stringResource(id = org.openedx.course.R.string.course_continue),
painter = painterResource(id = R.drawable.core_ic_forward),
color = MaterialTheme.appColors.buttonText,
textStyle = MaterialTheme.appTypography.labelLarge
@@ -471,7 +471,7 @@ private fun ResumeCourseTablet(
@Preview(uiMode = UI_MODE_NIGHT_YES)
@Composable
private fun CourseOutlineScreenPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
CourseOutlineScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
uiState = CourseOutlineUIState.CourseData(
@@ -497,7 +497,7 @@ private fun CourseOutlineScreenPreview() {
@Preview(uiMode = UI_MODE_NIGHT_YES, device = Devices.NEXUS_9)
@Composable
private fun CourseOutlineScreenTabletPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
CourseOutlineScreen(
windowSize = WindowSize(WindowType.Medium, WindowType.Medium),
uiState = CourseOutlineUIState.CourseData(
@@ -523,7 +523,7 @@ private fun CourseOutlineScreenTabletPreview() {
@Preview(uiMode = UI_MODE_NIGHT_YES)
@Composable
private fun ResumeCoursePreview() {
- NewEdxTheme {
+ OpenEdXTheme {
ResumeCourse(mockChapterBlock) {}
}
}
diff --git a/course/src/main/java/com/raccoongang/course/presentation/outline/CourseOutlineUIState.kt b/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineUIState.kt
similarity index 57%
rename from course/src/main/java/com/raccoongang/course/presentation/outline/CourseOutlineUIState.kt
rename to course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineUIState.kt
index bcf9a78b9..6d8c545e5 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/outline/CourseOutlineUIState.kt
+++ b/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineUIState.kt
@@ -1,8 +1,8 @@
-package com.raccoongang.course.presentation.outline
+package org.openedx.course.presentation.outline
-import com.raccoongang.core.domain.model.Block
-import com.raccoongang.core.domain.model.CourseStructure
-import com.raccoongang.core.module.db.DownloadedState
+import org.openedx.core.domain.model.Block
+import org.openedx.core.domain.model.CourseStructure
+import org.openedx.core.module.db.DownloadedState
sealed class CourseOutlineUIState {
data class CourseData(
diff --git a/course/src/main/java/com/raccoongang/course/presentation/outline/CourseOutlineViewModel.kt b/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineViewModel.kt
similarity index 77%
rename from course/src/main/java/com/raccoongang/course/presentation/outline/CourseOutlineViewModel.kt
rename to course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineViewModel.kt
index 16c2736f2..85f110d1f 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/outline/CourseOutlineViewModel.kt
+++ b/course/src/main/java/org/openedx/course/presentation/outline/CourseOutlineViewModel.kt
@@ -1,27 +1,28 @@
-package com.raccoongang.course.presentation.outline
+package org.openedx.course.presentation.outline
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.R
-import com.raccoongang.core.SingleEventLiveData
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.domain.model.Block
-import com.raccoongang.core.domain.model.CourseComponentStatus
-import com.raccoongang.core.extension.isInternetError
-import com.raccoongang.core.module.DownloadWorkerController
-import com.raccoongang.core.module.db.DownloadDao
-import com.raccoongang.core.module.download.BaseDownloadViewModel
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.core.system.connection.NetworkConnection
-import com.raccoongang.core.system.notifier.CourseNotifier
-import com.raccoongang.core.system.notifier.CourseStructureUpdated
-import com.raccoongang.course.domain.interactor.CourseInteractor
+import org.openedx.core.BlockType
+import org.openedx.core.R
+import org.openedx.core.SingleEventLiveData
+import org.openedx.core.UIMessage
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.domain.model.Block
+import org.openedx.core.domain.model.CourseComponentStatus
+import org.openedx.core.extension.isInternetError
+import org.openedx.core.module.DownloadWorkerController
+import org.openedx.core.module.db.DownloadDao
+import org.openedx.core.module.download.BaseDownloadViewModel
+import org.openedx.core.system.ResourceManager
+import org.openedx.core.system.connection.NetworkConnection
+import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.core.system.notifier.CourseStructureUpdated
+import org.openedx.course.domain.interactor.CourseInteractor
+import org.openedx.course.presentation.CourseAnalytics
import kotlinx.coroutines.launch
-import com.raccoongang.course.R as courseR
+import org.openedx.course.R as courseR
class CourseOutlineViewModel(
val courseId: String,
@@ -30,6 +31,7 @@ class CourseOutlineViewModel(
private val notifier: CourseNotifier,
private val networkConnection: NetworkConnection,
private val preferencesManager: PreferencesManager,
+ private val analytics: CourseAnalytics,
downloadDao: DownloadDao,
workerController: DownloadWorkerController
) : BaseDownloadViewModel(downloadDao, preferencesManager, workerController) {
@@ -179,4 +181,23 @@ class CourseOutlineViewModel(
return resumeVerticalBlock
}
+ fun resumeCourseTappedEvent(blockId: String) {
+ val currentState = uiState.value
+ if (currentState is CourseOutlineUIState.CourseData) {
+ analytics.resumeCourseTappedEvent(courseId, currentState.courseStructure.name, blockId)
+ }
+ }
+
+ fun sequentialClickedEvent(blockId: String, blockName: String) {
+ val currentState = uiState.value
+ if (currentState is CourseOutlineUIState.CourseData) {
+ analytics.sequentialClickedEvent(
+ courseId,
+ currentState.courseStructure.name,
+ blockId,
+ blockName
+ )
+ }
+ }
+
}
\ No newline at end of file
diff --git a/course/src/main/java/com/raccoongang/course/presentation/section/CourseSectionFragment.kt b/course/src/main/java/org/openedx/course/presentation/section/CourseSectionFragment.kt
similarity index 82%
rename from course/src/main/java/com/raccoongang/course/presentation/section/CourseSectionFragment.kt
rename to course/src/main/java/org/openedx/course/presentation/section/CourseSectionFragment.kt
index e555deba0..8682caba3 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/section/CourseSectionFragment.kt
+++ b/course/src/main/java/org/openedx/course/presentation/section/CourseSectionFragment.kt
@@ -1,17 +1,38 @@
-package com.raccoongang.course.presentation.section
+package org.openedx.course.presentation.section
import android.content.res.Configuration
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.navigationBarsPadding
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
-import androidx.compose.material.*
+import androidx.compose.material.CircularProgressIndicator
+import androidx.compose.material.Divider
+import androidx.compose.material.Icon
+import androidx.compose.material.IconButton
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.Scaffold
+import androidx.compose.material.Surface
+import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
-import androidx.compose.material.icons.filled.TaskAlt
+import androidx.compose.material.rememberScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
@@ -19,7 +40,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
-import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.res.painterResource
@@ -32,21 +52,27 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.Block
-import com.raccoongang.core.domain.model.BlockCounts
-import com.raccoongang.core.extension.serializable
-import com.raccoongang.core.module.db.DownloadedState
-import com.raccoongang.core.presentation.course.CourseViewMode
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appShapes
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.course.R
-import com.raccoongang.course.presentation.CourseRouter
-import com.raccoongang.course.presentation.ui.CardArrow
+import org.openedx.core.BlockType
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.Block
+import org.openedx.core.domain.model.BlockCounts
+import org.openedx.core.extension.serializable
+import org.openedx.core.module.db.DownloadedState
+import org.openedx.core.presentation.course.CourseViewMode
+import org.openedx.core.ui.BackBtn
+import org.openedx.core.ui.HandleUIMessage
+import org.openedx.core.ui.WindowSize
+import org.openedx.core.ui.WindowType
+import org.openedx.core.ui.rememberWindowSize
+import org.openedx.core.ui.statusBarsInset
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appShapes
+import org.openedx.core.ui.theme.appTypography
+import org.openedx.core.ui.windowSizeValue
+import org.openedx.course.R
+import org.openedx.course.presentation.CourseRouter
+import org.openedx.course.presentation.ui.CardArrow
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
@@ -77,7 +103,7 @@ class CourseSectionFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val uiState by viewModel.uiState.observeAsState(CourseSectionUIState.Loading)
@@ -91,13 +117,16 @@ class CourseSectionFragment : Fragment() {
requireActivity().supportFragmentManager.popBackStack()
},
onItemClick = { block ->
- router.navigateToCourseUnits(
- requireActivity().supportFragmentManager,
- courseId = viewModel.courseId,
- blockId = block.id,
- courseName = block.displayName,
- mode = viewModel.mode
- )
+ if (block.descendants.isNotEmpty()) {
+ viewModel.verticalClickedEvent(block.blockId, block.displayName)
+ router.navigateToCourseContainer(
+ requireActivity().supportFragmentManager,
+ block.id,
+ courseId = viewModel.courseId,
+ courseName = block.displayName,
+ mode = viewModel.mode
+ )
+ }
},
onDownloadClick = {
if (viewModel.isBlockDownloading(it.id)) {
@@ -109,7 +138,7 @@ class CourseSectionFragment : Fragment() {
requireContext().externalCacheDir.toString() +
File.separator +
requireContext()
- .getString(com.raccoongang.core.R.string.app_name)
+ .getString(org.openedx.core.R.string.app_name)
.replace(Regex("\\s"), "_"), it.id
)
}
@@ -224,6 +253,7 @@ private fun CourseSectionScreen(
CircularProgressIndicator(color = MaterialTheme.appColors.primary)
}
}
+
is CourseSectionUIState.Blocks -> {
Column(Modifier.fillMaxSize()) {
LazyColumn(
@@ -352,7 +382,7 @@ private fun getUnitBlockIcon(block: Block): Int {
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun CourseSectionScreenPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
CourseSectionScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
uiState = CourseSectionUIState.Blocks(
@@ -362,7 +392,8 @@ private fun CourseSectionScreenPreview() {
mockBlock,
mockBlock
),
- mapOf()
+ mapOf(),
+ ""
),
"Course default",
uiMessage = null,
@@ -377,7 +408,7 @@ private fun CourseSectionScreenPreview() {
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, device = Devices.NEXUS_9)
@Composable
private fun CourseSectionScreenTabletPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
CourseSectionScreen(
windowSize = WindowSize(WindowType.Medium, WindowType.Medium),
uiState = CourseSectionUIState.Blocks(
@@ -387,7 +418,8 @@ private fun CourseSectionScreenTabletPreview() {
mockBlock,
mockBlock
),
- mapOf()
+ mapOf(),
+ ""
),
"Course default",
uiMessage = null,
diff --git a/course/src/main/java/org/openedx/course/presentation/section/CourseSectionUIState.kt b/course/src/main/java/org/openedx/course/presentation/section/CourseSectionUIState.kt
new file mode 100644
index 000000000..8cbc39050
--- /dev/null
+++ b/course/src/main/java/org/openedx/course/presentation/section/CourseSectionUIState.kt
@@ -0,0 +1,13 @@
+package org.openedx.course.presentation.section
+
+import org.openedx.core.domain.model.Block
+import org.openedx.core.module.db.DownloadedState
+
+sealed class CourseSectionUIState {
+ data class Blocks(
+ val blocks: List,
+ val downloadedState: Map,
+ val courseName: String
+ ) : CourseSectionUIState()
+ object Loading : CourseSectionUIState()
+}
\ No newline at end of file
diff --git a/course/src/main/java/com/raccoongang/course/presentation/section/CourseSectionViewModel.kt b/course/src/main/java/org/openedx/course/presentation/section/CourseSectionViewModel.kt
similarity index 63%
rename from course/src/main/java/com/raccoongang/course/presentation/section/CourseSectionViewModel.kt
rename to course/src/main/java/org/openedx/course/presentation/section/CourseSectionViewModel.kt
index 660771018..bbcc5bad1 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/section/CourseSectionViewModel.kt
+++ b/course/src/main/java/org/openedx/course/presentation/section/CourseSectionViewModel.kt
@@ -1,23 +1,26 @@
-package com.raccoongang.course.presentation.section
+package org.openedx.course.presentation.section
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.R
-import com.raccoongang.core.SingleEventLiveData
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.domain.model.Block
-import com.raccoongang.core.extension.isInternetError
-import com.raccoongang.core.module.DownloadWorkerController
-import com.raccoongang.core.module.db.DownloadDao
-import com.raccoongang.core.module.download.BaseDownloadViewModel
-import com.raccoongang.core.presentation.course.CourseViewMode
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.core.system.connection.NetworkConnection
-import com.raccoongang.course.domain.interactor.CourseInteractor
+import org.openedx.core.BlockType
+import org.openedx.core.R
+import org.openedx.core.SingleEventLiveData
+import org.openedx.core.UIMessage
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.domain.model.Block
+import org.openedx.core.extension.isInternetError
+import org.openedx.core.module.DownloadWorkerController
+import org.openedx.core.module.db.DownloadDao
+import org.openedx.core.module.download.BaseDownloadViewModel
+import org.openedx.core.presentation.course.CourseViewMode
+import org.openedx.core.system.ResourceManager
+import org.openedx.core.system.connection.NetworkConnection
+import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.core.system.notifier.CourseSectionChanged
+import org.openedx.course.domain.interactor.CourseInteractor
+import org.openedx.course.presentation.CourseAnalytics
import kotlinx.coroutines.launch
class CourseSectionViewModel(
@@ -25,6 +28,8 @@ class CourseSectionViewModel(
private val resourceManager: ResourceManager,
private val networkConnection: NetworkConnection,
private val preferencesManager: PreferencesManager,
+ private val notifier: CourseNotifier,
+ private val analytics: CourseAnalytics,
workerController: DownloadWorkerController,
downloadDao: DownloadDao,
val courseId: String
@@ -46,8 +51,21 @@ class CourseSectionViewModel(
downloadModelsStatusFlow.collect { downloadModels ->
if (uiState.value is CourseSectionUIState.Blocks) {
val list = (uiState.value as CourseSectionUIState.Blocks).blocks
+ val courseName = (uiState.value as CourseSectionUIState.Blocks).courseName
_uiState.value =
- CourseSectionUIState.Blocks(ArrayList(list), downloadModels.toMap())
+ CourseSectionUIState.Blocks(
+ ArrayList(list),
+ downloadModels.toMap(),
+ courseName
+ )
+ }
+ }
+ }
+
+ viewModelScope.launch {
+ notifier.notifier.collect { event ->
+ if (event is CourseSectionChanged) {
+ getBlocks(event.blockId, mode)
}
}
}
@@ -65,8 +83,12 @@ class CourseSectionViewModel(
setBlocks(blocks)
val newList = getDescendantBlocks(blocks, blockId)
initDownloadModelsStatus()
-
- _uiState.value = CourseSectionUIState.Blocks(newList, getDownloadModelsStatus())
+ _uiState.value =
+ CourseSectionUIState.Blocks(
+ ArrayList(newList),
+ getDownloadModelsStatus(),
+ courseStructure.name
+ )
} catch (e: Exception) {
if (e.isInternetError()) {
_uiMessage.value =
@@ -85,7 +107,7 @@ class CourseSectionViewModel(
super.saveDownloadModels(folder, id)
} else {
_uiMessage.value =
- UIMessage.ToastMessage(resourceManager.getString(com.raccoongang.course.R.string.course_can_download_only_with_wifi))
+ UIMessage.ToastMessage(resourceManager.getString(org.openedx.course.R.string.course_can_download_only_with_wifi))
}
} else {
super.saveDownloadModels(folder, id)
@@ -111,4 +133,11 @@ class CourseSectionViewModel(
}
return resultList
}
+
+ fun verticalClickedEvent(blockId: String, blockName: String) {
+ val currentState = uiState.value
+ if (currentState is CourseSectionUIState.Blocks) {
+ analytics.verticalClickedEvent(courseId, currentState.courseName, blockId, blockName)
+ }
+ }
}
\ No newline at end of file
diff --git a/course/src/main/java/com/raccoongang/course/presentation/ui/CourseUI.kt b/course/src/main/java/org/openedx/course/presentation/ui/CourseUI.kt
similarity index 72%
rename from course/src/main/java/com/raccoongang/course/presentation/ui/CourseUI.kt
rename to course/src/main/java/org/openedx/course/presentation/ui/CourseUI.kt
index 184c87c38..09b782084 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/ui/CourseUI.kt
+++ b/course/src/main/java/org/openedx/course/presentation/ui/CourseUI.kt
@@ -1,17 +1,49 @@
-package com.raccoongang.course.presentation.ui
+package org.openedx.course.presentation.ui
import android.content.res.Configuration
+import androidx.compose.animation.animateColorAsState
+import androidx.compose.animation.core.animateDpAsState
+import androidx.compose.animation.core.tween
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.itemsIndexed
-import androidx.compose.material.*
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.Button
+import androidx.compose.material.ButtonDefaults
+import androidx.compose.material.CircularProgressIndicator
+import androidx.compose.material.Icon
+import androidx.compose.material.IconButton
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.OutlinedButton
+import androidx.compose.material.Scaffold
+import androidx.compose.material.Surface
+import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.*
-import androidx.compose.runtime.*
+import androidx.compose.material.icons.filled.ChevronRight
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material.icons.filled.Home
+import androidx.compose.material.icons.filled.TaskAlt
+import androidx.compose.material.icons.filled.Wifi
+import androidx.compose.material.rememberScaffoldState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -29,21 +61,32 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import coil.request.ImageRequest
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.BuildConfig
-import com.raccoongang.core.domain.model.*
-import com.raccoongang.core.extension.isLinkValid
-import com.raccoongang.core.module.db.DownloadedState
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appShapes
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.course.R
+import org.openedx.core.BlockType
+import org.openedx.core.BuildConfig
+import org.openedx.core.domain.model.Block
+import org.openedx.core.domain.model.BlockCounts
+import org.openedx.core.domain.model.Certificate
+import org.openedx.core.domain.model.CourseSharingUtmParameters
+import org.openedx.core.domain.model.CoursewareAccess
+import org.openedx.core.domain.model.EnrolledCourse
+import org.openedx.core.domain.model.EnrolledCourseData
+import org.openedx.core.extension.isLinkValid
+import org.openedx.core.module.db.DownloadedState
+import org.openedx.core.ui.IconText
+import org.openedx.core.ui.OpenEdXButton
+import org.openedx.core.ui.OpenEdXOutlinedButton
+import org.openedx.core.ui.WindowSize
+import org.openedx.core.ui.WindowType
+import org.openedx.core.ui.noRippleClickable
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appShapes
+import org.openedx.core.ui.theme.appTypography
+import org.openedx.course.R
import org.jsoup.Jsoup
import subtitleFile.TimedTextObject
-import java.util.*
-import com.raccoongang.course.R as courseR
+import java.util.Date
+import org.openedx.course.R as courseR
@Composable
fun CourseImageHeader(
@@ -55,14 +98,14 @@ fun CourseImageHeader(
val imageUrl = if (courseImage?.isLinkValid() == true) {
courseImage
} else {
- BuildConfig.BASE_URL.dropLast(1) + courseImage
+ org.openedx.core.BuildConfig.BASE_URL.dropLast(1) + courseImage
}
Box(modifier = modifier, contentAlignment = Alignment.Center) {
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(imageUrl)
- .error(com.raccoongang.core.R.drawable.core_no_image_course)
- .placeholder(com.raccoongang.core.R.drawable.core_no_image_course)
+ .error(org.openedx.core.R.drawable.core_no_image_course)
+ .placeholder(org.openedx.core.R.drawable.core_no_image_course)
.build(),
contentDescription = null,
contentScale = ContentScale.Crop,
@@ -97,7 +140,7 @@ fun CourseImageHeader(
color = Color.White
)
Spacer(Modifier.height(20.dp))
- NewEdxOutlinedButton(
+ OpenEdXOutlinedButton(
modifier = Modifier,
borderColor = Color.White,
textColor = MaterialTheme.appColors.buttonText,
@@ -285,109 +328,145 @@ fun VideoTitle(text: String) {
@Composable
fun NavigationUnitsButtons(
windowSize: WindowSize,
- prevButtonText: String?,
nextButtonText: String,
+ hasPrevBlock: Boolean,
hasNextBlock: Boolean,
onPrevClick: () -> Unit,
onNextClick: () -> Unit
) {
val nextButtonIcon = if (hasNextBlock) {
- painterResource(id = com.raccoongang.core.R.drawable.core_ic_forward)
+ painterResource(id = org.openedx.core.R.drawable.core_ic_down)
} else {
- painterResource(id = com.raccoongang.core.R.drawable.core_ic_check)
+ painterResource(id = org.openedx.core.R.drawable.core_ic_check)
}
- Box(
+ val nextButtonModifier = if (hasPrevBlock) {
Modifier
+ } else Modifier.fillMaxWidth(0.6f)
+
+ Row(
+ modifier = Modifier
.fillMaxWidth()
- .background(MaterialTheme.appColors.surface),
- contentAlignment = Alignment.Center
+ .padding(horizontal = 12.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
) {
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 12.dp),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.Center
- ) {
-
- val buttonWidth by remember(key1 = windowSize) {
- mutableStateOf(
- windowSize.windowSizeValue(
- expanded = Modifier.widthIn(152.dp, 162.dp),
- compact = Modifier.weight(1f)
- )
- )
- }
-
- if (prevButtonText != null) {
- OutlinedButton(
- modifier = Modifier
- .then(buttonWidth)
- .height(42.dp),
- colors = ButtonDefaults.outlinedButtonColors(
- contentColor = MaterialTheme.appColors.primary,
- backgroundColor = Color.Transparent
- ),
- border = BorderStroke(1.dp, MaterialTheme.appColors.primary),
- elevation = null,
- shape = MaterialTheme.appShapes.buttonShape,
- onClick = onPrevClick
- ) {
- Row(
- modifier = Modifier.fillMaxWidth(),
- verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = Arrangement.SpaceBetween
- ) {
- Icon(
- painter = painterResource(id = com.raccoongang.core.R.drawable.core_ic_back),
- contentDescription = null,
- tint = MaterialTheme.appColors.primary
- )
- Spacer(Modifier.width(8.dp))
- Text(
- text = prevButtonText,
- color = MaterialTheme.appColors.primary,
- style = MaterialTheme.appTypography.labelLarge
- )
- }
- }
- Spacer(Modifier.width(24.dp))
- }
- Button(
+ if (hasPrevBlock) {
+ OutlinedButton(
modifier = Modifier
- .then(buttonWidth)
.height(42.dp),
- colors = ButtonDefaults.buttonColors(
- backgroundColor = MaterialTheme.appColors.buttonBackground
+ colors = ButtonDefaults.outlinedButtonColors(
+ backgroundColor = MaterialTheme.appColors.background
),
+ border = BorderStroke(1.dp, MaterialTheme.appColors.primary),
elevation = null,
- shape = MaterialTheme.appShapes.buttonShape,
- onClick = onNextClick
+ shape = MaterialTheme.appShapes.navigationButtonShape,
+ onClick = onPrevClick,
) {
Row(
- modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
- horizontalArrangement = if (prevButtonText != null) Arrangement.SpaceBetween else Arrangement.Center
+ horizontalArrangement = Arrangement.Center
) {
Text(
- text = nextButtonText,
- color = MaterialTheme.appColors.buttonText,
+ text = stringResource(R.string.course_navigation_prev),
+ color = MaterialTheme.appColors.primary,
style = MaterialTheme.appTypography.labelLarge
)
Spacer(Modifier.width(8.dp))
Icon(
- painter = nextButtonIcon,
+ painter = painterResource(id = org.openedx.core.R.drawable.core_ic_up),
contentDescription = null,
- tint = MaterialTheme.appColors.buttonText
+ tint = MaterialTheme.appColors.primary
)
}
-
}
+ Spacer(Modifier.width(16.dp))
+ }
+ Button(
+ modifier = Modifier
+ .height(42.dp).then(nextButtonModifier),
+ colors = ButtonDefaults.buttonColors(
+ backgroundColor = MaterialTheme.appColors.buttonBackground
+ ),
+ elevation = null,
+ shape = MaterialTheme.appShapes.navigationButtonShape,
+ onClick = onNextClick
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.Center
+ ) {
+ Text(
+ text = nextButtonText,
+ color = MaterialTheme.appColors.buttonText,
+ style = MaterialTheme.appTypography.labelLarge
+ )
+ Spacer(Modifier.width(8.dp))
+ Icon(
+ painter = nextButtonIcon,
+ contentDescription = null,
+ tint = MaterialTheme.appColors.buttonText
+ )
+ }
+ }
+ }
+}
+
+@Composable
+fun VerticalPageIndicator(
+ numberOfPages: Int,
+ selectedPage: Int = 0,
+ selectedColor: Color = Color.White,
+ defaultColor: Color = Color.Gray,
+ defaultRadius: Dp = 8.dp,
+ selectedLength: Dp = 25.dp,
+ space: Dp = 4.dp,
+ modifier: Modifier = Modifier
+) {
+ Column(
+ verticalArrangement = Arrangement.Center,
+ horizontalAlignment = Alignment.CenterHorizontally,
+ modifier = modifier
+ ) {
+ repeat(numberOfPages) {
+ Indicator(
+ isSelected = it == selectedPage,
+ selectedColor = selectedColor,
+ defaultColor = defaultColor,
+ defaultRadius = defaultRadius,
+ selectedSize = selectedLength,
+ modifier = Modifier.padding(vertical = space)
+ )
}
}
}
+@Composable
+fun Indicator(
+ isSelected: Boolean,
+ selectedColor: Color,
+ defaultColor: Color,
+ defaultRadius: Dp,
+ selectedSize: Dp,
+ modifier: Modifier = Modifier
+) {
+ val size by animateDpAsState(
+ targetValue = if (isSelected) selectedSize else defaultRadius,
+ animationSpec = tween(300)
+ )
+ val color by animateColorAsState(
+ targetValue = if (isSelected) selectedColor else defaultColor,
+ animationSpec = tween(300)
+ )
+
+ Box(
+ modifier = modifier
+ .clip(CircleShape)
+ .background(color)
+ .size(size)
+ )
+}
+
@Composable
fun ConnectionErrorView(
modifier: Modifier,
@@ -413,10 +492,10 @@ fun ConnectionErrorView(
textAlign = TextAlign.Center
)
Spacer(Modifier.height(16.dp))
- NewEdxButton(
+ OpenEdXButton(
width = Modifier
.widthIn(Dp.Unspecified, 162.dp),
- text = stringResource(id = com.raccoongang.core.R.string.core_reload),
+ text = stringResource(id = org.openedx.core.R.string.core_reload),
onClick = onReloadClick
)
}
@@ -493,10 +572,10 @@ fun VideoSubtitles(
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun NavigationUnitsButtonsOnlyNextButtonPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
NavigationUnitsButtons(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
- prevButtonText = null,
+ hasPrevBlock = true,
hasNextBlock = true,
nextButtonText = "Next",
onPrevClick = {}) {}
@@ -507,10 +586,10 @@ private fun NavigationUnitsButtonsOnlyNextButtonPreview() {
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun NavigationUnitsButtonsOnlyFinishButtonPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
NavigationUnitsButtons(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
- prevButtonText = null,
+ hasPrevBlock = true,
hasNextBlock = false,
nextButtonText = "Finish",
onPrevClick = {}) {}
@@ -521,10 +600,10 @@ private fun NavigationUnitsButtonsOnlyFinishButtonPreview() {
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun NavigationUnitsButtonsWithFinishPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
NavigationUnitsButtons(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
- prevButtonText = "Previous",
+ hasPrevBlock = true,
hasNextBlock = false,
nextButtonText = "Finish",
onPrevClick = {}) {}
@@ -535,10 +614,10 @@ private fun NavigationUnitsButtonsWithFinishPreview() {
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun NavigationUnitsButtonsWithNextPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
NavigationUnitsButtons(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
- prevButtonText = "Previous",
+ hasPrevBlock = true,
hasNextBlock = true,
nextButtonText = "Next",
onPrevClick = {}) {}
@@ -549,7 +628,7 @@ private fun NavigationUnitsButtonsWithNextPreview() {
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun VideoRotateViewPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
VideoRotateView()
}
}
@@ -558,7 +637,7 @@ private fun VideoRotateViewPreview() {
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun SequentialItemPreview() {
- NewEdxTheme() {
+ OpenEdXTheme() {
Surface(color = MaterialTheme.appColors.background) {
SequentialItem(block = mockChapterBlock, onClick = {})
}
@@ -569,7 +648,7 @@ private fun SequentialItemPreview() {
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun CourseChapterItemPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
Surface(color = MaterialTheme.appColors.background) {
CourseSectionCard(
mockChapterBlock,
@@ -585,7 +664,7 @@ private fun CourseChapterItemPreview() {
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun CourseHeaderPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
Surface(color = MaterialTheme.appColors.background) {
CourseImageHeader(
modifier = Modifier
diff --git a/course/src/main/java/com/raccoongang/course/presentation/unit/NotSupportedUnitFragment.kt b/course/src/main/java/org/openedx/course/presentation/unit/NotSupportedUnitFragment.kt
similarity index 91%
rename from course/src/main/java/com/raccoongang/course/presentation/unit/NotSupportedUnitFragment.kt
rename to course/src/main/java/org/openedx/course/presentation/unit/NotSupportedUnitFragment.kt
index 17bf8bc6d..b89e4ae66 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/unit/NotSupportedUnitFragment.kt
+++ b/course/src/main/java/org/openedx/course/presentation/unit/NotSupportedUnitFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.course.presentation.unit
+package org.openedx.course.presentation.unit
import android.os.Bundle
import android.view.LayoutInflater
@@ -20,14 +20,14 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
-import com.raccoongang.core.ui.WindowSize
-import com.raccoongang.core.ui.rememberWindowSize
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appShapes
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.core.ui.windowSizeValue
-import com.raccoongang.course.R as courseR
+import org.openedx.core.ui.WindowSize
+import org.openedx.core.ui.rememberWindowSize
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appShapes
+import org.openedx.core.ui.theme.appTypography
+import org.openedx.core.ui.windowSizeValue
+import org.openedx.course.R as courseR
class NotSupportedUnitFragment : Fragment() {
@@ -45,7 +45,7 @@ class NotSupportedUnitFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
NotSupportedUnitScreen(
windowSize = windowSize,
diff --git a/course/src/main/java/org/openedx/course/presentation/unit/container/CourseUnitContainerAdapter.kt b/course/src/main/java/org/openedx/course/presentation/unit/container/CourseUnitContainerAdapter.kt
new file mode 100644
index 000000000..b0ac5a952
--- /dev/null
+++ b/course/src/main/java/org/openedx/course/presentation/unit/container/CourseUnitContainerAdapter.kt
@@ -0,0 +1,98 @@
+package org.openedx.course.presentation.unit.container
+
+import androidx.fragment.app.Fragment
+import androidx.viewpager2.adapter.FragmentStateAdapter
+import org.openedx.core.BlockType
+import org.openedx.core.FragmentViewType
+import org.openedx.core.domain.model.Block
+import org.openedx.course.presentation.unit.NotSupportedUnitFragment
+import org.openedx.course.presentation.unit.html.HtmlUnitFragment
+import org.openedx.course.presentation.unit.video.VideoUnitFragment
+import org.openedx.course.presentation.unit.video.YoutubeVideoUnitFragment
+import org.openedx.discussion.presentation.threads.DiscussionThreadsFragment
+import org.openedx.discussion.presentation.topics.DiscussionTopicsFragment
+
+class CourseUnitContainerAdapter(
+ fragment: Fragment,
+ private val viewModel: CourseUnitContainerViewModel,
+ private var blocks: List
+) : FragmentStateAdapter(fragment) {
+
+ override fun getItemCount(): Int = blocks.size
+
+ override fun createFragment(position: Int): Fragment = unitBlockFragment(blocks[position])
+
+ private fun unitBlockFragment(block: Block): Fragment {
+ return when (block.type) {
+ BlockType.HTML,
+ BlockType.PROBLEM,
+ BlockType.OPENASSESSMENT,
+ BlockType.DRAG_AND_DROP_V2,
+ BlockType.WORD_CLOUD,
+ BlockType.LTI_CONSUMER,
+ -> {
+ HtmlUnitFragment.newInstance(block.id, block.studentViewUrl)
+ }
+
+ BlockType.VIDEO -> {
+ val encodedVideos = block.studentViewData!!.encodedVideos!!
+ val transcripts = block.studentViewData!!.transcripts
+ with(encodedVideos) {
+ var isDownloaded = false
+ val videoUrl = if (viewModel.getDownloadModelById(block.id) != null) {
+ isDownloaded = true
+ viewModel.getDownloadModelById(block.id)!!.path
+ } else if (fallback != null) {
+ fallback!!.url
+ } else if (hls != null) {
+ hls!!.url
+ } else if (desktopMp4 != null) {
+ desktopMp4!!.url
+ } else if (mobileHigh != null) {
+ mobileHigh!!.url
+ } else if (mobileLow != null) {
+ mobileLow!!.url
+ } else {
+ ""
+ }
+ if (videoUrl.isNotEmpty()) {
+ VideoUnitFragment.newInstance(
+ block.id,
+ viewModel.courseId,
+ videoUrl,
+ transcripts?.toMap() ?: emptyMap(),
+ block.displayName,
+ isDownloaded
+ )
+ } else {
+ YoutubeVideoUnitFragment.newInstance(
+ block.id,
+ viewModel.courseId,
+ encodedVideos.youtube?.url!!,
+ transcripts?.toMap() ?: emptyMap(),
+ block.displayName
+ )
+ }
+ }
+ }
+
+ BlockType.DISCUSSION -> {
+ DiscussionThreadsFragment.newInstance(
+ DiscussionTopicsFragment.TOPIC,
+ viewModel.courseId,
+ block.studentViewData?.topicId ?: "",
+ block.displayName,
+ FragmentViewType.MAIN_CONTENT.name,
+ block.id
+ )
+ }
+
+ else -> {
+ NotSupportedUnitFragment.newInstance(
+ block.id,
+ block.lmsWebUrl
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/course/src/main/java/org/openedx/course/presentation/unit/container/CourseUnitContainerFragment.kt b/course/src/main/java/org/openedx/course/presentation/unit/container/CourseUnitContainerFragment.kt
new file mode 100644
index 000000000..8c7544e80
--- /dev/null
+++ b/course/src/main/java/org/openedx/course/presentation/unit/container/CourseUnitContainerFragment.kt
@@ -0,0 +1,285 @@
+package org.openedx.course.presentation.unit.container
+
+import android.os.Bundle
+import android.os.SystemClock
+import android.view.View
+import android.widget.FrameLayout
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.MaterialTheme
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.livedata.observeAsState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.os.bundleOf
+import androidx.fragment.app.Fragment
+import androidx.viewpager2.widget.ViewPager2
+import org.openedx.core.extension.serializable
+import org.openedx.core.presentation.course.CourseViewMode
+import org.openedx.core.presentation.global.InsetHolder
+import org.openedx.core.presentation.global.viewBinding
+import org.openedx.core.ui.BackBtn
+import org.openedx.core.ui.rememberWindowSize
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.course.R
+import org.openedx.course.databinding.FragmentCourseUnitContainerBinding
+import org.openedx.course.presentation.ChapterEndFragmentDialog
+import org.openedx.course.presentation.CourseRouter
+import org.openedx.course.presentation.DialogListener
+import org.openedx.course.presentation.ui.NavigationUnitsButtons
+import org.openedx.course.presentation.ui.VerticalPageIndicator
+import org.koin.android.ext.android.inject
+import org.koin.androidx.viewmodel.ext.android.viewModel
+import org.koin.core.parameter.parametersOf
+
+class CourseUnitContainerFragment : Fragment(R.layout.fragment_course_unit_container) {
+
+ private val binding by viewBinding(FragmentCourseUnitContainerBinding::bind)
+
+ private val viewModel by viewModel {
+ parametersOf(requireArguments().getString(ARG_COURSE_ID, ""))
+ }
+
+ private val router by inject()
+
+ private var blockId: String = ""
+
+ private lateinit var adapter: CourseUnitContainerAdapter
+
+ private var lastClickTime = 0L
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ lifecycle.addObserver(viewModel)
+ blockId = requireArguments().getString(ARG_BLOCK_ID, "")
+ viewModel.loadBlocks(requireArguments().serializable(ARG_MODE)!!)
+ viewModel.setupCurrentIndex(blockId)
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ val insetHolder = requireActivity() as InsetHolder
+ val statusBarParams = binding.statusBarInset.layoutParams as ConstraintLayout.LayoutParams
+ statusBarParams.topMargin = insetHolder.topInset
+ binding.statusBarInset.layoutParams = statusBarParams
+ val bottomNavigationParams =
+ binding.cvNavigationBar.layoutParams as ConstraintLayout.LayoutParams
+ bottomNavigationParams.bottomMargin = insetHolder.bottomInset
+ binding.cvNavigationBar.layoutParams = bottomNavigationParams
+ val containerParams =
+ binding.viewPager.layoutParams as FrameLayout.LayoutParams
+ containerParams.bottomMargin = insetHolder.bottomInset
+ binding.viewPager.layoutParams = containerParams
+
+ initViewPager()
+ if (savedInstanceState == null) {
+ val currentBlockIndex = viewModel.getUnitBlocks().indexOfFirst {
+ viewModel.getCurrentBlock().id == it.id
+ }
+ if (currentBlockIndex != -1) {
+ binding.viewPager.currentItem = currentBlockIndex
+ }
+ }
+
+ binding.cvNavigationBar.setContent {
+ OpenEdXTheme {
+ var nextButtonText by rememberSaveable {
+ mutableStateOf(viewModel.nextButtonText)
+ }
+ var hasNextBlock by rememberSaveable {
+ mutableStateOf(viewModel.hasNextBlock)
+ }
+ var hasPrevBlock by rememberSaveable {
+ mutableStateOf(viewModel.hasNextBlock)
+ }
+
+ val windowSize = rememberWindowSize()
+
+ updateNavigationButtons { next, hasPrev, hasNext ->
+ nextButtonText = next
+ hasPrevBlock = hasPrev
+ hasNextBlock = hasNext
+ }
+
+ NavigationUnitsButtons(
+ windowSize = windowSize,
+ hasPrevBlock = hasPrevBlock,
+ nextButtonText = nextButtonText,
+ hasNextBlock = hasNextBlock,
+ onPrevClick = {
+ if (!restrictDoubleClick()) {
+ val block = viewModel.moveToPrevBlock()
+ if (block != null) {
+ viewModel.prevBlockClickedEvent(block.blockId, block.displayName)
+ if (!block.type.isContainer()) {
+ binding.viewPager.setCurrentItem(
+ binding.viewPager.currentItem - 1,
+ true
+ )
+ updateNavigationButtons { next, hasPrev, hasNext ->
+ nextButtonText = next
+ hasPrevBlock = hasPrev
+ hasNextBlock = hasNext
+ }
+ }
+ }
+ }
+ },
+ onNextClick = {
+ if (!restrictDoubleClick()) {
+ val block = viewModel.moveToNextBlock()
+ if (block != null) {
+ viewModel.nextBlockClickedEvent(block.blockId, block.displayName)
+ if (!block.type.isContainer()) {
+ binding.viewPager.setCurrentItem(
+ binding.viewPager.currentItem + 1,
+ true
+ )
+ updateNavigationButtons { next, hasPrev, hasNext ->
+ nextButtonText = next
+ hasPrevBlock = hasPrev
+ hasNextBlock = hasNext
+ }
+ }
+ } else {
+ val currentVerticalBlock = viewModel.getCurrentVerticalBlock()
+ val nextVerticalBlock = viewModel.getNextVerticalBlock()
+ val dialog = ChapterEndFragmentDialog.newInstance(
+ currentVerticalBlock?.displayName ?: "",
+ nextVerticalBlock?.displayName ?: ""
+ )
+ currentVerticalBlock?.let {
+ viewModel.finishVerticalClickedEvent(
+ it.blockId,
+ it.displayName
+ )
+ }
+ dialog.listener = object : DialogListener {
+ override fun onClick(value: T) {
+ viewModel.proceedToNext()
+ val nextBlock = viewModel.getCurrentVerticalBlock()
+ nextBlock?.let {
+ viewModel.finishVerticalNextClickedEvent(
+ it.blockId,
+ it.displayName
+ )
+ if (it.type.isContainer()) {
+ router.replaceCourseContainer(
+ requireActivity().supportFragmentManager,
+ it.id,
+ viewModel.courseId,
+ requireArguments().getString(
+ ARG_COURSE_NAME,
+ ""
+ ),
+ requireArguments().serializable(ARG_MODE)!!
+ )
+ }
+ }
+ }
+
+ override fun onDismiss() {
+ viewModel.finishVerticalBackClickedEvent()
+ }
+ }
+ dialog.show(
+ requireActivity().supportFragmentManager,
+ ChapterEndFragmentDialog::class.simpleName
+ )
+ }
+ }
+ }
+ )
+ }
+ }
+
+ binding.cvCount.setContent {
+ OpenEdXTheme {
+
+ val index by viewModel.indexInContainer.observeAsState(1)
+ val units by viewModel.verticalBlockCounts.observeAsState(1)
+
+ VerticalPageIndicator(
+ numberOfPages = units,
+ selectedColor = MaterialTheme.appColors.primary,
+ defaultColor = MaterialTheme.appColors.bottomSheetToggle,
+ selectedPage = index,
+ defaultRadius = 3.dp,
+ selectedLength = 5.dp,
+ modifier = Modifier
+ .width(24.dp)
+ .padding(end = 6.dp)
+ )
+ }
+ }
+
+ binding.btnBack.setContent {
+ OpenEdXTheme {
+ BackBtn {
+ requireActivity().supportFragmentManager.popBackStack()
+ }
+ }
+ }
+
+ }
+
+ private fun updateNavigationButtons(updatedData: (String, Boolean, Boolean) -> Unit) {
+ val hasPrevBlock: Boolean = !viewModel.isFirstIndexInContainer
+ val hasNextBlock: Boolean
+ val nextButtonText = if (viewModel.isLastIndexInContainer) {
+ hasNextBlock = false
+ getString(R.string.course_navigation_finish)
+ } else {
+ hasNextBlock = true
+ getString(R.string.course_navigation_next)
+ }
+ updatedData(nextButtonText, hasPrevBlock, hasNextBlock)
+ }
+
+ private fun restrictDoubleClick(): Boolean {
+ if (SystemClock.elapsedRealtime() - lastClickTime < 500) {
+ return true
+ }
+ lastClickTime = SystemClock.elapsedRealtime()
+ return false
+ }
+
+ private fun initViewPager() {
+ binding.viewPager.orientation = ViewPager2.ORIENTATION_VERTICAL
+ binding.viewPager.offscreenPageLimit = 1
+ adapter = CourseUnitContainerAdapter(this, viewModel, viewModel.getUnitBlocks())
+ binding.viewPager.adapter = adapter
+ binding.viewPager.isUserInputEnabled = false
+ }
+
+ companion object {
+
+ private const val ARG_BLOCK_ID = "blockId"
+ private const val ARG_COURSE_ID = "courseId"
+ private const val ARG_COURSE_NAME = "courseName"
+ private const val ARG_MODE = "mode"
+
+ fun newInstance(
+ blockId: String,
+ courseId: String,
+ courseName: String,
+ mode: CourseViewMode,
+ ): CourseUnitContainerFragment {
+ val fragment = CourseUnitContainerFragment()
+ fragment.arguments = bundleOf(
+ ARG_BLOCK_ID to blockId,
+ ARG_COURSE_ID to courseId,
+ ARG_COURSE_NAME to courseName,
+ ARG_MODE to mode
+ )
+ return fragment
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/course/src/main/java/org/openedx/course/presentation/unit/container/CourseUnitContainerViewModel.kt b/course/src/main/java/org/openedx/course/presentation/unit/container/CourseUnitContainerViewModel.kt
new file mode 100644
index 000000000..a06c012dd
--- /dev/null
+++ b/course/src/main/java/org/openedx/course/presentation/unit/container/CourseUnitContainerViewModel.kt
@@ -0,0 +1,192 @@
+package org.openedx.course.presentation.unit.container
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.viewModelScope
+import org.openedx.core.BaseViewModel
+import org.openedx.core.BlockType
+import org.openedx.core.domain.model.Block
+import org.openedx.core.extension.clearAndAddAll
+import org.openedx.core.extension.indexOfFirstFromIndex
+import org.openedx.core.module.db.DownloadModel
+import org.openedx.core.module.db.DownloadedState
+import org.openedx.core.presentation.course.CourseViewMode
+import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.core.system.notifier.CourseSectionChanged
+import org.openedx.course.domain.interactor.CourseInteractor
+import org.openedx.course.presentation.CourseAnalytics
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.first
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.runBlocking
+
+class CourseUnitContainerViewModel(
+ private val interactor: CourseInteractor,
+ private val notifier: CourseNotifier,
+ private val analytics: CourseAnalytics,
+ val courseId: String
+) : BaseViewModel() {
+
+ private val blocks = ArrayList()
+
+ var currentIndex = 0
+ private set
+ var currentVerticalIndex = 0
+ private set
+ private var currentSectionIndex = -1
+
+ val isFirstIndexInContainer: Boolean
+ get() {
+ return descendants.first() == descendants[currentIndex]
+ }
+
+ val isLastIndexInContainer: Boolean
+ get() {
+ return descendants.last() == descendants[currentIndex]
+ }
+
+ private val _verticalBlockCounts = MutableLiveData()
+ val verticalBlockCounts: LiveData
+ get() = _verticalBlockCounts
+
+ private val _indexInContainer = MutableLiveData()
+ val indexInContainer: LiveData
+ get() = _indexInContainer
+
+ var nextButtonText = ""
+ var hasNextBlock = false
+ private var courseName = ""
+
+ private val descendants = mutableListOf()
+
+ fun loadBlocks(mode: CourseViewMode) {
+ try {
+ val courseStructure = when (mode) {
+ CourseViewMode.FULL -> interactor.getCourseStructureFromCache()
+ CourseViewMode.VIDEOS -> interactor.getCourseStructureForVideos()
+ }
+ val blocks = courseStructure.blockData
+ courseName = courseStructure.name
+ this.blocks.clearAndAddAll(blocks)
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+
+ init {
+ _indexInContainer.value = 0
+ }
+
+ fun setupCurrentIndex(blockId: String) {
+ blocks.forEachIndexed { index, block ->
+ if (block.id == blockId) {
+ currentVerticalIndex = index
+ currentSectionIndex = blocks.indexOfFirst {
+ it.descendants.contains(blocks[currentVerticalIndex].id)
+ }
+ if (block.descendants.isNotEmpty()) {
+ descendants.clearAndAddAll(block.descendants)
+ } else {
+ setNextVerticalIndex()
+ }
+ if (currentVerticalIndex != -1) {
+ _verticalBlockCounts.value = blocks[currentVerticalIndex].descendants.size
+ }
+ return
+ }
+ }
+ }
+
+ private fun setNextVerticalIndex() {
+ currentVerticalIndex = blocks.indexOfFirstFromIndex(currentVerticalIndex) {
+ it.type == BlockType.VERTICAL
+ }
+ }
+
+ fun proceedToNext() {
+ currentVerticalIndex = blocks.indexOfFirstFromIndex(currentVerticalIndex) {
+ it.type == BlockType.VERTICAL
+ }
+ if (currentVerticalIndex != -1) {
+ val sectionIndex = blocks.indexOfFirst {
+ it.descendants.contains(blocks[currentVerticalIndex].id)
+ }
+ if (sectionIndex != currentSectionIndex) {
+ currentSectionIndex = sectionIndex
+ blocks.getOrNull(currentSectionIndex)?.id?.let {
+ sendCourseSectionChanged(it)
+ }
+ }
+ }
+ }
+
+ fun getDownloadModelById(id: String): DownloadModel? = runBlocking(Dispatchers.IO) {
+ return@runBlocking interactor.getDownloadModels().first()
+ .find { it.id == id && it.downloadedState == DownloadedState.DOWNLOADED }
+ }
+
+ fun getCurrentBlock(): Block {
+ return blocks[currentIndex]
+ }
+
+ fun moveToNextBlock(): Block? {
+ for (i in currentIndex + 1 until descendants.size) {
+ val block = blocks.firstOrNull { descendants[i] == it.id }
+ currentIndex = i
+ if (currentVerticalIndex != -1) {
+ _indexInContainer.value = currentIndex
+ }
+ return block
+ }
+ return null
+ }
+
+ fun moveToPrevBlock(): Block? {
+ for (i in currentIndex - 1 downTo 0) {
+ val block = blocks.firstOrNull { descendants[i] == it.id }
+ currentIndex = i
+ if (currentVerticalIndex != -1) {
+ _indexInContainer.value = currentIndex
+ }
+ return block
+ }
+ return null
+ }
+
+ private fun sendCourseSectionChanged(blockId: String) {
+ viewModelScope.launch {
+ notifier.send(CourseSectionChanged(blockId))
+ }
+ }
+
+ fun getCurrentVerticalBlock(): Block? = blocks.getOrNull(currentVerticalIndex)
+
+ fun getNextVerticalBlock(): Block? {
+ val index = blocks.indexOfFirstFromIndex(currentVerticalIndex) {
+ it.type == BlockType.VERTICAL
+ }
+ return blocks.getOrNull(index)
+ }
+
+ fun getUnitBlocks(): List = blocks.filter { descendants.contains(it.id) }
+
+ fun nextBlockClickedEvent(blockId: String, blockName: String) {
+ analytics.nextBlockClickedEvent(courseId, courseName, blockId, blockName)
+ }
+
+ fun prevBlockClickedEvent(blockId: String, blockName: String) {
+ analytics.prevBlockClickedEvent(courseId, courseName, blockId, blockName)
+ }
+
+ fun finishVerticalClickedEvent(blockId: String, blockName: String) {
+ analytics.finishVerticalClickedEvent(courseId, courseName, blockId, blockName)
+ }
+
+ fun finishVerticalNextClickedEvent(blockId: String, blockName: String) {
+ analytics.finishVerticalNextClickedEvent(courseId, courseName, blockId, blockName)
+ }
+
+ fun finishVerticalBackClickedEvent() {
+ analytics.finishVerticalBackClickedEvent(courseId, courseName)
+ }
+}
\ No newline at end of file
diff --git a/course/src/main/java/com/raccoongang/course/presentation/unit/html/HtmlUnitFragment.kt b/course/src/main/java/org/openedx/course/presentation/unit/html/HtmlUnitFragment.kt
similarity index 91%
rename from course/src/main/java/com/raccoongang/course/presentation/unit/html/HtmlUnitFragment.kt
rename to course/src/main/java/org/openedx/course/presentation/unit/html/HtmlUnitFragment.kt
index ac8f4011e..b4724fcb7 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/unit/html/HtmlUnitFragment.kt
+++ b/course/src/main/java/org/openedx/course/presentation/unit/html/HtmlUnitFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.course.presentation.unit.html
+package org.openedx.course.presentation.unit.html
import android.annotation.SuppressLint
import android.content.Intent
@@ -24,17 +24,16 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.zIndex
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
-import com.raccoongang.core.extension.isEmailValid
-import com.raccoongang.core.system.AppCookieManager
-import com.raccoongang.core.system.connection.NetworkConnection
-import com.raccoongang.core.ui.WindowSize
-import com.raccoongang.core.ui.rememberWindowSize
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appShapes
-import com.raccoongang.core.ui.windowSizeValue
-import com.raccoongang.core.utils.EmailUtil
-import com.raccoongang.course.presentation.ui.ConnectionErrorView
+import org.openedx.core.extension.isEmailValid
+import org.openedx.core.system.AppCookieManager
+import org.openedx.core.system.connection.NetworkConnection
+import org.openedx.core.ui.WindowSize
+import org.openedx.core.ui.rememberWindowSize
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.windowSizeValue
+import org.openedx.core.utils.EmailUtil
+import org.openedx.course.presentation.ui.ConnectionErrorView
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
@@ -58,7 +57,7 @@ class HtmlUnitFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
var isLoading by remember {
@@ -69,13 +68,13 @@ class HtmlUnitFragment : Fragment() {
mutableStateOf(networkConnection.isOnline())
}
Surface(
- shape = MaterialTheme.appShapes.screenBackgroundShape,
color = Color.White
) {
Box(
modifier = Modifier
.fillMaxSize()
- .background(Color.White),
+ .background(Color.White)
+ .padding(bottom = 72.dp),
contentAlignment = Alignment.TopCenter
) {
if (hasInternetConnection) {
diff --git a/course/src/main/java/com/raccoongang/course/presentation/unit/video/VideoFullScreenFragment.kt b/course/src/main/java/org/openedx/course/presentation/unit/video/VideoFullScreenFragment.kt
similarity index 92%
rename from course/src/main/java/com/raccoongang/course/presentation/unit/video/VideoFullScreenFragment.kt
rename to course/src/main/java/org/openedx/course/presentation/unit/video/VideoFullScreenFragment.kt
index 3617f89c8..843895fb6 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/unit/video/VideoFullScreenFragment.kt
+++ b/course/src/main/java/org/openedx/course/presentation/unit/video/VideoFullScreenFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.course.presentation.unit.video
+package org.openedx.course.presentation.unit.video
import android.annotation.SuppressLint
import android.content.pm.ActivityInfo
@@ -12,11 +12,11 @@ import com.google.android.exoplayer2.C
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.Player
-import com.raccoongang.core.extension.requestApplyInsetsWhenAttached
-import com.raccoongang.core.presentation.global.WindowSizeHolder
-import com.raccoongang.core.presentation.global.viewBinding
-import com.raccoongang.course.R
-import com.raccoongang.course.databinding.FragmentVideoFullScreenBinding
+import org.openedx.core.extension.requestApplyInsetsWhenAttached
+import org.openedx.core.presentation.global.WindowSizeHolder
+import org.openedx.core.presentation.global.viewBinding
+import org.openedx.course.R
+import org.openedx.course.databinding.FragmentVideoFullScreenBinding
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
@@ -65,7 +65,6 @@ class VideoFullScreenFragment : Fragment(R.layout.fragment_video_full_screen) {
}
binding.root.requestApplyInsetsWhenAttached()
initPlayer()
-// updateCurrentVideoTime()
}
private fun initPlayer() {
@@ -80,7 +79,7 @@ class VideoFullScreenFragment : Fragment(R.layout.fragment_video_full_screen) {
val mediaItem = MediaItem.fromUri(viewModel.videoUrl)
exoPlayer?.setMediaItem(mediaItem, viewModel.currentVideoTime.toLong())
exoPlayer?.prepare()
- exoPlayer?.playWhenReady = true
+ exoPlayer?.playWhenReady = false
playerView.setFullscreenButtonClickListener { isFullScreen ->
requireActivity().supportFragmentManager.popBackStackImmediate()
diff --git a/course/src/main/java/com/raccoongang/course/presentation/unit/video/VideoUnitFragment.kt b/course/src/main/java/org/openedx/course/presentation/unit/video/VideoUnitFragment.kt
similarity index 84%
rename from course/src/main/java/com/raccoongang/course/presentation/unit/video/VideoUnitFragment.kt
rename to course/src/main/java/org/openedx/course/presentation/unit/video/VideoUnitFragment.kt
index 36e5227e8..c675209a1 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/unit/video/VideoUnitFragment.kt
+++ b/course/src/main/java/org/openedx/course/presentation/unit/video/VideoUnitFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.course.presentation.unit.video
+package org.openedx.course.presentation.unit.video
import android.graphics.Point
import android.os.Bundle
@@ -21,23 +21,23 @@ import androidx.fragment.app.Fragment
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.Player
-import com.raccoongang.core.extension.computeWindowSizeClasses
-import com.raccoongang.core.extension.dpToPixel
-import com.raccoongang.core.extension.objectToString
-import com.raccoongang.core.extension.stringToObject
-import com.raccoongang.core.presentation.dialog.SelectBottomDialogFragment
-import com.raccoongang.core.presentation.global.viewBinding
-import com.raccoongang.core.ui.WindowSize
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.utils.LocaleUtils
-import com.raccoongang.course.R
-import com.raccoongang.course.databinding.FragmentVideoUnitBinding
-import com.raccoongang.course.presentation.CourseRouter
-import com.raccoongang.course.presentation.ui.ConnectionErrorView
-import com.raccoongang.course.presentation.ui.VideoRotateView
-import com.raccoongang.course.presentation.ui.VideoSubtitles
-import com.raccoongang.course.presentation.ui.VideoTitle
+import org.openedx.core.extension.computeWindowSizeClasses
+import org.openedx.core.extension.dpToPixel
+import org.openedx.core.extension.objectToString
+import org.openedx.core.extension.stringToObject
+import org.openedx.core.presentation.dialog.SelectBottomDialogFragment
+import org.openedx.core.presentation.global.viewBinding
+import org.openedx.core.ui.WindowSize
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.utils.LocaleUtils
+import org.openedx.course.R
+import org.openedx.course.databinding.FragmentVideoUnitBinding
+import org.openedx.course.presentation.CourseRouter
+import org.openedx.course.presentation.ui.ConnectionErrorView
+import org.openedx.course.presentation.ui.VideoRotateView
+import org.openedx.course.presentation.ui.VideoSubtitles
+import org.openedx.course.presentation.ui.VideoTitle
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
@@ -64,11 +64,23 @@ class VideoUnitFragment : Fragment(R.layout.fragment_video_unit) {
if (it.isPlaying) {
viewModel.setCurrentVideoTime(it.currentPosition)
}
+ val completePercentage = it.currentPosition.toDouble() / it.duration.toDouble()
+ if (completePercentage >= 0.8f) {
+ viewModel.markBlockCompleted(blockId)
+ }
}
handler.postDelayed(this, 200)
}
}
+ private val exoPlayerListener = object : Player.Listener {
+ override fun onPlaybackStateChanged(playbackState: Int) {
+ super.onPlaybackStateChanged(playbackState)
+ if (playbackState == Player.STATE_ENDED) {
+ viewModel.markBlockCompleted(blockId)
+ }
+ }
+ }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -105,29 +117,25 @@ class VideoUnitFragment : Fragment(R.layout.fragment_video_unit) {
}
}
}
-
- viewModel.isVideoPaused.observe(this) {
- exoPlayer?.pause()
- }
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.cvRotateHelper.setContent {
- NewEdxTheme {
+ OpenEdXTheme {
VideoRotateView()
}
}
binding.cvVideoTitle.setContent {
- NewEdxTheme {
+ OpenEdXTheme {
VideoTitle(text = requireArguments().getString(ARG_TITLE) ?: "")
}
}
binding.connectionError.setContent {
- NewEdxTheme {
+ OpenEdXTheme {
ConnectionErrorView(
modifier = Modifier
.fillMaxSize()
@@ -140,7 +148,7 @@ class VideoUnitFragment : Fragment(R.layout.fragment_video_unit) {
}
binding.subtitles.setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val state = rememberLazyListState()
val currentIndex by viewModel.currentIndex.collectAsState(0)
val transcriptObject by viewModel.transcriptObject.observeAsState()
@@ -204,10 +212,11 @@ class VideoUnitFragment : Fragment(R.layout.fragment_video_unit) {
playerView.player = exoPlayer
playerView.setShowNextButton(false)
playerView.setShowPreviousButton(false)
+ playerView.controllerAutoShow = true
+ playerView.controllerShowTimeoutMs = 2000
val mediaItem = MediaItem.fromUri(viewModel.videoUrl)
exoPlayer?.setMediaItem(mediaItem, viewModel.getCurrentVideoTime())
exoPlayer?.prepare()
- exoPlayer?.playWhenReady = !(viewModel.isVideoPaused.value ?: false)
playerView.setFullscreenButtonClickListener { isFullScreen ->
router.navigateToFullScreenVideo(
@@ -219,15 +228,6 @@ class VideoUnitFragment : Fragment(R.layout.fragment_video_unit) {
)
viewModel.fullscreenHandled = true
}
-
- exoPlayer?.addListener(object : Player.Listener {
- override fun onPlaybackStateChanged(playbackState: Int) {
- super.onPlaybackStateChanged(playbackState)
- if (playbackState == Player.STATE_ENDED) {
- viewModel.markBlockCompleted(blockId)
- }
- }
- })
}
}
@@ -236,10 +236,12 @@ class VideoUnitFragment : Fragment(R.layout.fragment_video_unit) {
if (orientationListener?.canDetectOrientation() == true) {
orientationListener?.enable()
}
+ exoPlayer?.addListener(exoPlayerListener)
}
override fun onPause() {
super.onPause()
+ exoPlayer?.removeListener(exoPlayerListener)
exoPlayer?.pause()
orientationListener?.disable()
}
diff --git a/course/src/main/java/com/raccoongang/course/presentation/unit/video/VideoUnitViewModel.kt b/course/src/main/java/org/openedx/course/presentation/unit/video/VideoUnitViewModel.kt
similarity index 73%
rename from course/src/main/java/com/raccoongang/course/presentation/unit/video/VideoUnitViewModel.kt
rename to course/src/main/java/org/openedx/course/presentation/unit/video/VideoUnitViewModel.kt
index 52b068210..6a0b51434 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/unit/video/VideoUnitViewModel.kt
+++ b/course/src/main/java/org/openedx/course/presentation/unit/video/VideoUnitViewModel.kt
@@ -1,19 +1,17 @@
-package com.raccoongang.course.presentation.unit.video
+package org.openedx.course.presentation.unit.video
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.AppDataConstants
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.module.TranscriptManager
-import com.raccoongang.core.system.connection.NetworkConnection
-import com.raccoongang.core.system.notifier.CourseNotifier
-import com.raccoongang.core.system.notifier.CoursePauseVideo
-import com.raccoongang.core.system.notifier.CourseSubtitleLanguageChanged
-import com.raccoongang.core.system.notifier.CourseVideoPositionChanged
-import com.raccoongang.course.data.repository.CourseRepository
+import org.openedx.core.AppDataConstants
+import org.openedx.core.BaseViewModel
+import org.openedx.core.module.TranscriptManager
+import org.openedx.core.system.connection.NetworkConnection
+import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.core.system.notifier.CourseSubtitleLanguageChanged
+import org.openedx.core.system.notifier.CourseVideoPositionChanged
+import org.openedx.course.data.repository.CourseRepository
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -23,10 +21,9 @@ import subtitleFile.TimedTextObject
class VideoUnitViewModel(
val courseId: String,
private val courseRepository: CourseRepository,
- private val preferencesManager: PreferencesManager,
private val notifier: CourseNotifier,
private val networkConnection: NetworkConnection,
- private val transcriptManager: TranscriptManager
+ private val transcriptManager: TranscriptManager,
) : BaseViewModel() {
var videoUrl = ""
@@ -34,14 +31,14 @@ class VideoUnitViewModel(
var transcriptLanguage = AppDataConstants.defaultLocale.language ?: "en"
private set
- private val _currentVideoTime = MutableLiveData(0)
- val currentVideoTime: LiveData
- get() = _currentVideoTime
-
var fullscreenHandled = false
var isDownloaded = false
+ private val _currentVideoTime = MutableLiveData(0)
+ val currentVideoTime: LiveData
+ get() = _currentVideoTime
+
private val _isUpdated = MutableLiveData(true)
val isUpdated: LiveData
get() = _isUpdated
@@ -50,10 +47,6 @@ class VideoUnitViewModel(
val isPopUpViewShow: LiveData
get() = _isPopUpViewShow
- private val _isVideoPaused = MutableLiveData()
- val isVideoPaused: LiveData
- get() = _isVideoPaused
-
private val _currentIndex = MutableStateFlow(0)
val currentIndex = _currentIndex.asStateFlow()
@@ -66,6 +59,8 @@ class VideoUnitViewModel(
val hasInternetConnection: Boolean
get() = networkConnection.isOnline()
+ private var isBlockAlreadyCompleted = false
+
init {
viewModelScope.launch {
delay(4000)
@@ -81,8 +76,6 @@ class VideoUnitViewModel(
_isUpdated.value = false
_currentVideoTime.value = it.videoTime
_isUpdated.value = true
- } else if (it is CoursePauseVideo) {
- _isVideoPaused.value = true
} else if (it is CourseSubtitleLanguageChanged) {
transcriptLanguage = it.value
_transcriptObject.value = null
@@ -109,21 +102,25 @@ class VideoUnitViewModel(
}
if (transcripts.values.isNotEmpty()) {
transcriptLanguage = transcripts.keys.toList().first()
- return transcripts[transcriptLanguage]?:""
+ return transcripts[transcriptLanguage] ?: ""
}
return ""
}
fun markBlockCompleted(blockId: String) {
- viewModelScope.launch {
- try {
- courseRepository.markBlocksCompletion(
- courseId,
- listOf(blockId)
- )
- } catch (e: Exception) {
- e.printStackTrace()
+ if (!isBlockAlreadyCompleted) {
+ viewModelScope.launch {
+ try {
+ isBlockAlreadyCompleted = true
+ courseRepository.markBlocksCompletion(
+ courseId,
+ listOf(blockId)
+ )
+ } catch (e: Exception) {
+ isBlockAlreadyCompleted = false
+ e.printStackTrace()
+ }
}
}
}
diff --git a/course/src/main/java/org/openedx/course/presentation/unit/video/VideoViewModel.kt b/course/src/main/java/org/openedx/course/presentation/unit/video/VideoViewModel.kt
new file mode 100644
index 000000000..01ab0ca57
--- /dev/null
+++ b/course/src/main/java/org/openedx/course/presentation/unit/video/VideoViewModel.kt
@@ -0,0 +1,50 @@
+package org.openedx.course.presentation.unit.video
+
+import androidx.lifecycle.viewModelScope
+import com.google.android.exoplayer2.C
+import org.openedx.core.BaseViewModel
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.course.data.repository.CourseRepository
+import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.core.system.notifier.CourseVideoPositionChanged
+import kotlinx.coroutines.launch
+
+class VideoViewModel(
+ private val courseId: String,
+ private val courseRepository: CourseRepository,
+ private val preferencesManager: PreferencesManager,
+ private val notifier: CourseNotifier
+) : BaseViewModel() {
+
+ var videoUrl = ""
+ var currentVideoTime = 0L
+
+ private var isBlockAlreadyCompleted = false
+
+
+ fun sendTime() {
+ if (currentVideoTime != C.TIME_UNSET) {
+ viewModelScope.launch {
+ notifier.send(CourseVideoPositionChanged(videoUrl, currentVideoTime))
+ }
+ }
+ }
+
+ fun markBlockCompleted(blockId: String) {
+ if (!isBlockAlreadyCompleted) {
+ viewModelScope.launch {
+ try {
+ isBlockAlreadyCompleted = true
+ courseRepository.markBlocksCompletion(
+ courseId,
+ listOf(blockId)
+ )
+ } catch (e: Exception) {
+ isBlockAlreadyCompleted = false
+ e.printStackTrace()
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/course/src/main/java/com/raccoongang/course/presentation/unit/video/YoutubeVideoFullScreenFragment.kt b/course/src/main/java/org/openedx/course/presentation/unit/video/YoutubeVideoFullScreenFragment.kt
similarity index 86%
rename from course/src/main/java/com/raccoongang/course/presentation/unit/video/YoutubeVideoFullScreenFragment.kt
rename to course/src/main/java/org/openedx/course/presentation/unit/video/YoutubeVideoFullScreenFragment.kt
index e73ac53f2..a030828a8 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/unit/video/YoutubeVideoFullScreenFragment.kt
+++ b/course/src/main/java/org/openedx/course/presentation/unit/video/YoutubeVideoFullScreenFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.course.presentation.unit.video
+package org.openedx.course.presentation.unit.video
import android.annotation.SuppressLint
import android.content.pm.ActivityInfo
@@ -13,13 +13,14 @@ import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.PlayerConstan
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.YouTubePlayer
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.AbstractYouTubePlayerListener
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.options.IFramePlayerOptions
+import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.utils.YouTubePlayerTracker
import com.pierfrancescosoffritti.androidyoutubeplayer.core.ui.DefaultPlayerUiController
-import com.raccoongang.core.extension.requestApplyInsetsWhenAttached
-import com.raccoongang.core.presentation.global.WindowSizeHolder
-import com.raccoongang.core.presentation.global.viewBinding
-import com.raccoongang.core.ui.WindowType
-import com.raccoongang.course.R
-import com.raccoongang.course.databinding.FragmentYoutubeVideoFullScreenBinding
+import org.openedx.core.extension.requestApplyInsetsWhenAttached
+import org.openedx.core.presentation.global.WindowSizeHolder
+import org.openedx.core.presentation.global.viewBinding
+import org.openedx.core.ui.WindowType
+import org.openedx.course.R
+import org.openedx.course.databinding.FragmentYoutubeVideoFullScreenBinding
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
@@ -34,6 +35,9 @@ class YoutubeVideoFullScreenFragment : Fragment(R.layout.fragment_youtube_video_
private var blockId = ""
private var isTabletDevice = false
+ private val youtubeTrackerListener = YouTubePlayerTracker()
+
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel.videoUrl = requireArguments().getString(ARG_BLOCK_VIDEO_URL, "")
@@ -91,6 +95,10 @@ class YoutubeVideoFullScreenFragment : Fragment(R.layout.fragment_youtube_video_
override fun onCurrentSecond(youTubePlayer: YouTubePlayer, second: Float) {
super.onCurrentSecond(youTubePlayer, second)
viewModel.currentVideoTime = (second * 1000f).toLong()
+ val completePercentage = second / youtubeTrackerListener.videoDuration
+ if (completePercentage >= 0.8f) {
+ viewModel.markBlockCompleted(blockId)
+ }
}
override fun onReady(youTubePlayer: YouTubePlayer) {
@@ -105,7 +113,8 @@ class YoutubeVideoFullScreenFragment : Fragment(R.layout.fragment_youtube_video_
binding.youtubePlayerView.setCustomPlayerUi(defPlayerUiController.rootView)
val videoId = viewModel.videoUrl.split("watch?v=")[1]
- youTubePlayer.loadVideo(videoId, viewModel.currentVideoTime.toFloat())
+ youTubePlayer.cueVideo(videoId, viewModel.currentVideoTime.toFloat() / 1000)
+ youTubePlayer.addListener(youtubeTrackerListener)
}
diff --git a/course/src/main/java/com/raccoongang/course/presentation/unit/video/YoutubeVideoUnitFragment.kt b/course/src/main/java/org/openedx/course/presentation/unit/video/YoutubeVideoUnitFragment.kt
similarity index 71%
rename from course/src/main/java/com/raccoongang/course/presentation/unit/video/YoutubeVideoUnitFragment.kt
rename to course/src/main/java/org/openedx/course/presentation/unit/video/YoutubeVideoUnitFragment.kt
index ecf8a272f..67cc8e7a2 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/unit/video/YoutubeVideoUnitFragment.kt
+++ b/course/src/main/java/org/openedx/course/presentation/unit/video/YoutubeVideoUnitFragment.kt
@@ -1,8 +1,10 @@
-package com.raccoongang.course.presentation.unit.video
+package org.openedx.course.presentation.unit.video
import android.os.Bundle
+import android.view.LayoutInflater
import android.view.OrientationEventListener
import android.view.View
+import android.view.ViewGroup
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.rememberLazyListState
@@ -14,27 +16,26 @@ import androidx.compose.ui.Modifier
import androidx.core.os.bundleOf
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
-import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.PlayerConstants
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.YouTubePlayer
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.AbstractYouTubePlayerListener
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.options.IFramePlayerOptions
+import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.utils.YouTubePlayerTracker
import com.pierfrancescosoffritti.androidyoutubeplayer.core.ui.DefaultPlayerUiController
-import com.raccoongang.core.extension.computeWindowSizeClasses
-import com.raccoongang.core.extension.objectToString
-import com.raccoongang.core.extension.stringToObject
-import com.raccoongang.core.presentation.dialog.SelectBottomDialogFragment
-import com.raccoongang.core.presentation.global.viewBinding
-import com.raccoongang.core.ui.WindowSize
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.utils.LocaleUtils
-import com.raccoongang.course.R
-import com.raccoongang.course.databinding.FragmentYoutubeVideoUnitBinding
-import com.raccoongang.course.presentation.CourseRouter
-import com.raccoongang.course.presentation.ui.ConnectionErrorView
-import com.raccoongang.course.presentation.ui.VideoRotateView
-import com.raccoongang.course.presentation.ui.VideoSubtitles
-import com.raccoongang.course.presentation.ui.VideoTitle
+import org.openedx.core.extension.computeWindowSizeClasses
+import org.openedx.core.extension.objectToString
+import org.openedx.core.extension.stringToObject
+import org.openedx.core.presentation.dialog.SelectBottomDialogFragment
+import org.openedx.core.ui.WindowSize
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.utils.LocaleUtils
+import org.openedx.course.R
+import org.openedx.course.databinding.FragmentYoutubeVideoUnitBinding
+import org.openedx.course.presentation.CourseRouter
+import org.openedx.course.presentation.ui.ConnectionErrorView
+import org.openedx.course.presentation.ui.VideoRotateView
+import org.openedx.course.presentation.ui.VideoSubtitles
+import org.openedx.course.presentation.ui.VideoTitle
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
@@ -45,7 +46,8 @@ class YoutubeVideoUnitFragment : Fragment(R.layout.fragment_youtube_video_unit)
parametersOf(requireArguments().getString(ARG_COURSE_ID, ""))
}
private val router by inject()
- private val binding by viewBinding(FragmentYoutubeVideoUnitBinding::bind)
+ private var _binding: FragmentYoutubeVideoUnitBinding? = null
+ private val binding get() = _binding!!
private var windowSize: WindowSize? = null
private var orientationListener: OrientationEventListener? = null
@@ -53,6 +55,10 @@ class YoutubeVideoUnitFragment : Fragment(R.layout.fragment_youtube_video_unit)
private var blockId = ""
+ private var isPlayerInitialized = false
+
+ private val youtubeTrackerListener = YouTubePlayerTracker()
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
windowSize = computeWindowSizeClasses()
@@ -85,29 +91,34 @@ class YoutubeVideoUnitFragment : Fragment(R.layout.fragment_youtube_video_unit)
}
}
}
+ }
- viewModel.isVideoPaused.observe(this) {
- _youTubePlayer?.pause()
- }
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View {
+ _binding = FragmentYoutubeVideoUnitBinding.inflate(inflater, container, false)
+ return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.cvRotateHelper.setContent {
- NewEdxTheme {
+ OpenEdXTheme {
VideoRotateView()
}
}
binding.cvVideoTitle.setContent {
- NewEdxTheme {
+ OpenEdXTheme {
VideoTitle(text = requireArguments().getString(ARG_TITLE) ?: "")
}
}
binding.connectionError.setContent {
- NewEdxTheme {
+ OpenEdXTheme {
ConnectionErrorView(
modifier = Modifier
.fillMaxSize()
@@ -119,7 +130,7 @@ class YoutubeVideoUnitFragment : Fragment(R.layout.fragment_youtube_video_unit)
}
binding.subtitles.setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val state = rememberLazyListState()
val currentIndex by viewModel.currentIndex.collectAsState(0)
val transcriptObject by viewModel.transcriptObject.observeAsState()
@@ -154,49 +165,46 @@ class YoutubeVideoUnitFragment : Fragment(R.layout.fragment_youtube_video_unit)
.build()
val listener = object : AbstractYouTubePlayerListener() {
- override fun onStateChange(
- youTubePlayer: YouTubePlayer,
- state: PlayerConstants.PlayerState
- ) {
- super.onStateChange(youTubePlayer, state)
- if (state == PlayerConstants.PlayerState.ENDED) {
- viewModel.markBlockCompleted(blockId)
- }
- }
-
override fun onCurrentSecond(youTubePlayer: YouTubePlayer, second: Float) {
super.onCurrentSecond(youTubePlayer, second)
viewModel.setCurrentVideoTime((second * 1000f).toLong())
+ val completePercentage = second / youtubeTrackerListener.videoDuration
+ if (completePercentage >= 0.8f) {
+ viewModel.markBlockCompleted(blockId)
+ }
}
override fun onReady(youTubePlayer: YouTubePlayer) {
super.onReady(youTubePlayer)
_youTubePlayer = youTubePlayer
- val defPlayerUiController = DefaultPlayerUiController(
- binding.youtubePlayerView,
- youTubePlayer
- )
- defPlayerUiController.setFullScreenButtonClickListener {
- viewModel.fullscreenHandled = true
- router.navigateToFullScreenYoutubeVideo(
- requireActivity().supportFragmentManager,
- viewModel.videoUrl,
- viewModel.getCurrentVideoTime(),
- blockId,
- viewModel.courseId
+ if (_binding != null) {
+ val defPlayerUiController = DefaultPlayerUiController(
+ binding.youtubePlayerView,
+ youTubePlayer
)
+ defPlayerUiController.setFullScreenButtonClickListener {
+ viewModel.fullscreenHandled = true
+ router.navigateToFullScreenYoutubeVideo(
+ requireActivity().supportFragmentManager,
+ viewModel.videoUrl,
+ viewModel.getCurrentVideoTime(),
+ blockId,
+ viewModel.courseId
+ )
+ }
+ binding.youtubePlayerView.setCustomPlayerUi(defPlayerUiController.rootView)
}
- binding.youtubePlayerView.setCustomPlayerUi(defPlayerUiController.rootView)
val videoId = viewModel.videoUrl.split("watch?v=")[1]
- youTubePlayer.loadVideo(videoId, viewModel.getCurrentVideoTime().toFloat())
- if (viewModel.isVideoPaused.value == true) {
- youTubePlayer.pause()
- }
+ youTubePlayer.cueVideo(videoId, viewModel.getCurrentVideoTime().toFloat() / 1000)
+ youTubePlayer.addListener(youtubeTrackerListener)
}
}
- binding.youtubePlayerView.initialize(listener, options)
+ if (!isPlayerInitialized) {
+ binding.youtubePlayerView.initialize(listener, options)
+ isPlayerInitialized = true
+ }
viewModel.isPopUpViewShow.observe(viewLifecycleOwner) {
if (windowSize?.isTablet != true) {
@@ -214,9 +222,17 @@ class YoutubeVideoUnitFragment : Fragment(R.layout.fragment_youtube_video_unit)
override fun onPause() {
super.onPause()
+ _youTubePlayer?.pause()
orientationListener?.disable()
}
+ override fun onDestroyView() {
+ isPlayerInitialized = false
+ _youTubePlayer = null
+ super.onDestroyView()
+ _binding = null
+ }
+
companion object {
private const val ARG_VIDEO_URL = "videoUrl"
diff --git a/course/src/main/java/com/raccoongang/course/presentation/videos/CourseVideoViewModel.kt b/course/src/main/java/org/openedx/course/presentation/videos/CourseVideoViewModel.kt
similarity index 83%
rename from course/src/main/java/com/raccoongang/course/presentation/videos/CourseVideoViewModel.kt
rename to course/src/main/java/org/openedx/course/presentation/videos/CourseVideoViewModel.kt
index 88347cae3..c5086702a 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/videos/CourseVideoViewModel.kt
+++ b/course/src/main/java/org/openedx/course/presentation/videos/CourseVideoViewModel.kt
@@ -1,23 +1,23 @@
-package com.raccoongang.course.presentation.videos
+package org.openedx.course.presentation.videos
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.SingleEventLiveData
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.domain.model.Block
-import com.raccoongang.core.module.DownloadWorkerController
-import com.raccoongang.core.module.db.DownloadDao
-import com.raccoongang.core.module.download.BaseDownloadViewModel
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.core.system.connection.NetworkConnection
-import com.raccoongang.core.system.notifier.CourseNotifier
-import com.raccoongang.core.system.notifier.CourseStructureUpdated
-import com.raccoongang.course.R
-import com.raccoongang.course.domain.interactor.CourseInteractor
+import org.openedx.core.BlockType
+import org.openedx.core.SingleEventLiveData
+import org.openedx.core.UIMessage
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.domain.model.Block
+import org.openedx.core.module.DownloadWorkerController
+import org.openedx.core.module.db.DownloadDao
+import org.openedx.core.module.download.BaseDownloadViewModel
+import org.openedx.core.system.ResourceManager
+import org.openedx.core.system.connection.NetworkConnection
+import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.core.system.notifier.CourseStructureUpdated
+import org.openedx.course.R
+import org.openedx.course.domain.interactor.CourseInteractor
import kotlinx.coroutines.launch
class CourseVideoViewModel(
diff --git a/course/src/main/java/com/raccoongang/course/presentation/videos/CourseVideosFragment.kt b/course/src/main/java/org/openedx/course/presentation/videos/CourseVideosFragment.kt
similarity index 80%
rename from course/src/main/java/com/raccoongang/course/presentation/videos/CourseVideosFragment.kt
rename to course/src/main/java/org/openedx/course/presentation/videos/CourseVideosFragment.kt
index 5ad441a0c..854ba9cbc 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/videos/CourseVideosFragment.kt
+++ b/course/src/main/java/org/openedx/course/presentation/videos/CourseVideosFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.course.presentation.videos
+package org.openedx.course.presentation.videos
import android.content.res.Configuration
import android.os.Bundle
@@ -28,20 +28,20 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.*
-import com.raccoongang.core.presentation.course.CourseViewMode
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appShapes
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.course.presentation.CourseRouter
-import com.raccoongang.course.presentation.container.CourseContainerFragment
-import com.raccoongang.course.presentation.ui.CourseImageHeader
-import com.raccoongang.course.presentation.ui.CourseSectionCard
+import org.openedx.core.BlockType
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.*
+import org.openedx.core.presentation.course.CourseViewMode
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appShapes
+import org.openedx.core.ui.theme.appTypography
+import org.openedx.course.presentation.CourseRouter
+import org.openedx.course.presentation.container.CourseContainerFragment
+import org.openedx.course.presentation.ui.CourseImageHeader
+import org.openedx.course.presentation.ui.CourseSectionCard
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
@@ -70,7 +70,7 @@ class CourseVideosFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val uiState by viewModel.uiState.observeAsState(CourseVideosUIState.Loading)
@@ -179,11 +179,11 @@ private fun CourseVideosScreen(
)
}
- val imageHeight by remember(key1 = windowSize) {
+ val listBottomPadding by remember(key1 = windowSize) {
mutableStateOf(
windowSize.windowSizeValue(
- expanded = 260.dp,
- compact = 200.dp
+ expanded = PaddingValues(bottom = 24.dp),
+ compact = PaddingValues(bottom = 24.dp)
)
)
}
@@ -191,16 +191,8 @@ private fun CourseVideosScreen(
val listPadding by remember(key1 = windowSize) {
mutableStateOf(
windowSize.windowSizeValue(
- expanded = PaddingValues(
- start = 6.dp,
- end = 6.dp,
- bottom = 24.dp
- ),
- compact = PaddingValues(
- start = 24.dp,
- end = 24.dp,
- bottom = 24.dp
- )
+ expanded = Modifier.padding(horizontal = 6.dp),
+ compact = Modifier.padding(horizontal = 24.dp)
)
)
}
@@ -253,7 +245,7 @@ private fun CourseVideosScreen(
contentAlignment = Alignment.Center
) {
Text(
- text = stringResource(id = com.raccoongang.course.R.string.course_does_not_include_videos),
+ text = stringResource(id = org.openedx.course.R.string.course_does_not_include_videos),
color = MaterialTheme.appColors.textPrimary,
style = MaterialTheme.appTypography.headlineSmall,
textAlign = TextAlign.Center,
@@ -261,6 +253,7 @@ private fun CourseVideosScreen(
)
}
}
+
is CourseVideosUIState.Loading -> {
Box(
modifier = Modifier.fillMaxSize(),
@@ -269,40 +262,45 @@ private fun CourseVideosScreen(
CircularProgressIndicator(color = MaterialTheme.appColors.primary)
}
}
+
is CourseVideosUIState.CourseData -> {
- CourseImageHeader(
- modifier = Modifier
- .aspectRatio(1.86f)
- .padding(6.dp),
- courseImage = uiState.courseStructure.media?.image?.large
- ?: "",
- courseCertificate = uiState.courseStructure.certificate
- )
LazyColumn(
- modifier = Modifier.fillMaxWidth(),
- contentPadding = listPadding
+ modifier = Modifier.fillMaxSize(),
+ contentPadding = listBottomPadding
) {
+ item {
+ CourseImageHeader(
+ modifier = Modifier
+ .aspectRatio(1.86f)
+ .padding(6.dp),
+ courseImage = uiState.courseStructure.media?.image?.large
+ ?: "",
+ courseCertificate = uiState.courseStructure.certificate
+ )
+ }
items(uiState.courseStructure.blockData) { block ->
- if (block.type == BlockType.CHAPTER) {
- Text(
- modifier = Modifier.padding(
- top = 36.dp,
- bottom = 8.dp
- ),
- text = block.displayName,
- style = MaterialTheme.appTypography.titleMedium,
- color = MaterialTheme.appColors.textPrimaryVariant
- )
- } else {
- CourseSectionCard(
- block = block,
- downloadedState = uiState.downloadedState[block.id],
- onItemClick = { blockSelected ->
- onItemClick(blockSelected)
- },
- onDownloadClick = onDownloadClick
- )
- Divider()
+ Column(listPadding) {
+ if (block.type == BlockType.CHAPTER) {
+ Text(
+ modifier = Modifier.padding(
+ top = 36.dp,
+ bottom = 8.dp
+ ),
+ text = block.displayName,
+ style = MaterialTheme.appTypography.titleMedium,
+ color = MaterialTheme.appColors.textPrimaryVariant
+ )
+ } else {
+ CourseSectionCard(
+ block = block,
+ downloadedState = uiState.downloadedState[block.id],
+ onItemClick = { blockSelected ->
+ onItemClick(blockSelected)
+ },
+ onDownloadClick = onDownloadClick
+ )
+ Divider()
+ }
}
}
}
@@ -339,7 +337,7 @@ private fun CourseVideosScreen(
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun CourseVideosScreenPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
CourseVideosScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
uiMessage = null,
@@ -363,7 +361,7 @@ private fun CourseVideosScreenPreview() {
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun CourseVideosScreenEmptyPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
CourseVideosScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
uiMessage = null,
@@ -386,7 +384,7 @@ private fun CourseVideosScreenEmptyPreview() {
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, device = Devices.NEXUS_9)
@Composable
private fun CourseVideosScreenTabletPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
CourseVideosScreen(
windowSize = WindowSize(WindowType.Medium, WindowType.Medium),
uiMessage = null,
diff --git a/course/src/main/java/com/raccoongang/course/presentation/videos/CourseVideosUIState.kt b/course/src/main/java/org/openedx/course/presentation/videos/CourseVideosUIState.kt
similarity index 65%
rename from course/src/main/java/com/raccoongang/course/presentation/videos/CourseVideosUIState.kt
rename to course/src/main/java/org/openedx/course/presentation/videos/CourseVideosUIState.kt
index 882eba6ce..243d749be 100644
--- a/course/src/main/java/com/raccoongang/course/presentation/videos/CourseVideosUIState.kt
+++ b/course/src/main/java/org/openedx/course/presentation/videos/CourseVideosUIState.kt
@@ -1,7 +1,7 @@
-package com.raccoongang.course.presentation.videos
+package org.openedx.course.presentation.videos
-import com.raccoongang.core.domain.model.CourseStructure
-import com.raccoongang.core.module.db.DownloadedState
+import org.openedx.core.domain.model.CourseStructure
+import org.openedx.core.module.db.DownloadedState
sealed class CourseVideosUIState {
data class CourseData(
diff --git a/course/src/main/res/anim/course_slide_in_down.xml b/course/src/main/res/anim/course_slide_in_down.xml
new file mode 100644
index 000000000..6201b1915
--- /dev/null
+++ b/course/src/main/res/anim/course_slide_in_down.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/course/src/main/res/anim/course_slide_in_up.xml b/course/src/main/res/anim/course_slide_in_up.xml
new file mode 100644
index 000000000..8aa389749
--- /dev/null
+++ b/course/src/main/res/anim/course_slide_in_up.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/course/src/main/res/anim/course_slide_out_down.xml b/course/src/main/res/anim/course_slide_out_down.xml
new file mode 100644
index 000000000..5a97ec71e
--- /dev/null
+++ b/course/src/main/res/anim/course_slide_out_down.xml
@@ -0,0 +1,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/course/src/main/res/anim/course_slide_out_up.xml b/course/src/main/res/anim/course_slide_out_up.xml
new file mode 100644
index 000000000..2db3f4d94
--- /dev/null
+++ b/course/src/main/res/anim/course_slide_out_up.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/course/src/main/res/layout-w600dp-h480dp/fragment_course_unit_container.xml b/course/src/main/res/layout-w600dp-h480dp/fragment_course_unit_container.xml
index 23d36dd9c..8dbf3ac6f 100644
--- a/course/src/main/res/layout-w600dp-h480dp/fragment_course_unit_container.xml
+++ b/course/src/main/res/layout-w600dp-h480dp/fragment_course_unit_container.xml
@@ -29,8 +29,7 @@
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_marginStart="12dp"
- android:layout_marginTop="12dp"
- />
+ android:layout_marginTop="12dp" />
@@ -52,15 +51,33 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
-
+
+
+ app:layout_constraintTop_toTopOf="parent">
+
+
+
+
diff --git a/course/src/main/res/layout-w600dp-h480dp/fragment_video_unit.xml b/course/src/main/res/layout-w600dp-h480dp/fragment_video_unit.xml
index 0ab81bdab..b2c2a58b2 100644
--- a/course/src/main/res/layout-w600dp-h480dp/fragment_video_unit.xml
+++ b/course/src/main/res/layout-w600dp-h480dp/fragment_video_unit.xml
@@ -48,8 +48,9 @@
android:id="@+id/subtitles"
android:layout_width="560dp"
android:layout_height="0dp"
- android:layout_marginVertical="28dp"
+ android:layout_marginTop="28dp"
android:layout_marginHorizontal="24dp"
+ android:layout_marginBottom="64dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
@@ -62,7 +63,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
- android:layout_marginBottom="32dp"
+ android:layout_marginBottom="64dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/course/src/main/res/layout-w600dp-h480dp/fragment_youtube_video_unit.xml b/course/src/main/res/layout-w600dp-h480dp/fragment_youtube_video_unit.xml
index 5e4693586..4a0af6144 100644
--- a/course/src/main/res/layout-w600dp-h480dp/fragment_youtube_video_unit.xml
+++ b/course/src/main/res/layout-w600dp-h480dp/fragment_youtube_video_unit.xml
@@ -46,8 +46,9 @@
android:id="@+id/subtitles"
android:layout_width="560dp"
android:layout_height="0dp"
- android:layout_marginVertical="28dp"
+ android:layout_marginTop="28dp"
android:layout_marginHorizontal="24dp"
+ android:layout_marginBottom="64dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
@@ -60,7 +61,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
- android:layout_marginBottom="32dp"
+ android:layout_marginBottom="64dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/course/src/main/res/layout/fragment_course_unit_container.xml b/course/src/main/res/layout/fragment_course_unit_container.xml
index ac447d5f3..a99cdb98c 100644
--- a/course/src/main/res/layout/fragment_course_unit_container.xml
+++ b/course/src/main/res/layout/fragment_course_unit_container.xml
@@ -20,7 +20,7 @@
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="@id/statusBarInset"/>
+ app:layout_constraintTop_toTopOf="@id/statusBarInset" />
+ app:layout_constraintStart_toStartOf="parent" />
-
+
+
+ app:layout_constraintTop_toTopOf="parent">
+
+
+
+
diff --git a/course/src/main/res/layout/fragment_video_unit.xml b/course/src/main/res/layout/fragment_video_unit.xml
index eba5c7baa..ffe8694d7 100644
--- a/course/src/main/res/layout/fragment_video_unit.xml
+++ b/course/src/main/res/layout/fragment_video_unit.xml
@@ -41,8 +41,9 @@
android:id="@+id/subtitles"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_marginVertical="28dp"
+ android:layout_marginTop="28dp"
android:layout_marginHorizontal="24dp"
+ android:layout_marginBottom="64dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
@@ -55,7 +56,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
- android:layout_marginBottom="32dp"
+ android:layout_marginBottom="64dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/course/src/main/res/layout/fragment_youtube_video_unit.xml b/course/src/main/res/layout/fragment_youtube_video_unit.xml
index 04588a329..c6d61276a 100644
--- a/course/src/main/res/layout/fragment_youtube_video_unit.xml
+++ b/course/src/main/res/layout/fragment_youtube_video_unit.xml
@@ -39,8 +39,9 @@
android:id="@+id/subtitles"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_marginVertical="28dp"
+ android:layout_marginTop="28dp"
android:layout_marginHorizontal="24dp"
+ android:layout_marginBottom="64dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
@@ -53,7 +54,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
- android:layout_marginBottom="32dp"
+ android:layout_marginBottom="64dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/course/src/main/res/values-uk/strings.xml b/course/src/main/res/values-uk/strings.xml
index 0741f8b96..44ff2fc55 100644
--- a/course/src/main/res/values-uk/strings.xml
+++ b/course/src/main/res/values-uk/strings.xml
@@ -14,9 +14,9 @@
Переглянути сертифікат
Ви можете отримати сертифікат після проходження курсу (заробіть необхідну оцінку)
Отримати сертифікат
- Попередній
+ Назад
Попередня одиниця
- Наступний
+ Далі
Наступна одиниця
Завершити
Поверніть свій пристрій для перегляду цього відео на повний екран.
@@ -30,7 +30,8 @@
Будьте в курсі останніх новин
Гарна робота!
Секція \"%s\" завершена.
- Повернутися до змісту
+ Наступний розділ
+ Повернутись до модуля
Ви не можете записатися на цей курс, оскільки термін запису вже минув.
Цей курс ще не розпочався.
Ваш доступ до цього курсу закінчився.
@@ -46,4 +47,5 @@
Субтитри
Остання активність:
Продовжити
+ Щоб перейти до \"%s\", натисніть \"Наступний розділ\".
\ No newline at end of file
diff --git a/course/src/main/res/values/strings.xml b/course/src/main/res/values/strings.xml
index c42b409cb..a9fb56914 100644
--- a/course/src/main/res/values/strings.xml
+++ b/course/src/main/res/values/strings.xml
@@ -14,7 +14,7 @@
View the certificate
You can get a certificate after completing the course (earn required grade)
Get the certificate
- Previous
+ Prev
Previous Unit
Next
Next Unit
@@ -31,6 +31,7 @@
Good work!
Section \"%s\" is finished.
Back to outline
+ Next section
You cannot enroll in this course because the enrollment date is over.
This course hasn’t started yet.
Your access to this course has expired.
@@ -46,4 +47,5 @@
Subtitles
Continue with:
Continue
+ To proceed with \"%s\" press \"Next section\".
\ No newline at end of file
diff --git a/course/src/test/java/com/raccoongang/course/presentation/units/CourseUnitsViewModelTest.kt b/course/src/test/java/com/raccoongang/course/presentation/units/CourseUnitsViewModelTest.kt
deleted file mode 100644
index 0cacb2162..000000000
--- a/course/src/test/java/com/raccoongang/course/presentation/units/CourseUnitsViewModelTest.kt
+++ /dev/null
@@ -1,366 +0,0 @@
-package com.raccoongang.course.presentation.units
-
-import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleOwner
-import androidx.lifecycle.LifecycleRegistry
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.domain.model.*
-import com.raccoongang.core.module.DownloadWorkerController
-import com.raccoongang.core.module.db.DownloadModel
-import com.raccoongang.core.module.db.DownloadedState
-import com.raccoongang.core.module.db.FileType
-import com.raccoongang.core.presentation.course.CourseViewMode
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.core.system.connection.NetworkConnection
-import com.raccoongang.course.domain.interactor.CourseInteractor
-import io.mockk.*
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.flow
-import kotlinx.coroutines.test.*
-import org.junit.After
-import org.junit.Assert.*
-import org.junit.Before
-import org.junit.Rule
-import org.junit.Test
-import org.junit.rules.TestRule
-import java.net.UnknownHostException
-
-@OptIn(ExperimentalCoroutinesApi::class)
-class CourseUnitsViewModelTest {
-
- @get:Rule
- val testInstantTaskExecutorRule: TestRule = InstantTaskExecutorRule()
-
- private val dispatcher = UnconfinedTestDispatcher()
-
- private val resourceManager = mockk()
- private val interactor = mockk()
- private val workerController = mockk()
- private val networkConnection = mockk()
- private val preferencesManager = mockk()
-
- private val noInternet = "Slow or no internet connection"
- private val somethingWrong = "Something went wrong"
- private val cantDownload = "You can download content only from Wi-fi"
-
-
- private val blocks = listOf(
- Block(
- id = "id",
- blockId = "blockId",
- lmsWebUrl = "lmsWebUrl",
- legacyWebUrl = "legacyWebUrl",
- studentViewUrl = "studentViewUrl",
- type = BlockType.HTML,
- displayName = "Block",
- graded = false,
- studentViewData = StudentViewData(
- false,
- encodedVideos = EncodedVideos(
- mobileHigh = VideoInfo("", 0),
- youtube = null,
- hls = null,
- fallback = null,
- desktopMp4 = null,
- mobileLow = null
- ),
- duration = 0,
- topicId = "",
- transcripts = null
- ),
- studentViewMultiDevice = false,
- blockCounts = BlockCounts(0),
- descendants = listOf("1", "id1"),
- completion = 0.0
- ),
- Block(
- id = "id1",
- blockId = "blockId",
- lmsWebUrl = "lmsWebUrl",
- legacyWebUrl = "legacyWebUrl",
- studentViewUrl = "studentViewUrl",
- type = BlockType.HTML,
- displayName = "Block",
- graded = false,
- studentViewData = null,
- studentViewMultiDevice = false,
- blockCounts = BlockCounts(0),
- descendants = listOf("id2"),
- completion = 0.0
- ),
- Block(
- id = "id2",
- blockId = "blockId",
- lmsWebUrl = "lmsWebUrl",
- legacyWebUrl = "legacyWebUrl",
- studentViewUrl = "studentViewUrl",
- type = BlockType.HTML,
- displayName = "Block",
- graded = false,
- studentViewData = null,
- studentViewMultiDevice = false,
- blockCounts = BlockCounts(0),
- descendants = emptyList(),
- completion = 0.0
- )
- )
-
- private val courseStructure = CourseStructure(
- "", blocks, "", "", "", "",
- null, "", "", null,
- CoursewareAccess(false, "", "", "", "", ""),
- null,
- null,
- false
- )
-
- private val downloadModel = DownloadModel(
- "id",
- "title",
- 0,
- "",
- "url",
- FileType.VIDEO,
- DownloadedState.NOT_DOWNLOADED,
- null
- )
-
-
- @Before
- fun setUp() {
- Dispatchers.setMain(dispatcher)
- every { resourceManager.getString(R.string.core_error_no_connection) } returns noInternet
- every { resourceManager.getString(R.string.core_error_unknown_error) } returns somethingWrong
- every { resourceManager.getString(com.raccoongang.course.R.string.course_can_download_only_with_wifi) } returns cantDownload
- }
-
- @After
- fun tearDown() {
- Dispatchers.resetMain()
- }
-
- @Test
- fun `getBlocks no internet connection exception`() = runTest {
- val viewModel = CourseUnitsViewModel(
- interactor,
- resourceManager,
- preferencesManager,
- networkConnection,
- workerController
- )
- every { interactor.getCourseStructureFromCache() } throws UnknownHostException()
- every { interactor.getCourseStructureForVideos() } throws UnknownHostException()
-
- viewModel.getBlocks("", CourseViewMode.FULL)
- advanceUntilIdle()
-
- verify(exactly = 1) { interactor.getCourseStructureFromCache() }
-
- val message = viewModel.uiMessage.value as? UIMessage.SnackBarMessage
- assertEquals(noInternet, message?.message)
- assert(viewModel.uiState.value is CourseUnitsUIState.Loading)
- }
-
- @Test
- fun `getBlocks unknown exception`() = runTest {
- val viewModel = CourseUnitsViewModel(
- interactor,
- resourceManager,
- preferencesManager,
- networkConnection,
- workerController
- )
- every { interactor.getCourseStructureFromCache() } throws Exception()
- every { interactor.getCourseStructureForVideos() } throws Exception()
-
- viewModel.getBlocks("", CourseViewMode.FULL)
- advanceUntilIdle()
-
- verify(exactly = 1) { interactor.getCourseStructureFromCache() }
-
- val message = viewModel.uiMessage.value as? UIMessage.SnackBarMessage
- assertEquals(somethingWrong, message?.message)
- assert(viewModel.uiState.value is CourseUnitsUIState.Loading)
- }
-
- @Test
- fun `getBlocks success`() = runTest {
- val viewModel = CourseUnitsViewModel(
- interactor,
- resourceManager,
- preferencesManager,
- networkConnection,
- workerController
- )
- every { interactor.getCourseStructureFromCache() } returns courseStructure
- every { interactor.getCourseStructureForVideos() } returns courseStructure
- coEvery { interactor.getDownloadModels() } returns flow {
- emit(listOf(downloadModel))
- }
-
- viewModel.getBlocks("id", CourseViewMode.FULL)
- advanceUntilIdle()
-
- verify(exactly = 1) { interactor.getCourseStructureFromCache() }
-
- assert(viewModel.uiMessage.value == null)
- assert(viewModel.uiState.value is CourseUnitsUIState.Blocks)
- }
-
- @Test
- fun `getBlocks success videos`() = runTest {
- val viewModel = CourseUnitsViewModel(
- interactor,
- resourceManager,
- preferencesManager,
- networkConnection,
- workerController
- )
- every { interactor.getCourseStructureFromCache() } returns courseStructure
- every { interactor.getCourseStructureForVideos() } returns courseStructure
- coEvery { interactor.getDownloadModels() } returns flow {
- emit(listOf(downloadModel))
- }
-
- viewModel.getBlocks("id", CourseViewMode.VIDEOS)
- advanceUntilIdle()
-
- verify(exactly = 1) { interactor.getCourseStructureForVideos() }
-
- assert(viewModel.uiMessage.value == null)
- assert(viewModel.uiState.value is CourseUnitsUIState.Blocks)
- }
-
- @Test
- fun `getDownloadModels observe`() = runTest {
- val viewModel = CourseUnitsViewModel(
- interactor,
- resourceManager,
- preferencesManager,
- networkConnection,
- workerController
- )
- every { interactor.getCourseStructureFromCache() } returns courseStructure
- every { interactor.getCourseStructureForVideos() } returns courseStructure
- coEvery { interactor.getDownloadModels() } returns flow {
- repeat(5) {
- delay(10000)
- emit(listOf(downloadModel))
- }
- }
-
- val mockLifeCycleOwner: LifecycleOwner = mockk()
- val lifecycleRegistry = LifecycleRegistry(mockLifeCycleOwner)
- lifecycleRegistry.addObserver(viewModel)
- lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START)
- viewModel.getBlocks("id", CourseViewMode.VIDEOS)
- advanceUntilIdle()
-
-
- assert(viewModel.uiMessage.value == null)
- assert(viewModel.uiState.value is CourseUnitsUIState.Blocks)
- }
-
- @Test
- fun `removeDownload success`() = runTest {
- val viewModel = CourseUnitsViewModel(
- interactor,
- resourceManager,
- preferencesManager,
- networkConnection,
- workerController
- )
- coEvery { interactor.removeDownloadModel(any()) } returns Unit
-
- viewModel.removeDownloadedModel("")
- advanceUntilIdle()
-
- coVerify(exactly = 1) { interactor.removeDownloadModel(any()) }
- }
-
- @Test
- fun `cancelWork success`() = runTest {
- val viewModel = CourseUnitsViewModel(
- interactor,
- resourceManager,
- preferencesManager,
- networkConnection,
- workerController
- )
- coEvery { workerController.cancelWork(any()) } returns Unit
-
- viewModel.cancelWork("")
- advanceUntilIdle()
-
- coVerify(exactly = 1) { workerController.cancelWork(any()) }
- }
-
- @Test
- fun `saveDownloadModel test`() = runTest {
- val viewModel = CourseUnitsViewModel(
- interactor,
- resourceManager,
- preferencesManager,
- networkConnection,
- workerController
- )
- every { preferencesManager.videoSettings.wifiDownloadOnly } returns false
- every { preferencesManager.videoSettings.videoQuality } returns VideoQuality.AUTO
- every { networkConnection.isWifiConnected() } returns true
- coEvery { workerController.saveModels(*anyVararg()) } returns Unit
-
- viewModel.saveDownloadModel("", blocks[0])
- advanceUntilIdle()
-
- assert(viewModel.uiMessage.value == null)
- }
-
- @Test
- fun `saveDownloadModel only wifi download, with connection`() = runTest {
- val viewModel = CourseUnitsViewModel(
- interactor,
- resourceManager,
- preferencesManager,
- networkConnection,
- workerController
- )
- every { preferencesManager.videoSettings.wifiDownloadOnly } returns true
- every { preferencesManager.videoSettings.videoQuality } returns VideoQuality.AUTO
- every { networkConnection.isWifiConnected() } returns true
- coEvery { workerController.saveModels(*anyVararg()) } returns Unit
-
- viewModel.saveDownloadModel("", blocks[0])
- advanceUntilIdle()
-
- assert(viewModel.uiMessage.value == null)
- }
-
- @Test
- fun `saveDownloadModel only wifi download, without connection`() = runTest {
- val viewModel = CourseUnitsViewModel(
- interactor,
- resourceManager,
- preferencesManager,
- networkConnection,
- workerController
- )
- every { preferencesManager.videoSettings.wifiDownloadOnly } returns true
- every { preferencesManager.videoSettings.videoQuality } returns VideoQuality.AUTO
- every { networkConnection.isWifiConnected() } returns false
- every { networkConnection.isOnline() } returns false
- coEvery { workerController.saveModels(*anyVararg()) } returns Unit
-
- viewModel.saveDownloadModel("", blocks[0])
-
- advanceUntilIdle()
-
- assert(viewModel.uiMessage.value != null)
- }
-
-}
\ No newline at end of file
diff --git a/course/src/test/java/com/raccoongang/course/presentation/container/CourseContainerViewModelTest.kt b/course/src/test/java/org/openedx/course/presentation/container/CourseContainerViewModelTest.kt
similarity index 68%
rename from course/src/test/java/com/raccoongang/course/presentation/container/CourseContainerViewModelTest.kt
rename to course/src/test/java/org/openedx/course/presentation/container/CourseContainerViewModelTest.kt
index 0265d4750..3aff2df2b 100644
--- a/course/src/test/java/com/raccoongang/course/presentation/container/CourseContainerViewModelTest.kt
+++ b/course/src/test/java/org/openedx/course/presentation/container/CourseContainerViewModelTest.kt
@@ -1,14 +1,15 @@
-package com.raccoongang.course.presentation.container
+package org.openedx.course.presentation.container
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import com.raccoongang.core.system.connection.NetworkConnection
-import com.raccoongang.core.R
-import com.raccoongang.core.domain.model.CourseStructure
-import com.raccoongang.core.domain.model.CoursewareAccess
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.core.system.notifier.CourseNotifier
-import com.raccoongang.core.system.notifier.CourseStructureUpdated
-import com.raccoongang.course.domain.interactor.CourseInteractor
+import org.openedx.core.system.connection.NetworkConnection
+import org.openedx.core.R
+import org.openedx.core.domain.model.CourseStructure
+import org.openedx.core.domain.model.CoursewareAccess
+import org.openedx.core.system.ResourceManager
+import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.core.system.notifier.CourseStructureUpdated
+import org.openedx.course.domain.interactor.CourseInteractor
+import org.openedx.course.presentation.CourseAnalytics
import io.mockk.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -33,32 +34,33 @@ class CourseContainerViewModelTest {
private val interactor = mockk()
private val networkConnection = mockk()
private val notifier = spyk()
+ private val analytics = mockk()
private val noInternet = "Slow or no internet connection"
private val somethingWrong = "Something went wrong"
private val courseStructure = CourseStructure(
- root = "",
- blockData = listOf(),
- id = "id",
- name = "Course name",
- number = "",
- org = "Org",
- start = null,
- startDisplay = "",
- startType = "",
- end = null,
- coursewareAccess = CoursewareAccess(
- true,
- "",
- "",
- "",
- "",
- ""
- ),
- media = null,
- certificate = null,
- isSelfPaced = false
+ root = "",
+ blockData = listOf(),
+ id = "id",
+ name = "Course name",
+ number = "",
+ org = "Org",
+ start = null,
+ startDisplay = "",
+ startType = "",
+ end = null,
+ coursewareAccess = CoursewareAccess(
+ true,
+ "",
+ "",
+ "",
+ "",
+ ""
+ ),
+ media = null,
+ certificate = null,
+ isSelfPaced = false
)
@Before
@@ -75,7 +77,14 @@ class CourseContainerViewModelTest {
@Test
fun `preloadCourseStructure internet connection exception`() = runTest {
- val viewModel = CourseContainerViewModel("", interactor,resourceManager, notifier, networkConnection)
+ val viewModel = CourseContainerViewModel(
+ "",
+ interactor,
+ resourceManager,
+ notifier,
+ networkConnection,
+ analytics
+ )
every { networkConnection.isOnline() } returns true
coEvery { interactor.preloadCourseStructure(any()) } throws UnknownHostException()
viewModel.preloadCourseStructure()
@@ -91,7 +100,14 @@ class CourseContainerViewModelTest {
@Test
fun `preloadCourseStructure unknown exception`() = runTest {
- val viewModel = CourseContainerViewModel("", interactor,resourceManager, notifier, networkConnection)
+ val viewModel = CourseContainerViewModel(
+ "",
+ interactor,
+ resourceManager,
+ notifier,
+ networkConnection,
+ analytics
+ )
every { networkConnection.isOnline() } returns true
coEvery { interactor.preloadCourseStructure(any()) } throws Exception()
viewModel.preloadCourseStructure()
@@ -107,7 +123,14 @@ class CourseContainerViewModelTest {
@Test
fun `preloadCourseStructure success with internet`() = runTest {
- val viewModel = CourseContainerViewModel("", interactor,resourceManager, notifier, networkConnection)
+ val viewModel = CourseContainerViewModel(
+ "",
+ interactor,
+ resourceManager,
+ notifier,
+ networkConnection,
+ analytics
+ )
every { networkConnection.isOnline() } returns true
coEvery { interactor.preloadCourseStructure(any()) } returns Unit
every { interactor.getCourseStructureFromCache() } returns courseStructure
@@ -123,7 +146,14 @@ class CourseContainerViewModelTest {
@Test
fun `preloadCourseStructure success without internet`() = runTest {
- val viewModel = CourseContainerViewModel("", interactor,resourceManager, notifier, networkConnection)
+ val viewModel = CourseContainerViewModel(
+ "",
+ interactor,
+ resourceManager,
+ notifier,
+ networkConnection,
+ analytics
+ )
every { networkConnection.isOnline() } returns false
coEvery { interactor.preloadCourseStructureFromCache(any()) } returns Unit
every { interactor.getCourseStructureFromCache() } returns courseStructure
@@ -140,7 +170,14 @@ class CourseContainerViewModelTest {
@Test
fun `updateData no internet connection exception`() = runTest {
- val viewModel = CourseContainerViewModel("", interactor,resourceManager, notifier, networkConnection)
+ val viewModel = CourseContainerViewModel(
+ "",
+ interactor,
+ resourceManager,
+ notifier,
+ networkConnection,
+ analytics
+ )
coEvery { interactor.preloadCourseStructure(any()) } throws UnknownHostException()
coEvery { notifier.send(CourseStructureUpdated("", false)) } returns Unit
viewModel.updateData(false)
@@ -155,7 +192,14 @@ class CourseContainerViewModelTest {
@Test
fun `updateData unknown exception`() = runTest {
- val viewModel = CourseContainerViewModel("", interactor,resourceManager, notifier, networkConnection)
+ val viewModel = CourseContainerViewModel(
+ "",
+ interactor,
+ resourceManager,
+ notifier,
+ networkConnection,
+ analytics
+ )
coEvery { interactor.preloadCourseStructure(any()) } throws Exception()
coEvery { notifier.send(CourseStructureUpdated("", false)) } returns Unit
viewModel.updateData(false)
@@ -170,7 +214,14 @@ class CourseContainerViewModelTest {
@Test
fun `updateData success`() = runTest {
- val viewModel = CourseContainerViewModel("", interactor,resourceManager, notifier, networkConnection)
+ val viewModel = CourseContainerViewModel(
+ "",
+ interactor,
+ resourceManager,
+ notifier,
+ networkConnection,
+ analytics
+ )
coEvery { interactor.preloadCourseStructure(any()) } returns Unit
coEvery { notifier.send(CourseStructureUpdated("", false)) } returns Unit
viewModel.updateData(false)
diff --git a/course/src/test/java/com/raccoongang/course/presentation/detail/CourseDetailsViewModelTest.kt b/course/src/test/java/org/openedx/course/presentation/detail/CourseDetailsViewModelTest.kt
similarity index 71%
rename from course/src/test/java/com/raccoongang/course/presentation/detail/CourseDetailsViewModelTest.kt
rename to course/src/test/java/org/openedx/course/presentation/detail/CourseDetailsViewModelTest.kt
index d69e22cbc..e820a1ec7 100644
--- a/course/src/test/java/com/raccoongang/course/presentation/detail/CourseDetailsViewModelTest.kt
+++ b/course/src/test/java/org/openedx/course/presentation/detail/CourseDetailsViewModelTest.kt
@@ -1,13 +1,16 @@
-package com.raccoongang.course.presentation.detail
+package org.openedx.course.presentation.detail
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import com.raccoongang.core.system.connection.NetworkConnection
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.core.system.notifier.CourseDashboardUpdate
-import com.raccoongang.core.system.notifier.CourseNotifier
-import com.raccoongang.course.domain.interactor.CourseInteractor
+import org.openedx.core.system.connection.NetworkConnection
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.Course
+import org.openedx.core.domain.model.Media
+import org.openedx.core.system.ResourceManager
+import org.openedx.core.system.notifier.CourseDashboardUpdate
+import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.course.domain.interactor.CourseInteractor
+import org.openedx.course.presentation.CourseAnalytics
import io.mockk.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -33,10 +36,35 @@ class CourseDetailsViewModelTest {
private val interactor = mockk()
private val networkConnection = mockk()
private val notifier = spyk()
+ private val analytics = mockk()
private val noInternet = "Slow or no internet connection"
private val somethingWrong = "Something went wrong"
+ private val mockCourse = Course(
+ id = "id",
+ blocksUrl = "blocksUrl",
+ courseId = "courseId",
+ effort = "effort",
+ enrollmentStart = null,
+ enrollmentEnd = null,
+ hidden = false,
+ invitationOnly = false,
+ media = Media(),
+ mobileAvailable = true,
+ name = "Test course",
+ number = "number",
+ org = "EdX",
+ pacing = "pacing",
+ shortDescription = "shortDescription",
+ start = "start",
+ end = "end",
+ startDisplay = "startDisplay",
+ startType = "startType",
+ overview = "",
+ isEnrolled = false
+ )
+
@Before
fun setUp() {
Dispatchers.setMain(dispatcher)
@@ -52,7 +80,7 @@ class CourseDetailsViewModelTest {
@Test
fun `getCourseDetails no internet connection exception`() = runTest {
val viewModel =
- CourseDetailsViewModel("", networkConnection, interactor, resourceManager, notifier)
+ CourseDetailsViewModel("", networkConnection, interactor, resourceManager, notifier, analytics)
every { networkConnection.isOnline() } returns true
coEvery { interactor.getCourseDetails(any()) } throws UnknownHostException()
advanceUntilIdle()
@@ -68,7 +96,7 @@ class CourseDetailsViewModelTest {
@Test
fun `getCourseDetails unknown exception`() = runTest {
val viewModel =
- CourseDetailsViewModel("", networkConnection, interactor, resourceManager, notifier)
+ CourseDetailsViewModel("", networkConnection, interactor, resourceManager, notifier, analytics)
every { networkConnection.isOnline() } returns true
coEvery { interactor.getCourseDetails(any()) } throws Exception()
advanceUntilIdle()
@@ -84,7 +112,7 @@ class CourseDetailsViewModelTest {
@Test
fun `getCourseDetails success with internet`() = runTest {
val viewModel =
- CourseDetailsViewModel("", networkConnection, interactor, resourceManager, notifier)
+ CourseDetailsViewModel("", networkConnection, interactor, resourceManager, notifier, analytics)
every { networkConnection.isOnline() } returns true
coEvery { interactor.getCourseDetails(any()) } returns mockk()
@@ -99,7 +127,7 @@ class CourseDetailsViewModelTest {
@Test
fun `getCourseDetails success without internet`() = runTest {
val viewModel =
- CourseDetailsViewModel("", networkConnection, interactor, resourceManager, notifier)
+ CourseDetailsViewModel("", networkConnection, interactor, resourceManager, notifier, analytics)
every { networkConnection.isOnline() } returns false
coEvery { interactor.getCourseDetailsFromCache(any()) } returns mockk()
@@ -115,17 +143,20 @@ class CourseDetailsViewModelTest {
@Test
fun `enrollInACourse internet connection error`() = runTest {
val viewModel =
- CourseDetailsViewModel("", networkConnection, interactor, resourceManager, notifier)
+ CourseDetailsViewModel("", networkConnection, interactor, resourceManager, notifier, analytics)
coEvery { interactor.enrollInACourse(any()) } throws UnknownHostException()
coEvery { notifier.send(CourseDashboardUpdate()) } returns Unit
every { networkConnection.isOnline() } returns true
- coEvery { interactor.getCourseDetails(any()) } returns mockk()
-
+ coEvery { interactor.getCourseDetails(any()) } returns mockCourse
+ every { analytics.courseEnrollClickedEvent(any(), any()) } returns Unit
+ every { analytics.courseEnrollSuccessEvent(any(), any()) } returns Unit
viewModel.enrollInACourse("")
advanceUntilIdle()
coVerify(exactly = 1) { interactor.enrollInACourse(any()) }
+ verify(exactly = 1) { analytics.courseEnrollClickedEvent(any(), any()) }
+ verify(exactly = 0) { analytics.courseEnrollSuccessEvent(any(), any()) }
val message = viewModel.uiMessage.value as? UIMessage.SnackBarMessage
assertEquals(noInternet, message?.message)
@@ -135,17 +166,21 @@ class CourseDetailsViewModelTest {
@Test
fun `enrollInACourse unknown exception`() = runTest {
val viewModel =
- CourseDetailsViewModel("", networkConnection, interactor, resourceManager, notifier)
+ CourseDetailsViewModel("", networkConnection, interactor, resourceManager, notifier, analytics)
coEvery { interactor.enrollInACourse(any()) } throws Exception()
coEvery { notifier.send(CourseDashboardUpdate()) } returns Unit
every { networkConnection.isOnline() } returns true
- coEvery { interactor.getCourseDetails(any()) } returns mockk()
+ coEvery { interactor.getCourseDetails(any()) } returns mockCourse
+ every { analytics.courseEnrollClickedEvent(any(), any()) } returns Unit
+ every { analytics.courseEnrollSuccessEvent(any(), any()) } returns Unit
viewModel.enrollInACourse("")
advanceUntilIdle()
coVerify(exactly = 1) { interactor.enrollInACourse(any()) }
+ verify(exactly = 1) { analytics.courseEnrollClickedEvent(any(), any()) }
+ verify(exactly = 0) { analytics.courseEnrollSuccessEvent(any(), any()) }
val message = viewModel.uiMessage.value as? UIMessage.SnackBarMessage
assertEquals(somethingWrong, message?.message)
@@ -155,11 +190,13 @@ class CourseDetailsViewModelTest {
@Test
fun `enrollInACourse success`() = runTest {
val viewModel =
- CourseDetailsViewModel("", networkConnection, interactor, resourceManager, notifier)
+ CourseDetailsViewModel("", networkConnection, interactor, resourceManager, notifier, analytics)
+ every { analytics.courseEnrollClickedEvent(any(), any()) } returns Unit
+ every { analytics.courseEnrollSuccessEvent(any(), any()) } returns Unit
coEvery { interactor.enrollInACourse(any()) } returns Unit
coEvery { notifier.send(CourseDashboardUpdate()) } returns Unit
every { networkConnection.isOnline() } returns true
- coEvery { interactor.getCourseDetails(any()) } returns mockk()
+ coEvery { interactor.getCourseDetails(any()) } returns mockCourse
delay(200)
@@ -167,6 +204,8 @@ class CourseDetailsViewModelTest {
advanceUntilIdle()
coVerify(exactly = 1) { interactor.enrollInACourse(any()) }
+ verify(exactly = 1) { analytics.courseEnrollClickedEvent(any(), any()) }
+ verify(exactly = 1) { analytics.courseEnrollSuccessEvent(any(), any()) }
assert(viewModel.uiMessage.value == null)
assert(viewModel.uiState.value is CourseDetailsUIState.CourseData)
@@ -175,8 +214,7 @@ class CourseDetailsViewModelTest {
@Test
fun `getCourseAboutBody contains black`() {
val viewModel =
- CourseDetailsViewModel("", networkConnection, interactor, resourceManager, notifier)
-
+ CourseDetailsViewModel("", networkConnection, interactor, resourceManager, notifier, analytics)
val overview = viewModel.getCourseAboutBody(ULong.MAX_VALUE, ULong.MIN_VALUE)
val count = overview.contains("black")
assert(count)
@@ -185,8 +223,7 @@ class CourseDetailsViewModelTest {
@Test
fun `getCourseAboutBody don't contains black`() {
val viewModel =
- CourseDetailsViewModel("", networkConnection, interactor, resourceManager, notifier)
-
+ CourseDetailsViewModel("", networkConnection, interactor, resourceManager, notifier, analytics)
val overview = viewModel.getCourseAboutBody(ULong.MAX_VALUE, ULong.MAX_VALUE)
val count = overview.contains("black")
assert(!count)
diff --git a/course/src/test/java/com/raccoongang/course/presentation/handouts/HandoutsViewModelTest.kt b/course/src/test/java/org/openedx/course/presentation/handouts/HandoutsViewModelTest.kt
similarity index 95%
rename from course/src/test/java/com/raccoongang/course/presentation/handouts/HandoutsViewModelTest.kt
rename to course/src/test/java/org/openedx/course/presentation/handouts/HandoutsViewModelTest.kt
index cbd662459..cbd2821a9 100644
--- a/course/src/test/java/com/raccoongang/course/presentation/handouts/HandoutsViewModelTest.kt
+++ b/course/src/test/java/org/openedx/course/presentation/handouts/HandoutsViewModelTest.kt
@@ -1,8 +1,8 @@
-package com.raccoongang.course.presentation.handouts
+package org.openedx.course.presentation.handouts
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import com.raccoongang.core.domain.model.*
-import com.raccoongang.course.domain.interactor.CourseInteractor
+import org.openedx.core.domain.model.*
+import org.openedx.course.domain.interactor.CourseInteractor
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk
diff --git a/course/src/test/java/com/raccoongang/course/presentation/outline/CourseOutlineViewModelTest.kt b/course/src/test/java/org/openedx/course/presentation/outline/CourseOutlineViewModelTest.kt
similarity index 93%
rename from course/src/test/java/com/raccoongang/course/presentation/outline/CourseOutlineViewModelTest.kt
rename to course/src/test/java/org/openedx/course/presentation/outline/CourseOutlineViewModelTest.kt
index 59e1c7688..2d91dc339 100644
--- a/course/src/test/java/com/raccoongang/course/presentation/outline/CourseOutlineViewModelTest.kt
+++ b/course/src/test/java/org/openedx/course/presentation/outline/CourseOutlineViewModelTest.kt
@@ -1,21 +1,22 @@
-package com.raccoongang.course.presentation.outline
+package org.openedx.course.presentation.outline
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.domain.model.*
-import com.raccoongang.core.module.DownloadWorkerController
-import com.raccoongang.core.module.db.*
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.core.system.connection.NetworkConnection
-import com.raccoongang.core.system.notifier.CourseNotifier
-import com.raccoongang.core.system.notifier.CourseStructureUpdated
-import com.raccoongang.course.domain.interactor.CourseInteractor
+import org.openedx.core.BlockType
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.domain.model.*
+import org.openedx.core.module.DownloadWorkerController
+import org.openedx.core.module.db.*
+import org.openedx.core.system.ResourceManager
+import org.openedx.core.system.connection.NetworkConnection
+import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.core.system.notifier.CourseStructureUpdated
+import org.openedx.course.domain.interactor.CourseInteractor
+import org.openedx.course.presentation.CourseAnalytics
import io.mockk.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -46,6 +47,7 @@ class CourseOutlineViewModelTest {
private val notifier = spyk()
private val downloadDao = mockk()
private val workerController = mockk()
+ private val analytics = mockk()
private val noInternet = "Slow or no internet connection"
private val somethingWrong = "Something went wrong"
@@ -139,7 +141,7 @@ class CourseOutlineViewModelTest {
Dispatchers.setMain(dispatcher)
every { resourceManager.getString(R.string.core_error_no_connection) } returns noInternet
every { resourceManager.getString(R.string.core_error_unknown_error) } returns somethingWrong
- every { resourceManager.getString(com.raccoongang.course.R.string.course_can_download_only_with_wifi) } returns cantDownload
+ every { resourceManager.getString(org.openedx.course.R.string.course_can_download_only_with_wifi) } returns cantDownload
}
@After
@@ -160,6 +162,7 @@ class CourseOutlineViewModelTest {
notifier,
networkConnection,
preferencesManager,
+ analytics,
downloadDao,
workerController
)
@@ -187,6 +190,7 @@ class CourseOutlineViewModelTest {
notifier,
networkConnection,
preferencesManager,
+ analytics,
downloadDao,
workerController
)
@@ -224,6 +228,7 @@ class CourseOutlineViewModelTest {
notifier,
networkConnection,
preferencesManager,
+ analytics,
downloadDao,
workerController
)
@@ -260,6 +265,7 @@ class CourseOutlineViewModelTest {
notifier,
networkConnection,
preferencesManager,
+ analytics,
downloadDao,
workerController
)
@@ -296,6 +302,7 @@ class CourseOutlineViewModelTest {
notifier,
networkConnection,
preferencesManager,
+ analytics,
downloadDao,
workerController
)
@@ -320,6 +327,7 @@ class CourseOutlineViewModelTest {
notifier,
networkConnection,
preferencesManager,
+ analytics,
downloadDao,
workerController
)
@@ -363,6 +371,7 @@ class CourseOutlineViewModelTest {
notifier,
networkConnection,
preferencesManager,
+ analytics,
downloadDao,
workerController
)
@@ -391,6 +400,7 @@ class CourseOutlineViewModelTest {
notifier,
networkConnection,
preferencesManager,
+ analytics,
downloadDao,
workerController
)
@@ -417,6 +427,7 @@ class CourseOutlineViewModelTest {
notifier,
networkConnection,
preferencesManager,
+ analytics,
downloadDao,
workerController
)
diff --git a/course/src/test/java/com/raccoongang/course/presentation/section/CourseSectionViewModelTest.kt b/course/src/test/java/org/openedx/course/presentation/section/CourseSectionViewModelTest.kt
similarity index 87%
rename from course/src/test/java/com/raccoongang/course/presentation/section/CourseSectionViewModelTest.kt
rename to course/src/test/java/org/openedx/course/presentation/section/CourseSectionViewModelTest.kt
index 64c3df5c1..fdbd34d42 100644
--- a/course/src/test/java/com/raccoongang/course/presentation/section/CourseSectionViewModelTest.kt
+++ b/course/src/test/java/org/openedx/course/presentation/section/CourseSectionViewModelTest.kt
@@ -1,23 +1,25 @@
-package com.raccoongang.course.presentation.section
+package org.openedx.course.presentation.section
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.domain.model.Block
-import com.raccoongang.core.domain.model.BlockCounts
-import com.raccoongang.core.domain.model.CourseStructure
-import com.raccoongang.core.domain.model.CoursewareAccess
-import com.raccoongang.core.module.DownloadWorkerController
-import com.raccoongang.core.module.db.*
-import com.raccoongang.core.presentation.course.CourseViewMode
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.core.system.connection.NetworkConnection
-import com.raccoongang.course.domain.interactor.CourseInteractor
+import org.openedx.core.BlockType
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.domain.model.Block
+import org.openedx.core.domain.model.BlockCounts
+import org.openedx.core.domain.model.CourseStructure
+import org.openedx.core.domain.model.CoursewareAccess
+import org.openedx.core.module.DownloadWorkerController
+import org.openedx.core.module.db.*
+import org.openedx.core.presentation.course.CourseViewMode
+import org.openedx.core.system.ResourceManager
+import org.openedx.core.system.connection.NetworkConnection
+import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.course.domain.interactor.CourseInteractor
+import org.openedx.course.presentation.CourseAnalytics
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
@@ -50,6 +52,8 @@ class CourseSectionViewModelTest {
private val workerController = mockk()
private val networkConnection = mockk()
private val preferencesManager = mockk()
+ private val notifier = mockk()
+ private val analytics = mockk()
private val noInternet = "Slow or no internet connection"
private val somethingWrong = "Something went wrong"
@@ -144,7 +148,7 @@ class CourseSectionViewModelTest {
Dispatchers.setMain(dispatcher)
every { resourceManager.getString(R.string.core_error_no_connection) } returns noInternet
every { resourceManager.getString(R.string.core_error_unknown_error) } returns somethingWrong
- every { resourceManager.getString(com.raccoongang.course.R.string.course_can_download_only_with_wifi) } returns cantDownload
+ every { resourceManager.getString(org.openedx.course.R.string.course_can_download_only_with_wifi) } returns cantDownload
}
@After
@@ -159,6 +163,8 @@ class CourseSectionViewModelTest {
resourceManager,
networkConnection,
preferencesManager,
+ notifier,
+ analytics,
workerController,
downloadDao,
""
@@ -185,6 +191,8 @@ class CourseSectionViewModelTest {
resourceManager,
networkConnection,
preferencesManager,
+ notifier,
+ analytics,
workerController,
downloadDao,
""
@@ -211,6 +219,8 @@ class CourseSectionViewModelTest {
resourceManager,
networkConnection,
preferencesManager,
+ notifier,
+ analytics,
workerController,
downloadDao,
""
@@ -239,9 +249,11 @@ class CourseSectionViewModelTest {
resourceManager,
networkConnection,
preferencesManager,
+ notifier,
+ analytics,
workerController,
downloadDao,
- "",
+ ""
)
every { preferencesManager.videoSettings.wifiDownloadOnly } returns false
every { networkConnection.isWifiConnected() } returns true
@@ -260,9 +272,11 @@ class CourseSectionViewModelTest {
resourceManager,
networkConnection,
preferencesManager,
+ notifier,
+ analytics,
workerController,
downloadDao,
- "",
+ ""
)
every { preferencesManager.videoSettings.wifiDownloadOnly } returns true
every { networkConnection.isWifiConnected() } returns true
@@ -281,9 +295,11 @@ class CourseSectionViewModelTest {
resourceManager,
networkConnection,
preferencesManager,
+ notifier,
+ analytics,
workerController,
downloadDao,
- "",
+ ""
)
every { preferencesManager.videoSettings.wifiDownloadOnly } returns true
every { networkConnection.isWifiConnected() } returns false
@@ -305,9 +321,11 @@ class CourseSectionViewModelTest {
resourceManager,
networkConnection,
preferencesManager,
+ notifier,
+ analytics,
workerController,
downloadDao,
- "",
+ ""
)
every { downloadDao.readAllData() } returns flow {
@@ -316,6 +334,7 @@ class CourseSectionViewModelTest {
emit(emptyList())
}
}
+ coEvery { notifier.notifier } returns flow { }
coEvery { interactor.getCourseStructureFromCache() } returns courseStructure
coEvery { interactor.getCourseStructureForVideos() } returns courseStructure
diff --git a/course/src/test/java/com/raccoongang/course/presentation/unit/container/CourseUnitContainerViewModelTest.kt b/course/src/test/java/org/openedx/course/presentation/unit/container/CourseUnitContainerViewModelTest.kt
similarity index 76%
rename from course/src/test/java/com/raccoongang/course/presentation/unit/container/CourseUnitContainerViewModelTest.kt
rename to course/src/test/java/org/openedx/course/presentation/unit/container/CourseUnitContainerViewModelTest.kt
index af3f4413b..7313be328 100644
--- a/course/src/test/java/com/raccoongang/course/presentation/unit/container/CourseUnitContainerViewModelTest.kt
+++ b/course/src/test/java/org/openedx/course/presentation/unit/container/CourseUnitContainerViewModelTest.kt
@@ -1,14 +1,15 @@
-package com.raccoongang.course.presentation.unit.container
+package org.openedx.course.presentation.unit.container
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.domain.model.Block
-import com.raccoongang.core.domain.model.BlockCounts
-import com.raccoongang.core.domain.model.CourseStructure
-import com.raccoongang.core.domain.model.CoursewareAccess
-import com.raccoongang.core.presentation.course.CourseViewMode
-import com.raccoongang.core.system.notifier.CourseNotifier
-import com.raccoongang.course.domain.interactor.CourseInteractor
+import org.openedx.core.BlockType
+import org.openedx.core.domain.model.Block
+import org.openedx.core.domain.model.BlockCounts
+import org.openedx.core.domain.model.CourseStructure
+import org.openedx.core.domain.model.CoursewareAccess
+import org.openedx.core.presentation.course.CourseViewMode
+import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.course.domain.interactor.CourseInteractor
+import org.openedx.course.presentation.CourseAnalytics
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
@@ -33,6 +34,7 @@ class CourseUnitContainerViewModelTest {
private val interactor = mockk()
private val notifier = mockk()
+ private val analytics = mockk()
private val blocks = listOf(
Block(
@@ -121,7 +123,7 @@ class CourseUnitContainerViewModelTest {
certificate = null,
isSelfPaced = false
)
-
+
@Before
fun setUp() {
Dispatchers.setMain(dispatcher)
@@ -134,7 +136,7 @@ class CourseUnitContainerViewModelTest {
@Test
fun `getBlocks no internet connection exception`() = runTest {
- val viewModel = CourseUnitContainerViewModel(interactor, notifier, "")
+ val viewModel = CourseUnitContainerViewModel(interactor, notifier, analytics, "")
every { interactor.getCourseStructureFromCache() } throws UnknownHostException()
every { interactor.getCourseStructureForVideos() } throws UnknownHostException()
@@ -147,7 +149,7 @@ class CourseUnitContainerViewModelTest {
@Test
fun `getBlocks unknown exception`() = runTest {
- val viewModel = CourseUnitContainerViewModel(interactor, notifier, "")
+ val viewModel = CourseUnitContainerViewModel(interactor, notifier, analytics, "")
every { interactor.getCourseStructureFromCache() } throws UnknownHostException()
every { interactor.getCourseStructureForVideos() } throws UnknownHostException()
@@ -160,7 +162,7 @@ class CourseUnitContainerViewModelTest {
@Test
fun `getBlocks unknown success`() = runTest {
- val viewModel = CourseUnitContainerViewModel(interactor, notifier, "")
+ val viewModel = CourseUnitContainerViewModel(interactor, notifier, analytics, "")
every { interactor.getCourseStructureFromCache() } returns courseStructure
every { interactor.getCourseStructureForVideos() } returns courseStructure
@@ -175,7 +177,7 @@ class CourseUnitContainerViewModelTest {
@Test
fun `setupCurrentIndex`() = runTest {
- val viewModel = CourseUnitContainerViewModel(interactor, notifier, "")
+ val viewModel = CourseUnitContainerViewModel(interactor, notifier, analytics, "")
every { interactor.getCourseStructureFromCache() } returns courseStructure
every { interactor.getCourseStructureForVideos() } returns courseStructure
@@ -187,43 +189,9 @@ class CourseUnitContainerViewModelTest {
verify(exactly = 1) { interactor.getCourseStructureForVideos() }
}
- @Test
- fun `prevVertical`() = runTest {
- val viewModel = CourseUnitContainerViewModel(interactor, notifier, "")
- every { interactor.getCourseStructureFromCache() } returns courseStructure
- every { interactor.getCourseStructureForVideos() } returns courseStructure
-
- viewModel.loadBlocks(CourseViewMode.VIDEOS)
- viewModel.setupCurrentIndex("id2")
-
- advanceUntilIdle()
-
- verify(exactly = 0) { interactor.getCourseStructureFromCache() }
- verify(exactly = 1) { interactor.getCourseStructureForVideos() }
- assert(!viewModel.isLastIndexInContainer)
- assert(viewModel.isFirstIndexInContainer)
- }
-
- @Test
- fun `nextVertical null`() = runTest {
- val viewModel = CourseUnitContainerViewModel(interactor, notifier, "")
- every { interactor.getCourseStructureFromCache() } returns courseStructure
- every { interactor.getCourseStructureForVideos() } returns courseStructure
-
- viewModel.loadBlocks(CourseViewMode.VIDEOS)
- viewModel.setupCurrentIndex("id2")
-
- advanceUntilIdle()
-
- verify(exactly = 0) { interactor.getCourseStructureFromCache() }
- verify(exactly = 1) { interactor.getCourseStructureForVideos() }
- assert(!viewModel.isLastIndexInContainer)
- assert(viewModel.isFirstIndexInContainer)
- }
-
@Test
fun `getCurrentBlock test`() = runTest {
- val viewModel = CourseUnitContainerViewModel(interactor, notifier, "")
+ val viewModel = CourseUnitContainerViewModel(interactor, notifier, analytics, "")
every { interactor.getCourseStructureFromCache() } returns courseStructure
every { interactor.getCourseStructureForVideos() } returns courseStructure
@@ -239,7 +207,7 @@ class CourseUnitContainerViewModelTest {
@Test
fun `moveToPrevBlock null`() = runTest {
- val viewModel = CourseUnitContainerViewModel(interactor, notifier, "")
+ val viewModel = CourseUnitContainerViewModel(interactor, notifier, analytics, "")
every { interactor.getCourseStructureFromCache() } returns courseStructure
every { interactor.getCourseStructureForVideos() } returns courseStructure
@@ -255,7 +223,7 @@ class CourseUnitContainerViewModelTest {
@Test
fun `moveToPrevBlock not null`() = runTest {
- val viewModel = CourseUnitContainerViewModel(interactor, notifier, "")
+ val viewModel = CourseUnitContainerViewModel(interactor, notifier, analytics, "")
every { interactor.getCourseStructureFromCache() } returns courseStructure
every { interactor.getCourseStructureForVideos() } returns courseStructure
@@ -266,12 +234,12 @@ class CourseUnitContainerViewModelTest {
verify(exactly = 0) { interactor.getCourseStructureFromCache() }
verify(exactly = 1) { interactor.getCourseStructureForVideos() }
- assert(viewModel.moveToPrevBlock() != null)
+ assert(viewModel.moveToPrevBlock() == null)
}
@Test
fun `moveToNextBlock null`() = runTest {
- val viewModel = CourseUnitContainerViewModel(interactor, notifier, "")
+ val viewModel = CourseUnitContainerViewModel(interactor, notifier, analytics, "")
every { interactor.getCourseStructureFromCache() } returns courseStructure
every { interactor.getCourseStructureForVideos() } returns courseStructure
@@ -287,7 +255,7 @@ class CourseUnitContainerViewModelTest {
@Test
fun `moveToNextBlock not null`() = runTest {
- val viewModel = CourseUnitContainerViewModel(interactor, notifier, "")
+ val viewModel = CourseUnitContainerViewModel(interactor, notifier, analytics, "")
every { interactor.getCourseStructureFromCache() } returns courseStructure
every { interactor.getCourseStructureForVideos() } returns courseStructure
@@ -301,27 +269,9 @@ class CourseUnitContainerViewModelTest {
assert(viewModel.moveToNextBlock() != null)
}
- @Test
- fun `currentIndex equals 0`() = runTest {
- val viewModel = CourseUnitContainerViewModel(interactor, notifier, "")
- every { interactor.getCourseStructureFromCache() } returns courseStructure
- every { interactor.getCourseStructureForVideos() } returns courseStructure
-
- viewModel.loadBlocks(CourseViewMode.VIDEOS)
- viewModel.setupCurrentIndex("id")
-
- advanceUntilIdle()
-
- verify(exactly = 0) { interactor.getCourseStructureFromCache() }
- verify(exactly = 1) { interactor.getCourseStructureForVideos() }
- assert(viewModel.currentIndex == 0)
- assert(viewModel.currentVerticalIndex > 0)
- assert(!viewModel.isFirstIndexInContainer)
- }
-
@Test
fun `currentIndex isLastIndex`() = runTest {
- val viewModel = CourseUnitContainerViewModel(interactor, notifier, "")
+ val viewModel = CourseUnitContainerViewModel(interactor, notifier, analytics, "")
every { interactor.getCourseStructureFromCache() } returns courseStructure
every { interactor.getCourseStructureForVideos() } returns courseStructure
diff --git a/course/src/test/java/com/raccoongang/course/presentation/unit/video/VideoUnitViewModelTest.kt b/course/src/test/java/org/openedx/course/presentation/unit/video/VideoUnitViewModelTest.kt
similarity index 84%
rename from course/src/test/java/com/raccoongang/course/presentation/unit/video/VideoUnitViewModelTest.kt
rename to course/src/test/java/org/openedx/course/presentation/unit/video/VideoUnitViewModelTest.kt
index db9c48f77..30a06d0c7 100644
--- a/course/src/test/java/com/raccoongang/course/presentation/unit/video/VideoUnitViewModelTest.kt
+++ b/course/src/test/java/org/openedx/course/presentation/unit/video/VideoUnitViewModelTest.kt
@@ -1,15 +1,14 @@
-package com.raccoongang.course.presentation.unit.video
+package org.openedx.course.presentation.unit.video
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.module.TranscriptManager
-import com.raccoongang.core.system.connection.NetworkConnection
-import com.raccoongang.core.system.notifier.CourseNotifier
-import com.raccoongang.core.system.notifier.CourseVideoPositionChanged
-import com.raccoongang.course.data.repository.CourseRepository
+import org.openedx.core.module.TranscriptManager
+import org.openedx.core.system.connection.NetworkConnection
+import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.core.system.notifier.CourseVideoPositionChanged
+import org.openedx.course.data.repository.CourseRepository
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk
@@ -33,7 +32,6 @@ class VideoUnitViewModelTest {
private val courseRepository = mockk()
private val notifier = mockk()
- private val preferencesManager = mockk()
private val networkConnection = mockk()
private val transcriptManager = mockk()
@@ -53,7 +51,6 @@ class VideoUnitViewModelTest {
val viewModel = VideoUnitViewModel(
"",
courseRepository,
- preferencesManager,
notifier,
networkConnection,
transcriptManager
@@ -80,7 +77,6 @@ class VideoUnitViewModelTest {
val viewModel = VideoUnitViewModel(
"",
courseRepository,
- preferencesManager,
notifier,
networkConnection,
transcriptManager
@@ -107,7 +103,6 @@ class VideoUnitViewModelTest {
val viewModel = VideoUnitViewModel(
"",
courseRepository,
- preferencesManager,
notifier,
networkConnection,
transcriptManager
diff --git a/course/src/test/java/com/raccoongang/course/presentation/unit/video/VideoViewModelTest.kt b/course/src/test/java/org/openedx/course/presentation/unit/video/VideoViewModelTest.kt
similarity index 86%
rename from course/src/test/java/com/raccoongang/course/presentation/unit/video/VideoViewModelTest.kt
rename to course/src/test/java/org/openedx/course/presentation/unit/video/VideoViewModelTest.kt
index 6ce2b2d27..ff0b70871 100644
--- a/course/src/test/java/com/raccoongang/course/presentation/unit/video/VideoViewModelTest.kt
+++ b/course/src/test/java/org/openedx/course/presentation/unit/video/VideoViewModelTest.kt
@@ -1,11 +1,10 @@
-package com.raccoongang.course.presentation.unit.video
+package org.openedx.course.presentation.unit.video
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.core.system.notifier.CourseNotifier
-import com.raccoongang.core.system.notifier.CourseVideoPositionChanged
-import com.raccoongang.course.data.repository.CourseRepository
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.core.system.notifier.CourseVideoPositionChanged
+import org.openedx.course.data.repository.CourseRepository
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.mockk
diff --git a/course/src/test/java/com/raccoongang/course/presentation/videos/CourseVideoViewModelTest.kt b/course/src/test/java/org/openedx/course/presentation/videos/CourseVideoViewModelTest.kt
similarity index 83%
rename from course/src/test/java/com/raccoongang/course/presentation/videos/CourseVideoViewModelTest.kt
rename to course/src/test/java/org/openedx/course/presentation/videos/CourseVideoViewModelTest.kt
index 579cf1e76..18851bcd2 100644
--- a/course/src/test/java/com/raccoongang/course/presentation/videos/CourseVideoViewModelTest.kt
+++ b/course/src/test/java/org/openedx/course/presentation/videos/CourseVideoViewModelTest.kt
@@ -1,23 +1,24 @@
-package com.raccoongang.course.presentation.videos
+package org.openedx.course.presentation.videos
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
-import com.raccoongang.core.BlockType
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.domain.model.Block
-import com.raccoongang.core.domain.model.BlockCounts
-import com.raccoongang.core.domain.model.CourseStructure
-import com.raccoongang.core.domain.model.CoursewareAccess
-import com.raccoongang.core.module.DownloadWorkerController
-import com.raccoongang.core.module.db.DownloadDao
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.core.system.connection.NetworkConnection
-import com.raccoongang.core.system.notifier.CourseNotifier
-import com.raccoongang.core.system.notifier.CourseStructureUpdated
-import com.raccoongang.course.R
-import com.raccoongang.course.domain.interactor.CourseInteractor
+import org.openedx.core.BlockType
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.domain.model.Block
+import org.openedx.core.domain.model.BlockCounts
+import org.openedx.core.domain.model.CourseStructure
+import org.openedx.core.domain.model.CoursewareAccess
+import org.openedx.core.module.DownloadWorkerController
+import org.openedx.core.module.db.DownloadDao
+import org.openedx.core.module.db.DownloadModelEntity
+import org.openedx.core.system.ResourceManager
+import org.openedx.core.system.connection.NetworkConnection
+import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.core.system.notifier.CourseStructureUpdated
+import org.openedx.course.R
+import org.openedx.course.domain.interactor.CourseInteractor
import io.mockk.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -120,11 +121,14 @@ class CourseVideoViewModelTest {
isSelfPaced = false
)
+ private val downloadModelEntity =
+ DownloadModelEntity("", "", 1, "", "", "VIDEO", "DOWNLOADED", null)
+
@Before
fun setUp() {
every { resourceManager.getString(R.string.course_does_not_include_videos) } returns ""
- every { resourceManager.getString(com.raccoongang.course.R.string.course_can_download_only_with_wifi) } returns cantDownload
+ every { resourceManager.getString(org.openedx.course.R.string.course_can_download_only_with_wifi) } returns cantDownload
Dispatchers.setMain(dispatcher)
}
@@ -227,6 +231,8 @@ class CourseVideoViewModelTest {
downloadDao,
workerController
)
+ coEvery { interactor.getCourseStructureForVideos() } returns courseStructure
+ coEvery { downloadDao.readAllData() } returns flow { emit(listOf(downloadModelEntity)) }
viewModel.setIsUpdating()
advanceUntilIdle()
@@ -245,6 +251,8 @@ class CourseVideoViewModelTest {
downloadDao,
workerController
)
+ coEvery { interactor.getCourseStructureForVideos() } returns courseStructure
+ coEvery { downloadDao.readAllData() } returns flow { emit(listOf(downloadModelEntity)) }
every { preferencesManager.videoSettings.wifiDownloadOnly } returns false
every { networkConnection.isWifiConnected() } returns true
coEvery { workerController.saveModels(*anyVararg()) } returns Unit
@@ -267,6 +275,8 @@ class CourseVideoViewModelTest {
downloadDao,
workerController
)
+ coEvery { interactor.getCourseStructureForVideos() } returns courseStructure
+ coEvery { downloadDao.readAllData() } returns flow { emit(listOf(downloadModelEntity)) }
every { preferencesManager.videoSettings.wifiDownloadOnly } returns true
every { networkConnection.isWifiConnected() } returns true
coEvery { workerController.saveModels(*anyVararg()) } returns Unit
@@ -292,6 +302,8 @@ class CourseVideoViewModelTest {
every { preferencesManager.videoSettings.wifiDownloadOnly } returns true
every { networkConnection.isWifiConnected() } returns false
every { networkConnection.isOnline() } returns false
+ coEvery { interactor.getCourseStructureForVideos() } returns courseStructure
+ coEvery { downloadDao.readAllData() } returns flow { emit(listOf(downloadModelEntity)) }
coEvery { workerController.saveModels(*anyVararg()) } returns Unit
viewModel.saveDownloadModels("", "")
diff --git a/dashboard/build.gradle b/dashboard/build.gradle
index 932543e62..f78307dd4 100644
--- a/dashboard/build.gradle
+++ b/dashboard/build.gradle
@@ -14,7 +14,7 @@ android {
consumerProguardFiles "consumer-rules.pro"
}
- namespace 'com.raccoongang.dashboard'
+ namespace 'org.openedx.dashboard'
buildTypes {
release {
diff --git a/dashboard/src/androidTest/java/com/raccoongang/dashboard/presentation/MyCoursesScreenTest.kt b/dashboard/src/androidTest/java/org/openedx/dashboard/presentation/MyCoursesScreenTest.kt
similarity index 95%
rename from dashboard/src/androidTest/java/com/raccoongang/dashboard/presentation/MyCoursesScreenTest.kt
rename to dashboard/src/androidTest/java/org/openedx/dashboard/presentation/MyCoursesScreenTest.kt
index d4c2fda74..c7361b446 100644
--- a/dashboard/src/androidTest/java/com/raccoongang/dashboard/presentation/MyCoursesScreenTest.kt
+++ b/dashboard/src/androidTest/java/org/openedx/dashboard/presentation/MyCoursesScreenTest.kt
@@ -1,12 +1,12 @@
-package com.raccoongang.dashboard.presentation
+package org.openedx.dashboard.presentation
import androidx.activity.ComponentActivity
import androidx.compose.ui.semantics.ProgressBarRangeInfo
import androidx.compose.ui.test.*
import androidx.compose.ui.test.junit4.createAndroidComposeRule
-import com.raccoongang.core.domain.model.*
-import com.raccoongang.core.ui.WindowSize
-import com.raccoongang.core.ui.WindowType
+import org.openedx.core.domain.model.*
+import org.openedx.core.ui.WindowSize
+import org.openedx.core.ui.WindowType
import org.junit.Rule
import org.junit.Test
diff --git a/dashboard/src/main/java/com/raccoongang/dashboard/data/DashboardDao.kt b/dashboard/src/main/java/org/openedx/dashboard/data/DashboardDao.kt
similarity index 81%
rename from dashboard/src/main/java/com/raccoongang/dashboard/data/DashboardDao.kt
rename to dashboard/src/main/java/org/openedx/dashboard/data/DashboardDao.kt
index 9a222a9b7..d24afd05d 100644
--- a/dashboard/src/main/java/com/raccoongang/dashboard/data/DashboardDao.kt
+++ b/dashboard/src/main/java/org/openedx/dashboard/data/DashboardDao.kt
@@ -1,10 +1,10 @@
-package com.raccoongang.dashboard.data
+package org.openedx.dashboard.data
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
-import com.raccoongang.core.data.model.room.discovery.EnrolledCourseEntity
+import org.openedx.core.data.model.room.discovery.EnrolledCourseEntity
@Dao
interface DashboardDao {
diff --git a/dashboard/src/main/java/com/raccoongang/dashboard/data/repository/DashboardRepository.kt b/dashboard/src/main/java/org/openedx/dashboard/data/repository/DashboardRepository.kt
similarity index 69%
rename from dashboard/src/main/java/com/raccoongang/dashboard/data/repository/DashboardRepository.kt
rename to dashboard/src/main/java/org/openedx/dashboard/data/repository/DashboardRepository.kt
index 572f37cc2..b22fa1671 100644
--- a/dashboard/src/main/java/com/raccoongang/dashboard/data/repository/DashboardRepository.kt
+++ b/dashboard/src/main/java/org/openedx/dashboard/data/repository/DashboardRepository.kt
@@ -1,10 +1,10 @@
-package com.raccoongang.dashboard.data.repository
+package org.openedx.dashboard.data.repository
-import com.raccoongang.core.data.api.CourseApi
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.domain.model.DashboardCourseList
-import com.raccoongang.core.domain.model.EnrolledCourse
-import com.raccoongang.dashboard.data.DashboardDao
+import org.openedx.core.data.api.CourseApi
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.domain.model.DashboardCourseList
+import org.openedx.core.domain.model.EnrolledCourse
+import org.openedx.dashboard.data.DashboardDao
class DashboardRepository(
private val api: CourseApi,
diff --git a/dashboard/src/main/java/com/raccoongang/dashboard/domain/interactor/DashboardInteractor.kt b/dashboard/src/main/java/org/openedx/dashboard/domain/interactor/DashboardInteractor.kt
similarity index 62%
rename from dashboard/src/main/java/com/raccoongang/dashboard/domain/interactor/DashboardInteractor.kt
rename to dashboard/src/main/java/org/openedx/dashboard/domain/interactor/DashboardInteractor.kt
index 2226b9690..a29c2cc7e 100644
--- a/dashboard/src/main/java/com/raccoongang/dashboard/domain/interactor/DashboardInteractor.kt
+++ b/dashboard/src/main/java/org/openedx/dashboard/domain/interactor/DashboardInteractor.kt
@@ -1,7 +1,7 @@
-package com.raccoongang.dashboard.domain.interactor
+package org.openedx.dashboard.domain.interactor
-import com.raccoongang.core.domain.model.DashboardCourseList
-import com.raccoongang.dashboard.data.repository.DashboardRepository
+import org.openedx.core.domain.model.DashboardCourseList
+import org.openedx.dashboard.data.repository.DashboardRepository
class DashboardInteractor(
private val repository: DashboardRepository
diff --git a/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardAnalytics.kt b/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardAnalytics.kt
new file mode 100644
index 000000000..6a69e7a65
--- /dev/null
+++ b/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardAnalytics.kt
@@ -0,0 +1,5 @@
+package org.openedx.dashboard.presentation
+
+interface DashboardAnalytics {
+ fun dashboardCourseClickedEvent(courseId: String, courseName: String)
+}
diff --git a/dashboard/src/main/java/com/raccoongang/dashboard/presentation/DashboardFragment.kt b/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardFragment.kt
similarity index 96%
rename from dashboard/src/main/java/com/raccoongang/dashboard/presentation/DashboardFragment.kt
rename to dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardFragment.kt
index ef26afe64..45a81e378 100644
--- a/dashboard/src/main/java/com/raccoongang/dashboard/presentation/DashboardFragment.kt
+++ b/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.dashboard.presentation
+package org.openedx.dashboard.presentation
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
@@ -39,16 +39,16 @@ import androidx.compose.ui.unit.dp
import androidx.fragment.app.Fragment
import coil.compose.AsyncImage
import coil.request.ImageRequest
-import com.raccoongang.core.BuildConfig
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.*
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appShapes
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.core.utils.TimeUtils
-import com.raccoongang.dashboard.R
+import org.openedx.core.BuildConfig
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.*
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appShapes
+import org.openedx.core.ui.theme.appTypography
+import org.openedx.core.utils.TimeUtils
+import org.openedx.dashboard.R
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import java.util.*
@@ -70,7 +70,7 @@ class DashboardFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val uiState by viewModel.uiState.observeAsState()
val uiMessage by viewModel.uiMessage.observeAsState()
@@ -88,6 +88,7 @@ class DashboardFragment : Fragment() {
viewModel.getCourses()
},
onItemClick = {
+ viewModel.dashboardCourseClickedEvent(it.course.id, it.course.name)
router.navigateToCourseOutline(
requireParentFragment().parentFragmentManager,
it.course.id,
@@ -207,6 +208,7 @@ internal fun MyCoursesScreen(
CircularProgressIndicator(color = MaterialTheme.appColors.primary)
}
}
+
is DashboardUIState.Courses -> {
Box(
modifier = Modifier.fillMaxSize(),
@@ -259,6 +261,7 @@ internal fun MyCoursesScreen(
}
}
}
+
is DashboardUIState.Empty -> {
Box(
modifier = Modifier.fillMaxSize(),
@@ -325,7 +328,7 @@ private fun CourseItem(
)
)
}
- val imageUrl = BuildConfig.BASE_URL.dropLast(1) + enrolledCourse.course.courseImage
+ val imageUrl = org.openedx.core.BuildConfig.BASE_URL.dropLast(1) + enrolledCourse.course.courseImage
val context = LocalContext.current
Surface(
modifier = Modifier
@@ -344,8 +347,8 @@ private fun CourseItem(
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(imageUrl)
- .error(com.raccoongang.core.R.drawable.core_no_image_course)
- .placeholder(com.raccoongang.core.R.drawable.core_no_image_course)
+ .error(org.openedx.core.R.drawable.core_no_image_course)
+ .placeholder(org.openedx.core.R.drawable.core_no_image_course)
.build(),
contentDescription = null,
contentScale = ContentScale.Crop,
@@ -459,7 +462,7 @@ private fun EmptyState() {
@Preview(uiMode = UI_MODE_NIGHT_YES)
@Composable
private fun CourseItemPreview() {
- NewEdxTheme() {
+ OpenEdXTheme() {
CourseItem(
mockCourseEnrolled,
WindowSize(WindowType.Compact, WindowType.Compact),
@@ -471,7 +474,7 @@ private fun CourseItemPreview() {
@Preview(uiMode = UI_MODE_NIGHT_YES)
@Composable
private fun MyCoursesScreenDay() {
- NewEdxTheme {
+ OpenEdXTheme {
MyCoursesScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
state = DashboardUIState.Courses(
@@ -500,7 +503,7 @@ private fun MyCoursesScreenDay() {
@Preview(uiMode = UI_MODE_NIGHT_YES, device = Devices.NEXUS_9)
@Composable
private fun MyCoursesScreenTabletPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
MyCoursesScreen(
windowSize = WindowSize(WindowType.Medium, WindowType.Medium),
state = DashboardUIState.Courses(
diff --git a/dashboard/src/main/java/com/raccoongang/dashboard/presentation/DashboardRouter.kt b/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardRouter.kt
similarity index 80%
rename from dashboard/src/main/java/com/raccoongang/dashboard/presentation/DashboardRouter.kt
rename to dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardRouter.kt
index e06048ffa..42d229330 100644
--- a/dashboard/src/main/java/com/raccoongang/dashboard/presentation/DashboardRouter.kt
+++ b/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardRouter.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.dashboard.presentation
+package org.openedx.dashboard.presentation
import androidx.fragment.app.FragmentManager
diff --git a/dashboard/src/main/java/com/raccoongang/dashboard/presentation/DashboardUIState.kt b/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardUIState.kt
similarity index 65%
rename from dashboard/src/main/java/com/raccoongang/dashboard/presentation/DashboardUIState.kt
rename to dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardUIState.kt
index 8d2666bf9..5ea81cfd0 100644
--- a/dashboard/src/main/java/com/raccoongang/dashboard/presentation/DashboardUIState.kt
+++ b/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardUIState.kt
@@ -1,6 +1,6 @@
-package com.raccoongang.dashboard.presentation
+package org.openedx.dashboard.presentation
-import com.raccoongang.core.domain.model.EnrolledCourse
+import org.openedx.core.domain.model.EnrolledCourse
sealed class DashboardUIState {
data class Courses(val courses: List) : DashboardUIState()
diff --git a/dashboard/src/main/java/com/raccoongang/dashboard/presentation/DashboardViewModel.kt b/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardViewModel.kt
similarity index 82%
rename from dashboard/src/main/java/com/raccoongang/dashboard/presentation/DashboardViewModel.kt
rename to dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardViewModel.kt
index d108bea79..1a1073922 100644
--- a/dashboard/src/main/java/com/raccoongang/dashboard/presentation/DashboardViewModel.kt
+++ b/dashboard/src/main/java/org/openedx/dashboard/presentation/DashboardViewModel.kt
@@ -1,20 +1,20 @@
-package com.raccoongang.dashboard.presentation
+package org.openedx.dashboard.presentation
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.R
-import com.raccoongang.core.SingleEventLiveData
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.EnrolledCourse
-import com.raccoongang.core.extension.isInternetError
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.core.system.connection.NetworkConnection
-import com.raccoongang.core.system.notifier.CourseDashboardUpdate
-import com.raccoongang.core.system.notifier.CourseNotifier
-import com.raccoongang.dashboard.domain.interactor.DashboardInteractor
+import org.openedx.core.BaseViewModel
+import org.openedx.core.R
+import org.openedx.core.SingleEventLiveData
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.EnrolledCourse
+import org.openedx.core.extension.isInternetError
+import org.openedx.core.system.ResourceManager
+import org.openedx.core.system.connection.NetworkConnection
+import org.openedx.core.system.notifier.CourseDashboardUpdate
+import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.dashboard.domain.interactor.DashboardInteractor
import kotlinx.coroutines.launch
@@ -22,7 +22,8 @@ class DashboardViewModel(
private val networkConnection: NetworkConnection,
private val interactor: DashboardInteractor,
private val resourceManager: ResourceManager,
- private val notifier: CourseNotifier
+ private val notifier: CourseNotifier,
+ private val analytics: DashboardAnalytics
) : BaseViewModel() {
private val coursesList = mutableListOf()
@@ -87,7 +88,7 @@ class DashboardViewModel(
if (coursesList.isEmpty()) {
_uiState.value = DashboardUIState.Empty
} else {
- _uiState.value = DashboardUIState.Courses(coursesList)
+ _uiState.value = DashboardUIState.Courses(ArrayList(coursesList))
}
} catch (e: Exception) {
e.printStackTrace()
@@ -131,7 +132,7 @@ class DashboardViewModel(
if (coursesList.isEmpty()) {
_uiState.value = DashboardUIState.Empty
} else {
- _uiState.value = DashboardUIState.Courses(coursesList)
+ _uiState.value = DashboardUIState.Courses(ArrayList(coursesList))
}
} catch (e: Exception) {
e.printStackTrace()
@@ -154,4 +155,8 @@ class DashboardViewModel(
}
}
+ fun dashboardCourseClickedEvent(courseId: String, courseName: String) {
+ analytics.dashboardCourseClickedEvent(courseId, courseName)
+ }
+
}
\ No newline at end of file
diff --git a/dashboard/src/test/java/com/raccoongang/dashboard/presentation/DashboardViewModelTest.kt b/dashboard/src/test/java/org/openedx/dashboard/presentation/DashboardViewModelTest.kt
similarity index 94%
rename from dashboard/src/test/java/com/raccoongang/dashboard/presentation/DashboardViewModelTest.kt
rename to dashboard/src/test/java/org/openedx/dashboard/presentation/DashboardViewModelTest.kt
index fc1d94966..135fe248a 100644
--- a/dashboard/src/test/java/com/raccoongang/dashboard/presentation/DashboardViewModelTest.kt
+++ b/dashboard/src/test/java/org/openedx/dashboard/presentation/DashboardViewModelTest.kt
@@ -1,18 +1,18 @@
-package com.raccoongang.dashboard.presentation
+package org.openedx.dashboard.presentation
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
-import com.raccoongang.core.system.connection.NetworkConnection
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.DashboardCourseList
-import com.raccoongang.core.domain.model.Pagination
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.core.system.notifier.CourseDashboardUpdate
-import com.raccoongang.core.system.notifier.CourseNotifier
-import com.raccoongang.dashboard.domain.interactor.DashboardInteractor
+import org.openedx.core.system.connection.NetworkConnection
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.DashboardCourseList
+import org.openedx.core.domain.model.Pagination
+import org.openedx.core.system.ResourceManager
+import org.openedx.core.system.notifier.CourseDashboardUpdate
+import org.openedx.core.system.notifier.CourseNotifier
+import org.openedx.dashboard.domain.interactor.DashboardInteractor
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
diff --git a/discovery/build.gradle b/discovery/build.gradle
index 4f19d259b..f0b329ca1 100644
--- a/discovery/build.gradle
+++ b/discovery/build.gradle
@@ -16,7 +16,7 @@ android {
consumerProguardFiles "consumer-rules.pro"
}
- namespace 'com.raccoongang.discovery'
+ namespace 'org.openedx.discovery'
buildTypes {
release {
diff --git a/discovery/src/androidTest/java/com/raccoongang/discovery/presentation/DiscoveryScreenTest.kt b/discovery/src/androidTest/java/org/openedx/discovery/presentation/DiscoveryScreenTest.kt
similarity index 94%
rename from discovery/src/androidTest/java/com/raccoongang/discovery/presentation/DiscoveryScreenTest.kt
rename to discovery/src/androidTest/java/org/openedx/discovery/presentation/DiscoveryScreenTest.kt
index 3a5fed0a5..4ea5c7e43 100644
--- a/discovery/src/androidTest/java/com/raccoongang/discovery/presentation/DiscoveryScreenTest.kt
+++ b/discovery/src/androidTest/java/org/openedx/discovery/presentation/DiscoveryScreenTest.kt
@@ -1,13 +1,13 @@
-package com.raccoongang.discovery.presentation
+package org.openedx.discovery.presentation
import androidx.activity.ComponentActivity
import androidx.compose.ui.semantics.ProgressBarRangeInfo
import androidx.compose.ui.test.*
import androidx.compose.ui.test.junit4.createAndroidComposeRule
-import com.raccoongang.core.domain.model.Course
-import com.raccoongang.core.domain.model.Media
-import com.raccoongang.core.ui.WindowSize
-import com.raccoongang.core.ui.WindowType
+import org.openedx.core.domain.model.Course
+import org.openedx.core.domain.model.Media
+import org.openedx.core.ui.WindowSize
+import org.openedx.core.ui.WindowType
import org.junit.Rule
import org.junit.Test
diff --git a/discovery/src/main/java/com/raccoongang/discovery/data/converter/DiscoveryConverter.kt b/discovery/src/main/java/org/openedx/discovery/data/converter/DiscoveryConverter.kt
similarity index 85%
rename from discovery/src/main/java/com/raccoongang/discovery/data/converter/DiscoveryConverter.kt
rename to discovery/src/main/java/org/openedx/discovery/data/converter/DiscoveryConverter.kt
index 5c193dc93..22ab5cac7 100644
--- a/discovery/src/main/java/com/raccoongang/discovery/data/converter/DiscoveryConverter.kt
+++ b/discovery/src/main/java/org/openedx/discovery/data/converter/DiscoveryConverter.kt
@@ -1,11 +1,11 @@
-package com.raccoongang.discovery.data.converter
+package org.openedx.discovery.data.converter
import androidx.room.TypeConverter
import com.google.gson.Gson
-import com.raccoongang.core.data.model.room.BannerImageDb
-import com.raccoongang.core.data.model.room.CourseImageDb
-import com.raccoongang.core.data.model.room.CourseVideoDb
-import com.raccoongang.core.data.model.room.ImageDb
+import org.openedx.core.data.model.room.BannerImageDb
+import org.openedx.core.data.model.room.CourseImageDb
+import org.openedx.core.data.model.room.CourseVideoDb
+import org.openedx.core.data.model.room.ImageDb
class DiscoveryConverter {
diff --git a/discovery/src/main/java/com/raccoongang/discovery/data/repository/DiscoveryRepository.kt b/discovery/src/main/java/org/openedx/discovery/data/repository/DiscoveryRepository.kt
similarity index 81%
rename from discovery/src/main/java/com/raccoongang/discovery/data/repository/DiscoveryRepository.kt
rename to discovery/src/main/java/org/openedx/discovery/data/repository/DiscoveryRepository.kt
index 0dd9493e3..2762cf1aa 100644
--- a/discovery/src/main/java/com/raccoongang/discovery/data/repository/DiscoveryRepository.kt
+++ b/discovery/src/main/java/org/openedx/discovery/data/repository/DiscoveryRepository.kt
@@ -1,10 +1,10 @@
-package com.raccoongang.discovery.data.repository
+package org.openedx.discovery.data.repository
-import com.raccoongang.core.data.api.CourseApi
-import com.raccoongang.core.data.model.room.CourseEntity
-import com.raccoongang.core.domain.model.CourseList
-import com.raccoongang.core.domain.model.Course
-import com.raccoongang.discovery.data.storage.DiscoveryDao
+import org.openedx.core.data.api.CourseApi
+import org.openedx.core.data.model.room.CourseEntity
+import org.openedx.core.domain.model.CourseList
+import org.openedx.core.domain.model.Course
+import org.openedx.discovery.data.storage.DiscoveryDao
class DiscoveryRepository(
diff --git a/discovery/src/main/java/com/raccoongang/discovery/data/storage/DiscoveryDao.kt b/discovery/src/main/java/org/openedx/discovery/data/storage/DiscoveryDao.kt
similarity index 81%
rename from discovery/src/main/java/com/raccoongang/discovery/data/storage/DiscoveryDao.kt
rename to discovery/src/main/java/org/openedx/discovery/data/storage/DiscoveryDao.kt
index d5b30d0e7..48733b6c6 100644
--- a/discovery/src/main/java/com/raccoongang/discovery/data/storage/DiscoveryDao.kt
+++ b/discovery/src/main/java/org/openedx/discovery/data/storage/DiscoveryDao.kt
@@ -1,10 +1,10 @@
-package com.raccoongang.discovery.data.storage
+package org.openedx.discovery.data.storage
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
-import com.raccoongang.core.data.model.room.CourseEntity
+import org.openedx.core.data.model.room.CourseEntity
@Dao
interface DiscoveryDao {
diff --git a/discovery/src/main/java/com/raccoongang/discovery/domain/interactor/DiscoveryInteractor.kt b/discovery/src/main/java/org/openedx/discovery/domain/interactor/DiscoveryInteractor.kt
similarity index 71%
rename from discovery/src/main/java/com/raccoongang/discovery/domain/interactor/DiscoveryInteractor.kt
rename to discovery/src/main/java/org/openedx/discovery/domain/interactor/DiscoveryInteractor.kt
index 7baef9681..8517871d7 100644
--- a/discovery/src/main/java/com/raccoongang/discovery/domain/interactor/DiscoveryInteractor.kt
+++ b/discovery/src/main/java/org/openedx/discovery/domain/interactor/DiscoveryInteractor.kt
@@ -1,8 +1,8 @@
-package com.raccoongang.discovery.domain.interactor
+package org.openedx.discovery.domain.interactor
-import com.raccoongang.core.domain.model.Course
-import com.raccoongang.core.domain.model.CourseList
-import com.raccoongang.discovery.data.repository.DiscoveryRepository
+import org.openedx.core.domain.model.Course
+import org.openedx.core.domain.model.CourseList
+import org.openedx.discovery.data.repository.DiscoveryRepository
class DiscoveryInteractor(private val repository: DiscoveryRepository) {
diff --git a/discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryAnalytics.kt b/discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryAnalytics.kt
new file mode 100644
index 000000000..93a5086b2
--- /dev/null
+++ b/discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryAnalytics.kt
@@ -0,0 +1,7 @@
+package org.openedx.discovery.presentation
+
+interface DiscoveryAnalytics {
+ fun discoverySearchBarClickedEvent()
+ fun discoveryCourseSearchEvent(label: String, coursesCount: Int)
+ fun discoveryCourseClickedEvent(courseId: String, courseName: String)
+}
\ No newline at end of file
diff --git a/discovery/src/main/java/com/raccoongang/discovery/presentation/DiscoveryFragment.kt b/discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryFragment.kt
similarity index 95%
rename from discovery/src/main/java/com/raccoongang/discovery/presentation/DiscoveryFragment.kt
rename to discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryFragment.kt
index 9688008ae..2d7809826 100644
--- a/discovery/src/main/java/com/raccoongang/discovery/presentation/DiscoveryFragment.kt
+++ b/discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.discovery.presentation
+package org.openedx.discovery.presentation
import android.content.res.Configuration
import android.os.Bundle
@@ -25,14 +25,14 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.fragment.app.Fragment
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.Course
-import com.raccoongang.core.domain.model.Media
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.discovery.R
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.Course
+import org.openedx.core.domain.model.Media
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appTypography
+import org.openedx.discovery.R
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
@@ -48,7 +48,7 @@ class DiscoveryFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val uiState by viewModel.uiState.observeAsState()
@@ -64,6 +64,7 @@ class DiscoveryFragment : Fragment() {
refreshing = refreshing,
hasInternetConnection = viewModel.hasInternetConnection,
onSearchClick = {
+ viewModel.discoverySearchBarClickedEvent()
router.navigateToCourseSearch(
requireActivity().supportFragmentManager
)
@@ -77,10 +78,11 @@ class DiscoveryFragment : Fragment() {
onReloadClick = {
viewModel.getCoursesList()
},
- onItemClick = { courseId ->
+ onItemClick = { course ->
+ viewModel.discoveryCourseClicked(course.id, course.name)
router.navigateToCourseDetail(
requireParentFragment().parentFragmentManager,
- courseId
+ course.id
)
})
}
@@ -102,7 +104,7 @@ internal fun DiscoveryScreen(
onSwipeRefresh: () -> Unit,
onReloadClick: () -> Unit,
paginationCallback: () -> Unit,
- onItemClick: (String) -> Unit
+ onItemClick: (Course) -> Unit
) {
val scaffoldState = rememberScaffoldState()
val scrollState = rememberLazyListState()
@@ -202,6 +204,7 @@ internal fun DiscoveryScreen(
CircularProgressIndicator(color = MaterialTheme.appColors.primary)
}
}
+
is DiscoveryUIState.Courses -> {
Box(
modifier = Modifier.fillMaxSize(),
@@ -235,7 +238,7 @@ internal fun DiscoveryScreen(
course,
windowSize = windowSize,
onClick = { courseId ->
- onItemClick(courseId)
+ onItemClick(course)
})
Divider()
}
@@ -287,7 +290,7 @@ internal fun DiscoveryScreen(
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun CourseItemPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
DiscoveryCourseItem(
course = mockCourse,
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
@@ -299,7 +302,7 @@ private fun CourseItemPreview() {
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun DiscoveryScreenPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
DiscoveryScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
state = DiscoveryUIState.Courses(
@@ -332,7 +335,7 @@ private fun DiscoveryScreenPreview() {
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES, device = Devices.NEXUS_9)
@Composable
private fun DiscoveryScreenTabletPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
DiscoveryScreen(
windowSize = WindowSize(WindowType.Medium, WindowType.Medium),
state = DiscoveryUIState.Courses(
diff --git a/discovery/src/main/java/com/raccoongang/discovery/presentation/DiscoveryRouter.kt b/discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryRouter.kt
similarity index 81%
rename from discovery/src/main/java/com/raccoongang/discovery/presentation/DiscoveryRouter.kt
rename to discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryRouter.kt
index 8e263eefd..12ee9ce93 100644
--- a/discovery/src/main/java/com/raccoongang/discovery/presentation/DiscoveryRouter.kt
+++ b/discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryRouter.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.discovery.presentation
+package org.openedx.discovery.presentation
import androidx.fragment.app.FragmentManager
diff --git a/discovery/src/main/java/com/raccoongang/discovery/presentation/DiscoveryUIState.kt b/discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryUIState.kt
similarity index 60%
rename from discovery/src/main/java/com/raccoongang/discovery/presentation/DiscoveryUIState.kt
rename to discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryUIState.kt
index b5ad690ab..3db5d7c04 100644
--- a/discovery/src/main/java/com/raccoongang/discovery/presentation/DiscoveryUIState.kt
+++ b/discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryUIState.kt
@@ -1,6 +1,6 @@
-package com.raccoongang.discovery.presentation
+package org.openedx.discovery.presentation
-import com.raccoongang.core.domain.model.Course
+import org.openedx.core.domain.model.Course
sealed class DiscoveryUIState {
data class Courses(val courses: List) : DiscoveryUIState()
diff --git a/discovery/src/main/java/com/raccoongang/discovery/presentation/DiscoveryViewModel.kt b/discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryViewModel.kt
similarity index 84%
rename from discovery/src/main/java/com/raccoongang/discovery/presentation/DiscoveryViewModel.kt
rename to discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryViewModel.kt
index 04ef85aa7..a1352333e 100644
--- a/discovery/src/main/java/com/raccoongang/discovery/presentation/DiscoveryViewModel.kt
+++ b/discovery/src/main/java/org/openedx/discovery/presentation/DiscoveryViewModel.kt
@@ -1,20 +1,21 @@
-package com.raccoongang.discovery.presentation
+package org.openedx.discovery.presentation
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.*
-import com.raccoongang.core.domain.model.Course
-import com.raccoongang.core.extension.isInternetError
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.core.system.connection.NetworkConnection
-import com.raccoongang.discovery.domain.interactor.DiscoveryInteractor
+import org.openedx.core.*
+import org.openedx.core.domain.model.Course
+import org.openedx.core.extension.isInternetError
+import org.openedx.core.system.ResourceManager
+import org.openedx.core.system.connection.NetworkConnection
+import org.openedx.discovery.domain.interactor.DiscoveryInteractor
import kotlinx.coroutines.launch
class DiscoveryViewModel(
private val networkConnection: NetworkConnection,
private val interactor: DiscoveryInteractor,
private val resourceManager: ResourceManager,
+ private val analytics: DiscoveryAnalytics
) : BaseViewModel() {
private val _uiState = MutableLiveData(DiscoveryUIState.Loading)
@@ -69,7 +70,7 @@ class DiscoveryViewModel(
page = -1
coursesList.addAll(cachedList)
}
- _uiState.value = DiscoveryUIState.Courses(coursesList)
+ _uiState.value = DiscoveryUIState.Courses(ArrayList(coursesList))
} catch (e: Exception) {
if (e.isInternetError()) {
_uiMessage.value =
@@ -112,7 +113,7 @@ class DiscoveryViewModel(
}
coursesList.clear()
coursesList.addAll(response.results)
- _uiState.value = DiscoveryUIState.Courses(coursesList)
+ _uiState.value = DiscoveryUIState.Courses(ArrayList(coursesList))
} catch (e: Exception) {
if (e.isInternetError()) {
_uiMessage.value =
@@ -134,4 +135,12 @@ class DiscoveryViewModel(
loadCoursesInternal()
}
}
+
+ fun discoverySearchBarClickedEvent() {
+ analytics.discoverySearchBarClickedEvent()
+ }
+
+ fun discoveryCourseClicked(courseId: String, courseName: String) {
+ analytics.discoveryCourseClickedEvent(courseId, courseName)
+ }
}
\ No newline at end of file
diff --git a/discovery/src/main/java/com/raccoongang/discovery/presentation/search/CourseSearchFragment.kt b/discovery/src/main/java/org/openedx/discovery/presentation/search/CourseSearchFragment.kt
similarity index 95%
rename from discovery/src/main/java/com/raccoongang/discovery/presentation/search/CourseSearchFragment.kt
rename to discovery/src/main/java/org/openedx/discovery/presentation/search/CourseSearchFragment.kt
index acb970bca..b81e6bea3 100644
--- a/discovery/src/main/java/com/raccoongang/discovery/presentation/search/CourseSearchFragment.kt
+++ b/discovery/src/main/java/org/openedx/discovery/presentation/search/CourseSearchFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.discovery.presentation.search
+package org.openedx.discovery.presentation.search
import android.content.res.Configuration
import android.os.Bundle
@@ -33,17 +33,17 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.fragment.app.Fragment
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.Course
-import com.raccoongang.core.domain.model.Media
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.discovery.presentation.DiscoveryRouter
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.Course
+import org.openedx.core.domain.model.Media
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appTypography
+import org.openedx.discovery.presentation.DiscoveryRouter
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
-import com.raccoongang.discovery.R as discoveryR
+import org.openedx.discovery.R as discoveryR
class CourseSearchFragment : Fragment() {
@@ -58,7 +58,7 @@ class CourseSearchFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val uiState by viewModel.uiState.observeAsState(
@@ -203,7 +203,7 @@ private fun CourseSearchScreen(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 56.dp),
- text = stringResource(id = com.raccoongang.core.R.string.core_search),
+ text = stringResource(id = org.openedx.core.R.string.core_search),
color = MaterialTheme.appColors.textPrimary,
style = MaterialTheme.appTypography.titleMedium,
maxLines = 1,
@@ -329,7 +329,7 @@ private fun CourseSearchScreen(
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun CourseSearchScreenPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
CourseSearchScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
state = CourseSearchUIState.Courses(listOf(mockCourse, mockCourse), 2),
@@ -349,7 +349,7 @@ fun CourseSearchScreenPreview() {
@Preview(device = Devices.NEXUS_9, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun CourseSearchScreenTabletPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
CourseSearchScreen(
windowSize = WindowSize(WindowType.Medium, WindowType.Medium),
state = CourseSearchUIState.Courses(listOf(mockCourse, mockCourse), 2),
diff --git a/discovery/src/main/java/com/raccoongang/discovery/presentation/search/CourseSearchUIState.kt b/discovery/src/main/java/org/openedx/discovery/presentation/search/CourseSearchUIState.kt
similarity index 63%
rename from discovery/src/main/java/com/raccoongang/discovery/presentation/search/CourseSearchUIState.kt
rename to discovery/src/main/java/org/openedx/discovery/presentation/search/CourseSearchUIState.kt
index fbca3bc41..2b52b750c 100644
--- a/discovery/src/main/java/com/raccoongang/discovery/presentation/search/CourseSearchUIState.kt
+++ b/discovery/src/main/java/org/openedx/discovery/presentation/search/CourseSearchUIState.kt
@@ -1,6 +1,6 @@
-package com.raccoongang.discovery.presentation.search
+package org.openedx.discovery.presentation.search
-import com.raccoongang.core.domain.model.Course
+import org.openedx.core.domain.model.Course
sealed class CourseSearchUIState {
data class Courses(val courses: List, val numCourses: Int) : CourseSearchUIState()
diff --git a/discovery/src/main/java/com/raccoongang/discovery/presentation/search/CourseSearchViewModel.kt b/discovery/src/main/java/org/openedx/discovery/presentation/search/CourseSearchViewModel.kt
similarity index 82%
rename from discovery/src/main/java/com/raccoongang/discovery/presentation/search/CourseSearchViewModel.kt
rename to discovery/src/main/java/org/openedx/discovery/presentation/search/CourseSearchViewModel.kt
index 5e3feb172..3a8d2d43d 100644
--- a/discovery/src/main/java/com/raccoongang/discovery/presentation/search/CourseSearchViewModel.kt
+++ b/discovery/src/main/java/org/openedx/discovery/presentation/search/CourseSearchViewModel.kt
@@ -1,16 +1,17 @@
-package com.raccoongang.discovery.presentation.search
+package org.openedx.discovery.presentation.search
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.R
-import com.raccoongang.core.SingleEventLiveData
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.Course
-import com.raccoongang.core.extension.isInternetError
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.discovery.domain.interactor.DiscoveryInteractor
+import org.openedx.core.BaseViewModel
+import org.openedx.core.R
+import org.openedx.core.SingleEventLiveData
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.Course
+import org.openedx.core.extension.isInternetError
+import org.openedx.core.system.ResourceManager
+import org.openedx.discovery.domain.interactor.DiscoveryInteractor
+import org.openedx.discovery.presentation.DiscoveryAnalytics
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
@@ -19,7 +20,8 @@ import kotlinx.coroutines.launch
class CourseSearchViewModel(
private val interactor: DiscoveryInteractor,
- private val resourceManager: ResourceManager
+ private val resourceManager: ResourceManager,
+ private val analytics: DiscoveryAnalytics
) : BaseViewModel() {
private val _uiState =
@@ -109,6 +111,7 @@ class CourseSearchViewModel(
_canLoadMore.value = false
nextPage = null
}
+ discoveryCourseSearchEvent(query, response.pagination.count)
coursesList.addAll(response.results)
_uiState.value = CourseSearchUIState.Courses(coursesList, response.pagination.count)
} catch (e: Exception) {
@@ -126,4 +129,10 @@ class CourseSearchViewModel(
}
}
+ private fun discoveryCourseSearchEvent(query: String, coursesCount: Int) {
+ if (query.isNotEmpty()) {
+ analytics.discoveryCourseSearchEvent(query, coursesCount)
+ }
+ }
+
}
\ No newline at end of file
diff --git a/discovery/src/test/java/com/raccoongang/discovery/presentation/DiscoveryViewModelTest.kt b/discovery/src/test/java/org/openedx/discovery/presentation/DiscoveryViewModelTest.kt
similarity index 83%
rename from discovery/src/test/java/com/raccoongang/discovery/presentation/DiscoveryViewModelTest.kt
rename to discovery/src/test/java/org/openedx/discovery/presentation/DiscoveryViewModelTest.kt
index 7abdb92ed..deb539ef8 100644
--- a/discovery/src/test/java/com/raccoongang/discovery/presentation/DiscoveryViewModelTest.kt
+++ b/discovery/src/test/java/org/openedx/discovery/presentation/DiscoveryViewModelTest.kt
@@ -1,13 +1,13 @@
-package com.raccoongang.discovery.presentation
+package org.openedx.discovery.presentation
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import com.raccoongang.core.system.connection.NetworkConnection
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.CourseList
-import com.raccoongang.core.domain.model.Pagination
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.discovery.domain.interactor.DiscoveryInteractor
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.CourseList
+import org.openedx.core.domain.model.Pagination
+import org.openedx.core.system.ResourceManager
+import org.openedx.core.system.connection.NetworkConnection
+import org.openedx.discovery.domain.interactor.DiscoveryInteractor
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
@@ -35,6 +35,7 @@ class DiscoveryViewModelTest {
private val resourceManager = mockk()
private val interactor = mockk()
private val networkConnection = mockk()
+ private val analytics = mockk()
private val noInternet = "Slow or no internet connection"
private val somethingWrong = "Something went wrong"
@@ -53,7 +54,8 @@ class DiscoveryViewModelTest {
@Test
fun `getCoursesList no internet connection`() = runTest {
- val viewModel = DiscoveryViewModel(networkConnection, interactor, resourceManager)
+ val viewModel =
+ DiscoveryViewModel(networkConnection, interactor, resourceManager, analytics)
every { networkConnection.isOnline() } returns true
coEvery { interactor.getCoursesList(any(), any(), any()) } throws UnknownHostException()
advanceUntilIdle()
@@ -69,7 +71,8 @@ class DiscoveryViewModelTest {
@Test
fun `getCoursesList unknown exception`() = runTest {
- val viewModel = DiscoveryViewModel(networkConnection, interactor, resourceManager)
+ val viewModel =
+ DiscoveryViewModel(networkConnection, interactor, resourceManager, analytics)
every { networkConnection.isOnline() } returns true
coEvery { interactor.getCoursesList(any(), any(), any()) } throws Exception()
advanceUntilIdle()
@@ -85,7 +88,8 @@ class DiscoveryViewModelTest {
@Test
fun `getCoursesList from cache`() = runTest {
- val viewModel = DiscoveryViewModel(networkConnection, interactor, resourceManager)
+ val viewModel =
+ DiscoveryViewModel(networkConnection, interactor, resourceManager, analytics)
every { networkConnection.isOnline() } returns false
coEvery { interactor.getCoursesListFromCache() } returns emptyList()
advanceUntilIdle()
@@ -100,7 +104,8 @@ class DiscoveryViewModelTest {
@Test
fun `getCoursesList from network with next page`() = runTest {
- val viewModel = DiscoveryViewModel(networkConnection, interactor, resourceManager)
+ val viewModel =
+ DiscoveryViewModel(networkConnection, interactor, resourceManager, analytics)
every { networkConnection.isOnline() } returns true
coEvery { interactor.getCoursesList(any(), any(), any()) } returns CourseList(
Pagination(
@@ -122,7 +127,8 @@ class DiscoveryViewModelTest {
@Test
fun `getCoursesList from network without next page`() = runTest {
- val viewModel = DiscoveryViewModel(networkConnection, interactor, resourceManager)
+ val viewModel =
+ DiscoveryViewModel(networkConnection, interactor, resourceManager, analytics)
every { networkConnection.isOnline() } returns true
coEvery { interactor.getCoursesList(any(), any(), any()) } returns CourseList(
Pagination(
@@ -145,7 +151,8 @@ class DiscoveryViewModelTest {
@Test
fun `updateData no internet connection`() = runTest {
- val viewModel = DiscoveryViewModel(networkConnection, interactor, resourceManager)
+ val viewModel =
+ DiscoveryViewModel(networkConnection, interactor, resourceManager, analytics)
every { networkConnection.isOnline() } returns true
coEvery { interactor.getCoursesList(any(), any(), any()) } throws UnknownHostException()
@@ -163,7 +170,8 @@ class DiscoveryViewModelTest {
@Test
fun `updateData unknown exception`() = runTest {
- val viewModel = DiscoveryViewModel(networkConnection, interactor, resourceManager)
+ val viewModel =
+ DiscoveryViewModel(networkConnection, interactor, resourceManager, analytics)
every { networkConnection.isOnline() } returns true
coEvery { interactor.getCoursesList(any(), any(), any()) } throws Exception()
@@ -181,7 +189,8 @@ class DiscoveryViewModelTest {
@Test
fun `updateData success with next page`() = runTest {
- val viewModel = DiscoveryViewModel(networkConnection, interactor, resourceManager)
+ val viewModel =
+ DiscoveryViewModel(networkConnection, interactor, resourceManager, analytics)
every { networkConnection.isOnline() } returns true
coEvery { interactor.getCoursesList(any(), any(), any()) } returns CourseList(
@@ -205,7 +214,8 @@ class DiscoveryViewModelTest {
@Test
fun `updateData success without next page`() = runTest {
- val viewModel = DiscoveryViewModel(networkConnection, interactor, resourceManager)
+ val viewModel =
+ DiscoveryViewModel(networkConnection, interactor, resourceManager, analytics)
every { networkConnection.isOnline() } returns true
coEvery { interactor.getCoursesList(any(), any(), any()) } returns CourseList(
diff --git a/discovery/src/test/java/com/raccoongang/discovery/presentation/search/CourseSearchViewModelTest.kt b/discovery/src/test/java/org/openedx/discovery/presentation/search/CourseSearchViewModelTest.kt
similarity index 85%
rename from discovery/src/test/java/com/raccoongang/discovery/presentation/search/CourseSearchViewModelTest.kt
rename to discovery/src/test/java/org/openedx/discovery/presentation/search/CourseSearchViewModelTest.kt
index 916ca1a2d..615e17f58 100644
--- a/discovery/src/test/java/com/raccoongang/discovery/presentation/search/CourseSearchViewModelTest.kt
+++ b/discovery/src/test/java/org/openedx/discovery/presentation/search/CourseSearchViewModelTest.kt
@@ -1,18 +1,20 @@
-package com.raccoongang.discovery.presentation.search
+package org.openedx.discovery.presentation.search
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.Course
-import com.raccoongang.core.domain.model.CourseList
-import com.raccoongang.core.domain.model.Media
-import com.raccoongang.core.domain.model.Pagination
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.discovery.domain.interactor.DiscoveryInteractor
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.Course
+import org.openedx.core.domain.model.CourseList
+import org.openedx.core.domain.model.Media
+import org.openedx.core.domain.model.Pagination
+import org.openedx.core.system.ResourceManager
+import org.openedx.discovery.domain.interactor.DiscoveryInteractor
+import org.openedx.discovery.presentation.DiscoveryAnalytics
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
+import io.mockk.verify
import kotlinx.coroutines.*
import kotlinx.coroutines.test.*
import org.junit.After
@@ -34,6 +36,7 @@ class CourseSearchViewModelTest {
private val resourceManager = mockk()
private val interactor = mockk()
+ private val analytics = mockk()
private val noInternet = "Slow or no internet connection"
private val somethingWrong = "Something went wrong"
@@ -80,7 +83,7 @@ class CourseSearchViewModelTest {
@Test
fun `search empty query`() = runTest {
- val viewModel = CourseSearchViewModel(interactor, resourceManager)
+ val viewModel = CourseSearchViewModel(interactor, resourceManager, analytics)
viewModel.search("")
advanceUntilIdle()
@@ -94,7 +97,7 @@ class CourseSearchViewModelTest {
@Test
fun `search query no internet connection exception`() = runTest {
- val viewModel = CourseSearchViewModel(interactor, resourceManager)
+ val viewModel = CourseSearchViewModel(interactor, resourceManager, analytics)
coEvery { interactor.getCoursesListByQuery(any(), any()) } throws UnknownHostException()
viewModel.search("course")
@@ -109,7 +112,7 @@ class CourseSearchViewModelTest {
@Test
fun `search query unknown exception`() = runTest {
- val viewModel = CourseSearchViewModel(interactor, resourceManager)
+ val viewModel = CourseSearchViewModel(interactor, resourceManager, analytics)
coEvery { interactor.getCoursesListByQuery(any(), any()) } throws Exception()
viewModel.search("course")
@@ -124,7 +127,7 @@ class CourseSearchViewModelTest {
@Test
fun `search query success without next page`() = runTest {
- val viewModel = CourseSearchViewModel(interactor, resourceManager)
+ val viewModel = CourseSearchViewModel(interactor, resourceManager, analytics)
coEvery { interactor.getCoursesListByQuery(any(), any()) } returns CourseList(
Pagination(
10,
@@ -133,12 +136,14 @@ class CourseSearchViewModelTest {
""
), emptyList()
)
+ every { analytics.discoveryCourseSearchEvent(any(), any()) } returns Unit
viewModel.search("course")
viewModel.fetchMore()
advanceUntilIdle()
coVerify(exactly = 1) { interactor.getCoursesListByQuery(any(), any()) }
+ verify(exactly = 1) { analytics.discoveryCourseSearchEvent(any(), any()) }
assert(viewModel.uiState.value is CourseSearchUIState.Courses)
assert(viewModel.uiMessage.value == null)
@@ -147,7 +152,7 @@ class CourseSearchViewModelTest {
@Test
fun `search query success with next page and fetch`() = runTest {
- val viewModel = CourseSearchViewModel(interactor, resourceManager)
+ val viewModel = CourseSearchViewModel(interactor, resourceManager, analytics)
coEvery { interactor.getCoursesListByQuery(any(), eq(1)) } returns CourseList(
Pagination(
10,
@@ -162,6 +167,7 @@ class CourseSearchViewModelTest {
not(1)
)
} returns CourseList(Pagination(10, "", 5, ""), listOf(mockCourse))
+ every { analytics.discoveryCourseSearchEvent(any(), any()) } returns Unit
viewModel.search("course")
delay(1000)
@@ -169,6 +175,7 @@ class CourseSearchViewModelTest {
advanceUntilIdle()
coVerify(exactly = 2) { interactor.getCoursesListByQuery(any(), any()) }
+ verify(exactly = 2) { analytics.discoveryCourseSearchEvent(any(), any()) }
assert(viewModel.uiState.value is CourseSearchUIState.Courses)
assert((viewModel.uiState.value as CourseSearchUIState.Courses).courses.size == 3)
@@ -179,7 +186,7 @@ class CourseSearchViewModelTest {
@Test
fun `search query success with next page and fetch, update`() = runTest {
- val viewModel = CourseSearchViewModel(interactor, resourceManager)
+ val viewModel = CourseSearchViewModel(interactor, resourceManager, analytics)
coEvery { interactor.getCoursesListByQuery(any(), eq(1)) } returns CourseList(
Pagination(
10,
@@ -194,6 +201,7 @@ class CourseSearchViewModelTest {
not(1)
)
} returns CourseList(Pagination(10, "0", 5, ""), listOf(mockCourse))
+ every { analytics.discoveryCourseSearchEvent(any(), any()) } returns Unit
viewModel.search("course")
delay(1000)
@@ -202,6 +210,7 @@ class CourseSearchViewModelTest {
advanceUntilIdle()
coVerify(exactly = 3) { interactor.getCoursesListByQuery(any(), any()) }
+ verify(exactly = 3) { analytics.discoveryCourseSearchEvent(any(), any()) }
assert(viewModel.uiState.value is CourseSearchUIState.Courses)
assert((viewModel.uiState.value as CourseSearchUIState.Courses).courses.size == 2)
@@ -212,7 +221,7 @@ class CourseSearchViewModelTest {
@Test
fun `search query update in empty state`() = runTest {
- val viewModel = CourseSearchViewModel(interactor, resourceManager)
+ val viewModel = CourseSearchViewModel(interactor, resourceManager, analytics)
coEvery { interactor.getCoursesListByQuery(any(), eq(1)) } returns CourseList(
Pagination(
10,
diff --git a/discussion/build.gradle b/discussion/build.gradle
index 8d70f1fd8..0504167e8 100644
--- a/discussion/build.gradle
+++ b/discussion/build.gradle
@@ -5,7 +5,7 @@ plugins {
}
android {
- namespace 'com.raccoongang.discussion'
+ namespace 'org.openedx.discussion'
compileSdk 33
defaultConfig {
diff --git a/profile/src/androidTest/java/com/raccoongang/profile/ExampleInstrumentedTest.kt b/discussion/src/androidTest/java/org/openedx/discussion/ExampleInstrumentedTest.kt
similarity index 83%
rename from profile/src/androidTest/java/com/raccoongang/profile/ExampleInstrumentedTest.kt
rename to discussion/src/androidTest/java/org/openedx/discussion/ExampleInstrumentedTest.kt
index c979e1fdd..ef1311ede 100644
--- a/profile/src/androidTest/java/com/raccoongang/profile/ExampleInstrumentedTest.kt
+++ b/discussion/src/androidTest/java/org/openedx/discussion/ExampleInstrumentedTest.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.profile
+package org.openedx.discussion
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -19,6 +19,6 @@ class ExampleInstrumentedTest {
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("com.raccoongang.profile.test", appContext.packageName)
+ assertEquals("org.openedx.discussion.test", appContext.packageName)
}
}
\ No newline at end of file
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/search/DiscussionSearchThreadUIState.kt b/discussion/src/main/java/com/raccoongang/discussion/presentation/search/DiscussionSearchThreadUIState.kt
deleted file mode 100644
index 0c9e3354c..000000000
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/search/DiscussionSearchThreadUIState.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.raccoongang.discussion.presentation.search
-
-sealed class DiscussionSearchThreadUIState {
- class Threads(val data: List, val count: Int) :
- DiscussionSearchThreadUIState()
-
- object Loading : DiscussionSearchThreadUIState()
-}
\ No newline at end of file
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/threads/DiscussionThreadsUIState.kt b/discussion/src/main/java/com/raccoongang/discussion/presentation/threads/DiscussionThreadsUIState.kt
deleted file mode 100644
index c79b5e8db..000000000
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/threads/DiscussionThreadsUIState.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.raccoongang.discussion.presentation.threads
-
-sealed class DiscussionThreadsUIState() {
- class Threads(val data: List) :
- DiscussionThreadsUIState()
-
- object Loading : DiscussionThreadsUIState()
-}
\ No newline at end of file
diff --git a/discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionCommentAdded.kt b/discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionCommentAdded.kt
deleted file mode 100644
index 4f51ab2bc..000000000
--- a/discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionCommentAdded.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.raccoongang.discussion.system.notifier
-
-import com.raccoongang.discussion.domain.model.DiscussionComment
-
-
-data class DiscussionCommentAdded(
- val comment: DiscussionComment
-) : DiscussionEvent
\ No newline at end of file
diff --git a/discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionCommentDataChanged.kt b/discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionCommentDataChanged.kt
deleted file mode 100644
index 20ce340ff..000000000
--- a/discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionCommentDataChanged.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.raccoongang.discussion.system.notifier
-
-import com.raccoongang.discussion.domain.model.DiscussionComment
-
-class DiscussionCommentDataChanged(val discussionComment: DiscussionComment) : DiscussionEvent
\ No newline at end of file
diff --git a/discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionEvent.kt b/discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionEvent.kt
deleted file mode 100644
index b3f94bbd1..000000000
--- a/discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionEvent.kt
+++ /dev/null
@@ -1,4 +0,0 @@
-package com.raccoongang.discussion.system.notifier
-
-interface DiscussionEvent {
-}
\ No newline at end of file
diff --git a/discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionResponseAdded.kt b/discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionResponseAdded.kt
deleted file mode 100644
index 807c66554..000000000
--- a/discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionResponseAdded.kt
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.raccoongang.discussion.system.notifier
-
-import com.raccoongang.discussion.domain.model.DiscussionComment
-
-data class DiscussionResponseAdded(
- val comment: DiscussionComment
-) : DiscussionEvent
diff --git a/discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionThreadAdded.kt b/discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionThreadAdded.kt
deleted file mode 100644
index ca9b770cf..000000000
--- a/discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionThreadAdded.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package com.raccoongang.discussion.system.notifier
-
-class DiscussionThreadAdded : DiscussionEvent
\ No newline at end of file
diff --git a/discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionThreadDataChanged.kt b/discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionThreadDataChanged.kt
deleted file mode 100644
index ee4603506..000000000
--- a/discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionThreadDataChanged.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.raccoongang.discussion.system.notifier
-
-class DiscussionThreadDataChanged(
- val thread: com.raccoongang.discussion.domain.model.Thread
-) : DiscussionEvent
\ No newline at end of file
diff --git a/discussion/src/main/java/com/raccoongang/discussion/data/api/DiscussionApi.kt b/discussion/src/main/java/org/openedx/discussion/data/api/DiscussionApi.kt
similarity index 87%
rename from discussion/src/main/java/com/raccoongang/discussion/data/api/DiscussionApi.kt
rename to discussion/src/main/java/org/openedx/discussion/data/api/DiscussionApi.kt
index d0756b8ee..6969522fe 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/data/api/DiscussionApi.kt
+++ b/discussion/src/main/java/org/openedx/discussion/data/api/DiscussionApi.kt
@@ -1,10 +1,11 @@
-package com.raccoongang.discussion.data.api
-
-import com.raccoongang.discussion.data.model.request.*
-import com.raccoongang.discussion.data.model.response.CommentResult
-import com.raccoongang.discussion.data.model.response.CommentsResponse
-import com.raccoongang.discussion.data.model.response.ThreadsResponse
-import com.raccoongang.discussion.data.model.response.TopicsResponse
+package org.openedx.discussion.data.api
+
+import org.openedx.core.data.model.BlocksCompletionBody
+import org.openedx.discussion.data.model.request.*
+import org.openedx.discussion.data.model.response.CommentResult
+import org.openedx.discussion.data.model.response.CommentsResponse
+import org.openedx.discussion.data.model.response.ThreadsResponse
+import org.openedx.discussion.data.model.response.TopicsResponse
import retrofit2.http.*
interface DiscussionApi {
@@ -105,4 +106,10 @@ interface DiscussionApi {
@POST("/api/discussion/v1/threads/")
suspend fun createThread(@Body threadBody: ThreadBody) : ThreadsResponse.Thread
+ @POST("/api/completion/v1/completion-batch")
+ suspend fun markBlocksCompletion(
+ @Body
+ blocksCompletionBody: BlocksCompletionBody
+ )
+
}
\ No newline at end of file
diff --git a/discussion/src/main/java/com/raccoongang/discussion/data/model/request/CommentBody.kt b/discussion/src/main/java/org/openedx/discussion/data/model/request/CommentBody.kt
similarity index 82%
rename from discussion/src/main/java/com/raccoongang/discussion/data/model/request/CommentBody.kt
rename to discussion/src/main/java/org/openedx/discussion/data/model/request/CommentBody.kt
index e35edc3b8..20f2dd193 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/data/model/request/CommentBody.kt
+++ b/discussion/src/main/java/org/openedx/discussion/data/model/request/CommentBody.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.discussion.data.model.request
+package org.openedx.discussion.data.model.request
import com.google.gson.annotations.SerializedName
diff --git a/discussion/src/main/java/com/raccoongang/discussion/data/model/request/FollowBody.kt b/discussion/src/main/java/org/openedx/discussion/data/model/request/FollowBody.kt
similarity index 71%
rename from discussion/src/main/java/com/raccoongang/discussion/data/model/request/FollowBody.kt
rename to discussion/src/main/java/org/openedx/discussion/data/model/request/FollowBody.kt
index 24f8502db..eec64c7b3 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/data/model/request/FollowBody.kt
+++ b/discussion/src/main/java/org/openedx/discussion/data/model/request/FollowBody.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.discussion.data.model.request
+package org.openedx.discussion.data.model.request
import com.google.gson.annotations.SerializedName
diff --git a/discussion/src/main/java/com/raccoongang/discussion/data/model/request/ReadBody.kt b/discussion/src/main/java/org/openedx/discussion/data/model/request/ReadBody.kt
similarity index 69%
rename from discussion/src/main/java/com/raccoongang/discussion/data/model/request/ReadBody.kt
rename to discussion/src/main/java/org/openedx/discussion/data/model/request/ReadBody.kt
index cdb36a213..054d11f9f 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/data/model/request/ReadBody.kt
+++ b/discussion/src/main/java/org/openedx/discussion/data/model/request/ReadBody.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.discussion.data.model.request
+package org.openedx.discussion.data.model.request
import com.google.gson.annotations.SerializedName
diff --git a/discussion/src/main/java/com/raccoongang/discussion/data/model/request/ReportBody.kt b/discussion/src/main/java/org/openedx/discussion/data/model/request/ReportBody.kt
similarity index 72%
rename from discussion/src/main/java/com/raccoongang/discussion/data/model/request/ReportBody.kt
rename to discussion/src/main/java/org/openedx/discussion/data/model/request/ReportBody.kt
index 46b89e119..05b556ce8 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/data/model/request/ReportBody.kt
+++ b/discussion/src/main/java/org/openedx/discussion/data/model/request/ReportBody.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.discussion.data.model.request
+package org.openedx.discussion.data.model.request
import com.google.gson.annotations.SerializedName
diff --git a/discussion/src/main/java/com/raccoongang/discussion/data/model/request/ThreadBody.kt b/discussion/src/main/java/org/openedx/discussion/data/model/request/ThreadBody.kt
similarity index 88%
rename from discussion/src/main/java/com/raccoongang/discussion/data/model/request/ThreadBody.kt
rename to discussion/src/main/java/org/openedx/discussion/data/model/request/ThreadBody.kt
index e36fe0a9a..d9b8d9cff 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/data/model/request/ThreadBody.kt
+++ b/discussion/src/main/java/org/openedx/discussion/data/model/request/ThreadBody.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.discussion.data.model.request
+package org.openedx.discussion.data.model.request
import com.google.gson.annotations.SerializedName
diff --git a/discussion/src/main/java/com/raccoongang/discussion/data/model/request/VoteBody.kt b/discussion/src/main/java/org/openedx/discussion/data/model/request/VoteBody.kt
similarity index 70%
rename from discussion/src/main/java/com/raccoongang/discussion/data/model/request/VoteBody.kt
rename to discussion/src/main/java/org/openedx/discussion/data/model/request/VoteBody.kt
index 7ff432382..cb93889de 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/data/model/request/VoteBody.kt
+++ b/discussion/src/main/java/org/openedx/discussion/data/model/request/VoteBody.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.discussion.data.model.request
+package org.openedx.discussion.data.model.request
import com.google.gson.annotations.SerializedName
diff --git a/discussion/src/main/java/com/raccoongang/discussion/data/model/response/CommentsResponse.kt b/discussion/src/main/java/org/openedx/discussion/data/model/response/CommentsResponse.kt
similarity index 88%
rename from discussion/src/main/java/com/raccoongang/discussion/data/model/response/CommentsResponse.kt
rename to discussion/src/main/java/org/openedx/discussion/data/model/response/CommentsResponse.kt
index 9e483d4ec..a2248b036 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/data/model/response/CommentsResponse.kt
+++ b/discussion/src/main/java/org/openedx/discussion/data/model/response/CommentsResponse.kt
@@ -1,11 +1,11 @@
-package com.raccoongang.discussion.data.model.response
+package org.openedx.discussion.data.model.response
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.data.model.Pagination
-import com.raccoongang.core.data.model.ProfileImage
-import com.raccoongang.core.extension.TextConverter
-import com.raccoongang.discussion.domain.model.CommentsData
-import com.raccoongang.discussion.domain.model.DiscussionComment
+import org.openedx.core.data.model.Pagination
+import org.openedx.core.data.model.ProfileImage
+import org.openedx.core.extension.TextConverter
+import org.openedx.discussion.domain.model.CommentsData
+import org.openedx.discussion.domain.model.DiscussionComment
data class CommentsResponse(
@SerializedName("results")
diff --git a/discussion/src/main/java/com/raccoongang/discussion/data/model/response/ThreadsResponse.kt b/discussion/src/main/java/org/openedx/discussion/data/model/response/ThreadsResponse.kt
similarity index 87%
rename from discussion/src/main/java/com/raccoongang/discussion/data/model/response/ThreadsResponse.kt
rename to discussion/src/main/java/org/openedx/discussion/data/model/response/ThreadsResponse.kt
index e8c699318..2aea8cd43 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/data/model/response/ThreadsResponse.kt
+++ b/discussion/src/main/java/org/openedx/discussion/data/model/response/ThreadsResponse.kt
@@ -1,11 +1,11 @@
-package com.raccoongang.discussion.data.model.response
+package org.openedx.discussion.data.model.response
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.data.model.Pagination
-import com.raccoongang.core.data.model.ProfileImage
-import com.raccoongang.core.extension.TextConverter
-import com.raccoongang.discussion.domain.model.DiscussionType
-import com.raccoongang.discussion.domain.model.ThreadsData
+import org.openedx.core.data.model.Pagination
+import org.openedx.core.data.model.ProfileImage
+import org.openedx.core.extension.TextConverter
+import org.openedx.discussion.domain.model.DiscussionType
+import org.openedx.discussion.domain.model.ThreadsData
data class ThreadsResponse(
@SerializedName("results")
@@ -83,8 +83,8 @@ data class ThreadsResponse(
@SerializedName("profile")
val profile: ProfileResponse
) {
- fun mapToDomain(): com.raccoongang.discussion.domain.model.DiscussionProfile {
- return com.raccoongang.discussion.domain.model.DiscussionProfile(
+ fun mapToDomain(): org.openedx.discussion.domain.model.DiscussionProfile {
+ return org.openedx.discussion.domain.model.DiscussionProfile(
image = profile.image.mapToDomain()
)
}
@@ -95,8 +95,8 @@ data class ThreadsResponse(
val image: ProfileImage
)
- fun mapToDomain(): com.raccoongang.discussion.domain.model.Thread {
- return com.raccoongang.discussion.domain.model.Thread(
+ fun mapToDomain(): org.openedx.discussion.domain.model.Thread {
+ return org.openedx.discussion.domain.model.Thread(
id,
author ?: "",
authorLabel ?: "",
diff --git a/discussion/src/main/java/com/raccoongang/discussion/data/model/response/TopicsResponse.kt b/discussion/src/main/java/org/openedx/discussion/data/model/response/TopicsResponse.kt
similarity index 80%
rename from discussion/src/main/java/com/raccoongang/discussion/data/model/response/TopicsResponse.kt
rename to discussion/src/main/java/org/openedx/discussion/data/model/response/TopicsResponse.kt
index 78c8c9906..b1e751755 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/data/model/response/TopicsResponse.kt
+++ b/discussion/src/main/java/org/openedx/discussion/data/model/response/TopicsResponse.kt
@@ -1,7 +1,7 @@
-package com.raccoongang.discussion.data.model.response
+package org.openedx.discussion.data.model.response
import com.google.gson.annotations.SerializedName
-import com.raccoongang.discussion.domain.model.TopicsData
+import org.openedx.discussion.domain.model.TopicsData
data class TopicsResponse(
@SerializedName("courseware_topics")
@@ -20,8 +20,8 @@ data class TopicsResponse(
@SerializedName("children")
val children: List?
) {
- fun mapToDomain(): com.raccoongang.discussion.domain.model.Topic {
- return com.raccoongang.discussion.domain.model.Topic(
+ fun mapToDomain(): org.openedx.discussion.domain.model.Topic {
+ return org.openedx.discussion.domain.model.Topic(
id = id ?: "",
name = name ?: "",
threadListUrl = threadListUrl ?: "",
diff --git a/discussion/src/main/java/com/raccoongang/discussion/data/repository/DiscussionRepository.kt b/discussion/src/main/java/org/openedx/discussion/data/repository/DiscussionRepository.kt
similarity index 77%
rename from discussion/src/main/java/com/raccoongang/discussion/data/repository/DiscussionRepository.kt
rename to discussion/src/main/java/org/openedx/discussion/data/repository/DiscussionRepository.kt
index c57af4537..e2c18dff1 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/data/repository/DiscussionRepository.kt
+++ b/discussion/src/main/java/org/openedx/discussion/data/repository/DiscussionRepository.kt
@@ -1,12 +1,17 @@
-package com.raccoongang.discussion.data.repository
+package org.openedx.discussion.data.repository
-import com.raccoongang.discussion.data.api.DiscussionApi
-import com.raccoongang.discussion.data.model.request.*
-import com.raccoongang.discussion.domain.model.CommentsData
-import com.raccoongang.discussion.domain.model.ThreadsData
-import com.raccoongang.discussion.domain.model.Topic
+import org.openedx.core.data.model.BlocksCompletionBody
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.discussion.data.api.DiscussionApi
+import org.openedx.discussion.data.model.request.*
+import org.openedx.discussion.domain.model.CommentsData
+import org.openedx.discussion.domain.model.ThreadsData
+import org.openedx.discussion.domain.model.Topic
-class DiscussionRepository(private val api: DiscussionApi) {
+class DiscussionRepository(
+ private val api: DiscussionApi,
+ private val preferencesManager: PreferencesManager
+ ) {
private val topics = mutableListOf()
private var currentCourseId = ""
@@ -113,4 +118,14 @@ class DiscussionRepository(private val api: DiscussionApi) {
follow: Boolean
) = api.createThread(ThreadBody(type, topicId, courseId, title, rawBody, follow)).mapToDomain()
+ suspend fun markBlocksCompletion(courseId: String, blocksId: List) {
+ val username = preferencesManager.user?.username ?: ""
+ val blocksCompletionBody = BlocksCompletionBody(
+ username,
+ courseId,
+ blocksId.associateWith { "1" }.toMap()
+ )
+ return api.markBlocksCompletion(blocksCompletionBody)
+ }
+
}
\ No newline at end of file
diff --git a/discussion/src/main/java/com/raccoongang/discussion/domain/interactor/DiscussionInteractor.kt b/discussion/src/main/java/org/openedx/discussion/domain/interactor/DiscussionInteractor.kt
similarity index 91%
rename from discussion/src/main/java/com/raccoongang/discussion/domain/interactor/DiscussionInteractor.kt
rename to discussion/src/main/java/org/openedx/discussion/domain/interactor/DiscussionInteractor.kt
index 1d08d49a9..7225cc443 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/domain/interactor/DiscussionInteractor.kt
+++ b/discussion/src/main/java/org/openedx/discussion/domain/interactor/DiscussionInteractor.kt
@@ -1,6 +1,6 @@
-package com.raccoongang.discussion.domain.interactor
+package org.openedx.discussion.domain.interactor
-import com.raccoongang.discussion.data.repository.DiscussionRepository
+import org.openedx.discussion.data.repository.DiscussionRepository
class DiscussionInteractor(
private val repository: DiscussionRepository
@@ -86,4 +86,6 @@ class DiscussionInteractor(
rawBody: String,
follow: Boolean
) = repository.createThread(topicId, courseId, type, title, rawBody, follow)
+
+ suspend fun markBlocksCompletion(courseId: String, blocksId: List) = repository.markBlocksCompletion(courseId, blocksId)
}
\ No newline at end of file
diff --git a/discussion/src/main/java/com/raccoongang/discussion/domain/model/CommentsData.kt b/discussion/src/main/java/org/openedx/discussion/domain/model/CommentsData.kt
similarity index 50%
rename from discussion/src/main/java/com/raccoongang/discussion/domain/model/CommentsData.kt
rename to discussion/src/main/java/org/openedx/discussion/domain/model/CommentsData.kt
index 8b5ff81b3..01937f16e 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/domain/model/CommentsData.kt
+++ b/discussion/src/main/java/org/openedx/discussion/domain/model/CommentsData.kt
@@ -1,6 +1,6 @@
-package com.raccoongang.discussion.domain.model
+package org.openedx.discussion.domain.model
-import com.raccoongang.core.domain.model.Pagination
+import org.openedx.core.domain.model.Pagination
data class CommentsData(
val results: List,
diff --git a/discussion/src/main/java/com/raccoongang/discussion/domain/model/DiscussionComment.kt b/discussion/src/main/java/org/openedx/discussion/domain/model/DiscussionComment.kt
similarity index 83%
rename from discussion/src/main/java/com/raccoongang/discussion/domain/model/DiscussionComment.kt
rename to discussion/src/main/java/org/openedx/discussion/domain/model/DiscussionComment.kt
index fe4b4ffef..9f71a0617 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/domain/model/DiscussionComment.kt
+++ b/discussion/src/main/java/org/openedx/discussion/domain/model/DiscussionComment.kt
@@ -1,8 +1,8 @@
-package com.raccoongang.discussion.domain.model
+package org.openedx.discussion.domain.model
import android.os.Parcelable
-import com.raccoongang.core.domain.model.ProfileImage
-import com.raccoongang.core.extension.LinkedImageText
+import org.openedx.core.domain.model.ProfileImage
+import org.openedx.core.extension.LinkedImageText
import kotlinx.parcelize.Parcelize
@Parcelize
diff --git a/discussion/src/main/java/com/raccoongang/discussion/domain/model/Thread.kt b/discussion/src/main/java/org/openedx/discussion/domain/model/Thread.kt
similarity index 87%
rename from discussion/src/main/java/com/raccoongang/discussion/domain/model/Thread.kt
rename to discussion/src/main/java/org/openedx/discussion/domain/model/Thread.kt
index c37a67600..8d2572788 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/domain/model/Thread.kt
+++ b/discussion/src/main/java/org/openedx/discussion/domain/model/Thread.kt
@@ -1,9 +1,9 @@
-package com.raccoongang.discussion.domain.model
+package org.openedx.discussion.domain.model
import android.os.Parcelable
-import com.raccoongang.core.domain.model.ProfileImage
-import com.raccoongang.core.extension.LinkedImageText
-import com.raccoongang.discussion.R
+import org.openedx.core.domain.model.ProfileImage
+import org.openedx.core.extension.LinkedImageText
+import org.openedx.discussion.R
import kotlinx.parcelize.Parcelize
@Parcelize
diff --git a/discussion/src/main/java/com/raccoongang/discussion/domain/model/ThreadsData.kt b/discussion/src/main/java/org/openedx/discussion/domain/model/ThreadsData.kt
similarity index 56%
rename from discussion/src/main/java/com/raccoongang/discussion/domain/model/ThreadsData.kt
rename to discussion/src/main/java/org/openedx/discussion/domain/model/ThreadsData.kt
index 8279ed976..2575d1cf6 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/domain/model/ThreadsData.kt
+++ b/discussion/src/main/java/org/openedx/discussion/domain/model/ThreadsData.kt
@@ -1,6 +1,6 @@
-package com.raccoongang.discussion.domain.model
+package org.openedx.discussion.domain.model
-import com.raccoongang.core.domain.model.Pagination
+import org.openedx.core.domain.model.Pagination
data class ThreadsData(
val results: List,
diff --git a/discussion/src/main/java/com/raccoongang/discussion/domain/model/TopicsData.kt b/discussion/src/main/java/org/openedx/discussion/domain/model/TopicsData.kt
similarity index 82%
rename from discussion/src/main/java/com/raccoongang/discussion/domain/model/TopicsData.kt
rename to discussion/src/main/java/org/openedx/discussion/domain/model/TopicsData.kt
index e36d609c6..6a3f5c5d5 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/domain/model/TopicsData.kt
+++ b/discussion/src/main/java/org/openedx/discussion/domain/model/TopicsData.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.discussion.domain.model
+package org.openedx.discussion.domain.model
data class TopicsData(
val coursewareTopics: List,
diff --git a/discussion/src/main/java/org/openedx/discussion/presentation/DiscussionAnalytics.kt b/discussion/src/main/java/org/openedx/discussion/presentation/DiscussionAnalytics.kt
new file mode 100644
index 000000000..79b2130bd
--- /dev/null
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/DiscussionAnalytics.kt
@@ -0,0 +1,12 @@
+package org.openedx.discussion.presentation
+
+interface DiscussionAnalytics {
+ fun discussionAllPostsClickedEvent(courseId: String, courseName: String)
+ fun discussionFollowingClickedEvent(courseId: String, courseName: String)
+ fun discussionTopicClickedEvent(
+ courseId: String,
+ courseName: String,
+ topicId: String,
+ topicName: String
+ )
+}
\ No newline at end of file
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/DiscussionRouter.kt b/discussion/src/main/java/org/openedx/discussion/presentation/DiscussionRouter.kt
similarity index 76%
rename from discussion/src/main/java/com/raccoongang/discussion/presentation/DiscussionRouter.kt
rename to discussion/src/main/java/org/openedx/discussion/presentation/DiscussionRouter.kt
index 45f094211..71386a645 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/DiscussionRouter.kt
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/DiscussionRouter.kt
@@ -1,8 +1,8 @@
-package com.raccoongang.discussion.presentation
+package org.openedx.discussion.presentation
import androidx.fragment.app.FragmentManager
-import com.raccoongang.core.FragmentViewType
-import com.raccoongang.discussion.domain.model.DiscussionComment
+import org.openedx.core.FragmentViewType
+import org.openedx.discussion.domain.model.DiscussionComment
interface DiscussionRouter {
@@ -17,7 +17,7 @@ interface DiscussionRouter {
fun navigateToDiscussionComments(
fm: FragmentManager,
- thread: com.raccoongang.discussion.domain.model.Thread
+ thread: org.openedx.discussion.domain.model.Thread
)
fun navigateToDiscussionResponses(
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/comments/DiscussionCommentsFragment.kt b/discussion/src/main/java/org/openedx/discussion/presentation/comments/DiscussionCommentsFragment.kt
similarity index 94%
rename from discussion/src/main/java/com/raccoongang/discussion/presentation/comments/DiscussionCommentsFragment.kt
rename to discussion/src/main/java/org/openedx/discussion/presentation/comments/DiscussionCommentsFragment.kt
index c22a1fd6a..1a5b1315a 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/comments/DiscussionCommentsFragment.kt
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/comments/DiscussionCommentsFragment.kt
@@ -1,6 +1,6 @@
@file:OptIn(ExperimentalComposeUiApi::class)
-package com.raccoongang.discussion.presentation.comments
+package org.openedx.discussion.presentation.comments
import android.content.res.Configuration
import android.os.Bundle
@@ -39,20 +39,20 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.ProfileImage
-import com.raccoongang.core.extension.TextConverter
-import com.raccoongang.core.extension.parcelable
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appShapes
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.discussion.domain.model.DiscussionComment
-import com.raccoongang.discussion.domain.model.DiscussionType
-import com.raccoongang.discussion.presentation.DiscussionRouter
-import com.raccoongang.discussion.presentation.ui.CommentItem
-import com.raccoongang.discussion.presentation.ui.ThreadMainItem
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.ProfileImage
+import org.openedx.core.extension.TextConverter
+import org.openedx.core.extension.parcelable
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appShapes
+import org.openedx.core.ui.theme.appTypography
+import org.openedx.discussion.domain.model.DiscussionComment
+import org.openedx.discussion.domain.model.DiscussionType
+import org.openedx.discussion.presentation.DiscussionRouter
+import org.openedx.discussion.presentation.ui.CommentItem
+import org.openedx.discussion.presentation.ui.ThreadMainItem
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
@@ -77,7 +77,7 @@ class DiscussionCommentsFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val uiState by viewModel.uiState.observeAsState(DiscussionCommentsUIState.Loading)
@@ -141,7 +141,7 @@ class DiscussionCommentsFragment : Fragment() {
private const val ARG_THREAD = "argThread"
- fun newInstance(thread: com.raccoongang.discussion.domain.model.Thread): DiscussionCommentsFragment {
+ fun newInstance(thread: org.openedx.discussion.domain.model.Thread): DiscussionCommentsFragment {
val fragment = DiscussionCommentsFragment()
fragment.arguments = bundleOf(
ARG_THREAD to thread
@@ -297,7 +297,7 @@ private fun DiscussionCommentsScreen(
.padding(horizontal = paddingContent)
.padding(top = 24.dp, bottom = 4.dp),
text = pluralStringResource(
- id = com.raccoongang.discussion.R.plurals.discussion_responses_capitalized,
+ id = org.openedx.discussion.R.plurals.discussion_responses_capitalized,
uiState.count,
uiState.count
),
@@ -367,7 +367,7 @@ private fun DiscussionCommentsScreen(
maxLines = 3,
placeholder = {
Text(
- text = stringResource(id = com.raccoongang.discussion.R.string.discussion_add_response),
+ text = stringResource(id = org.openedx.discussion.R.string.discussion_add_response),
color = MaterialTheme.appColors.textFieldHint,
style = MaterialTheme.appTypography.labelLarge,
)
@@ -394,7 +394,7 @@ private fun DiscussionCommentsScreen(
) {
Icon(
modifier = Modifier.padding(7.dp),
- painter = painterResource(id = com.raccoongang.discussion.R.drawable.discussion_ic_send),
+ painter = painterResource(id = org.openedx.discussion.R.drawable.discussion_ic_send),
contentDescription = null,
tint = iconButtonColor
)
@@ -430,7 +430,7 @@ private fun DiscussionCommentsScreen(
@Preview(name = "NEXUS_5_Dark", device = Devices.NEXUS_5, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun DiscussionCommentsScreenPreview() {
- NewEdxTheme() {
+ OpenEdXTheme() {
DiscussionCommentsScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
uiState = DiscussionCommentsUIState.Success(
@@ -460,7 +460,7 @@ private fun DiscussionCommentsScreenPreview() {
@Preview(name = "NEXUS_9_Dark", device = Devices.NEXUS_9, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun DiscussionCommentsScreenTabletPreview() {
- NewEdxTheme() {
+ OpenEdXTheme() {
DiscussionCommentsScreen(
windowSize = WindowSize(WindowType.Medium, WindowType.Medium),
uiState = DiscussionCommentsUIState.Success(
@@ -485,7 +485,7 @@ private fun DiscussionCommentsScreenTabletPreview() {
}
}
-private val mockThread = com.raccoongang.discussion.domain.model.Thread(
+private val mockThread = org.openedx.discussion.domain.model.Thread(
"",
"",
"",
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/comments/DiscussionCommentsUIState.kt b/discussion/src/main/java/org/openedx/discussion/presentation/comments/DiscussionCommentsUIState.kt
similarity index 54%
rename from discussion/src/main/java/com/raccoongang/discussion/presentation/comments/DiscussionCommentsUIState.kt
rename to discussion/src/main/java/org/openedx/discussion/presentation/comments/DiscussionCommentsUIState.kt
index afaf01a2e..389e8b44c 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/comments/DiscussionCommentsUIState.kt
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/comments/DiscussionCommentsUIState.kt
@@ -1,11 +1,11 @@
-package com.raccoongang.discussion.presentation.comments
+package org.openedx.discussion.presentation.comments
-import com.raccoongang.discussion.domain.model.DiscussionComment
+import org.openedx.discussion.domain.model.DiscussionComment
sealed class DiscussionCommentsUIState {
data class Success(
- val thread: com.raccoongang.discussion.domain.model.Thread,
+ val thread: org.openedx.discussion.domain.model.Thread,
val commentsData: List,
val count: Int
) : DiscussionCommentsUIState()
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/comments/DiscussionCommentsViewModel.kt b/discussion/src/main/java/org/openedx/discussion/presentation/comments/DiscussionCommentsViewModel.kt
similarity index 90%
rename from discussion/src/main/java/com/raccoongang/discussion/presentation/comments/DiscussionCommentsViewModel.kt
rename to discussion/src/main/java/org/openedx/discussion/presentation/comments/DiscussionCommentsViewModel.kt
index bf0d59d5f..e79b153ed 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/comments/DiscussionCommentsViewModel.kt
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/comments/DiscussionCommentsViewModel.kt
@@ -1,23 +1,23 @@
-package com.raccoongang.discussion.presentation.comments
+package org.openedx.discussion.presentation.comments
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.R
-import com.raccoongang.core.SingleEventLiveData
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.extension.isInternetError
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.discussion.domain.interactor.DiscussionInteractor
-import com.raccoongang.discussion.domain.model.DiscussionComment
-import com.raccoongang.discussion.domain.model.DiscussionType
-import com.raccoongang.discussion.system.notifier.DiscussionCommentAdded
-import com.raccoongang.discussion.system.notifier.DiscussionCommentDataChanged
-import com.raccoongang.discussion.system.notifier.DiscussionNotifier
-import com.raccoongang.discussion.system.notifier.DiscussionThreadDataChanged
+import org.openedx.core.BaseViewModel
+import org.openedx.core.R
+import org.openedx.core.SingleEventLiveData
+import org.openedx.core.UIMessage
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.extension.isInternetError
+import org.openedx.core.system.ResourceManager
+import org.openedx.discussion.domain.interactor.DiscussionInteractor
+import org.openedx.discussion.domain.model.DiscussionComment
+import org.openedx.discussion.domain.model.DiscussionType
+import org.openedx.discussion.system.notifier.DiscussionCommentAdded
+import org.openedx.discussion.system.notifier.DiscussionCommentDataChanged
+import org.openedx.discussion.system.notifier.DiscussionNotifier
+import org.openedx.discussion.system.notifier.DiscussionThreadDataChanged
import kotlinx.coroutines.launch
class DiscussionCommentsViewModel(
@@ -25,12 +25,12 @@ class DiscussionCommentsViewModel(
private val resourceManager: ResourceManager,
private val preferencesManager: PreferencesManager,
private val notifier: DiscussionNotifier,
- thread: com.raccoongang.discussion.domain.model.Thread,
+ thread: org.openedx.discussion.domain.model.Thread,
) : BaseViewModel() {
val title = resourceManager.getString(thread.type.resId)
- var thread: com.raccoongang.discussion.domain.model.Thread
+ var thread: org.openedx.discussion.domain.model.Thread
private set
private var commentCount = 0
@@ -73,7 +73,7 @@ class DiscussionCommentsViewModel(
_scrollToBottom.value = true
} else {
_uiMessage.value =
- UIMessage.ToastMessage(resourceManager.getString(com.raccoongang.discussion.R.string.discussion_comment_added))
+ UIMessage.ToastMessage(resourceManager.getString(org.openedx.discussion.R.string.discussion_comment_added))
}
thread = thread.copy(commentCount = thread.commentCount + 1)
sendThreadUpdated()
@@ -292,7 +292,7 @@ class DiscussionCommentsViewModel(
comments.add(response)
} else {
_uiMessage.value =
- UIMessage.ToastMessage(resourceManager.getString(com.raccoongang.discussion.R.string.discussion_comment_added))
+ UIMessage.ToastMessage(resourceManager.getString(org.openedx.discussion.R.string.discussion_comment_added))
}
_uiState.value =
DiscussionCommentsUIState.Success(thread, comments.toList(), commentCount)
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/responses/DiscussionResponsesFragment.kt b/discussion/src/main/java/org/openedx/discussion/presentation/responses/DiscussionResponsesFragment.kt
similarity index 95%
rename from discussion/src/main/java/com/raccoongang/discussion/presentation/responses/DiscussionResponsesFragment.kt
rename to discussion/src/main/java/org/openedx/discussion/presentation/responses/DiscussionResponsesFragment.kt
index de88ce103..169b65b97 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/responses/DiscussionResponsesFragment.kt
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/responses/DiscussionResponsesFragment.kt
@@ -1,6 +1,6 @@
@file:OptIn(ExperimentalComposeUiApi::class)
-package com.raccoongang.discussion.presentation.responses
+package org.openedx.discussion.presentation.responses
import android.content.res.Configuration
import android.os.Bundle
@@ -39,21 +39,21 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.ProfileImage
-import com.raccoongang.core.extension.TextConverter
-import com.raccoongang.core.extension.parcelable
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appShapes
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.discussion.domain.model.DiscussionComment
-import com.raccoongang.discussion.presentation.comments.DiscussionCommentsFragment
-import com.raccoongang.discussion.presentation.ui.CommentMainItem
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.ProfileImage
+import org.openedx.core.extension.TextConverter
+import org.openedx.core.extension.parcelable
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appShapes
+import org.openedx.core.ui.theme.appTypography
+import org.openedx.discussion.domain.model.DiscussionComment
+import org.openedx.discussion.presentation.comments.DiscussionCommentsFragment
+import org.openedx.discussion.presentation.ui.CommentMainItem
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
-import com.raccoongang.discussion.R as discussionR
+import org.openedx.discussion.R as discussionR
class DiscussionResponsesFragment : Fragment() {
@@ -74,7 +74,7 @@ class DiscussionResponsesFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val uiState by viewModel.uiState.observeAsState(DiscussionResponsesUIState.Loading)
@@ -300,7 +300,7 @@ private fun DiscussionResponsesScreen(
.padding(horizontal = paddingContent)
.padding(top = 24.dp, bottom = 8.dp),
text = pluralStringResource(
- id = com.raccoongang.discussion.R.plurals.discussion_comments,
+ id = org.openedx.discussion.R.plurals.discussion_comments,
uiState.mainComment.childCount,
uiState.mainComment.childCount
),
@@ -381,7 +381,7 @@ private fun DiscussionResponsesScreen(
shape = MaterialTheme.appShapes.buttonShape,
placeholder = {
Text(
- text = stringResource(id = com.raccoongang.discussion.R.string.discussion_add_comment),
+ text = stringResource(id = org.openedx.discussion.R.string.discussion_add_comment),
color = MaterialTheme.appColors.textFieldHint,
style = MaterialTheme.appTypography.labelLarge,
)
@@ -408,7 +408,7 @@ private fun DiscussionResponsesScreen(
) {
Icon(
modifier = Modifier.padding(7.dp),
- painter = painterResource(id = com.raccoongang.discussion.R.drawable.discussion_ic_send),
+ painter = painterResource(id = org.openedx.discussion.R.drawable.discussion_ic_send),
contentDescription = null,
tint = iconButtonColor
)
@@ -443,7 +443,7 @@ private fun DiscussionResponsesScreen(
@Preview(name = "NEXUS_5_Dark", device = Devices.NEXUS_5, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun DiscussionResponsesScreenPreview() {
- NewEdxTheme() {
+ OpenEdXTheme() {
DiscussionResponsesScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
uiState = DiscussionResponsesUIState.Success(
@@ -473,7 +473,7 @@ private fun DiscussionResponsesScreenPreview() {
@Preview(name = "NEXUS_9_Dark", device = Devices.NEXUS_9, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun DiscussionResponsesScreenTabletPreview() {
- NewEdxTheme() {
+ OpenEdXTheme() {
DiscussionResponsesScreen(
windowSize = WindowSize(WindowType.Medium, WindowType.Medium),
uiState = DiscussionResponsesUIState.Success(
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/responses/DiscussionResponsesUIState.kt b/discussion/src/main/java/org/openedx/discussion/presentation/responses/DiscussionResponsesUIState.kt
similarity index 67%
rename from discussion/src/main/java/com/raccoongang/discussion/presentation/responses/DiscussionResponsesUIState.kt
rename to discussion/src/main/java/org/openedx/discussion/presentation/responses/DiscussionResponsesUIState.kt
index c0f68f31a..95f216988 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/responses/DiscussionResponsesUIState.kt
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/responses/DiscussionResponsesUIState.kt
@@ -1,6 +1,6 @@
-package com.raccoongang.discussion.presentation.responses
+package org.openedx.discussion.presentation.responses
-import com.raccoongang.discussion.domain.model.DiscussionComment
+import org.openedx.discussion.domain.model.DiscussionComment
sealed class DiscussionResponsesUIState {
data class Success(
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/responses/DiscussionResponsesViewModel.kt b/discussion/src/main/java/org/openedx/discussion/presentation/responses/DiscussionResponsesViewModel.kt
similarity index 89%
rename from discussion/src/main/java/com/raccoongang/discussion/presentation/responses/DiscussionResponsesViewModel.kt
rename to discussion/src/main/java/org/openedx/discussion/presentation/responses/DiscussionResponsesViewModel.kt
index 8b548730b..7a13f42f5 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/responses/DiscussionResponsesViewModel.kt
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/responses/DiscussionResponsesViewModel.kt
@@ -1,19 +1,19 @@
-package com.raccoongang.discussion.presentation.responses
+package org.openedx.discussion.presentation.responses
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.R
-import com.raccoongang.core.SingleEventLiveData
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.extension.isInternetError
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.discussion.domain.interactor.DiscussionInteractor
-import com.raccoongang.discussion.domain.model.DiscussionComment
-import com.raccoongang.discussion.system.notifier.DiscussionCommentDataChanged
-import com.raccoongang.discussion.system.notifier.DiscussionNotifier
+import org.openedx.core.BaseViewModel
+import org.openedx.core.R
+import org.openedx.core.SingleEventLiveData
+import org.openedx.core.UIMessage
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.extension.isInternetError
+import org.openedx.core.system.ResourceManager
+import org.openedx.discussion.domain.interactor.DiscussionInteractor
+import org.openedx.discussion.domain.model.DiscussionComment
+import org.openedx.discussion.system.notifier.DiscussionCommentDataChanged
+import org.openedx.discussion.system.notifier.DiscussionNotifier
import kotlinx.coroutines.launch
class DiscussionResponsesViewModel(
@@ -164,7 +164,7 @@ class DiscussionResponsesViewModel(
comments.add(response)
} else {
_uiMessage.value =
- UIMessage.ToastMessage(resourceManager.getString(com.raccoongang.discussion.R.string.discussion_comment_added))
+ UIMessage.ToastMessage(resourceManager.getString(org.openedx.discussion.R.string.discussion_comment_added))
}
_uiState.value =
DiscussionResponsesUIState.Success(comment, comments.toList())
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/search/DiscussionSearchThreadFragment.kt b/discussion/src/main/java/org/openedx/discussion/presentation/search/DiscussionSearchThreadFragment.kt
similarity index 95%
rename from discussion/src/main/java/com/raccoongang/discussion/presentation/search/DiscussionSearchThreadFragment.kt
rename to discussion/src/main/java/org/openedx/discussion/presentation/search/DiscussionSearchThreadFragment.kt
index e72c006a7..05fceeffc 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/search/DiscussionSearchThreadFragment.kt
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/search/DiscussionSearchThreadFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.discussion.presentation.search
+package org.openedx.discussion.presentation.search
import android.content.res.Configuration
import android.os.Bundle
@@ -34,21 +34,21 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.extension.TextConverter
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.discussion.domain.model.DiscussionType
-import com.raccoongang.discussion.presentation.DiscussionRouter
-import com.raccoongang.discussion.presentation.ui.ThreadItem
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.extension.TextConverter
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appTypography
+import org.openedx.discussion.domain.model.DiscussionType
+import org.openedx.discussion.presentation.DiscussionRouter
+import org.openedx.discussion.presentation.ui.ThreadItem
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
-import com.raccoongang.discussion.R as discussionR
+import org.openedx.discussion.R as discussionR
class DiscussionSearchThreadFragment : Fragment() {
@@ -70,7 +70,7 @@ class DiscussionSearchThreadFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val uiState by viewModel.uiState.observeAsState(
@@ -129,7 +129,7 @@ private fun DiscussionSearchThreadScreen(
uiMessage: UIMessage?,
refreshing: Boolean,
canLoadMore: Boolean,
- onItemClick: (com.raccoongang.discussion.domain.model.Thread) -> Unit,
+ onItemClick: (org.openedx.discussion.domain.model.Thread) -> Unit,
onSearchTextChanged: (String) -> Unit,
onSwipeRefresh: () -> Unit,
paginationCallback: () -> Unit,
@@ -342,7 +342,7 @@ private fun DiscussionSearchThreadScreen(
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun DiscussionSearchThreadScreenPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
DiscussionSearchThreadScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
uiState = DiscussionSearchThreadUIState.Threads(listOf(mockThread, mockThread), 2),
@@ -363,7 +363,7 @@ fun DiscussionSearchThreadScreenPreview() {
@Preview(device = Devices.NEXUS_9, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
fun DiscussionSearchThreadScreenTabletPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
DiscussionSearchThreadScreen(
windowSize = WindowSize(WindowType.Medium, WindowType.Medium),
uiState = DiscussionSearchThreadUIState.Threads(listOf(mockThread, mockThread), 2),
@@ -380,7 +380,7 @@ fun DiscussionSearchThreadScreenTabletPreview() {
}
}
-private val mockThread = com.raccoongang.discussion.domain.model.Thread(
+private val mockThread = org.openedx.discussion.domain.model.Thread(
"",
"",
"",
diff --git a/discussion/src/main/java/org/openedx/discussion/presentation/search/DiscussionSearchThreadUIState.kt b/discussion/src/main/java/org/openedx/discussion/presentation/search/DiscussionSearchThreadUIState.kt
new file mode 100644
index 000000000..b3f75f9e3
--- /dev/null
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/search/DiscussionSearchThreadUIState.kt
@@ -0,0 +1,8 @@
+package org.openedx.discussion.presentation.search
+
+sealed class DiscussionSearchThreadUIState {
+ class Threads(val data: List, val count: Int) :
+ DiscussionSearchThreadUIState()
+
+ object Loading : DiscussionSearchThreadUIState()
+}
\ No newline at end of file
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/search/DiscussionSearchThreadViewModel.kt b/discussion/src/main/java/org/openedx/discussion/presentation/search/DiscussionSearchThreadViewModel.kt
similarity index 88%
rename from discussion/src/main/java/com/raccoongang/discussion/presentation/search/DiscussionSearchThreadViewModel.kt
rename to discussion/src/main/java/org/openedx/discussion/presentation/search/DiscussionSearchThreadViewModel.kt
index 034d53391..6f14fa265 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/search/DiscussionSearchThreadViewModel.kt
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/search/DiscussionSearchThreadViewModel.kt
@@ -1,18 +1,18 @@
-package com.raccoongang.discussion.presentation.search
+package org.openedx.discussion.presentation.search
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.R
-import com.raccoongang.core.SingleEventLiveData
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.extension.isInternetError
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.discussion.domain.interactor.DiscussionInteractor
-import com.raccoongang.discussion.system.notifier.DiscussionNotifier
-import com.raccoongang.discussion.system.notifier.DiscussionThreadDataChanged
+import org.openedx.core.BaseViewModel
+import org.openedx.core.R
+import org.openedx.core.SingleEventLiveData
+import org.openedx.core.UIMessage
+import org.openedx.core.extension.isInternetError
+import org.openedx.core.system.ResourceManager
+import org.openedx.discussion.domain.interactor.DiscussionInteractor
+import org.openedx.discussion.system.notifier.DiscussionNotifier
+import org.openedx.discussion.system.notifier.DiscussionThreadDataChanged
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
@@ -49,7 +49,7 @@ class DiscussionSearchThreadViewModel(
private var nextPage: Int? = 1
private var currentQuery: String? = null
- private val threadsList = mutableListOf()
+ private val threadsList = mutableListOf()
private var isLoading = false
private val queryChannel = MutableSharedFlow(replay = 0, extraBufferCapacity = 0)
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/threads/DiscussionAddThreadFragment.kt b/discussion/src/main/java/org/openedx/discussion/presentation/threads/DiscussionAddThreadFragment.kt
similarity index 92%
rename from discussion/src/main/java/com/raccoongang/discussion/presentation/threads/DiscussionAddThreadFragment.kt
rename to discussion/src/main/java/org/openedx/discussion/presentation/threads/DiscussionAddThreadFragment.kt
index 958ba16f8..cb18d376a 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/threads/DiscussionAddThreadFragment.kt
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/threads/DiscussionAddThreadFragment.kt
@@ -1,6 +1,6 @@
@file:OptIn(ExperimentalComposeUiApi::class)
-package com.raccoongang.discussion.presentation.threads
+package org.openedx.discussion.presentation.threads
import android.content.res.Configuration
import android.content.res.Configuration.UI_MODE_NIGHT_NO
@@ -24,10 +24,12 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Devices
@@ -36,14 +38,14 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.*
-import com.raccoongang.discussion.domain.model.DiscussionType
+import org.openedx.core.UIMessage
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.*
+import org.openedx.discussion.domain.model.DiscussionType
import kotlinx.coroutines.launch
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
-import com.raccoongang.discussion.R as discussionR
+import org.openedx.discussion.R as discussionR
class DiscussionAddThreadFragment : Fragment() {
@@ -58,7 +60,7 @@ class DiscussionAddThreadFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val uiMessage by viewModel.uiMessage.observeAsState()
@@ -124,10 +126,11 @@ private fun DiscussionAddThreadScreen(
onBackClick: () -> Unit,
) {
val scaffoldState = rememberScaffoldState()
- val configuration = LocalConfiguration.current
+ val focusManager = LocalFocusManager.current
val keyboardController = LocalSoftwareKeyboardController.current
val bottomSheetScaffoldState = rememberModalBottomSheetState(
- initialValue = ModalBottomSheetValue.Hidden
+ initialValue = ModalBottomSheetValue.Hidden,
+ skipHalfExpanded = true
)
val coroutine = rememberCoroutineScope()
var currentPage by rememberSaveable {
@@ -151,6 +154,17 @@ private fun DiscussionAddThreadScreen(
val expandedList by rememberSaveable {
mutableStateOf(topics)
}
+ var searchValue by rememberSaveable(stateSaver = TextFieldValue.Saver) {
+ mutableStateOf(TextFieldValue())
+ }
+ val isImeVisible by isImeVisibleState()
+
+ LaunchedEffect(bottomSheetScaffoldState.isVisible) {
+ if (!bottomSheetScaffoldState.isVisible) {
+ focusManager.clearFocus()
+ searchValue = TextFieldValue()
+ }
+ }
Scaffold(
scaffoldState = scaffoldState,
modifier = Modifier
@@ -186,17 +200,9 @@ private fun DiscussionAddThreadScreen(
)
}
- val bottomSheetWeight by remember(key1 = windowSize) {
- mutableStateOf(
- windowSize.windowSizeValue(
- expanded = if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) 0.8f else 0.6f,
- compact = 1f
- )
- )
- }
-
ModalBottomSheetLayout(
modifier = Modifier
+ .padding(bottom = if (isImeVisible && bottomSheetScaffoldState.isVisible) 120.dp else 0.dp)
.noRippleClickable {
if (bottomSheetScaffoldState.isVisible) {
coroutine.launch {
@@ -205,21 +211,22 @@ private fun DiscussionAddThreadScreen(
}
},
sheetState = bottomSheetScaffoldState,
- sheetShape = BottomSheetShape(
- width = configuration.screenWidthDp.px,
- height = configuration.screenHeightDp.px,
- weight = bottomSheetWeight
- ),
+ sheetShape = MaterialTheme.appShapes.screenBackgroundShape,
scrimColor = Color.Black.copy(alpha = 0.4f),
sheetBackgroundColor = MaterialTheme.appColors.background,
sheetContent = {
SheetContent(
+ title = stringResource(id = discussionR.string.discussion_topic),
+ searchValue = searchValue,
expandedList = expandedList,
onItemClick = { item ->
postToTopic = item
coroutine.launch {
bottomSheetScaffoldState.hide()
}
+ },
+ searchValueChanged = {
+ searchValue = TextFieldValue(it)
}
)
}
@@ -312,7 +319,7 @@ private fun DiscussionAddThreadScreen(
}
})
Spacer(Modifier.height(24.dp))
- NewEdxOutlinedTextField(
+ OpenEdXOutlinedTextField(
modifier = Modifier
.fillMaxWidth(),
stringResource(id = discussionR.string.discussion_title),
@@ -327,11 +334,11 @@ private fun DiscussionAddThreadScreen(
}
)
Spacer(Modifier.height(24.dp))
- NewEdxOutlinedTextField(
+ OpenEdXOutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.height(150.dp),
- title = if (currentPage == 0) stringResource(id = com.raccoongang.discussion.R.string.discussion_discussion) else stringResource(
+ title = if (currentPage == 0) stringResource(id = org.openedx.discussion.R.string.discussion_discussion) else stringResource(
id = discussionR.string.discussion_question
),
isSingleLine = false,
@@ -378,7 +385,7 @@ private fun DiscussionAddThreadScreen(
if (isLoading) {
CircularProgressIndicator(color = MaterialTheme.appColors.primary)
} else {
- NewEdxButton(
+ OpenEdXButton(
width = buttonWidth,
text = stringResource(id = discussionR.string.discussion_create_post),
onClick = {
@@ -499,7 +506,7 @@ private fun SelectableField(
@Preview(uiMode = UI_MODE_NIGHT_YES)
@Composable
private fun DiscussionAddThreadScreenPreview() {
- NewEdxTheme() {
+ OpenEdXTheme() {
DiscussionAddThreadScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
topicData = Pair("", "General"),
@@ -519,7 +526,7 @@ private fun DiscussionAddThreadScreenPreview() {
@Preview(uiMode = UI_MODE_NIGHT_YES, device = Devices.NEXUS_9)
@Composable
private fun DiscussionAddThreadScreenTabletPreview() {
- NewEdxTheme() {
+ OpenEdXTheme() {
DiscussionAddThreadScreen(
windowSize = WindowSize(WindowType.Medium, WindowType.Medium),
topicData = Pair("", "General"),
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/threads/DiscussionAddThreadViewModel.kt b/discussion/src/main/java/org/openedx/discussion/presentation/threads/DiscussionAddThreadViewModel.kt
similarity index 74%
rename from discussion/src/main/java/com/raccoongang/discussion/presentation/threads/DiscussionAddThreadViewModel.kt
rename to discussion/src/main/java/org/openedx/discussion/presentation/threads/DiscussionAddThreadViewModel.kt
index f8ea16115..278f6b4f0 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/threads/DiscussionAddThreadViewModel.kt
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/threads/DiscussionAddThreadViewModel.kt
@@ -1,17 +1,17 @@
-package com.raccoongang.discussion.presentation.threads
+package org.openedx.discussion.presentation.threads
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.R
-import com.raccoongang.core.SingleEventLiveData
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.extension.isInternetError
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.discussion.domain.interactor.DiscussionInteractor
-import com.raccoongang.discussion.system.notifier.DiscussionNotifier
-import com.raccoongang.discussion.system.notifier.DiscussionThreadAdded
+import org.openedx.core.BaseViewModel
+import org.openedx.core.R
+import org.openedx.core.SingleEventLiveData
+import org.openedx.core.UIMessage
+import org.openedx.core.extension.isInternetError
+import org.openedx.core.system.ResourceManager
+import org.openedx.discussion.domain.interactor.DiscussionInteractor
+import org.openedx.discussion.system.notifier.DiscussionNotifier
+import org.openedx.discussion.system.notifier.DiscussionThreadAdded
import kotlinx.coroutines.launch
class DiscussionAddThreadViewModel(
@@ -21,8 +21,8 @@ class DiscussionAddThreadViewModel(
private val courseId: String
) : BaseViewModel() {
- private val _newThread = MutableLiveData()
- val newThread: LiveData
+ private val _newThread = MutableLiveData()
+ val newThread: LiveData
get() = _newThread
private val _uiMessage = SingleEventLiveData()
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/threads/DiscussionThreadsFragment.kt b/discussion/src/main/java/org/openedx/discussion/presentation/threads/DiscussionThreadsFragment.kt
similarity index 65%
rename from discussion/src/main/java/com/raccoongang/discussion/presentation/threads/DiscussionThreadsFragment.kt
rename to discussion/src/main/java/org/openedx/discussion/presentation/threads/DiscussionThreadsFragment.kt
index eab2ea97c..7922919fa 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/threads/DiscussionThreadsFragment.kt
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/threads/DiscussionThreadsFragment.kt
@@ -1,14 +1,16 @@
-package com.raccoongang.discussion.presentation.threads
+package org.openedx.discussion.presentation.threads
import android.content.res.Configuration
import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.*
import androidx.compose.material.pullrefresh.PullRefreshIndicator
@@ -19,13 +21,16 @@ import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Devices
@@ -34,25 +39,26 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
-import com.raccoongang.core.FragmentViewType
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.extension.TextConverter
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.*
-import com.raccoongang.discussion.domain.model.DiscussionType
-import com.raccoongang.discussion.presentation.DiscussionRouter
-import com.raccoongang.discussion.presentation.ui.ThreadItem
+import org.openedx.core.FragmentViewType
+import org.openedx.core.UIMessage
+import org.openedx.core.extension.TextConverter
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.*
+import org.openedx.discussion.domain.model.DiscussionType
+import org.openedx.discussion.presentation.DiscussionRouter
+import org.openedx.discussion.presentation.ui.ThreadItem
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
-import com.raccoongang.discussion.R as discussionR
+import org.openedx.discussion.R as discussionR
class DiscussionThreadsFragment : Fragment() {
private val viewModel by viewModel {
parametersOf(
requireArguments().getString(ARG_COURSE_ID, ""),
+ requireArguments().getString(ARG_TOPIC_ID, ""),
requireArguments().getString(ARG_THREAD_TYPE, "")
)
}
@@ -64,8 +70,9 @@ class DiscussionThreadsFragment : Fragment() {
lifecycle.addObserver(viewModel)
viewType =
FragmentViewType.valueOf(requireArguments().getString(ARG_FRAGMENT_VIEW_TYPE, ""))
- viewModel.topicId = requireArguments().getString(ARG_TOPIC_ID, "")
- viewModel.getThreadByType(SortType.LAST_ACTIVITY_AT.queryParam)
+ if (viewType == FragmentViewType.MAIN_CONTENT) {
+ viewModel.markBlockCompleted(requireArguments().getString(ARG_BLOCK_ID, ""))
+ }
}
override fun onCreateView(
@@ -75,7 +82,7 @@ class DiscussionThreadsFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val uiState by viewModel.uiState.observeAsState(DiscussionThreadsUIState.Loading)
@@ -128,6 +135,7 @@ class DiscussionThreadsFragment : Fragment() {
companion object {
private const val ARG_THREAD_TYPE = "threadType"
private const val ARG_COURSE_ID = "courseId"
+ private const val ARG_BLOCK_ID = "blockId"
private const val ARG_TOPIC_ID = "topicId"
private const val ARG_TITLE = "title"
private const val ARG_FRAGMENT_VIEW_TYPE = "fragmentViewType"
@@ -137,7 +145,8 @@ class DiscussionThreadsFragment : Fragment() {
courseId: String,
topicId: String,
title: String,
- viewType: String
+ viewType: String,
+ blockId: String = ""
): DiscussionThreadsFragment {
val fragment = DiscussionThreadsFragment()
fragment.arguments = bundleOf(
@@ -145,7 +154,8 @@ class DiscussionThreadsFragment : Fragment() {
ARG_COURSE_ID to courseId,
ARG_TOPIC_ID to topicId,
ARG_TITLE to title,
- ARG_FRAGMENT_VIEW_TYPE to viewType
+ ARG_FRAGMENT_VIEW_TYPE to viewType,
+ ARG_BLOCK_ID to blockId
)
return fragment
}
@@ -165,7 +175,7 @@ private fun DiscussionThreadsScreen(
onSwipeRefresh: () -> Unit,
updatedOrder: (String) -> Unit,
updatedFilter: (String) -> Unit,
- onItemClick: (com.raccoongang.discussion.domain.model.Thread) -> Unit,
+ onItemClick: (org.openedx.discussion.domain.model.Thread) -> Unit,
onCreatePostClick: () -> Unit,
paginationCallback: () -> Unit,
onBackClick: () -> Unit
@@ -173,9 +183,10 @@ private fun DiscussionThreadsScreen(
val scaffoldState = rememberScaffoldState()
val bottomSheetScaffoldState = rememberModalBottomSheetState(
- initialValue = ModalBottomSheetValue.Hidden
+ initialValue = ModalBottomSheetValue.Hidden,
+ skipHalfExpanded = true
)
- val configuration = LocalConfiguration.current
+ val focusManager = LocalFocusManager.current
val pullRefreshState =
rememberPullRefreshState(refreshing = refreshing, onRefresh = { onSwipeRefresh() })
val coroutine = rememberCoroutineScope()
@@ -207,6 +218,12 @@ private fun DiscussionThreadsScreen(
mutableStateOf("")
}
+ var searchValue by rememberSaveable(stateSaver = TextFieldValue.Saver) {
+ mutableStateOf(TextFieldValue())
+ }
+
+ val isImeVisible by isImeVisibleState()
+
val scaffoldModifier = if (viewType == FragmentViewType.FULL_CONTENT) {
Modifier
.fillMaxSize()
@@ -224,6 +241,13 @@ private fun DiscussionThreadsScreen(
.fillMaxSize()
}
+ LaunchedEffect(bottomSheetScaffoldState.isVisible) {
+ if (!bottomSheetScaffoldState.isVisible) {
+ focusManager.clearFocus()
+ searchValue = TextFieldValue()
+ }
+ }
+
Scaffold(
scaffoldState = scaffoldState,
modifier = scaffoldModifier,
@@ -262,19 +286,11 @@ private fun DiscussionThreadsScreen(
)
}
- val bottomSheetWeight by remember(key1 = windowSize) {
- mutableStateOf(
- windowSize.windowSizeValue(
- expanded = if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) 0.8f else 0.6f,
- compact = 1f
- )
- )
- }
-
HandleUIMessage(uiMessage = uiMessage, scaffoldState = scaffoldState)
ModalBottomSheetLayout(
modifier = Modifier
+ .padding(bottom = if (isImeVisible && bottomSheetScaffoldState.isVisible) 120.dp else 0.dp)
.noRippleClickable {
if (bottomSheetScaffoldState.isVisible) {
coroutine.launch {
@@ -282,16 +298,13 @@ private fun DiscussionThreadsScreen(
}
}
},
- sheetShape = BottomSheetShape(
- width = configuration.screenWidthDp.px,
- height = configuration.screenHeightDp.px,
- weight = bottomSheetWeight
- ),
+ sheetShape = MaterialTheme.appShapes.screenBackgroundShape,
sheetState = bottomSheetScaffoldState,
scrimColor = Color.Black.copy(alpha = 0.4f),
sheetBackgroundColor = MaterialTheme.appColors.background,
sheetContent = {
SheetContent(
+ searchValue = searchValue,
expandedList = expandedList,
onItemClick = { item ->
when (currentSelectedList) {
@@ -299,6 +312,7 @@ private fun DiscussionThreadsScreen(
filterType = item
updatedFilter(filterType.second)
}
+
SortType.type -> {
sortType = item
updatedOrder(sortType.second)
@@ -307,6 +321,9 @@ private fun DiscussionThreadsScreen(
coroutine.launch {
bottomSheetScaffoldState.hide()
}
+ },
+ searchValueChanged = {
+ searchValue = TextFieldValue(it)
}
)
}
@@ -433,70 +450,141 @@ private fun DiscussionThreadsScreen(
)
}
Divider()
- LazyColumn(
- modifier = Modifier.fillMaxSize(),
- contentPadding = listPadding,
- state = scrollState
- ) {
- item {
- Text(
- modifier = Modifier.padding(vertical = 8.dp),
- text = title,
- color = MaterialTheme.appColors.textPrimary,
- style = MaterialTheme.appTypography.titleLarge
- )
- }
- items(uiState.data) { threadItem ->
- ThreadItem(thread = threadItem, onClick = {
- onItemClick(it)
- })
- Divider()
- }
- item {
- if (canLoadMore) {
- Box(
+ if (uiState.data.isNotEmpty()) {
+ LazyColumn(
+ modifier = Modifier.fillMaxSize(),
+ contentPadding = listPadding,
+ state = scrollState
+ ) {
+ item {
+ Row(
modifier = Modifier
.fillMaxWidth()
- .padding(vertical = 16.dp),
- contentAlignment = Alignment.Center
+ .padding(vertical = 8.dp),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
) {
- CircularProgressIndicator(color = MaterialTheme.appColors.primary)
+ Text(
+ modifier = Modifier.weight(1f),
+ text = title,
+ color = MaterialTheme.appColors.textPrimary,
+ style = MaterialTheme.appTypography.titleLarge
+ )
+ Box(
+ Modifier
+ .size(40.dp)
+ .clip(CircleShape)
+ .background(MaterialTheme.appColors.primary)
+ .clickable {
+ onCreatePostClick()
+ },
+ contentAlignment = Alignment.Center
+ ) {
+ Icon(
+ modifier = Modifier.size(16.dp),
+ painter = painterResource(id = discussionR.drawable.discussion_ic_add_comment),
+ contentDescription = null,
+ tint = MaterialTheme.appColors.buttonText
+ )
+ }
+ }
+ }
+ items(uiState.data) { threadItem ->
+ ThreadItem(thread = threadItem, onClick = {
+ onItemClick(it)
+ })
+ Divider()
+ }
+ item {
+ if (canLoadMore) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(vertical = 16.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ CircularProgressIndicator(color = MaterialTheme.appColors.primary)
+ }
}
}
+ if (scrollState.shouldLoadMore(
+ firstVisibleIndex,
+ 4
+ )
+ ) {
+ paginationCallback()
+ }
}
- if (scrollState.shouldLoadMore(
- firstVisibleIndex,
- 4
- )
+ } else {
+ Box(
+ modifier = Modifier.fillMaxSize(),
+ contentAlignment = Alignment.TopStart
) {
- paginationCallback()
+ Text(
+ modifier = Modifier
+ .padding(start = 24.dp, top = 32.dp),
+ text = title,
+ color = MaterialTheme.appColors.textPrimary,
+ style = MaterialTheme.appTypography.titleLarge
+ )
+ Column(
+ modifier = Modifier.align(Alignment.Center),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center
+ ) {
+ Icon(
+ modifier = Modifier.size(100.dp),
+ painter = painterResource(id = discussionR.drawable.discussion_ic_empty),
+ contentDescription = null,
+ tint = MaterialTheme.appColors.textPrimary
+ )
+ Spacer(Modifier.height(36.dp))
+ Text(
+ modifier = Modifier.fillMaxWidth(),
+ text = stringResource(discussionR.string.discussion_no_yet),
+ style = MaterialTheme.appTypography.titleLarge,
+ color = MaterialTheme.appColors.textPrimary,
+ textAlign = TextAlign.Center
+ )
+ Spacer(Modifier.height(12.dp))
+ Text(
+ modifier = Modifier.fillMaxWidth(),
+ text = stringResource(discussionR.string.discussion_click_button_create_discussion),
+ style = MaterialTheme.appTypography.bodyLarge,
+ color = MaterialTheme.appColors.textPrimary,
+ textAlign = TextAlign.Center
+ )
+ Spacer(Modifier.height(40.dp))
+ OpenEdXOutlinedButton(
+ modifier = Modifier
+ .widthIn(184.dp, Dp.Unspecified),
+ text = stringResource(id = discussionR.string.discussion_create_post),
+ onClick = {
+ onCreatePostClick()
+ },
+ content = {
+ Icon(
+ painter = painterResource(id = discussionR.drawable.discussion_ic_add_comment),
+ contentDescription = null,
+ tint = MaterialTheme.appColors.primary
+ )
+ Spacer(modifier = Modifier.width(6.dp))
+ Text(
+ text = stringResource(id = discussionR.string.discussion_create_post),
+ color = MaterialTheme.appColors.primary,
+ style = MaterialTheme.appTypography.labelLarge
+ )
+ },
+ borderColor = MaterialTheme.appColors.primary,
+ textColor = MaterialTheme.appColors.primary
+ )
+ }
}
}
}
- NewEdxButton(
- width = Modifier
- .padding(bottom = 24.dp)
- .widthIn(184.dp, Dp.Unspecified),
- text = stringResource(id = discussionR.string.discussion_create_post),
- onClick = {
- onCreatePostClick()
- },
- content = {
- Icon(
- painter = painterResource(id = discussionR.drawable.discussion_ic_add_comment),
- contentDescription = null,
- tint = MaterialTheme.appColors.buttonText
- )
- Spacer(modifier = Modifier.width(6.dp))
- Text(
- text = stringResource(id = discussionR.string.discussion_create_post),
- color = MaterialTheme.appColors.buttonText,
- style = MaterialTheme.appTypography.labelLarge
- )
- }
- )
}
}
+
is DiscussionThreadsUIState.Loading -> {
Box(
Modifier
@@ -525,7 +613,7 @@ private fun DiscussionThreadsScreen(
@Preview(name = "NEXUS_5_Dark", device = Devices.NEXUS_5, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun DiscussionThreadsScreenPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
DiscussionThreadsScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
"All posts",
@@ -549,7 +637,7 @@ private fun DiscussionThreadsScreenPreview() {
@Preview(name = "NEXUS_9_Dark", device = Devices.NEXUS_9, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun DiscussionThreadsScreenTabletPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
DiscussionThreadsScreen(
windowSize = WindowSize(WindowType.Medium, WindowType.Medium),
"All posts",
@@ -569,7 +657,7 @@ private fun DiscussionThreadsScreenTabletPreview() {
}
}
-private val mockThread = com.raccoongang.discussion.domain.model.Thread(
+private val mockThread = org.openedx.discussion.domain.model.Thread(
"",
"",
"",
diff --git a/discussion/src/main/java/org/openedx/discussion/presentation/threads/DiscussionThreadsUIState.kt b/discussion/src/main/java/org/openedx/discussion/presentation/threads/DiscussionThreadsUIState.kt
new file mode 100644
index 000000000..94106edc0
--- /dev/null
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/threads/DiscussionThreadsUIState.kt
@@ -0,0 +1,8 @@
+package org.openedx.discussion.presentation.threads
+
+sealed class DiscussionThreadsUIState {
+ data class Threads(val data: List) :
+ DiscussionThreadsUIState()
+
+ object Loading : DiscussionThreadsUIState()
+}
\ No newline at end of file
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/threads/DiscussionThreadsViewModel.kt b/discussion/src/main/java/org/openedx/discussion/presentation/threads/DiscussionThreadsViewModel.kt
similarity index 83%
rename from discussion/src/main/java/com/raccoongang/discussion/presentation/threads/DiscussionThreadsViewModel.kt
rename to discussion/src/main/java/org/openedx/discussion/presentation/threads/DiscussionThreadsViewModel.kt
index 49addb963..08709538b 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/threads/DiscussionThreadsViewModel.kt
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/threads/DiscussionThreadsViewModel.kt
@@ -1,20 +1,20 @@
-package com.raccoongang.discussion.presentation.threads
+package org.openedx.discussion.presentation.threads
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.R
-import com.raccoongang.core.SingleEventLiveData
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.extension.isInternetError
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.discussion.domain.interactor.DiscussionInteractor
-import com.raccoongang.discussion.presentation.topics.DiscussionTopicsFragment
-import com.raccoongang.discussion.system.notifier.DiscussionNotifier
-import com.raccoongang.discussion.system.notifier.DiscussionThreadAdded
-import com.raccoongang.discussion.system.notifier.DiscussionThreadDataChanged
+import org.openedx.core.BaseViewModel
+import org.openedx.core.R
+import org.openedx.core.SingleEventLiveData
+import org.openedx.core.UIMessage
+import org.openedx.core.extension.isInternetError
+import org.openedx.core.system.ResourceManager
+import org.openedx.discussion.domain.interactor.DiscussionInteractor
+import org.openedx.discussion.presentation.topics.DiscussionTopicsFragment
+import org.openedx.discussion.system.notifier.DiscussionNotifier
+import org.openedx.discussion.system.notifier.DiscussionThreadAdded
+import org.openedx.discussion.system.notifier.DiscussionThreadDataChanged
import kotlinx.coroutines.launch
class DiscussionThreadsViewModel(
@@ -22,6 +22,7 @@ class DiscussionThreadsViewModel(
private val resourceManager: ResourceManager,
private val notifier: DiscussionNotifier,
val courseId: String,
+ val topicId: String,
private val threadType: String
) : BaseViewModel() {
@@ -41,14 +42,14 @@ class DiscussionThreadsViewModel(
val canLoadMore: LiveData
get() = _canLoadMore
- private val threadsList = mutableListOf()
+ private val threadsList = mutableListOf()
private var nextPage = 1
private var isLoading = false
-
- var topicId = ""
private var lastOrderBy = ""
private var filterType: String? = null
+ private var isBlockAlreadyCompleted = false
+
override fun onCreate(owner: LifecycleOwner) {
super.onCreate(owner)
viewModelScope.launch {
@@ -70,6 +71,10 @@ class DiscussionThreadsViewModel(
}
}
+ init {
+ getThreadByType(SortType.LAST_ACTIVITY_AT.queryParam)
+ }
+
fun getThreadByType(orderBy: String) {
_uiState.value = DiscussionThreadsUIState.Loading
internalLoadThreads(orderBy)
@@ -99,9 +104,11 @@ class DiscussionThreadsViewModel(
DiscussionTopicsFragment.ALL_POSTS -> {
getAllThreads(orderBy)
}
+
DiscussionTopicsFragment.FOLLOWING_POSTS -> {
getFollowingThreads(orderBy)
}
+
DiscussionTopicsFragment.TOPIC -> {
getThreads(
topicId,
@@ -125,9 +132,11 @@ class DiscussionThreadsViewModel(
DiscussionTopicsFragment.ALL_POSTS -> {
getAllThreads(lastOrderBy)
}
+
DiscussionTopicsFragment.FOLLOWING_POSTS -> {
getFollowingThreads(lastOrderBy)
}
+
DiscussionTopicsFragment.TOPIC -> {
getThreads(
topicId,
@@ -219,4 +228,21 @@ class DiscussionThreadsViewModel(
isLoading = false
}
}
+
+ fun markBlockCompleted(blockId: String) {
+ if (!isBlockAlreadyCompleted) {
+ viewModelScope.launch {
+ try {
+ isBlockAlreadyCompleted = true
+ interactor.markBlocksCompletion(
+ courseId,
+ listOf(blockId)
+ )
+ } catch (e: Exception) {
+ isBlockAlreadyCompleted = false
+ e.printStackTrace()
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/threads/FilterType.kt b/discussion/src/main/java/org/openedx/discussion/presentation/threads/FilterType.kt
similarity index 80%
rename from discussion/src/main/java/com/raccoongang/discussion/presentation/threads/FilterType.kt
rename to discussion/src/main/java/org/openedx/discussion/presentation/threads/FilterType.kt
index f419c4aa0..377717f45 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/threads/FilterType.kt
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/threads/FilterType.kt
@@ -1,6 +1,6 @@
-package com.raccoongang.discussion.presentation.threads
+package org.openedx.discussion.presentation.threads
-import com.raccoongang.discussion.R
+import org.openedx.discussion.R
enum class FilterType(
val textRes: Int,
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/threads/SortType.kt b/discussion/src/main/java/org/openedx/discussion/presentation/threads/SortType.kt
similarity index 82%
rename from discussion/src/main/java/com/raccoongang/discussion/presentation/threads/SortType.kt
rename to discussion/src/main/java/org/openedx/discussion/presentation/threads/SortType.kt
index 14646e601..1aeac0c33 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/threads/SortType.kt
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/threads/SortType.kt
@@ -1,6 +1,6 @@
-package com.raccoongang.discussion.presentation.threads
+package org.openedx.discussion.presentation.threads
-import com.raccoongang.discussion.R
+import org.openedx.discussion.R
enum class SortType(
val textRes: Int,
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/topics/DiscussionTopicsFragment.kt b/discussion/src/main/java/org/openedx/discussion/presentation/topics/DiscussionTopicsFragment.kt
similarity index 93%
rename from discussion/src/main/java/com/raccoongang/discussion/presentation/topics/DiscussionTopicsFragment.kt
rename to discussion/src/main/java/org/openedx/discussion/presentation/topics/DiscussionTopicsFragment.kt
index 4af2b3ac2..6d2d7d4f1 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/topics/DiscussionTopicsFragment.kt
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/topics/DiscussionTopicsFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.discussion.presentation.topics
+package org.openedx.discussion.presentation.topics
import android.content.res.Configuration
import android.os.Bundle
@@ -31,21 +31,21 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
-import com.raccoongang.core.FragmentViewType
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appShapes
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.discussion.domain.model.Topic
-import com.raccoongang.discussion.presentation.DiscussionRouter
-import com.raccoongang.discussion.presentation.ui.ThreadItemCategory
-import com.raccoongang.discussion.presentation.ui.TopicItem
+import org.openedx.core.FragmentViewType
+import org.openedx.core.UIMessage
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appShapes
+import org.openedx.core.ui.theme.appTypography
+import org.openedx.discussion.domain.model.Topic
+import org.openedx.discussion.presentation.DiscussionRouter
+import org.openedx.discussion.presentation.ui.ThreadItemCategory
+import org.openedx.discussion.presentation.ui.TopicItem
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.parameter.parametersOf
-import com.raccoongang.discussion.R as discussionR
+import org.openedx.discussion.R as discussionR
class DiscussionTopicsFragment : Fragment() {
@@ -56,6 +56,7 @@ class DiscussionTopicsFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+ viewModel.courseName = requireArguments().getString(ARG_COURSE_NAME, "")
viewModel.getCourseTopics()
}
@@ -66,7 +67,7 @@ class DiscussionTopicsFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val uiState by viewModel.uiState.observeAsState(DiscussionTopicsUIState.Loading)
@@ -81,6 +82,7 @@ class DiscussionTopicsFragment : Fragment() {
viewModel.updateCourseTopics()
},
onItemClick = { action, data, title ->
+ viewModel.discussionClickedEvent(action, data, title)
router.navigateToDiscussionThread(
requireActivity().supportFragmentManager,
action,
@@ -110,12 +112,15 @@ class DiscussionTopicsFragment : Fragment() {
const val FOLLOWING_POSTS = "Following"
private const val ARG_COURSE_ID = "argCourseID"
+ private const val ARG_COURSE_NAME = "argCourseName"
fun newInstance(
- courseId: String
+ courseId: String,
+ courseName: String
): DiscussionTopicsFragment {
val fragment = DiscussionTopicsFragment()
fragment.arguments = bundleOf(
- ARG_COURSE_ID to courseId
+ ARG_COURSE_ID to courseId,
+ ARG_COURSE_NAME to courseName,
)
return fragment
}
@@ -311,6 +316,7 @@ private fun DiscussionTopicsScreen(
}
}
}
+
DiscussionTopicsUIState.Loading -> {
Box(
Modifier
@@ -340,7 +346,7 @@ private fun DiscussionTopicsScreen(
@Preview(name = "NEXUS_5_Dark", device = Devices.NEXUS_5, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun DiscussionTopicsScreenPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
DiscussionTopicsScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
uiState = DiscussionTopicsUIState.Topics(listOf(mockTopic, mockTopic)),
@@ -358,7 +364,7 @@ private fun DiscussionTopicsScreenPreview() {
@Preview(name = "NEXUS_9_Dark", device = Devices.NEXUS_9, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun DiscussionTopicsScreenTabletPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
DiscussionTopicsScreen(
windowSize = WindowSize(WindowType.Medium, WindowType.Medium),
uiState = DiscussionTopicsUIState.Topics(listOf(mockTopic, mockTopic)),
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/topics/DiscussionTopicsUIState.kt b/discussion/src/main/java/org/openedx/discussion/presentation/topics/DiscussionTopicsUIState.kt
similarity index 60%
rename from discussion/src/main/java/com/raccoongang/discussion/presentation/topics/DiscussionTopicsUIState.kt
rename to discussion/src/main/java/org/openedx/discussion/presentation/topics/DiscussionTopicsUIState.kt
index 62452976f..c57f55e9b 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/topics/DiscussionTopicsUIState.kt
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/topics/DiscussionTopicsUIState.kt
@@ -1,6 +1,6 @@
-package com.raccoongang.discussion.presentation.topics
+package org.openedx.discussion.presentation.topics
-import com.raccoongang.discussion.domain.model.Topic
+import org.openedx.discussion.domain.model.Topic
sealed class DiscussionTopicsUIState {
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/topics/DiscussionTopicsViewModel.kt b/discussion/src/main/java/org/openedx/discussion/presentation/topics/DiscussionTopicsViewModel.kt
similarity index 64%
rename from discussion/src/main/java/com/raccoongang/discussion/presentation/topics/DiscussionTopicsViewModel.kt
rename to discussion/src/main/java/org/openedx/discussion/presentation/topics/DiscussionTopicsViewModel.kt
index 98e595dd2..9b2bafd48 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/topics/DiscussionTopicsViewModel.kt
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/topics/DiscussionTopicsViewModel.kt
@@ -1,20 +1,25 @@
-package com.raccoongang.discussion.presentation.topics
+package org.openedx.discussion.presentation.topics
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.R
-import com.raccoongang.core.SingleEventLiveData
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.extension.isInternetError
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.discussion.domain.interactor.DiscussionInteractor
+import org.openedx.core.BaseViewModel
+import org.openedx.core.R
+import org.openedx.core.SingleEventLiveData
+import org.openedx.core.UIMessage
+import org.openedx.core.extension.isInternetError
+import org.openedx.core.system.ResourceManager
+import org.openedx.discussion.domain.interactor.DiscussionInteractor
+import org.openedx.discussion.presentation.DiscussionAnalytics
+import org.openedx.discussion.presentation.topics.DiscussionTopicsFragment.Companion.ALL_POSTS
+import org.openedx.discussion.presentation.topics.DiscussionTopicsFragment.Companion.FOLLOWING_POSTS
+import org.openedx.discussion.presentation.topics.DiscussionTopicsFragment.Companion.TOPIC
import kotlinx.coroutines.launch
class DiscussionTopicsViewModel(
private val interactor: DiscussionInteractor,
private val resourceManager: ResourceManager,
+ private val analytics: DiscussionAnalytics,
val courseId: String
) : BaseViewModel() {
@@ -30,6 +35,8 @@ class DiscussionTopicsViewModel(
val isUpdating: LiveData
get() = _isUpdating
+ var courseName = ""
+
fun updateCourseTopics() {
viewModelScope.launch {
try {
@@ -70,4 +77,20 @@ class DiscussionTopicsViewModel(
}
}
}
+
+ fun discussionClickedEvent(action: String, data: String, title: String) {
+ when (action) {
+ ALL_POSTS -> {
+ analytics.discussionAllPostsClickedEvent(courseId, courseName)
+ }
+
+ FOLLOWING_POSTS -> {
+ analytics.discussionFollowingClickedEvent(courseId, courseName)
+ }
+
+ TOPIC -> {
+ analytics.discussionTopicClickedEvent(courseId, courseName, data, title)
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/discussion/src/main/java/com/raccoongang/discussion/presentation/ui/DiscussionUI.kt b/discussion/src/main/java/org/openedx/discussion/presentation/ui/DiscussionUI.kt
similarity index 93%
rename from discussion/src/main/java/com/raccoongang/discussion/presentation/ui/DiscussionUI.kt
rename to discussion/src/main/java/org/openedx/discussion/presentation/ui/DiscussionUI.kt
index add2cd76e..bd285b9d6 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/presentation/ui/DiscussionUI.kt
+++ b/discussion/src/main/java/org/openedx/discussion/presentation/ui/DiscussionUI.kt
@@ -1,6 +1,6 @@
@file:OptIn(ExperimentalComposeUiApi::class)
-package com.raccoongang.discussion.presentation.ui
+package org.openedx.discussion.presentation.ui
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
@@ -31,26 +31,26 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import coil.compose.rememberAsyncImagePainter
-import com.raccoongang.core.domain.model.ProfileImage
-import com.raccoongang.core.extension.TextConverter
-import com.raccoongang.core.ui.AutoSizeText
-import com.raccoongang.core.ui.HyperlinkImageText
-import com.raccoongang.core.ui.IconText
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appShapes
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.core.utils.TimeUtils
-import com.raccoongang.discussion.R
-import com.raccoongang.discussion.domain.model.DiscussionComment
-import com.raccoongang.discussion.domain.model.DiscussionType
-import com.raccoongang.discussion.domain.model.Topic
-import com.raccoongang.discussion.presentation.comments.DiscussionCommentsFragment
+import org.openedx.core.domain.model.ProfileImage
+import org.openedx.core.extension.TextConverter
+import org.openedx.core.ui.AutoSizeText
+import org.openedx.core.ui.HyperlinkImageText
+import org.openedx.core.ui.IconText
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appShapes
+import org.openedx.core.ui.theme.appTypography
+import org.openedx.core.utils.TimeUtils
+import org.openedx.discussion.R
+import org.openedx.discussion.domain.model.DiscussionComment
+import org.openedx.discussion.domain.model.DiscussionType
+import org.openedx.discussion.domain.model.Topic
+import org.openedx.discussion.presentation.comments.DiscussionCommentsFragment
@Composable
fun ThreadMainItem(
modifier: Modifier,
- thread: com.raccoongang.discussion.domain.model.Thread,
+ thread: org.openedx.discussion.domain.model.Thread,
onClick: (String, Boolean) -> Unit,
) {
val profileImageUrl = thread.users?.get(thread.author)?.image?.imageUrlFull ?: ""
@@ -85,7 +85,7 @@ fun ThreadMainItem(
Image(
painter = rememberAsyncImagePainter(
model = profileImageUrl,
- error = painterResource(id = com.raccoongang.core.R.drawable.core_ic_default_profile_picture)
+ error = painterResource(id = org.openedx.core.R.drawable.core_ic_default_profile_picture)
),
contentDescription = null,
modifier = Modifier
@@ -218,7 +218,7 @@ fun CommentItem(
Image(
painter = rememberAsyncImagePainter(
model = profileImageUrl,
- error = painterResource(id = com.raccoongang.core.R.drawable.core_ic_default_profile_picture)
+ error = painterResource(id = org.openedx.core.R.drawable.core_ic_default_profile_picture)
),
contentDescription = null,
modifier = Modifier
@@ -355,7 +355,7 @@ fun CommentMainItem(
Image(
painter = rememberAsyncImagePainter(
model = profileImageUrl,
- error = painterResource(id = com.raccoongang.core.R.drawable.core_ic_default_profile_picture)
+ error = painterResource(id = org.openedx.core.R.drawable.core_ic_default_profile_picture)
),
contentDescription = null,
modifier = Modifier
@@ -429,8 +429,8 @@ fun CommentMainItem(
@Composable
fun ThreadItem(
- thread: com.raccoongang.discussion.domain.model.Thread,
- onClick: (com.raccoongang.discussion.domain.model.Thread) -> Unit,
+ thread: org.openedx.discussion.domain.model.Thread,
+ onClick: (org.openedx.discussion.domain.model.Thread) -> Unit,
) {
val icon = when (thread.type) {
DiscussionType.DISCUSSION -> painterResource(id = R.drawable.discussion_ic_discussion)
@@ -598,7 +598,7 @@ fun TopicItem(
@Preview
@Composable
private fun TopicItemPreview() {
- NewEdxTheme() {
+ OpenEdXTheme() {
TopicItem(topic = mockTopic,
onClick = { _, _ ->
@@ -610,7 +610,7 @@ private fun TopicItemPreview() {
@Preview(uiMode = UI_MODE_NIGHT_YES)
@Composable
private fun ThreadItemPreview() {
- NewEdxTheme() {
+ OpenEdXTheme() {
ThreadItem(
thread = mockThread,
onClick = {})
@@ -620,7 +620,7 @@ private fun ThreadItemPreview() {
@Preview
@Composable
private fun CommentItemPreview() {
- NewEdxTheme() {
+ OpenEdXTheme() {
CommentItem(
modifier = Modifier.fillMaxWidth(),
comment = mockComment,
@@ -666,7 +666,7 @@ private val mockComment = DiscussionComment(
mapOf()
)
-private val mockThread = com.raccoongang.discussion.domain.model.Thread(
+private val mockThread = org.openedx.discussion.domain.model.Thread(
"",
"",
"",
diff --git a/discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionCommentAdded.kt b/discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionCommentAdded.kt
new file mode 100644
index 000000000..d90f329a8
--- /dev/null
+++ b/discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionCommentAdded.kt
@@ -0,0 +1,8 @@
+package org.openedx.discussion.system.notifier
+
+import org.openedx.discussion.domain.model.DiscussionComment
+
+
+data class DiscussionCommentAdded(
+ val comment: DiscussionComment
+) : DiscussionEvent
\ No newline at end of file
diff --git a/discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionCommentDataChanged.kt b/discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionCommentDataChanged.kt
new file mode 100644
index 000000000..04d46aa1e
--- /dev/null
+++ b/discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionCommentDataChanged.kt
@@ -0,0 +1,5 @@
+package org.openedx.discussion.system.notifier
+
+import org.openedx.discussion.domain.model.DiscussionComment
+
+class DiscussionCommentDataChanged(val discussionComment: DiscussionComment) : DiscussionEvent
\ No newline at end of file
diff --git a/discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionEvent.kt b/discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionEvent.kt
new file mode 100644
index 000000000..abd2c1891
--- /dev/null
+++ b/discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionEvent.kt
@@ -0,0 +1,4 @@
+package org.openedx.discussion.system.notifier
+
+interface DiscussionEvent {
+}
\ No newline at end of file
diff --git a/discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionNotifier.kt b/discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionNotifier.kt
similarity index 93%
rename from discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionNotifier.kt
rename to discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionNotifier.kt
index fd0936672..2d1d01206 100644
--- a/discussion/src/main/java/com/raccoongang/discussion/system/notifier/DiscussionNotifier.kt
+++ b/discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionNotifier.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.discussion.system.notifier
+package org.openedx.discussion.system.notifier
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
diff --git a/discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionResponseAdded.kt b/discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionResponseAdded.kt
new file mode 100644
index 000000000..896562262
--- /dev/null
+++ b/discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionResponseAdded.kt
@@ -0,0 +1,7 @@
+package org.openedx.discussion.system.notifier
+
+import org.openedx.discussion.domain.model.DiscussionComment
+
+data class DiscussionResponseAdded(
+ val comment: DiscussionComment
+) : DiscussionEvent
diff --git a/discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionThreadAdded.kt b/discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionThreadAdded.kt
new file mode 100644
index 000000000..2b2f23525
--- /dev/null
+++ b/discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionThreadAdded.kt
@@ -0,0 +1,3 @@
+package org.openedx.discussion.system.notifier
+
+class DiscussionThreadAdded : DiscussionEvent
\ No newline at end of file
diff --git a/discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionThreadDataChanged.kt b/discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionThreadDataChanged.kt
new file mode 100644
index 000000000..752814095
--- /dev/null
+++ b/discussion/src/main/java/org/openedx/discussion/system/notifier/DiscussionThreadDataChanged.kt
@@ -0,0 +1,5 @@
+package org.openedx.discussion.system.notifier
+
+class DiscussionThreadDataChanged(
+ val thread: org.openedx.discussion.domain.model.Thread
+) : DiscussionEvent
\ No newline at end of file
diff --git a/discussion/src/main/res/drawable/discussion_ic_empty.xml b/discussion/src/main/res/drawable/discussion_ic_empty.xml
new file mode 100644
index 000000000..7258379ee
--- /dev/null
+++ b/discussion/src/main/res/drawable/discussion_ic_empty.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/discussion/src/main/res/values-uk/strings.xml b/discussion/src/main/res/values-uk/strings.xml
index a396d06c4..212932f49 100644
--- a/discussion/src/main/res/values-uk/strings.xml
+++ b/discussion/src/main/res/values-uk/strings.xml
@@ -9,7 +9,7 @@
Остання активність
Найбільша активність
Найбільше голосів
- Створити нову публікацію
+ Створити дискусію
Додати відповідь
Останнє повідомлення: %1$s
Стежити
@@ -33,6 +33,8 @@
Результати пошуку
Почніть вводити, щоб знайти тему
anonymous
+ Ще немає обговорень
+ Натисніть кнопку нижче, щоб створити перше обговорення.
- %1$d голос
diff --git a/discussion/src/main/res/values/strings.xml b/discussion/src/main/res/values/strings.xml
index a4f18d95c..8d66fc99a 100644
--- a/discussion/src/main/res/values/strings.xml
+++ b/discussion/src/main/res/values/strings.xml
@@ -8,7 +8,7 @@
Recent activity
Most activity
Most votes
- Create new post
+ Create discussion
Add a response
Last post: %1$s
Follow
@@ -32,6 +32,8 @@
Search results
Start typing to find the thread
anonymous
+ No discussions yet
+ Click the button below to create your first discussion.
diff --git a/discussion/src/test/java/com/raccoongang/discussion/presentation/comments/DiscussionCommentsViewModelTest.kt b/discussion/src/test/java/org/openedx/discussion/presentation/comments/DiscussionCommentsViewModelTest.kt
similarity index 97%
rename from discussion/src/test/java/com/raccoongang/discussion/presentation/comments/DiscussionCommentsViewModelTest.kt
rename to discussion/src/test/java/org/openedx/discussion/presentation/comments/DiscussionCommentsViewModelTest.kt
index dad00d8da..62a569a3e 100644
--- a/discussion/src/test/java/com/raccoongang/discussion/presentation/comments/DiscussionCommentsViewModelTest.kt
+++ b/discussion/src/test/java/org/openedx/discussion/presentation/comments/DiscussionCommentsViewModelTest.kt
@@ -1,20 +1,20 @@
-package com.raccoongang.discussion.presentation.comments
+package org.openedx.discussion.presentation.comments
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.domain.model.Pagination
-import com.raccoongang.core.extension.TextConverter
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.discussion.domain.interactor.DiscussionInteractor
-import com.raccoongang.discussion.domain.model.CommentsData
-import com.raccoongang.discussion.domain.model.DiscussionComment
-import com.raccoongang.discussion.domain.model.DiscussionType
-import com.raccoongang.discussion.system.notifier.*
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.domain.model.Pagination
+import org.openedx.core.extension.TextConverter
+import org.openedx.core.system.ResourceManager
+import org.openedx.discussion.domain.interactor.DiscussionInteractor
+import org.openedx.discussion.domain.model.CommentsData
+import org.openedx.discussion.domain.model.DiscussionComment
+import org.openedx.discussion.domain.model.DiscussionType
+import org.openedx.discussion.system.notifier.*
import io.mockk.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -44,7 +44,7 @@ class DiscussionCommentsViewModelTest {
//region mockThread
- val mockThread = com.raccoongang.discussion.domain.model.Thread(
+ val mockThread = org.openedx.discussion.domain.model.Thread(
"",
"",
"",
@@ -121,7 +121,7 @@ class DiscussionCommentsViewModelTest {
Dispatchers.setMain(dispatcher)
every { resourceManager.getString(R.string.core_error_no_connection) } returns noInternet
every { resourceManager.getString(R.string.core_error_unknown_error) } returns somethingWrong
- every { resourceManager.getString(com.raccoongang.discussion.R.string.discussion_comment_added) } returns commentAddedSuccessfully
+ every { resourceManager.getString(org.openedx.discussion.R.string.discussion_comment_added) } returns commentAddedSuccessfully
}
@After
diff --git a/discussion/src/test/java/com/raccoongang/discussion/presentation/responses/DiscussionResponsesViewModelTest.kt b/discussion/src/test/java/org/openedx/discussion/presentation/responses/DiscussionResponsesViewModelTest.kt
similarity index 95%
rename from discussion/src/test/java/com/raccoongang/discussion/presentation/responses/DiscussionResponsesViewModelTest.kt
rename to discussion/src/test/java/org/openedx/discussion/presentation/responses/DiscussionResponsesViewModelTest.kt
index d586e431d..039c34e62 100644
--- a/discussion/src/test/java/com/raccoongang/discussion/presentation/responses/DiscussionResponsesViewModelTest.kt
+++ b/discussion/src/test/java/org/openedx/discussion/presentation/responses/DiscussionResponsesViewModelTest.kt
@@ -1,17 +1,17 @@
-package com.raccoongang.discussion.presentation.responses
+package org.openedx.discussion.presentation.responses
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.domain.model.Pagination
-import com.raccoongang.core.extension.LinkedImageText
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.discussion.domain.interactor.DiscussionInteractor
-import com.raccoongang.discussion.domain.model.CommentsData
-import com.raccoongang.discussion.domain.model.DiscussionComment
-import com.raccoongang.discussion.domain.model.DiscussionType
-import com.raccoongang.discussion.system.notifier.DiscussionNotifier
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.domain.model.Pagination
+import org.openedx.core.extension.LinkedImageText
+import org.openedx.core.system.ResourceManager
+import org.openedx.discussion.domain.interactor.DiscussionInteractor
+import org.openedx.discussion.domain.model.CommentsData
+import org.openedx.discussion.domain.model.DiscussionComment
+import org.openedx.discussion.domain.model.DiscussionType
+import org.openedx.discussion.system.notifier.DiscussionNotifier
import io.mockk.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -40,7 +40,7 @@ class DiscussionResponsesViewModelTest {
//region mockThread
- val mockThread = com.raccoongang.discussion.domain.model.Thread(
+ val mockThread = org.openedx.discussion.domain.model.Thread(
"",
"",
"",
@@ -117,7 +117,7 @@ class DiscussionResponsesViewModelTest {
Dispatchers.setMain(dispatcher)
every { resourceManager.getString(R.string.core_error_no_connection) } returns noInternet
every { resourceManager.getString(R.string.core_error_unknown_error) } returns somethingWrong
- every { resourceManager.getString(com.raccoongang.discussion.R.string.discussion_comment_added) } returns commentAddedSuccessfully
+ every { resourceManager.getString(org.openedx.discussion.R.string.discussion_comment_added) } returns commentAddedSuccessfully
}
@After
diff --git a/discussion/src/test/java/com/raccoongang/discussion/presentation/search/DiscussionSearchThreadViewModelTest.kt b/discussion/src/test/java/org/openedx/discussion/presentation/search/DiscussionSearchThreadViewModelTest.kt
similarity index 93%
rename from discussion/src/test/java/com/raccoongang/discussion/presentation/search/DiscussionSearchThreadViewModelTest.kt
rename to discussion/src/test/java/org/openedx/discussion/presentation/search/DiscussionSearchThreadViewModelTest.kt
index fb2753180..57d35df20 100644
--- a/discussion/src/test/java/com/raccoongang/discussion/presentation/search/DiscussionSearchThreadViewModelTest.kt
+++ b/discussion/src/test/java/org/openedx/discussion/presentation/search/DiscussionSearchThreadViewModelTest.kt
@@ -1,19 +1,19 @@
-package com.raccoongang.discussion.presentation.search
+package org.openedx.discussion.presentation.search
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.Pagination
-import com.raccoongang.core.extension.TextConverter
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.discussion.domain.interactor.DiscussionInteractor
-import com.raccoongang.discussion.domain.model.DiscussionType
-import com.raccoongang.discussion.domain.model.ThreadsData
-import com.raccoongang.discussion.system.notifier.DiscussionNotifier
-import com.raccoongang.discussion.system.notifier.DiscussionThreadDataChanged
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.Pagination
+import org.openedx.core.extension.TextConverter
+import org.openedx.core.system.ResourceManager
+import org.openedx.discussion.domain.interactor.DiscussionInteractor
+import org.openedx.discussion.domain.model.DiscussionType
+import org.openedx.discussion.domain.model.ThreadsData
+import org.openedx.discussion.system.notifier.DiscussionNotifier
+import org.openedx.discussion.system.notifier.DiscussionThreadDataChanged
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
@@ -48,7 +48,7 @@ class DiscussionSearchThreadViewModelTest {
//region thread
- private val mockThread = com.raccoongang.discussion.domain.model.Thread(
+ private val mockThread = org.openedx.discussion.domain.model.Thread(
"",
"",
"",
diff --git a/discussion/src/test/java/com/raccoongang/discussion/presentation/threads/DiscussionAddThreadViewModelTest.kt b/discussion/src/test/java/org/openedx/discussion/presentation/threads/DiscussionAddThreadViewModelTest.kt
similarity index 88%
rename from discussion/src/test/java/com/raccoongang/discussion/presentation/threads/DiscussionAddThreadViewModelTest.kt
rename to discussion/src/test/java/org/openedx/discussion/presentation/threads/DiscussionAddThreadViewModelTest.kt
index f0559e59c..f8441d28d 100644
--- a/discussion/src/test/java/com/raccoongang/discussion/presentation/threads/DiscussionAddThreadViewModelTest.kt
+++ b/discussion/src/test/java/org/openedx/discussion/presentation/threads/DiscussionAddThreadViewModelTest.kt
@@ -1,19 +1,19 @@
-package com.raccoongang.discussion.presentation.threads
+package org.openedx.discussion.presentation.threads
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.domain.model.ProfileImage
-import com.raccoongang.core.extension.TextConverter
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.discussion.domain.interactor.DiscussionInteractor
-import com.raccoongang.discussion.domain.model.DiscussionComment
-import com.raccoongang.discussion.domain.model.DiscussionProfile
-import com.raccoongang.discussion.domain.model.DiscussionType
-import com.raccoongang.discussion.domain.model.Topic
-import com.raccoongang.discussion.system.notifier.DiscussionNotifier
-import com.raccoongang.discussion.system.notifier.DiscussionThreadAdded
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.domain.model.ProfileImage
+import org.openedx.core.extension.TextConverter
+import org.openedx.core.system.ResourceManager
+import org.openedx.discussion.domain.interactor.DiscussionInteractor
+import org.openedx.discussion.domain.model.DiscussionComment
+import org.openedx.discussion.domain.model.DiscussionProfile
+import org.openedx.discussion.domain.model.DiscussionType
+import org.openedx.discussion.domain.model.Topic
+import org.openedx.discussion.system.notifier.DiscussionNotifier
+import org.openedx.discussion.system.notifier.DiscussionThreadAdded
import io.mockk.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -44,7 +44,7 @@ class DiscussionAddThreadViewModelTest {
//region mockThread
- val mockThread = com.raccoongang.discussion.domain.model.Thread(
+ val mockThread = org.openedx.discussion.domain.model.Thread(
"",
"",
"",
diff --git a/discussion/src/test/java/com/raccoongang/discussion/presentation/threads/DiscussionThreadsViewModelTest.kt b/discussion/src/test/java/org/openedx/discussion/presentation/threads/DiscussionThreadsViewModelTest.kt
similarity index 83%
rename from discussion/src/test/java/com/raccoongang/discussion/presentation/threads/DiscussionThreadsViewModelTest.kt
rename to discussion/src/test/java/org/openedx/discussion/presentation/threads/DiscussionThreadsViewModelTest.kt
index 14887621d..e34df3ed8 100644
--- a/discussion/src/test/java/com/raccoongang/discussion/presentation/threads/DiscussionThreadsViewModelTest.kt
+++ b/discussion/src/test/java/org/openedx/discussion/presentation/threads/DiscussionThreadsViewModelTest.kt
@@ -1,21 +1,21 @@
-package com.raccoongang.discussion.presentation.threads
+package org.openedx.discussion.presentation.threads
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.Pagination
-import com.raccoongang.core.extension.TextConverter
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.discussion.domain.interactor.DiscussionInteractor
-import com.raccoongang.discussion.domain.model.DiscussionType
-import com.raccoongang.discussion.domain.model.ThreadsData
-import com.raccoongang.discussion.presentation.topics.DiscussionTopicsFragment
-import com.raccoongang.discussion.system.notifier.DiscussionNotifier
-import com.raccoongang.discussion.system.notifier.DiscussionThreadAdded
-import com.raccoongang.discussion.system.notifier.DiscussionThreadDataChanged
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.Pagination
+import org.openedx.core.extension.TextConverter
+import org.openedx.core.system.ResourceManager
+import org.openedx.discussion.domain.interactor.DiscussionInteractor
+import org.openedx.discussion.domain.model.DiscussionType
+import org.openedx.discussion.domain.model.ThreadsData
+import org.openedx.discussion.presentation.topics.DiscussionTopicsFragment
+import org.openedx.discussion.system.notifier.DiscussionNotifier
+import org.openedx.discussion.system.notifier.DiscussionThreadAdded
+import org.openedx.discussion.system.notifier.DiscussionThreadDataChanged
import io.mockk.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -47,7 +47,7 @@ class DiscussionThreadsViewModelTest {
//region mockThread
- val mockThread = com.raccoongang.discussion.domain.model.Thread(
+ val mockThread = org.openedx.discussion.domain.model.Thread(
"",
"",
"",
@@ -84,7 +84,7 @@ class DiscussionThreadsViewModelTest {
//endregion
- private val threads = listOf(
+ private val threads = listOf(
mockThread.copy(id = "0"),
mockThread.copy(id = "1"),
mockThread.copy(id = "2")
@@ -105,13 +105,6 @@ class DiscussionThreadsViewModelTest {
@Test
fun `getThreadByType AllThreads no internet connection`() = runTest {
- val viewModel = DiscussionThreadsViewModel(
- interactor,
- resourceManager,
- notifier,
- "",
- DiscussionTopicsFragment.ALL_POSTS
- )
coEvery {
interactor.getAllThreads(
any(),
@@ -120,7 +113,14 @@ class DiscussionThreadsViewModelTest {
any()
)
} throws UnknownHostException()
- viewModel.getThreadByType("")
+ val viewModel = DiscussionThreadsViewModel(
+ interactor,
+ resourceManager,
+ notifier,
+ "",
+ "",
+ DiscussionTopicsFragment.ALL_POSTS
+ )
advanceUntilIdle()
coVerify(exactly = 1) { interactor.getAllThreads(any(), any(), any(), any()) }
@@ -138,10 +138,10 @@ class DiscussionThreadsViewModelTest {
resourceManager,
notifier,
"",
+ "",
DiscussionTopicsFragment.ALL_POSTS
)
coEvery { interactor.getAllThreads(any(), any(), any(), any()) } throws Exception()
- viewModel.getThreadByType("")
advanceUntilIdle()
coVerify(exactly = 1) { interactor.getAllThreads(any(), any(), any(), any()) }
@@ -154,24 +154,24 @@ class DiscussionThreadsViewModelTest {
@Test
fun `getThreadByType AllThreads success`() = runTest {
- val viewModel = DiscussionThreadsViewModel(
- interactor,
- resourceManager,
- notifier,
- "",
- DiscussionTopicsFragment.ALL_POSTS
- )
- coEvery { interactor.getAllThreads("", "", null, range(1, 2)) } returns ThreadsData(
+ coEvery { interactor.getAllThreads("", any(), null, range(1, 2)) } returns ThreadsData(
threads,
"",
Pagination(10, "2", 4, "1")
)
- coEvery { interactor.getAllThreads("", "", null, eq(3)) } returns ThreadsData(
+ coEvery { interactor.getAllThreads("", any(), null, eq(3)) } returns ThreadsData(
threads,
"",
Pagination(10, "", 4, "1")
)
- viewModel.getThreadByType("")
+ val viewModel = DiscussionThreadsViewModel(
+ interactor,
+ resourceManager,
+ notifier,
+ "",
+ "",
+ DiscussionTopicsFragment.ALL_POSTS
+ )
advanceUntilIdle()
coVerify(exactly = 1) { interactor.getAllThreads(any(), any(), any(), any()) }
@@ -183,13 +183,6 @@ class DiscussionThreadsViewModelTest {
@Test
fun `getThreadByType FollowingPosts no internet connection`() = runTest {
- val viewModel = DiscussionThreadsViewModel(
- interactor,
- resourceManager,
- notifier,
- "",
- DiscussionTopicsFragment.FOLLOWING_POSTS
- )
coEvery {
interactor.getFollowingThreads(
any(),
@@ -199,7 +192,14 @@ class DiscussionThreadsViewModelTest {
any()
)
} throws UnknownHostException()
- viewModel.getThreadByType("")
+ val viewModel = DiscussionThreadsViewModel(
+ interactor,
+ resourceManager,
+ notifier,
+ "",
+ "",
+ DiscussionTopicsFragment.FOLLOWING_POSTS
+ )
advanceUntilIdle()
coVerify(exactly = 1) { interactor.getFollowingThreads(any(), any(), any(), any(), any()) }
@@ -217,6 +217,7 @@ class DiscussionThreadsViewModelTest {
resourceManager,
notifier,
"",
+ "",
DiscussionTopicsFragment.FOLLOWING_POSTS
)
coEvery {
@@ -228,7 +229,6 @@ class DiscussionThreadsViewModelTest {
any()
)
} throws Exception()
- viewModel.getThreadByType("")
advanceUntilIdle()
coVerify(exactly = 1) { interactor.getFollowingThreads(any(), any(), any(), any(), any()) }
@@ -241,18 +241,11 @@ class DiscussionThreadsViewModelTest {
@Test
fun `getThreadByType FollowingPosts success`() = runTest {
- val viewModel = DiscussionThreadsViewModel(
- interactor,
- resourceManager,
- notifier,
- "",
- DiscussionTopicsFragment.FOLLOWING_POSTS
- )
coEvery {
interactor.getFollowingThreads(
"",
any(),
- "",
+ any(),
null,
range(1, 2)
)
@@ -261,12 +254,27 @@ class DiscussionThreadsViewModelTest {
"",
Pagination(10, "2", 4, "1")
)
- coEvery { interactor.getFollowingThreads("", any(), "", null, eq(3)) } returns ThreadsData(
+ coEvery {
+ interactor.getFollowingThreads(
+ "",
+ any(),
+ any(),
+ null,
+ eq(3)
+ )
+ } returns ThreadsData(
threads,
"",
Pagination(10, "", 4, "1")
)
- viewModel.getThreadByType("")
+ val viewModel = DiscussionThreadsViewModel(
+ interactor,
+ resourceManager,
+ notifier,
+ "",
+ "",
+ DiscussionTopicsFragment.FOLLOWING_POSTS
+ )
advanceUntilIdle()
coVerify(exactly = 1) { interactor.getFollowingThreads(any(), any(), any(), any(), any()) }
@@ -278,13 +286,6 @@ class DiscussionThreadsViewModelTest {
@Test
fun `getThreadByType Topic no internet connection`() = runTest {
- val viewModel = DiscussionThreadsViewModel(
- interactor,
- resourceManager,
- notifier,
- "",
- DiscussionTopicsFragment.TOPIC
- )
coEvery {
interactor.getThreads(
any(),
@@ -294,7 +295,14 @@ class DiscussionThreadsViewModelTest {
any()
)
} throws UnknownHostException()
- viewModel.getThreadByType("")
+ val viewModel = DiscussionThreadsViewModel(
+ interactor,
+ resourceManager,
+ notifier,
+ "",
+ "",
+ DiscussionTopicsFragment.TOPIC
+ )
advanceUntilIdle()
coVerify(exactly = 1) { interactor.getThreads(any(), any(), any(), any(), any()) }
@@ -312,10 +320,10 @@ class DiscussionThreadsViewModelTest {
resourceManager,
notifier,
"",
+ "",
DiscussionTopicsFragment.TOPIC
)
coEvery { interactor.getThreads(any(), any(), any(), any(), any()) } throws Exception()
- viewModel.getThreadByType("")
advanceUntilIdle()
coVerify(exactly = 1) { interactor.getThreads(any(), any(), any(), any(), any()) }
@@ -328,24 +336,24 @@ class DiscussionThreadsViewModelTest {
@Test
fun `getThreadByType Topic success`() = runTest {
- val viewModel = DiscussionThreadsViewModel(
- interactor,
- resourceManager,
- notifier,
- "",
- DiscussionTopicsFragment.TOPIC
- )
- coEvery { interactor.getThreads("", any(), "", null, range(1, 2)) } returns ThreadsData(
+ coEvery { interactor.getThreads("", any(), any(), null, range(1, 2)) } returns ThreadsData(
threads,
"",
Pagination(10, "2", 4, "1")
)
- coEvery { interactor.getThreads("", any(), "", null, eq(3)) } returns ThreadsData(
+ coEvery { interactor.getThreads("", any(), any(), null, eq(3)) } returns ThreadsData(
threads,
"",
Pagination(10, "", 4, "1")
)
- viewModel.getThreadByType("")
+ val viewModel = DiscussionThreadsViewModel(
+ interactor,
+ resourceManager,
+ notifier,
+ "",
+ "",
+ DiscussionTopicsFragment.TOPIC
+ )
advanceUntilIdle()
coVerify(exactly = 1) { interactor.getThreads(any(), any(), any(), any(), any()) }
@@ -362,12 +370,13 @@ class DiscussionThreadsViewModelTest {
resourceManager,
notifier,
"",
+ "",
DiscussionTopicsFragment.TOPIC
)
- coEvery { interactor.getThreads(any(),any(), any(), any(), any()) } returns ThreadsData(
+ coEvery { interactor.getThreads(any(), any(), any(), any(), any()) } returns ThreadsData(
threads,
"",
- pagination = Pagination(10,"", 2,"")
+ pagination = Pagination(10, "", 2, "")
)
viewModel.filterThreads(FilterType.ALL_POSTS.value)
advanceUntilIdle()
@@ -381,12 +390,13 @@ class DiscussionThreadsViewModelTest {
resourceManager,
notifier,
"",
+ "",
DiscussionTopicsFragment.TOPIC
)
- coEvery { interactor.getThreads(any(),any(), any(), any(), any()) } returns ThreadsData(
+ coEvery { interactor.getThreads(any(), any(), any(), any(), any()) } returns ThreadsData(
threads,
"",
- pagination = Pagination(10,"", 2,"")
+ pagination = Pagination(10, "", 2, "")
)
viewModel.filterThreads(FilterType.UNREAD.value)
advanceUntilIdle()
@@ -400,12 +410,13 @@ class DiscussionThreadsViewModelTest {
resourceManager,
notifier,
"",
+ "",
DiscussionTopicsFragment.TOPIC
)
- coEvery { interactor.getThreads(any(),any(), any(), any(), any()) } returns ThreadsData(
+ coEvery { interactor.getThreads(any(), any(), any(), any(), any()) } returns ThreadsData(
threads,
"",
- pagination = Pagination(10,"", 2,"")
+ pagination = Pagination(10, "", 2, "")
)
viewModel.filterThreads(FilterType.UNANSWERED.value)
advanceUntilIdle()
@@ -414,27 +425,28 @@ class DiscussionThreadsViewModelTest {
@Test
fun `updateThread Topic success`() = runTest {
- val viewModel = DiscussionThreadsViewModel(
- interactor,
- resourceManager,
- notifier,
- "",
- DiscussionTopicsFragment.TOPIC
- )
- coEvery { interactor.getThreads("", any(), "", null, range(1, 2)) } returns ThreadsData(
+ coEvery { interactor.getThreads("", any(), any(), null, range(1, 2)) } returns ThreadsData(
threads,
"",
Pagination(10, "2", 4, "1")
)
- coEvery { interactor.getThreads("", any(), "", null, eq(3)) } returns ThreadsData(
+ coEvery { interactor.getThreads("", any(), any(), null, eq(3)) } returns ThreadsData(
threads,
"",
Pagination(10, "", 4, "1")
)
+ val viewModel = DiscussionThreadsViewModel(
+ interactor,
+ resourceManager,
+ notifier,
+ "",
+ "",
+ DiscussionTopicsFragment.TOPIC
+ )
viewModel.updateThread("")
advanceUntilIdle()
- coVerify(exactly = 1) { interactor.getThreads(any(), any(), any(), any(), any()) }
+ coVerify(exactly = 2) { interactor.getThreads(any(), any(), any(), any(), any()) }
assert(viewModel.uiMessage.value == null)
assert(viewModel.isUpdating.value == false)
@@ -443,13 +455,6 @@ class DiscussionThreadsViewModelTest {
@Test
fun `DiscussionThreadAdded notifier test`() = runTest {
- val viewModel = DiscussionThreadsViewModel(
- interactor,
- resourceManager,
- notifier,
- "",
- DiscussionTopicsFragment.TOPIC
- )
coEvery { interactor.getThreads("", any(), any(), null, range(1, 2)) } returns ThreadsData(
threads,
"",
@@ -466,6 +471,15 @@ class DiscussionThreadsViewModelTest {
delay(100)
emit(DiscussionThreadAdded())
}
+ val viewModel = DiscussionThreadsViewModel(
+ interactor,
+ resourceManager,
+ notifier,
+ "",
+ "",
+ DiscussionTopicsFragment.TOPIC
+ )
+
val mockLifeCycleOwner: LifecycleOwner = mockk()
val lifecycleRegistry = LifecycleRegistry(mockLifeCycleOwner)
@@ -475,24 +489,17 @@ class DiscussionThreadsViewModelTest {
viewModel.updateThread("date")
advanceUntilIdle()
- coVerify(exactly = 2) { interactor.getThreads(any(), any(), any(), any(), any()) }
+ coVerify(exactly = 3) { interactor.getThreads(any(), any(), any(), any(), any()) }
}
@Test
fun `DiscussionThreadDataChanged notifier test`() = runTest {
- val viewModel = DiscussionThreadsViewModel(
- interactor,
- resourceManager,
- notifier,
- "",
- DiscussionTopicsFragment.TOPIC
- )
- coEvery { interactor.getThreads("", any(), any(), null,range(1, 2)) } returns ThreadsData(
+ coEvery { interactor.getThreads("", any(), any(), null, range(1, 2)) } returns ThreadsData(
threads,
"",
Pagination(10, "2", 4, "1")
)
- coEvery { interactor.getThreads("", any(), any(), null,eq(3)) } returns ThreadsData(
+ coEvery { interactor.getThreads("", any(), any(), null, eq(3)) } returns ThreadsData(
threads,
"",
Pagination(10, "", 4, "1")
@@ -503,6 +510,14 @@ class DiscussionThreadsViewModelTest {
delay(100)
emit(DiscussionThreadDataChanged(mockThread.copy(id = "1")))
}
+ val viewModel = DiscussionThreadsViewModel(
+ interactor,
+ resourceManager,
+ notifier,
+ "",
+ "",
+ DiscussionTopicsFragment.TOPIC
+ )
val mockLifeCycleOwner: LifecycleOwner = mockk()
val lifecycleRegistry = LifecycleRegistry(mockLifeCycleOwner)
@@ -512,7 +527,7 @@ class DiscussionThreadsViewModelTest {
viewModel.updateThread("date")
advanceUntilIdle()
- coVerify(exactly = 1) { interactor.getThreads(any(), any(), any(), any(), any()) }
+ coVerify(exactly = 2) { interactor.getThreads(any(), any(), any(), any(), any()) }
}
diff --git a/discussion/src/test/java/com/raccoongang/discussion/presentation/topics/DiscussionTopicsViewModelTest.kt b/discussion/src/test/java/org/openedx/discussion/presentation/topics/DiscussionTopicsViewModelTest.kt
similarity index 89%
rename from discussion/src/test/java/com/raccoongang/discussion/presentation/topics/DiscussionTopicsViewModelTest.kt
rename to discussion/src/test/java/org/openedx/discussion/presentation/topics/DiscussionTopicsViewModelTest.kt
index 3c4b5b9c0..2e7998a73 100644
--- a/discussion/src/test/java/com/raccoongang/discussion/presentation/topics/DiscussionTopicsViewModelTest.kt
+++ b/discussion/src/test/java/org/openedx/discussion/presentation/topics/DiscussionTopicsViewModelTest.kt
@@ -1,10 +1,11 @@
-package com.raccoongang.discussion.presentation.topics
+package org.openedx.discussion.presentation.topics
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.discussion.domain.interactor.DiscussionInteractor
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.system.ResourceManager
+import org.openedx.discussion.domain.interactor.DiscussionInteractor
+import org.openedx.discussion.presentation.DiscussionAnalytics
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
@@ -30,6 +31,7 @@ class DiscussionTopicsViewModelTest {
private val resourceManager = mockk()
private val interactor = mockk()
+ private val analytics = mockk()
private val noInternet = "Slow or no internet connection"
private val somethingWrong = "Something went wrong"
@@ -48,7 +50,7 @@ class DiscussionTopicsViewModelTest {
@Test
fun `getCourseTopics no internet exception`() = runTest {
- val viewModel = DiscussionTopicsViewModel(interactor, resourceManager, "")
+ val viewModel = DiscussionTopicsViewModel(interactor, resourceManager, analytics,"")
coEvery { interactor.getCourseTopics(any()) } throws UnknownHostException()
viewModel.getCourseTopics()
@@ -63,7 +65,7 @@ class DiscussionTopicsViewModelTest {
@Test
fun `getCourseTopics unknown exception`() = runTest {
- val viewModel = DiscussionTopicsViewModel(interactor, resourceManager, "")
+ val viewModel = DiscussionTopicsViewModel(interactor, resourceManager, analytics,"")
coEvery { interactor.getCourseTopics(any()) } throws Exception()
viewModel.getCourseTopics()
@@ -78,7 +80,7 @@ class DiscussionTopicsViewModelTest {
@Test
fun `getCourseTopics success`() = runTest {
- val viewModel = DiscussionTopicsViewModel(interactor, resourceManager, "")
+ val viewModel = DiscussionTopicsViewModel(interactor, resourceManager, analytics,"")
coEvery { interactor.getCourseTopics(any()) } returns mockk()
viewModel.getCourseTopics()
@@ -92,7 +94,7 @@ class DiscussionTopicsViewModelTest {
@Test
fun `updateCourseTopics no internet exception`() = runTest {
- val viewModel = DiscussionTopicsViewModel(interactor, resourceManager, "")
+ val viewModel = DiscussionTopicsViewModel(interactor, resourceManager, analytics,"")
coEvery { interactor.getCourseTopics(any()) } throws UnknownHostException()
viewModel.updateCourseTopics()
@@ -107,7 +109,7 @@ class DiscussionTopicsViewModelTest {
@Test
fun `updateCourseTopics unknown exception`() = runTest {
- val viewModel = DiscussionTopicsViewModel(interactor, resourceManager, "")
+ val viewModel = DiscussionTopicsViewModel(interactor, resourceManager, analytics,"")
coEvery { interactor.getCourseTopics(any()) } throws Exception()
viewModel.updateCourseTopics()
@@ -122,7 +124,7 @@ class DiscussionTopicsViewModelTest {
@Test
fun `updateCourseTopics success`() = runTest {
- val viewModel = DiscussionTopicsViewModel(interactor, resourceManager, "")
+ val viewModel = DiscussionTopicsViewModel(interactor, resourceManager, analytics,"")
coEvery { interactor.getCourseTopics(any()) } returns mockk()
viewModel.updateCourseTopics()
diff --git a/profile/build.gradle b/profile/build.gradle
index 20f2a944b..6f290cc71 100644
--- a/profile/build.gradle
+++ b/profile/build.gradle
@@ -5,6 +5,7 @@ plugins {
}
android {
+ namespace 'org.openedx.profile'
compileSdk 33
defaultConfig {
diff --git a/discussion/src/androidTest/java/com/raccoongang/discussion/ExampleInstrumentedTest.kt b/profile/src/androidTest/java/org/openedx/profile/ExampleInstrumentedTest.kt
similarity index 83%
rename from discussion/src/androidTest/java/com/raccoongang/discussion/ExampleInstrumentedTest.kt
rename to profile/src/androidTest/java/org/openedx/profile/ExampleInstrumentedTest.kt
index 83032bfc8..12ab3a4c1 100644
--- a/discussion/src/androidTest/java/com/raccoongang/discussion/ExampleInstrumentedTest.kt
+++ b/profile/src/androidTest/java/org/openedx/profile/ExampleInstrumentedTest.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.discussion
+package org.openedx.profile
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -19,6 +19,6 @@ class ExampleInstrumentedTest {
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("com.raccoongang.discussion.test", appContext.packageName)
+ assertEquals("org.openedx.profile.test", appContext.packageName)
}
}
\ No newline at end of file
diff --git a/profile/src/main/AndroidManifest.xml b/profile/src/main/AndroidManifest.xml
index d12e154b3..e81cbf6cf 100644
--- a/profile/src/main/AndroidManifest.xml
+++ b/profile/src/main/AndroidManifest.xml
@@ -1,5 +1,5 @@
+ package="org.openedx.profile">
\ No newline at end of file
diff --git a/profile/src/main/java/com/raccoongang/profile/domain/model/UpdateProfileRequest.kt b/profile/src/main/java/com/raccoongang/profile/domain/model/UpdateProfileRequest.kt
deleted file mode 100644
index 706ff383a..000000000
--- a/profile/src/main/java/com/raccoongang/profile/domain/model/UpdateProfileRequest.kt
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.raccoongang.profile.domain.model
-
-class UpdateProfileRequest(
-)
-
diff --git a/profile/src/main/java/com/raccoongang/profile/system/notifier/AccountDeactivated.kt b/profile/src/main/java/com/raccoongang/profile/system/notifier/AccountDeactivated.kt
deleted file mode 100644
index d4326fff7..000000000
--- a/profile/src/main/java/com/raccoongang/profile/system/notifier/AccountDeactivated.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package com.raccoongang.profile.system.notifier
-
-class AccountDeactivated : ProfileEvent
\ No newline at end of file
diff --git a/profile/src/main/java/com/raccoongang/profile/system/notifier/AccountUpdated.kt b/profile/src/main/java/com/raccoongang/profile/system/notifier/AccountUpdated.kt
deleted file mode 100644
index 90a1f4916..000000000
--- a/profile/src/main/java/com/raccoongang/profile/system/notifier/AccountUpdated.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package com.raccoongang.profile.system.notifier
-
-class AccountUpdated : ProfileEvent
\ No newline at end of file
diff --git a/profile/src/main/java/com/raccoongang/profile/system/notifier/ProfileEvent.kt b/profile/src/main/java/com/raccoongang/profile/system/notifier/ProfileEvent.kt
deleted file mode 100644
index 57bce1e54..000000000
--- a/profile/src/main/java/com/raccoongang/profile/system/notifier/ProfileEvent.kt
+++ /dev/null
@@ -1,4 +0,0 @@
-package com.raccoongang.profile.system.notifier
-
-interface ProfileEvent {
-}
\ No newline at end of file
diff --git a/profile/src/main/java/com/raccoongang/profile/system/notifier/VideoQualityChanged.kt b/profile/src/main/java/com/raccoongang/profile/system/notifier/VideoQualityChanged.kt
deleted file mode 100644
index 5a340ddc3..000000000
--- a/profile/src/main/java/com/raccoongang/profile/system/notifier/VideoQualityChanged.kt
+++ /dev/null
@@ -1,3 +0,0 @@
-package com.raccoongang.profile.system.notifier
-
-class VideoQualityChanged : ProfileEvent
\ No newline at end of file
diff --git a/profile/src/main/java/com/raccoongang/profile/data/api/ProfileApi.kt b/profile/src/main/java/org/openedx/profile/data/api/ProfileApi.kt
similarity index 92%
rename from profile/src/main/java/com/raccoongang/profile/data/api/ProfileApi.kt
rename to profile/src/main/java/org/openedx/profile/data/api/ProfileApi.kt
index 9706779cb..cfe14bdf4 100644
--- a/profile/src/main/java/com/raccoongang/profile/data/api/ProfileApi.kt
+++ b/profile/src/main/java/org/openedx/profile/data/api/ProfileApi.kt
@@ -1,7 +1,7 @@
-package com.raccoongang.profile.data.api
+package org.openedx.profile.data.api
-import com.raccoongang.core.ApiConstants
-import com.raccoongang.profile.data.model.Account
+import org.openedx.core.ApiConstants
+import org.openedx.profile.data.model.Account
import okhttp3.RequestBody
import okhttp3.ResponseBody
import retrofit2.Response
diff --git a/profile/src/main/java/com/raccoongang/profile/data/model/Account.kt b/profile/src/main/java/org/openedx/profile/data/model/Account.kt
similarity index 88%
rename from profile/src/main/java/com/raccoongang/profile/data/model/Account.kt
rename to profile/src/main/java/org/openedx/profile/data/model/Account.kt
index 90e4c0f17..e1fe4a52b 100644
--- a/profile/src/main/java/com/raccoongang/profile/data/model/Account.kt
+++ b/profile/src/main/java/org/openedx/profile/data/model/Account.kt
@@ -1,9 +1,9 @@
-package com.raccoongang.profile.data.model
+package org.openedx.profile.data.model
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.data.model.ProfileImage
+import org.openedx.core.data.model.ProfileImage
import java.util.*
-import com.raccoongang.core.domain.model.Account as DomainAccount
+import org.openedx.core.domain.model.Account as DomainAccount
data class Account(
@SerializedName("username")
@@ -47,8 +47,8 @@ data class Account(
ALL_USERS
}
- fun mapToDomain(): com.raccoongang.core.domain.model.Account {
- return com.raccoongang.core.domain.model.Account(
+ fun mapToDomain(): org.openedx.core.domain.model.Account {
+ return org.openedx.core.domain.model.Account(
username = username ?: "",
bio = bio?:"",
requiresParentalConsent = requiresParentalConsent ?: false,
diff --git a/profile/src/main/java/com/raccoongang/profile/data/model/LanguageProficiency.kt b/profile/src/main/java/org/openedx/profile/data/model/LanguageProficiency.kt
similarity index 71%
rename from profile/src/main/java/com/raccoongang/profile/data/model/LanguageProficiency.kt
rename to profile/src/main/java/org/openedx/profile/data/model/LanguageProficiency.kt
index 6b63b7056..5a88b03e3 100644
--- a/profile/src/main/java/com/raccoongang/profile/data/model/LanguageProficiency.kt
+++ b/profile/src/main/java/org/openedx/profile/data/model/LanguageProficiency.kt
@@ -1,7 +1,7 @@
-package com.raccoongang.profile.data.model
+package org.openedx.profile.data.model
import com.google.gson.annotations.SerializedName
-import com.raccoongang.core.domain.model.LanguageProficiency
+import org.openedx.core.domain.model.LanguageProficiency
data class LanguageProficiency(
@SerializedName("code")
diff --git a/profile/src/main/java/com/raccoongang/profile/data/repository/ProfileRepository.kt b/profile/src/main/java/org/openedx/profile/data/repository/ProfileRepository.kt
similarity index 72%
rename from profile/src/main/java/com/raccoongang/profile/data/repository/ProfileRepository.kt
rename to profile/src/main/java/org/openedx/profile/data/repository/ProfileRepository.kt
index 02913a2f8..a1381a5e5 100644
--- a/profile/src/main/java/com/raccoongang/profile/data/repository/ProfileRepository.kt
+++ b/profile/src/main/java/org/openedx/profile/data/repository/ProfileRepository.kt
@@ -1,10 +1,10 @@
-package com.raccoongang.profile.data.repository
+package org.openedx.profile.data.repository
import androidx.room.RoomDatabase
-import com.raccoongang.core.ApiConstants
-import com.raccoongang.core.BuildConfig
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.profile.data.api.ProfileApi
+import org.openedx.core.ApiConstants
+import org.openedx.core.BuildConfig
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.profile.data.api.ProfileApi
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.asRequestBody
import java.io.File
@@ -15,11 +15,11 @@ class ProfileRepository(
private val preferencesManager: PreferencesManager
) {
- suspend fun getAccount(): com.raccoongang.core.domain.model.Account {
+ suspend fun getAccount(): org.openedx.core.domain.model.Account {
return api.getAccount(preferencesManager.user?.username!!).mapToDomain()
}
- suspend fun updateAccount(fields: Map): com.raccoongang.core.domain.model.Account {
+ suspend fun updateAccount(fields: Map): org.openedx.core.domain.model.Account {
return api.updateAccount(preferencesManager.user?.username!!, fields).mapToDomain()
}
@@ -40,7 +40,7 @@ class ProfileRepository(
suspend fun logout() {
api.revokeAccessToken(
- BuildConfig.CLIENT_ID,
+ org.openedx.core.BuildConfig.CLIENT_ID,
preferencesManager.refreshToken,
ApiConstants.TOKEN_TYPE_REFRESH
)
diff --git a/profile/src/main/java/com/raccoongang/profile/domain/interactor/ProfileInteractor.kt b/profile/src/main/java/org/openedx/profile/domain/interactor/ProfileInteractor.kt
similarity index 83%
rename from profile/src/main/java/com/raccoongang/profile/domain/interactor/ProfileInteractor.kt
rename to profile/src/main/java/org/openedx/profile/domain/interactor/ProfileInteractor.kt
index e4dfd727a..1364c7370 100644
--- a/profile/src/main/java/com/raccoongang/profile/domain/interactor/ProfileInteractor.kt
+++ b/profile/src/main/java/org/openedx/profile/domain/interactor/ProfileInteractor.kt
@@ -1,6 +1,6 @@
-package com.raccoongang.profile.domain.interactor
+package org.openedx.profile.domain.interactor
-import com.raccoongang.profile.data.repository.ProfileRepository
+import org.openedx.profile.data.repository.ProfileRepository
import java.io.File
class ProfileInteractor(private val repository: ProfileRepository) {
diff --git a/profile/src/main/java/org/openedx/profile/domain/model/UpdateProfileRequest.kt b/profile/src/main/java/org/openedx/profile/domain/model/UpdateProfileRequest.kt
new file mode 100644
index 000000000..1327cc90c
--- /dev/null
+++ b/profile/src/main/java/org/openedx/profile/domain/model/UpdateProfileRequest.kt
@@ -0,0 +1,5 @@
+package org.openedx.profile.domain.model
+
+class UpdateProfileRequest(
+)
+
diff --git a/profile/src/main/java/org/openedx/profile/presentation/ProfileAnalytics.kt b/profile/src/main/java/org/openedx/profile/presentation/ProfileAnalytics.kt
new file mode 100644
index 000000000..27990b563
--- /dev/null
+++ b/profile/src/main/java/org/openedx/profile/presentation/ProfileAnalytics.kt
@@ -0,0 +1,12 @@
+package org.openedx.profile.presentation
+
+interface ProfileAnalytics {
+ fun profileEditClickedEvent()
+ fun profileEditDoneClickedEvent()
+ fun profileDeleteAccountClickedEvent()
+ fun profileVideoSettingsClickedEvent()
+ fun privacyPolicyClickedEvent()
+ fun cookiePolicyClickedEvent()
+ fun emailSupportClickedEvent()
+ fun logoutEvent(force: Boolean)
+}
\ No newline at end of file
diff --git a/profile/src/main/java/com/raccoongang/profile/presentation/ProfileRouter.kt b/profile/src/main/java/org/openedx/profile/presentation/ProfileRouter.kt
similarity index 78%
rename from profile/src/main/java/com/raccoongang/profile/presentation/ProfileRouter.kt
rename to profile/src/main/java/org/openedx/profile/presentation/ProfileRouter.kt
index b4414c6f1..59fc683e1 100644
--- a/profile/src/main/java/com/raccoongang/profile/presentation/ProfileRouter.kt
+++ b/profile/src/main/java/org/openedx/profile/presentation/ProfileRouter.kt
@@ -1,7 +1,7 @@
-package com.raccoongang.profile.presentation
+package org.openedx.profile.presentation
import androidx.fragment.app.FragmentManager
-import com.raccoongang.core.domain.model.Account
+import org.openedx.core.domain.model.Account
interface ProfileRouter {
diff --git a/profile/src/main/java/com/raccoongang/profile/presentation/delete/DeleteProfileFragment.kt b/profile/src/main/java/org/openedx/profile/presentation/delete/DeleteProfileFragment.kt
similarity index 93%
rename from profile/src/main/java/com/raccoongang/profile/presentation/delete/DeleteProfileFragment.kt
rename to profile/src/main/java/org/openedx/profile/presentation/delete/DeleteProfileFragment.kt
index 03f769fcb..f4ea03806 100644
--- a/profile/src/main/java/com/raccoongang/profile/presentation/delete/DeleteProfileFragment.kt
+++ b/profile/src/main/java/org/openedx/profile/presentation/delete/DeleteProfileFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.profile.presentation.delete
+package org.openedx.profile.presentation.delete
import android.content.res.Configuration
import android.os.Bundle
@@ -33,18 +33,18 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.profile.presentation.ProfileRouter
-import com.raccoongang.profile.presentation.edit.EditProfileFragment
-import com.raccoongang.profile.presentation.profile.ProfileViewModel
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appTypography
+import org.openedx.profile.presentation.ProfileRouter
+import org.openedx.profile.presentation.edit.EditProfileFragment
+import org.openedx.profile.presentation.profile.ProfileViewModel
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
-import com.raccoongang.profile.R as profileR
+import org.openedx.profile.R as profileR
class DeleteProfileFragment : Fragment() {
@@ -64,7 +64,7 @@ class DeleteProfileFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val uiState by viewModel.uiState.observeAsState(DeleteProfileFragmentUIState.Initial)
@@ -192,7 +192,7 @@ fun DeleteProfileScreen(
Spacer(Modifier.height(48.dp))
Image(
modifier = Modifier.size(145.dp),
- painter = painterResource(id = com.raccoongang.profile.R.drawable.profile_delete_box),
+ painter = painterResource(id = org.openedx.profile.R.drawable.profile_delete_box),
contentDescription = null,
)
Spacer(Modifier.height(32.dp))
@@ -229,7 +229,7 @@ fun DeleteProfileScreen(
textAlign = TextAlign.Center
)
Spacer(Modifier.height(40.dp))
- NewEdxOutlinedTextField(
+ OpenEdXOutlinedTextField(
modifier = Modifier
.fillMaxWidth(),
title = stringResource(id = R.string.core_password),
@@ -246,7 +246,7 @@ fun DeleteProfileScreen(
errorText = errorText
)
Spacer(Modifier.height(38.dp))
- NewEdxButton(
+ OpenEdXButton(
text = stringResource(id = profileR.string.profile_yes_delete_account),
enabled = uiState !is DeleteProfileFragmentUIState.Loading && password.isNotEmpty(),
backgroundColor = MaterialTheme.appColors.error,
@@ -284,7 +284,7 @@ fun DeleteProfileScreen(
)
@Composable
fun DeleteProfileScreenPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
DeleteProfileScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
uiState = DeleteProfileFragmentUIState.Initial,
diff --git a/profile/src/main/java/com/raccoongang/profile/presentation/delete/DeleteProfileFragmentUIState.kt b/profile/src/main/java/org/openedx/profile/presentation/delete/DeleteProfileFragmentUIState.kt
similarity index 84%
rename from profile/src/main/java/com/raccoongang/profile/presentation/delete/DeleteProfileFragmentUIState.kt
rename to profile/src/main/java/org/openedx/profile/presentation/delete/DeleteProfileFragmentUIState.kt
index def779a72..128642077 100644
--- a/profile/src/main/java/com/raccoongang/profile/presentation/delete/DeleteProfileFragmentUIState.kt
+++ b/profile/src/main/java/org/openedx/profile/presentation/delete/DeleteProfileFragmentUIState.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.profile.presentation.delete
+package org.openedx.profile.presentation.delete
sealed class DeleteProfileFragmentUIState {
object Initial: DeleteProfileFragmentUIState()
diff --git a/profile/src/main/java/com/raccoongang/profile/presentation/delete/DeleteProfileViewModel.kt b/profile/src/main/java/org/openedx/profile/presentation/delete/DeleteProfileViewModel.kt
similarity index 73%
rename from profile/src/main/java/com/raccoongang/profile/presentation/delete/DeleteProfileViewModel.kt
rename to profile/src/main/java/org/openedx/profile/presentation/delete/DeleteProfileViewModel.kt
index e650f22b4..40e03d53f 100644
--- a/profile/src/main/java/com/raccoongang/profile/presentation/delete/DeleteProfileViewModel.kt
+++ b/profile/src/main/java/org/openedx/profile/presentation/delete/DeleteProfileViewModel.kt
@@ -1,18 +1,18 @@
-package com.raccoongang.profile.presentation.delete
+package org.openedx.profile.presentation.delete
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.Validator
-import com.raccoongang.core.extension.isInternetError
-import com.raccoongang.core.system.EdxError
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.profile.domain.interactor.ProfileInteractor
-import com.raccoongang.profile.system.notifier.AccountDeactivated
-import com.raccoongang.profile.system.notifier.ProfileNotifier
+import org.openedx.core.BaseViewModel
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.Validator
+import org.openedx.core.extension.isInternetError
+import org.openedx.core.system.EdxError
+import org.openedx.core.system.ResourceManager
+import org.openedx.profile.domain.interactor.ProfileInteractor
+import org.openedx.profile.system.notifier.AccountDeactivated
+import org.openedx.profile.system.notifier.ProfileNotifier
import kotlinx.coroutines.launch
class DeleteProfileViewModel(
@@ -34,7 +34,7 @@ class DeleteProfileViewModel(
fun deleteProfile(password: String) {
if (!validator.isPasswordValid(password)) {
_uiState.value =
- DeleteProfileFragmentUIState.Error(resourceManager.getString(com.raccoongang.profile.R.string.profile_invalid_password))
+ DeleteProfileFragmentUIState.Error(resourceManager.getString(org.openedx.profile.R.string.profile_invalid_password))
return
}
viewModelScope.launch {
@@ -54,7 +54,7 @@ class DeleteProfileViewModel(
_uiState.value = DeleteProfileFragmentUIState.Initial
} else {
_uiState.value =
- DeleteProfileFragmentUIState.Error(resourceManager.getString(com.raccoongang.profile.R.string.profile_password_is_incorrect))
+ DeleteProfileFragmentUIState.Error(resourceManager.getString(org.openedx.profile.R.string.profile_password_is_incorrect))
}
}
}
diff --git a/profile/src/main/java/com/raccoongang/profile/presentation/edit/EditProfileFields.kt b/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFields.kt
similarity index 78%
rename from profile/src/main/java/com/raccoongang/profile/presentation/edit/EditProfileFields.kt
rename to profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFields.kt
index 8bc1b259d..e13b7ad4d 100644
--- a/profile/src/main/java/com/raccoongang/profile/presentation/edit/EditProfileFields.kt
+++ b/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFields.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.profile.presentation.edit
+package org.openedx.profile.presentation.edit
const val YEAR_OF_BIRTH = "year_of_birth"
const val LANGUAGE = "language_proficiencies"
diff --git a/profile/src/main/java/com/raccoongang/profile/presentation/edit/EditProfileFragment.kt b/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFragment.kt
similarity index 92%
rename from profile/src/main/java/com/raccoongang/profile/presentation/edit/EditProfileFragment.kt
rename to profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFragment.kt
index 93ad7bdc5..9c9b6f568 100644
--- a/profile/src/main/java/com/raccoongang/profile/presentation/edit/EditProfileFragment.kt
+++ b/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileFragment.kt
@@ -1,6 +1,6 @@
-@file:OptIn(ExperimentalComposeUiApi::class)
+@file:OptIn(ExperimentalComposeUiApi::class, ExperimentalComposeUiApi::class)
-package com.raccoongang.profile.presentation.edit
+package org.openedx.profile.presentation.edit
import android.content.res.Configuration
import android.content.res.Configuration.UI_MODE_NIGHT_YES
@@ -45,6 +45,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
@@ -57,19 +58,19 @@ import androidx.core.content.FileProvider
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import coil.compose.rememberAsyncImagePainter
-import com.raccoongang.core.AppDataConstants.DEFAULT_MIME_TYPE
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.Account
-import com.raccoongang.core.domain.model.LanguageProficiency
-import com.raccoongang.core.domain.model.ProfileImage
-import com.raccoongang.core.domain.model.RegistrationField
-import com.raccoongang.core.extension.getFileName
-import com.raccoongang.core.extension.parcelable
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.*
-import com.raccoongang.core.utils.LocaleUtils
-import com.raccoongang.profile.presentation.ProfileRouter
+import org.openedx.core.AppDataConstants.DEFAULT_MIME_TYPE
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.Account
+import org.openedx.core.domain.model.LanguageProficiency
+import org.openedx.core.domain.model.ProfileImage
+import org.openedx.core.domain.model.RegistrationField
+import org.openedx.core.extension.getFileName
+import org.openedx.core.extension.parcelable
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.*
+import org.openedx.core.utils.LocaleUtils
+import org.openedx.profile.presentation.ProfileRouter
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
@@ -77,7 +78,7 @@ import org.koin.core.parameter.parametersOf
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileOutputStream
-import com.raccoongang.profile.R as profileR
+import org.openedx.profile.R as profileR
private const val BIO_TEXT_FIELD_LIMIT = 300
@@ -118,7 +119,7 @@ class EditProfileFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val uiState by viewModel.uiState.observeAsState(
@@ -148,6 +149,7 @@ class EditProfileFragment : Fragment() {
}
},
onSaveClick = { fields ->
+ viewModel.profileEditDoneClickedEvent()
if (selectedImageUri == null) {
viewModel.updateAccount(fields)
} else {
@@ -173,6 +175,7 @@ class EditProfileFragment : Fragment() {
viewModel.setShowLeaveDialog(false)
},
onDeleteClick = {
+ viewModel.profileDeleteAccountClickedEvent()
router.navigateToDeleteAccount(
requireActivity().supportFragmentManager
)
@@ -283,10 +286,11 @@ private fun EditProfileScreen(
) {
val scaffoldState = rememberScaffoldState()
val coroutine = rememberCoroutineScope()
- val configuration = LocalConfiguration.current
+ val focusManager = LocalFocusManager.current
val bottomSheetScaffoldState = rememberModalBottomSheetState(
- initialValue = ModalBottomSheetValue.Hidden
+ initialValue = ModalBottomSheetValue.Hidden,
+ skipHalfExpanded = true
)
val keyboardController = LocalSoftwareKeyboardController.current
@@ -297,6 +301,14 @@ private fun EditProfileScreen(
mutableStateOf(false)
}
+ var bottomDialogTitle by rememberSaveable {
+ mutableStateOf("")
+ }
+
+ var searchValue by rememberSaveable(stateSaver = TextFieldValue.Saver) {
+ mutableStateOf(TextFieldValue())
+ }
+
val mapFields = rememberSaveableMap {
mutableStateMapOf(
Pair(
@@ -334,6 +346,15 @@ private fun EditProfileScreen(
mutableStateOf(false)
}
+ val isImeVisible by isImeVisibleState()
+
+ LaunchedEffect(bottomSheetScaffoldState.isVisible) {
+ if (!bottomSheetScaffoldState.isVisible) {
+ focusManager.clearFocus()
+ searchValue = TextFieldValue()
+ }
+ }
+
Scaffold(
modifier = Modifier
.fillMaxSize()
@@ -376,17 +397,9 @@ private fun EditProfileScreen(
)
}
- val bottomSheetWeight by remember(key1 = windowSize) {
- mutableStateOf(
- windowSize.windowSizeValue(
- expanded = if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) 0.8f else 0.6f,
- compact = 1f
- )
- )
- }
-
ModalBottomSheetLayout(
modifier = Modifier
+ .padding(bottom = if (isImeVisible && bottomSheetScaffoldState.isVisible) 120.dp else 0.dp)
.noRippleClickable {
if (bottomSheetScaffoldState.isVisible) {
coroutine.launch {
@@ -394,16 +407,14 @@ private fun EditProfileScreen(
}
}
},
- sheetShape = BottomSheetShape(
- width = configuration.screenWidthDp.px,
- height = configuration.screenHeightDp.px,
- weight = bottomSheetWeight
- ),
+ sheetShape = MaterialTheme.appShapes.screenBackgroundShape,
sheetState = bottomSheetScaffoldState,
scrimColor = Color.Black.copy(alpha = 0.4f),
sheetBackgroundColor = MaterialTheme.appColors.background,
sheetContent = {
SheetContent(
+ title = bottomDialogTitle,
+ searchValue = searchValue,
expandedList = expandedList,
listState = modalListState,
onItemClick = { item ->
@@ -417,7 +428,11 @@ private fun EditProfileScreen(
bottomSheetScaffoldState.hide()
modalListState.scrollToItem(0)
}
- })
+ },
+ searchValueChanged = {
+ searchValue = TextFieldValue(it)
+ }
+ )
}) {
HandleUIMessage(uiMessage = uiMessage, scaffoldState = scaffoldState)
@@ -583,7 +598,7 @@ private fun EditProfileScreen(
Spacer(modifier = Modifier.height(20.dp))
ProfileFields(
disabled = uiState.isLimited,
- onFieldClick = { it ->
+ onFieldClick = { it, title ->
if (it == YEAR_OF_BIRTH) {
serverFieldName.value = YEAR_OF_BIRTH
expandedList =
@@ -596,6 +611,7 @@ private fun EditProfileScreen(
serverFieldName.value = LANGUAGE
expandedList = LocaleUtils.getLanguages()
}
+ bottomDialogTitle = title
keyboardController?.hide()
coroutine.launch {
val index = expandedList.indexOfFirst { option ->
@@ -623,7 +639,7 @@ private fun EditProfileScreen(
)
Spacer(Modifier.height(40.dp))
IconText(
- text = stringResource(id = com.raccoongang.profile.R.string.profile_delete_profile),
+ text = stringResource(id = org.openedx.profile.R.string.profile_delete_profile),
painter = painterResource(id = profileR.drawable.profile_ic_trash),
textStyle = MaterialTheme.appTypography.labelLarge,
color = MaterialTheme.appColors.error,
@@ -741,7 +757,7 @@ private fun ChangeImageDialog(
color = MaterialTheme.appColors.textPrimary
)
Spacer(Modifier.height(20.dp))
- NewEdxButton(
+ OpenEdXButton(
text = stringResource(id = profileR.string.profile_select_from_gallery),
onClick = onSelectFromGalleryClick,
content = {
@@ -754,7 +770,7 @@ private fun ChangeImageDialog(
}
)
Spacer(Modifier.height(16.dp))
- NewEdxOutlinedButton(
+ OpenEdXOutlinedButton(
borderColor = MaterialTheme.appColors.error,
textColor = MaterialTheme.appColors.textPrimary,
text = stringResource(id = profileR.string.profile_remove_photo),
@@ -769,7 +785,7 @@ private fun ChangeImageDialog(
}
)
Spacer(Modifier.height(40.dp))
- NewEdxOutlinedButton(
+ OpenEdXOutlinedButton(
borderColor = MaterialTheme.appColors.textPrimaryVariant,
textColor = MaterialTheme.appColors.textPrimary,
text = stringResource(id = R.string.core_cancel),
@@ -786,10 +802,11 @@ private fun ChangeImageDialog(
private fun ProfileFields(
disabled: Boolean,
mapFields: MutableMap,
- onFieldClick: (String) -> Unit,
+ onFieldClick: (String, String) -> Unit,
onValueChanged: (String) -> Unit,
onDoneClick: () -> Unit
) {
+ val context = LocalContext.current
val languageProficiency = (mapFields[LANGUAGE] as List)
val lang = if (languageProficiency.isNotEmpty()) {
LocaleUtils.getLanguageByLanguageCode(languageProficiency[0].code)
@@ -799,7 +816,7 @@ private fun ProfileFields(
name = stringResource(id = profileR.string.profile_year),
initialValue = mapFields[YEAR_OF_BIRTH].toString(),
onClick = {
- onFieldClick(YEAR_OF_BIRTH)
+ onFieldClick(YEAR_OF_BIRTH, context.getString(profileR.string.profile_year))
}
)
if (!disabled) {
@@ -807,14 +824,14 @@ private fun ProfileFields(
name = stringResource(id = profileR.string.profile_location),
initialValue = LocaleUtils.getCountryByCountryCode(mapFields[COUNTRY].toString()),
onClick = {
- onFieldClick(COUNTRY)
+ onFieldClick(COUNTRY, context.getString(profileR.string.profile_location))
}
)
SelectableField(
name = stringResource(id = profileR.string.profile_spoken_language),
initialValue = lang,
onClick = {
- onFieldClick(LANGUAGE)
+ onFieldClick(LANGUAGE, context.getString(profileR.string.profile_spoken_language))
}
)
InputEditField(
@@ -975,32 +992,32 @@ private fun LeaveProfile(
Icon(
modifier = Modifier
.size(100.dp),
- painter = painterResource(com.raccoongang.profile.R.drawable.profile_ic_save),
+ painter = painterResource(org.openedx.profile.R.drawable.profile_ic_save),
contentDescription = null
)
Spacer(Modifier.size(48.dp))
Text(
- text = stringResource(id = com.raccoongang.profile.R.string.profile_leave_profile),
+ text = stringResource(id = org.openedx.profile.R.string.profile_leave_profile),
color = MaterialTheme.appColors.textPrimary,
style = MaterialTheme.appTypography.titleLarge,
textAlign = TextAlign.Center
)
Spacer(Modifier.size(12.dp))
Text(
- text = stringResource(id = com.raccoongang.profile.R.string.profile_changes_you_made),
+ text = stringResource(id = org.openedx.profile.R.string.profile_changes_you_made),
color = MaterialTheme.appColors.textPrimary,
style = MaterialTheme.appTypography.bodyMedium,
textAlign = TextAlign.Center
)
Spacer(Modifier.size(40.dp))
- NewEdxButton(
- text = stringResource(id = com.raccoongang.profile.R.string.profile_leave),
+ OpenEdXButton(
+ text = stringResource(id = org.openedx.profile.R.string.profile_leave),
onClick = onLeaveClick,
backgroundColor = MaterialTheme.appColors.warning,
content = {
Text(
modifier = Modifier.fillMaxWidth(),
- text = stringResource(id = com.raccoongang.profile.R.string.profile_leave),
+ text = stringResource(id = org.openedx.profile.R.string.profile_leave),
color = MaterialTheme.appColors.textDark,
style = MaterialTheme.appTypography.labelLarge,
textAlign = TextAlign.Center
@@ -1008,10 +1025,10 @@ private fun LeaveProfile(
}
)
Spacer(Modifier.height(24.dp))
- NewEdxOutlinedButton(
+ OpenEdXOutlinedButton(
borderColor = MaterialTheme.appColors.textPrimary,
textColor = MaterialTheme.appColors.textPrimary,
- text = stringResource(id = com.raccoongang.profile.R.string.profile_keep_editing),
+ text = stringResource(id = org.openedx.profile.R.string.profile_keep_editing),
onClick = onDismissRequest
)
}
@@ -1025,7 +1042,7 @@ private fun LeaveProfile(
@Preview(name = "NEXUS_5_Dark", device = Devices.NEXUS_5, uiMode = UI_MODE_NIGHT_YES)
@Composable
private fun EditProfileScreenPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
EditProfileScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
uiState = EditProfileUIState(account = mockAccount, isUpdating = false, false),
@@ -1049,7 +1066,7 @@ private fun EditProfileScreenPreview() {
@Preview(name = "NEXUS_9_Dark", device = Devices.NEXUS_9, uiMode = UI_MODE_NIGHT_YES)
@Composable
private fun EditProfileScreenTabletPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
EditProfileScreen(
windowSize = WindowSize(WindowType.Medium, WindowType.Medium),
uiState = EditProfileUIState(account = mockAccount, isUpdating = false, false),
diff --git a/profile/src/main/java/com/raccoongang/profile/presentation/edit/EditProfileUIState.kt b/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileUIState.kt
similarity index 56%
rename from profile/src/main/java/com/raccoongang/profile/presentation/edit/EditProfileUIState.kt
rename to profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileUIState.kt
index 01337c0d0..7329207f7 100644
--- a/profile/src/main/java/com/raccoongang/profile/presentation/edit/EditProfileUIState.kt
+++ b/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileUIState.kt
@@ -1,6 +1,6 @@
-package com.raccoongang.profile.presentation.edit
+package org.openedx.profile.presentation.edit
-import com.raccoongang.core.domain.model.Account
+import org.openedx.core.domain.model.Account
data class EditProfileUIState(
val account: Account,
diff --git a/profile/src/main/java/com/raccoongang/profile/presentation/edit/EditProfileViewModel.kt b/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileViewModel.kt
similarity index 84%
rename from profile/src/main/java/com/raccoongang/profile/presentation/edit/EditProfileViewModel.kt
rename to profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileViewModel.kt
index dbcf8ca16..f114e9ab4 100644
--- a/profile/src/main/java/com/raccoongang/profile/presentation/edit/EditProfileViewModel.kt
+++ b/profile/src/main/java/org/openedx/profile/presentation/edit/EditProfileViewModel.kt
@@ -1,18 +1,19 @@
-package com.raccoongang.profile.presentation.edit
+package org.openedx.profile.presentation.edit
import android.net.Uri
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.Account
-import com.raccoongang.core.extension.isInternetError
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.profile.domain.interactor.ProfileInteractor
-import com.raccoongang.profile.system.notifier.AccountUpdated
-import com.raccoongang.profile.system.notifier.ProfileNotifier
+import org.openedx.core.BaseViewModel
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.Account
+import org.openedx.core.extension.isInternetError
+import org.openedx.core.system.ResourceManager
+import org.openedx.profile.domain.interactor.ProfileInteractor
+import org.openedx.profile.presentation.ProfileAnalytics
+import org.openedx.profile.system.notifier.AccountUpdated
+import org.openedx.profile.system.notifier.ProfileNotifier
import kotlinx.coroutines.launch
import java.io.File
@@ -20,6 +21,7 @@ class EditProfileViewModel(
private val interactor: ProfileInteractor,
private val resourceManager: ResourceManager,
private val notifier: ProfileNotifier,
+ private val analytics: ProfileAnalytics,
account: Account
) : BaseViewModel() {
@@ -125,4 +127,12 @@ class EditProfileViewModel(
notifier.send(AccountUpdated())
}
+ fun profileEditDoneClickedEvent() {
+ analytics.profileEditDoneClickedEvent()
+ }
+
+ fun profileDeleteAccountClickedEvent() {
+ analytics.profileDeleteAccountClickedEvent()
+ }
+
}
\ No newline at end of file
diff --git a/profile/src/main/java/com/raccoongang/profile/presentation/profile/ProfileFragment.kt b/profile/src/main/java/org/openedx/profile/presentation/profile/ProfileFragment.kt
similarity index 88%
rename from profile/src/main/java/com/raccoongang/profile/presentation/profile/ProfileFragment.kt
rename to profile/src/main/java/org/openedx/profile/presentation/profile/ProfileFragment.kt
index 0c31ccc46..dabf67849 100644
--- a/profile/src/main/java/com/raccoongang/profile/presentation/profile/ProfileFragment.kt
+++ b/profile/src/main/java/org/openedx/profile/presentation/profile/ProfileFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.profile.presentation.profile
+package org.openedx.profile.presentation.profile
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
@@ -38,19 +38,19 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.fragment.app.Fragment
import coil.compose.rememberAsyncImagePainter
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.Account
-import com.raccoongang.core.domain.model.ProfileImage
-import com.raccoongang.core.presentation.global.AppData
-import com.raccoongang.core.presentation.global.AppDataHolder
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appShapes
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.core.utils.EmailUtil
-import com.raccoongang.profile.presentation.ProfileRouter
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.Account
+import org.openedx.core.domain.model.ProfileImage
+import org.openedx.core.presentation.global.AppData
+import org.openedx.core.presentation.global.AppDataHolder
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appShapes
+import org.openedx.core.ui.theme.appTypography
+import org.openedx.core.utils.EmailUtil
+import org.openedx.profile.presentation.ProfileRouter
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
@@ -71,7 +71,7 @@ class ProfileFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val uiState by viewModel.uiState.observeAsState()
@@ -89,18 +89,27 @@ class ProfileFragment : Fragment() {
viewModel.logout()
},
editAccountClicked = {
+ viewModel.profileEditClickedEvent()
router.navigateToEditProfile(
requireParentFragment().parentFragmentManager,
it
)
},
onSwipeRefresh = {
- viewModel.updateAccount()
+ viewModel.updateAccount()
},
onVideoSettingsClick = {
+ viewModel.profileVideoSettingsClickedEvent()
router.navigateToVideoSettings(
requireParentFragment().parentFragmentManager
)
+ },
+ onSupportClick = { action ->
+ when (action) {
+ SupportClickAction.SUPPORT -> viewModel.emailSupportClickedEvent()
+ SupportClickAction.COOKIE_POLICY -> viewModel.cookiePolicyClickedEvent()
+ SupportClickAction.PRIVACY_POLICY -> viewModel.privacyPolicyClickedEvent()
+ }
}
)
@@ -114,6 +123,10 @@ class ProfileFragment : Fragment() {
}
}
+private enum class SupportClickAction {
+ SUPPORT, PRIVACY_POLICY, COOKIE_POLICY
+}
+
@OptIn(ExperimentalMaterialApi::class)
@Composable
@@ -126,6 +139,7 @@ private fun ProfileScreen(
onVideoSettingsClick: () -> Unit,
logout: () -> Unit,
onSwipeRefresh: () -> Unit,
+ onSupportClick: (SupportClickAction) -> Unit,
editAccountClicked: (Account) -> Unit
) {
val scaffoldState = rememberScaffoldState()
@@ -198,7 +212,7 @@ private fun ProfileScreen(
modifier = Modifier
.height(48.dp)
.padding(end = 24.dp),
- text = stringResource(com.raccoongang.profile.R.string.profile_edit),
+ text = stringResource(org.openedx.profile.R.string.profile_edit),
painter = painterResource(id = R.drawable.core_ic_edit),
textStyle = MaterialTheme.appTypography.labelLarge,
color = MaterialTheme.appColors.primary,
@@ -230,6 +244,7 @@ private fun ProfileScreen(
CircularProgressIndicator(color = MaterialTheme.appColors.primary)
}
}
+
is ProfileUIState.Data -> {
Column(
Modifier
@@ -283,7 +298,7 @@ private fun ProfileScreen(
Spacer(modifier = Modifier.height(24.dp))
- SupportInfoSection(appData)
+ SupportInfoSection(appData, onClick = onSupportClick)
Spacer(modifier = Modifier.height(24.dp))
@@ -315,7 +330,7 @@ private fun ProfileInfoSection(account: Account) {
if (account.yearOfBirth != null || account.bio.isNotEmpty()) {
Column {
Text(
- text = stringResource(id = com.raccoongang.profile.R.string.profile_prof_info),
+ text = stringResource(id = org.openedx.profile.R.string.profile_prof_info),
style = MaterialTheme.appTypography.labelLarge,
color = MaterialTheme.appColors.textSecondary
)
@@ -339,7 +354,7 @@ private fun ProfileInfoSection(account: Account) {
account.yearOfBirth.toString()
} else ""
val text = stringResource(
- id = com.raccoongang.profile.R.string.profile_year_of_birth,
+ id = org.openedx.profile.R.string.profile_year_of_birth,
value
)
append(text)
@@ -359,7 +374,7 @@ private fun ProfileInfoSection(account: Account) {
Text(
text = buildAnnotatedString {
val text = stringResource(
- id = com.raccoongang.profile.R.string.profile_bio,
+ id = org.openedx.profile.R.string.profile_bio,
account.bio
)
append(text)
@@ -385,7 +400,7 @@ private fun ProfileInfoSection(account: Account) {
fun SettingsSection(onVideoSettingsClick: () -> Unit) {
Column {
Text(
- text = stringResource(id = com.raccoongang.profile.R.string.profile_settings),
+ text = stringResource(id = org.openedx.profile.R.string.profile_settings),
style = MaterialTheme.appTypography.labelLarge,
color = MaterialTheme.appColors.textSecondary
)
@@ -403,7 +418,7 @@ fun SettingsSection(onVideoSettingsClick: () -> Unit) {
verticalArrangement = Arrangement.spacedBy(24.dp)
) {
ProfileInfoItem(
- text = stringResource(id = com.raccoongang.profile.R.string.profile_video_settings),
+ text = stringResource(id = org.openedx.profile.R.string.profile_video_settings),
onClick = onVideoSettingsClick
)
}
@@ -412,12 +427,15 @@ fun SettingsSection(onVideoSettingsClick: () -> Unit) {
}
@Composable
-private fun SupportInfoSection(appData: AppData) {
+private fun SupportInfoSection(
+ appData: AppData,
+ onClick: (SupportClickAction) -> Unit
+) {
val uriHandler = LocalUriHandler.current
val context = LocalContext.current
Column {
Text(
- text = stringResource(id = com.raccoongang.profile.R.string.profile_support_info),
+ text = stringResource(id = org.openedx.profile.R.string.profile_support_info),
style = MaterialTheme.appTypography.labelLarge,
color = MaterialTheme.appColors.textSecondary
)
@@ -435,8 +453,9 @@ private fun SupportInfoSection(appData: AppData) {
verticalArrangement = Arrangement.spacedBy(24.dp)
) {
ProfileInfoItem(
- text = stringResource(id = com.raccoongang.profile.R.string.profile_contact_support),
+ text = stringResource(id = org.openedx.profile.R.string.profile_contact_support),
onClick = {
+ onClick(SupportClickAction.SUPPORT)
EmailUtil.showFeedbackScreen(
context,
context.getString(R.string.core_email_subject),
@@ -448,6 +467,7 @@ private fun SupportInfoSection(appData: AppData) {
ProfileInfoItem(
text = stringResource(id = R.string.core_terms_of_use),
onClick = {
+ onClick(SupportClickAction.COOKIE_POLICY)
uriHandler.openUri(context.getString(R.string.terms_of_service_link))
}
)
@@ -455,6 +475,7 @@ private fun SupportInfoSection(appData: AppData) {
ProfileInfoItem(
text = stringResource(id = R.string.core_privacy_policy),
onClick = {
+ onClick(SupportClickAction.PRIVACY_POLICY)
uriHandler.openUri(context.getString(R.string.privacy_policy_link))
}
)
@@ -480,7 +501,7 @@ private fun LogoutButton(onClick: () -> Unit) {
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
- text = stringResource(id = com.raccoongang.profile.R.string.profile_logout),
+ text = stringResource(id = org.openedx.profile.R.string.profile_logout),
style = MaterialTheme.appTypography.titleMedium,
color = MaterialTheme.appColors.error
)
@@ -522,20 +543,20 @@ private fun LogoutDialog(
modifier = Modifier
.width(88.dp)
.height(85.dp),
- painter = painterResource(com.raccoongang.profile.R.drawable.profile_ic_exit),
+ painter = painterResource(org.openedx.profile.R.drawable.profile_ic_exit),
contentDescription = null,
tint = MaterialTheme.appColors.onBackground
)
Spacer(Modifier.size(40.dp))
Text(
- text = stringResource(id = com.raccoongang.profile.R.string.profile_logout_dialog_body),
+ text = stringResource(id = org.openedx.profile.R.string.profile_logout_dialog_body),
color = MaterialTheme.appColors.textPrimary,
style = MaterialTheme.appTypography.titleLarge,
textAlign = TextAlign.Center
)
Spacer(Modifier.size(44.dp))
- NewEdxButton(
- text = stringResource(id = com.raccoongang.profile.R.string.profile_logout),
+ OpenEdXButton(
+ text = stringResource(id = org.openedx.profile.R.string.profile_logout),
backgroundColor = MaterialTheme.appColors.warning,
onClick = onLogoutClick,
content = {
@@ -546,13 +567,13 @@ private fun LogoutDialog(
) {
Text(
modifier = Modifier.fillMaxWidth(),
- text = stringResource(id = com.raccoongang.profile.R.string.profile_logout),
+ text = stringResource(id = org.openedx.profile.R.string.profile_logout),
color = MaterialTheme.appColors.textDark,
style = MaterialTheme.appTypography.labelLarge,
textAlign = TextAlign.Center
)
Icon(
- painter = painterResource(id = com.raccoongang.profile.R.drawable.profile_ic_logout),
+ painter = painterResource(id = org.openedx.profile.R.drawable.profile_ic_logout),
contentDescription = null,
tint = Color.Black
)
@@ -592,7 +613,7 @@ private fun ProfileInfoItem(text: String, onClick: () -> Unit) {
@Preview(name = "NEXUS_5_Dark", device = Devices.NEXUS_5, uiMode = UI_MODE_NIGHT_YES)
@Composable
private fun ProfileScreenPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
ProfileScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
uiState = ProfileUIState.Data(mockAccount),
@@ -602,6 +623,7 @@ private fun ProfileScreenPreview() {
onSwipeRefresh = {},
editAccountClicked = {},
onVideoSettingsClick = {},
+ onSupportClick = {},
appData = AppData("1")
)
}
@@ -612,7 +634,7 @@ private fun ProfileScreenPreview() {
@Preview(name = "NEXUS_9_Dark", device = Devices.NEXUS_9, uiMode = UI_MODE_NIGHT_YES)
@Composable
private fun ProfileScreenTabletPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
ProfileScreen(
windowSize = WindowSize(WindowType.Medium, WindowType.Medium),
uiState = ProfileUIState.Data(mockAccount),
@@ -622,6 +644,7 @@ private fun ProfileScreenTabletPreview() {
onSwipeRefresh = {},
editAccountClicked = {},
onVideoSettingsClick = {},
+ onSupportClick = {},
appData = AppData("1")
)
}
diff --git a/profile/src/main/java/com/raccoongang/profile/presentation/profile/ProfileUIState.kt b/profile/src/main/java/org/openedx/profile/presentation/profile/ProfileUIState.kt
similarity index 56%
rename from profile/src/main/java/com/raccoongang/profile/presentation/profile/ProfileUIState.kt
rename to profile/src/main/java/org/openedx/profile/presentation/profile/ProfileUIState.kt
index 617a45926..3bb0b9646 100644
--- a/profile/src/main/java/com/raccoongang/profile/presentation/profile/ProfileUIState.kt
+++ b/profile/src/main/java/org/openedx/profile/presentation/profile/ProfileUIState.kt
@@ -1,6 +1,6 @@
-package com.raccoongang.profile.presentation.profile
+package org.openedx.profile.presentation.profile
-import com.raccoongang.core.domain.model.Account
+import org.openedx.core.domain.model.Account
sealed class ProfileUIState {
data class Data(val account: Account) : ProfileUIState()
diff --git a/profile/src/main/java/com/raccoongang/profile/presentation/profile/ProfileViewModel.kt b/profile/src/main/java/org/openedx/profile/presentation/profile/ProfileViewModel.kt
similarity index 71%
rename from profile/src/main/java/com/raccoongang/profile/presentation/profile/ProfileViewModel.kt
rename to profile/src/main/java/org/openedx/profile/presentation/profile/ProfileViewModel.kt
index 74da654e9..15dc9b46a 100644
--- a/profile/src/main/java/com/raccoongang/profile/presentation/profile/ProfileViewModel.kt
+++ b/profile/src/main/java/org/openedx/profile/presentation/profile/ProfileViewModel.kt
@@ -1,21 +1,22 @@
-package com.raccoongang.profile.presentation.profile
+package org.openedx.profile.presentation.profile
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.extension.isInternetError
-import com.raccoongang.core.module.DownloadWorkerController
-import com.raccoongang.core.system.AppCookieManager
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.profile.domain.interactor.ProfileInteractor
-import com.raccoongang.profile.system.notifier.AccountDeactivated
-import com.raccoongang.profile.system.notifier.AccountUpdated
-import com.raccoongang.profile.system.notifier.ProfileNotifier
+import org.openedx.core.BaseViewModel
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.extension.isInternetError
+import org.openedx.core.module.DownloadWorkerController
+import org.openedx.core.system.AppCookieManager
+import org.openedx.core.system.ResourceManager
+import org.openedx.profile.domain.interactor.ProfileInteractor
+import org.openedx.profile.presentation.ProfileAnalytics
+import org.openedx.profile.system.notifier.AccountDeactivated
+import org.openedx.profile.system.notifier.AccountUpdated
+import org.openedx.profile.system.notifier.ProfileNotifier
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -27,7 +28,8 @@ class ProfileViewModel(
private val notifier: ProfileNotifier,
private val dispatcher: CoroutineDispatcher,
private val cookieManager: AppCookieManager,
- private val workerController: DownloadWorkerController
+ private val workerController: DownloadWorkerController,
+ private val analytics: ProfileAnalytics
) : BaseViewModel() {
private val _uiState = MutableLiveData(ProfileUIState.Loading)
@@ -97,6 +99,7 @@ class ProfileViewModel(
interactor.logout()
}
cookieManager.clearWebViewCookie()
+ analytics.logoutEvent(false)
_successLogout.value = true
} catch (e: Exception) {
e.printStackTrace()
@@ -111,4 +114,24 @@ class ProfileViewModel(
}
}
+ fun profileEditClickedEvent() {
+ analytics.profileEditClickedEvent()
+ }
+
+ fun profileVideoSettingsClickedEvent() {
+ analytics.profileVideoSettingsClickedEvent()
+ }
+
+ fun privacyPolicyClickedEvent() {
+ analytics.privacyPolicyClickedEvent()
+ }
+
+ fun cookiePolicyClickedEvent() {
+ analytics.cookiePolicyClickedEvent()
+ }
+
+ fun emailSupportClickedEvent() {
+ analytics.emailSupportClickedEvent()
+ }
+
}
\ No newline at end of file
diff --git a/profile/src/main/java/com/raccoongang/profile/presentation/settings/video/VideoQualityFragment.kt b/profile/src/main/java/org/openedx/profile/presentation/settings/video/VideoQualityFragment.kt
similarity index 95%
rename from profile/src/main/java/com/raccoongang/profile/presentation/settings/video/VideoQualityFragment.kt
rename to profile/src/main/java/org/openedx/profile/presentation/settings/video/VideoQualityFragment.kt
index 90838b705..ccda6e9d2 100644
--- a/profile/src/main/java/com/raccoongang/profile/presentation/settings/video/VideoQualityFragment.kt
+++ b/profile/src/main/java/org/openedx/profile/presentation/settings/video/VideoQualityFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.profile.presentation.settings.video
+package org.openedx.profile.presentation.settings.video
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
@@ -25,14 +25,14 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.fragment.app.Fragment
-import com.raccoongang.core.R
-import com.raccoongang.core.domain.model.VideoQuality
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appTypography
+import org.openedx.core.R
+import org.openedx.core.domain.model.VideoQuality
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appTypography
import org.koin.androidx.viewmodel.ext.android.viewModel
-import com.raccoongang.profile.R as profileR
+import org.openedx.profile.R as profileR
class VideoQualityFragment : Fragment() {
@@ -45,7 +45,7 @@ class VideoQualityFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val videoQuality by viewModel.videoQuality.observeAsState(viewModel.currentVideoQuality)
@@ -232,7 +232,7 @@ private fun QualityOption(
@Preview(uiMode = UI_MODE_NIGHT_YES)
@Composable
private fun VideoQualityScreenPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
VideoQualityScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
videoQuality = VideoQuality.OPTION_720P,
diff --git a/profile/src/main/java/com/raccoongang/profile/presentation/settings/video/VideoQualityViewModel.kt b/profile/src/main/java/org/openedx/profile/presentation/settings/video/VideoQualityViewModel.kt
similarity index 74%
rename from profile/src/main/java/com/raccoongang/profile/presentation/settings/video/VideoQualityViewModel.kt
rename to profile/src/main/java/org/openedx/profile/presentation/settings/video/VideoQualityViewModel.kt
index d85650a3f..b8fff185b 100644
--- a/profile/src/main/java/com/raccoongang/profile/presentation/settings/video/VideoQualityViewModel.kt
+++ b/profile/src/main/java/org/openedx/profile/presentation/settings/video/VideoQualityViewModel.kt
@@ -1,13 +1,13 @@
-package com.raccoongang.profile.presentation.settings.video
+package org.openedx.profile.presentation.settings.video
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.domain.model.VideoQuality
-import com.raccoongang.profile.system.notifier.ProfileNotifier
-import com.raccoongang.profile.system.notifier.VideoQualityChanged
+import org.openedx.core.BaseViewModel
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.domain.model.VideoQuality
+import org.openedx.profile.system.notifier.ProfileNotifier
+import org.openedx.profile.system.notifier.VideoQualityChanged
import kotlinx.coroutines.launch
class VideoQualityViewModel(
diff --git a/profile/src/main/java/com/raccoongang/profile/presentation/settings/video/VideoSettingsFragment.kt b/profile/src/main/java/org/openedx/profile/presentation/settings/video/VideoSettingsFragment.kt
similarity index 94%
rename from profile/src/main/java/com/raccoongang/profile/presentation/settings/video/VideoSettingsFragment.kt
rename to profile/src/main/java/org/openedx/profile/presentation/settings/video/VideoSettingsFragment.kt
index fc8dbdab8..8816decd9 100644
--- a/profile/src/main/java/com/raccoongang/profile/presentation/settings/video/VideoSettingsFragment.kt
+++ b/profile/src/main/java/org/openedx/profile/presentation/settings/video/VideoSettingsFragment.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.profile.presentation.settings.video
+package org.openedx.profile.presentation.settings.video
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
@@ -23,15 +23,15 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.fragment.app.Fragment
-import com.raccoongang.core.domain.model.VideoSettings
-import com.raccoongang.core.ui.*
-import com.raccoongang.core.ui.theme.NewEdxTheme
-import com.raccoongang.core.ui.theme.appColors
-import com.raccoongang.core.ui.theme.appTypography
-import com.raccoongang.profile.presentation.ProfileRouter
+import org.openedx.core.domain.model.VideoSettings
+import org.openedx.core.ui.*
+import org.openedx.core.ui.theme.OpenEdXTheme
+import org.openedx.core.ui.theme.appColors
+import org.openedx.core.ui.theme.appTypography
+import org.openedx.profile.presentation.ProfileRouter
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
-import com.raccoongang.profile.R as profileR
+import org.openedx.profile.R as profileR
class VideoSettingsFragment : Fragment() {
@@ -50,7 +50,7 @@ class VideoSettingsFragment : Fragment() {
) = ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
- NewEdxTheme {
+ OpenEdXTheme {
val windowSize = rememberWindowSize()
val videoSettings by viewModel.videoSettings.observeAsState(viewModel.currentSettings)
@@ -133,7 +133,7 @@ private fun VideoSettingsScreen(
Text(
modifier = Modifier
.fillMaxWidth(),
- text = stringResource(id = com.raccoongang.profile.R.string.profile_video_settings),
+ text = stringResource(id = org.openedx.profile.R.string.profile_video_settings),
color = MaterialTheme.appColors.textPrimary,
textAlign = TextAlign.Center,
style = MaterialTheme.appTypography.titleMedium
@@ -225,7 +225,7 @@ private fun VideoSettingsScreen(
@Preview(uiMode = UI_MODE_NIGHT_YES)
@Composable
private fun VideoSettingsScreenPreview() {
- NewEdxTheme {
+ OpenEdXTheme {
VideoSettingsScreen(
windowSize = WindowSize(WindowType.Compact, WindowType.Compact),
wifiDownloadChanged = {},
diff --git a/profile/src/main/java/com/raccoongang/profile/presentation/settings/video/VideoSettingsViewModel.kt b/profile/src/main/java/org/openedx/profile/presentation/settings/video/VideoSettingsViewModel.kt
similarity index 79%
rename from profile/src/main/java/com/raccoongang/profile/presentation/settings/video/VideoSettingsViewModel.kt
rename to profile/src/main/java/org/openedx/profile/presentation/settings/video/VideoSettingsViewModel.kt
index 788322665..4ad67259f 100644
--- a/profile/src/main/java/com/raccoongang/profile/presentation/settings/video/VideoSettingsViewModel.kt
+++ b/profile/src/main/java/org/openedx/profile/presentation/settings/video/VideoSettingsViewModel.kt
@@ -1,14 +1,14 @@
-package com.raccoongang.profile.presentation.settings.video
+package org.openedx.profile.presentation.settings.video
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
-import com.raccoongang.core.BaseViewModel
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.domain.model.VideoSettings
-import com.raccoongang.profile.system.notifier.ProfileNotifier
-import com.raccoongang.profile.system.notifier.VideoQualityChanged
+import org.openedx.core.BaseViewModel
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.domain.model.VideoSettings
+import org.openedx.profile.system.notifier.ProfileNotifier
+import org.openedx.profile.system.notifier.VideoQualityChanged
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
diff --git a/profile/src/main/java/org/openedx/profile/system/notifier/AccountDeactivated.kt b/profile/src/main/java/org/openedx/profile/system/notifier/AccountDeactivated.kt
new file mode 100644
index 000000000..71f1d8d92
--- /dev/null
+++ b/profile/src/main/java/org/openedx/profile/system/notifier/AccountDeactivated.kt
@@ -0,0 +1,3 @@
+package org.openedx.profile.system.notifier
+
+class AccountDeactivated : ProfileEvent
\ No newline at end of file
diff --git a/profile/src/main/java/org/openedx/profile/system/notifier/AccountUpdated.kt b/profile/src/main/java/org/openedx/profile/system/notifier/AccountUpdated.kt
new file mode 100644
index 000000000..26e940a3a
--- /dev/null
+++ b/profile/src/main/java/org/openedx/profile/system/notifier/AccountUpdated.kt
@@ -0,0 +1,3 @@
+package org.openedx.profile.system.notifier
+
+class AccountUpdated : ProfileEvent
\ No newline at end of file
diff --git a/profile/src/main/java/org/openedx/profile/system/notifier/ProfileEvent.kt b/profile/src/main/java/org/openedx/profile/system/notifier/ProfileEvent.kt
new file mode 100644
index 000000000..e95caacdb
--- /dev/null
+++ b/profile/src/main/java/org/openedx/profile/system/notifier/ProfileEvent.kt
@@ -0,0 +1,4 @@
+package org.openedx.profile.system.notifier
+
+interface ProfileEvent {
+}
\ No newline at end of file
diff --git a/profile/src/main/java/com/raccoongang/profile/system/notifier/ProfileNotifier.kt b/profile/src/main/java/org/openedx/profile/system/notifier/ProfileNotifier.kt
similarity index 91%
rename from profile/src/main/java/com/raccoongang/profile/system/notifier/ProfileNotifier.kt
rename to profile/src/main/java/org/openedx/profile/system/notifier/ProfileNotifier.kt
index e4cb29798..4d826c5d9 100644
--- a/profile/src/main/java/com/raccoongang/profile/system/notifier/ProfileNotifier.kt
+++ b/profile/src/main/java/org/openedx/profile/system/notifier/ProfileNotifier.kt
@@ -1,4 +1,4 @@
-package com.raccoongang.profile.system.notifier
+package org.openedx.profile.system.notifier
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
diff --git a/profile/src/main/java/org/openedx/profile/system/notifier/VideoQualityChanged.kt b/profile/src/main/java/org/openedx/profile/system/notifier/VideoQualityChanged.kt
new file mode 100644
index 000000000..4f372db31
--- /dev/null
+++ b/profile/src/main/java/org/openedx/profile/system/notifier/VideoQualityChanged.kt
@@ -0,0 +1,3 @@
+package org.openedx.profile.system.notifier
+
+class VideoQualityChanged : ProfileEvent
\ No newline at end of file
diff --git a/profile/src/test/java/com/raccoongang/profile/presentation/edit/EditProfileViewModelTest.kt b/profile/src/test/java/org/openedx/profile/presentation/edit/EditProfileViewModelTest.kt
similarity index 81%
rename from profile/src/test/java/com/raccoongang/profile/presentation/edit/EditProfileViewModelTest.kt
rename to profile/src/test/java/org/openedx/profile/presentation/edit/EditProfileViewModelTest.kt
index 41ec01e2e..0cef5b03a 100644
--- a/profile/src/test/java/com/raccoongang/profile/presentation/edit/EditProfileViewModelTest.kt
+++ b/profile/src/test/java/org/openedx/profile/presentation/edit/EditProfileViewModelTest.kt
@@ -1,14 +1,15 @@
-package com.raccoongang.profile.presentation.edit
+package org.openedx.profile.presentation.edit
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.domain.model.Account
-import com.raccoongang.core.domain.model.ProfileImage
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.profile.domain.interactor.ProfileInteractor
-import com.raccoongang.profile.system.notifier.AccountUpdated
-import com.raccoongang.profile.system.notifier.ProfileNotifier
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.domain.model.Account
+import org.openedx.core.domain.model.ProfileImage
+import org.openedx.core.system.ResourceManager
+import org.openedx.profile.domain.interactor.ProfileInteractor
+import org.openedx.profile.presentation.ProfileAnalytics
+import org.openedx.profile.system.notifier.AccountUpdated
+import org.openedx.profile.system.notifier.ProfileNotifier
import io.mockk.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -33,6 +34,7 @@ class EditProfileViewModelTest {
private val resourceManager = mockk()
private val interactor = mockk()
private val notifier = mockk()
+ private val analytics = mockk()
private val account = Account(
username = "thom84",
@@ -71,7 +73,8 @@ class EditProfileViewModelTest {
@Test
fun `updateAccount no internet connection`() = runTest {
- val viewModel = EditProfileViewModel(interactor, resourceManager, notifier, account)
+ val viewModel =
+ EditProfileViewModel(interactor, resourceManager, notifier, analytics, account)
coEvery { interactor.updateAccount(any()) } throws UnknownHostException()
viewModel.updateAccount(emptyMap())
advanceUntilIdle()
@@ -85,9 +88,10 @@ class EditProfileViewModelTest {
@Test
fun `updateAccount unknown exception`() = runTest {
- val viewModel = EditProfileViewModel(interactor, resourceManager, notifier, account)
+ val viewModel =
+ EditProfileViewModel(interactor, resourceManager, notifier, analytics, account)
coEvery { interactor.updateAccount(any()) } throws Exception()
-
+
viewModel.updateAccount(emptyMap())
advanceUntilIdle()
@@ -100,7 +104,8 @@ class EditProfileViewModelTest {
@Test
fun `updateAccount success`() = runTest {
- val viewModel = EditProfileViewModel(interactor, resourceManager, notifier, account)
+ val viewModel =
+ EditProfileViewModel(interactor, resourceManager, notifier, analytics, account)
coEvery { interactor.updateAccount(any()) } returns account
coEvery { notifier.send(any()) } returns Unit
viewModel.updateAccount(emptyMap())
@@ -114,11 +119,12 @@ class EditProfileViewModelTest {
@Test
fun `updateAccountAndImage no internet connection`() = runTest {
- val viewModel = EditProfileViewModel(interactor, resourceManager, notifier, account)
+ val viewModel =
+ EditProfileViewModel(interactor, resourceManager, notifier, analytics, account)
coEvery { interactor.setProfileImage(any(), any()) } throws UnknownHostException()
coEvery { interactor.updateAccount(any()) } returns account
coEvery { notifier.send(AccountUpdated()) } returns Unit
-
+
viewModel.updateAccountAndImage(emptyMap(), file, "")
advanceUntilIdle()
@@ -133,11 +139,12 @@ class EditProfileViewModelTest {
@Test
fun `updateAccountAndImage unknown exception`() = runTest {
- val viewModel = EditProfileViewModel(interactor, resourceManager, notifier, account)
+ val viewModel =
+ EditProfileViewModel(interactor, resourceManager, notifier, analytics, account)
coEvery { interactor.setProfileImage(any(), any()) } throws Exception()
coEvery { interactor.updateAccount(any()) } returns account
coEvery { notifier.send(AccountUpdated()) } returns Unit
-
+
viewModel.updateAccountAndImage(emptyMap(), file, "")
advanceUntilIdle()
@@ -152,11 +159,12 @@ class EditProfileViewModelTest {
@Test
fun `updateAccountAndImage success`() = runTest {
- val viewModel = EditProfileViewModel(interactor, resourceManager, notifier, account)
+ val viewModel =
+ EditProfileViewModel(interactor, resourceManager, notifier, analytics, account)
coEvery { interactor.setProfileImage(any(), any()) } returns Unit
coEvery { interactor.updateAccount(any()) } returns account
coEvery { notifier.send(any()) } returns Unit
-
+
viewModel.updateAccountAndImage(emptyMap(), file, "")
advanceUntilIdle()
@@ -170,7 +178,8 @@ class EditProfileViewModelTest {
@Test
fun `setImageUri set new value`() {
- val viewModel = EditProfileViewModel(interactor, resourceManager, notifier, account)
+ val viewModel =
+ EditProfileViewModel(interactor, resourceManager, notifier, analytics, account)
viewModel.setImageUri(mockk())
assert(viewModel.selectedImageUri.value != null)
diff --git a/profile/src/test/java/com/raccoongang/profile/presentation/profile/ProfileViewModelTest.kt b/profile/src/test/java/org/openedx/profile/presentation/profile/ProfileViewModelTest.kt
similarity index 84%
rename from profile/src/test/java/com/raccoongang/profile/presentation/profile/ProfileViewModelTest.kt
rename to profile/src/test/java/org/openedx/profile/presentation/profile/ProfileViewModelTest.kt
index ae7a671fe..d27e95851 100644
--- a/profile/src/test/java/com/raccoongang/profile/presentation/profile/ProfileViewModelTest.kt
+++ b/profile/src/test/java/org/openedx/profile/presentation/profile/ProfileViewModelTest.kt
@@ -1,23 +1,25 @@
-package com.raccoongang.profile.presentation.profile
+package org.openedx.profile.presentation.profile
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.LifecycleRegistry
-import com.raccoongang.core.R
-import com.raccoongang.core.UIMessage
-import com.raccoongang.core.data.storage.PreferencesManager
-import com.raccoongang.core.domain.model.Account
-import com.raccoongang.core.module.DownloadWorkerController
-import com.raccoongang.core.system.AppCookieManager
-import com.raccoongang.core.system.ResourceManager
-import com.raccoongang.profile.domain.interactor.ProfileInteractor
-import com.raccoongang.profile.system.notifier.AccountUpdated
-import com.raccoongang.profile.system.notifier.ProfileNotifier
+import org.openedx.core.R
+import org.openedx.core.UIMessage
+import org.openedx.core.data.storage.PreferencesManager
+import org.openedx.core.domain.model.Account
+import org.openedx.core.module.DownloadWorkerController
+import org.openedx.core.system.AppCookieManager
+import org.openedx.core.system.ResourceManager
+import org.openedx.profile.domain.interactor.ProfileInteractor
+import org.openedx.profile.presentation.ProfileAnalytics
+import org.openedx.profile.system.notifier.AccountUpdated
+import org.openedx.profile.system.notifier.ProfileNotifier
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
import io.mockk.mockk
+import io.mockk.verify
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flow
@@ -45,6 +47,7 @@ class ProfileViewModelTest {
private val notifier = mockk()
private val cookieManager = mockk()
private val workerController = mockk()
+ private val analytics = mockk()
private val account = mockk()
@@ -73,7 +76,8 @@ class ProfileViewModelTest {
notifier,
dispatcher,
cookieManager,
- workerController
+ workerController,
+ analytics
)
coEvery { interactor.getAccount() } throws UnknownHostException()
advanceUntilIdle()
@@ -95,7 +99,8 @@ class ProfileViewModelTest {
notifier,
dispatcher,
cookieManager,
- workerController
+ workerController,
+ analytics
)
coEvery { interactor.getAccount() } throws Exception()
advanceUntilIdle()
@@ -117,7 +122,8 @@ class ProfileViewModelTest {
notifier,
dispatcher,
cookieManager,
- workerController
+ workerController,
+ analytics
)
coEvery { interactor.getAccount() } returns account
every { preferencesManager.profile = any() } returns Unit
@@ -139,7 +145,8 @@ class ProfileViewModelTest {
notifier,
dispatcher,
cookieManager,
- workerController
+ workerController,
+ analytics
)
coEvery { interactor.logout() } throws UnknownHostException()
coEvery { workerController.cancelWork() } returns Unit
@@ -164,7 +171,8 @@ class ProfileViewModelTest {
notifier,
dispatcher,
cookieManager,
- workerController
+ workerController,
+ analytics
)
coEvery { interactor.logout() } throws Exception()
coEvery { workerController.cancelWork() } returns Unit
@@ -185,11 +193,13 @@ class ProfileViewModelTest {
preferencesManager,
resourceManager,
notifier,
- dispatcherIO,
+ dispatcher,
cookieManager,
- workerController
+ workerController,
+ analytics
)
coEvery { interactor.getAccount() } returns mockk()
+ every { analytics.logoutEvent(false) } returns Unit
every { preferencesManager.profile = any() } returns Unit
coEvery { interactor.logout() } returns Unit
coEvery { workerController.cancelWork() } returns Unit
@@ -197,6 +207,7 @@ class ProfileViewModelTest {
viewModel.logout()
advanceUntilIdle()
coVerify(exactly = 1) { interactor.logout() }
+ verify { analytics.logoutEvent(false) }
assert(viewModel.uiMessage.value == null)
assert(viewModel.successLogout.value == true)
@@ -210,9 +221,10 @@ class ProfileViewModelTest {
preferencesManager,
resourceManager,
notifier,
- dispatcherIO,
+ dispatcher,
cookieManager,
- workerController
+ workerController,
+ analytics
)
every { notifier.notifier } returns flow { emit(AccountUpdated()) }
val mockLifeCycleOwner: LifecycleOwner = mockk()
diff --git a/settings.gradle b/settings.gradle
index 4cd302a0d..1bb570281 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -12,7 +12,7 @@ dependencyResolutionManagement {
mavenCentral()
}
}
-rootProject.name = "NewEdX"
+rootProject.name = "OpenEdX"
include ':app'
include ':core'
include ':auth'