Skip to content

Commit

Permalink
feat: [FC-0047] Full-Bleed Header + Top Navigation (openedx#278)
Browse files Browse the repository at this point in the history
* feat: New header and navigation on CourseContainerFragment

* feat: Changed logic of swipe to refresh, added blur for android <= 12, minor code refactoring

* feat: removed COURSE_TOP_BAR_ENABLED and COURSE_BANNER_ENABLED feature flags

* feat: special header style for android < 12

* fix: Fixed header image blur for android < 12. Added courseContainerTabClickedEvent

* feat: auto scrolling header to  collapsed and expanded states

* fix: Fixed junit tests

* fix: Fixed CourseContainerViewModelTest

* fix: auto scroll fling fix

* feat: CourseContainerFragment refactoring

* fix: Removed expanded header content top padding

* fix: Collapsing header and navigation tabs UI fixes

* fix: Fixes according to PR feedback

* refactor: Course home tabs layout

* fix: Fixes according to PR feedback

* fix: Fixes according to PR feedback

* refactor: Refactored view models of course container screens

* fix: Fixes according to PR feedback
  • Loading branch information
PavloNetrebchuk authored Apr 30, 2024
1 parent 0494642 commit 29e1c8e
Show file tree
Hide file tree
Showing 57 changed files with 2,597 additions and 1,667 deletions.
2 changes: 0 additions & 2 deletions Documentation/ConfigurationManagement.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,6 @@ android:
- **WHATS_NEW_ENABLED:** Enables the "What's New" feature to present the latest changes to the user.
- **SOCIAL_AUTH_ENABLED:** Enables SSO buttons on the SignIn and SignUp screens.
- **COURSE_NESTED_LIST_ENABLED:** Enables an alternative visual representation for the course structure.
- **COURSE_BANNER_ENABLED:** Enables the display of the course image on the Course Home screen.
- **COURSE_TOP_TAB_BAR_ENABLED:** Enables an alternative navigation on the Course Home screen.
- **COURSE_UNIT_PROGRESS_ENABLED:** Enables the display of the unit progress within the courseware.

## Future Support
Expand Down
3 changes: 3 additions & 0 deletions app/src/main/java/org/openedx/app/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import org.openedx.auth.presentation.sso.FacebookAuthHelper
import org.openedx.auth.presentation.sso.GoogleAuthHelper
import org.openedx.auth.presentation.sso.MicrosoftAuthHelper
import org.openedx.auth.presentation.sso.OAuthHelper
import org.openedx.core.ImageProcessor
import org.openedx.core.config.Config
import org.openedx.core.data.model.CourseEnrollments
import org.openedx.core.data.storage.CorePreferences
Expand Down Expand Up @@ -81,6 +82,8 @@ val appModule = module {
single { ReviewManagerFactory.create(get()) }
single { CalendarManager(get(), get(), get()) }

single { ImageProcessor(get()) }

single<Gson> {
GsonBuilder()
.registerTypeAdapter(CourseEnrollments::class.java, CourseEnrollments.Deserializer())
Expand Down
18 changes: 9 additions & 9 deletions app/src/main/java/org/openedx/app/di/ScreenModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,14 @@ val screenModule = module {
get(),
get(),
get(),
get(),
get()
)
}
viewModel { (courseId: String) ->
viewModel { (courseId: String, courseTitle: String) ->
CourseOutlineViewModel(
courseId,
courseTitle,
get(),
get(),
get(),
Expand Down Expand Up @@ -234,9 +237,10 @@ val screenModule = module {
get(),
)
}
viewModel { (courseId: String) ->
viewModel { (courseId: String, courseTitle: String) ->
CourseVideoViewModel(
courseId,
courseTitle,
get(),
get(),
get(),
Expand Down Expand Up @@ -275,11 +279,8 @@ val screenModule = module {
get(),
)
}
viewModel { (courseId: String, courseName: String, isSelfPaced: Boolean, enrollmentMode: String) ->
viewModel { (enrollmentMode: String) ->
CourseDatesViewModel(
courseId,
courseName,
isSelfPaced,
enrollmentMode,
get(),
get(),
Expand All @@ -288,7 +289,6 @@ val screenModule = module {
get(),
get(),
get(),
get(),
)
}
viewModel { (courseId: String, handoutsType: String) ->
Expand All @@ -305,13 +305,13 @@ val screenModule = module {

single { DiscussionRepository(get(), get(), get()) }
factory { DiscussionInteractor(get()) }
viewModel { (courseId: String) ->
viewModel {
DiscussionTopicsViewModel(
get(),
get(),
get(),
get(),
courseId
get()
)
}
viewModel { (courseId: String, topicId: String, threadType: String) ->
Expand Down
1 change: 1 addition & 0 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ dependencies {
// Koin DI
api "io.insert-koin:koin-core:$koin_version"
api "io.insert-koin:koin-android:$koin_version"
api "io.insert-koin:koin-androidx-compose:$koin_version"

api "io.coil-kt:coil-compose:$coil_version"
api "io.coil-kt:coil-gif:$coil_version"
Expand Down
57 changes: 57 additions & 0 deletions core/src/main/java/org/openedx/core/ImageProcessor.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
@file:Suppress("DEPRECATION")

package org.openedx.core

import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.renderscript.Allocation
import android.renderscript.RenderScript
import android.renderscript.ScriptIntrinsicBlur
import androidx.annotation.DrawableRes
import coil.ImageLoader
import coil.request.ImageRequest

class ImageProcessor(private val context: Context) {
fun loadImage(
@DrawableRes
defaultImage: Int,
imageUrl: String,
onComplete: (result: Drawable) -> Unit
) {
val loader = ImageLoader(context)
val request = ImageRequest.Builder(context)
.data(imageUrl)
.target { result ->
onComplete(result)
}
.error(defaultImage)
.placeholder(defaultImage)
.allowHardware(false)
.build()
loader.enqueue(request)
}

fun applyBlur(
bitmap: Bitmap,
blurRadio: Float
): Bitmap {
val renderScript = RenderScript.create(context)
val bitmapAlloc = Allocation.createFromBitmap(renderScript, bitmap)
ScriptIntrinsicBlur.create(renderScript, bitmapAlloc.element).apply {
setRadius(blurRadio)
setInput(bitmapAlloc)
repeat(3) {
forEach(bitmapAlloc)
}
}
val newBitmap: Bitmap = Bitmap.createBitmap(
bitmap.width,
bitmap.height,
Bitmap.Config.ARGB_8888
)
bitmapAlloc.copyTo(newBitmap)
renderScript.destroy()
return newBitmap
}
}
10 changes: 0 additions & 10 deletions core/src/main/java/org/openedx/core/config/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,6 @@ class Config(context: Context) {
return getBoolean(COURSE_NESTED_LIST_ENABLED, false)
}

fun isCourseBannerEnabled(): Boolean {
return getBoolean(COURSE_BANNER_ENABLED, true)
}

fun isCourseTopTabBarEnabled(): Boolean {
return getBoolean(COURSE_TOP_TAB_BAR_ENABLED, false)
}

fun isCourseUnitProgressEnabled(): Boolean {
return getBoolean(COURSE_UNIT_PROGRESS_ENABLED, false)
}
Expand Down Expand Up @@ -174,8 +166,6 @@ class Config(context: Context) {
private const val PROGRAM = "PROGRAM"
private const val BRANCH = "BRANCH"
private const val COURSE_NESTED_LIST_ENABLED = "COURSE_NESTED_LIST_ENABLED"
private const val COURSE_BANNER_ENABLED = "COURSE_BANNER_ENABLED"
private const val COURSE_TOP_TAB_BAR_ENABLED = "COURSE_TOP_TAB_BAR_ENABLED"
private const val COURSE_UNIT_PROGRESS_ENABLED = "COURSE_UNIT_PROGRESS_ENABLED"
private const val PLATFORM_NAME = "PLATFORM_NAME"
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.openedx.core.module.download

import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
Expand Down Expand Up @@ -41,8 +40,7 @@ abstract class BaseDownloadViewModel(
private val _downloadingModelsFlow = MutableSharedFlow<List<DownloadModel>>()
protected val downloadingModelsFlow = _downloadingModelsFlow.asSharedFlow()

override fun onCreate(owner: LifecycleOwner) {
super.onCreate(owner)
init {
viewModelScope.launch {
downloadDao.readAllData().map { list -> list.map { it.mapToDomain() } }
.collect { downloadModels ->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.openedx.core.presentation.course

import androidx.annotation.StringRes
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Chat
import androidx.compose.material.icons.automirrored.filled.TextSnippet
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.outlined.CalendarMonth
import androidx.compose.material.icons.rounded.PlayCircleFilled
import androidx.compose.ui.graphics.vector.ImageVector
import org.openedx.core.R
import org.openedx.core.ui.TabItem

enum class CourseContainerTab(
@StringRes
override val labelResId: Int,
override val icon: ImageVector
) : TabItem {
HOME(R.string.core_course_container_nav_home, Icons.Default.Home),
VIDEOS(R.string.core_course_container_nav_videos, Icons.Rounded.PlayCircleFilled),
DATES(R.string.core_course_container_nav_dates, Icons.Outlined.CalendarMonth),
DISCUSSIONS(R.string.core_course_container_nav_discussions, Icons.AutoMirrored.Filled.Chat),
MORE(R.string.core_course_container_nav_more, Icons.AutoMirrored.Filled.TextSnippet)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.openedx.core.system.notifier

import org.openedx.core.domain.model.CourseStructure

data class CourseDataReady(val courseStructure: CourseStructure) : CourseEvent
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.openedx.core.system.notifier

object CourseDatesShifted : CourseEvent
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package org.openedx.core.system.notifier

data class CourseLoading(val isLoading: Boolean) : CourseEvent
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,8 @@ class CourseNotifier {
suspend fun send(event: CourseSectionChanged) = channel.emit(event)
suspend fun send(event: CourseCompletionSet) = channel.emit(event)
suspend fun send(event: CalendarSyncEvent) = channel.emit(event)
suspend fun send(event: CourseDatesShifted) = channel.emit(event)
suspend fun send(event: CourseLoading) = channel.emit(event)
suspend fun send(event: CourseDataReady) = channel.emit(event)
suspend fun send(event: CourseRefresh) = channel.emit(event)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package org.openedx.core.system.notifier

import org.openedx.core.presentation.course.CourseContainerTab

data class CourseRefresh(val courseContainerTab: CourseContainerTab) : CourseEvent
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.openedx.core.system.notifier

class CourseStructureUpdated(
val courseId: String,
val withSwipeRefresh: Boolean,
val courseId: String
) : CourseEvent
Loading

0 comments on commit 29e1c8e

Please sign in to comment.