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 @@ - - -