Skip to content

Commit

Permalink
refactor: Reorganize offline logic for better readability (openedx#386)
Browse files Browse the repository at this point in the history
  • Loading branch information
PavloNetrebchuk authored Oct 17, 2024
1 parent bbf71e3 commit 1daedbe
Show file tree
Hide file tree
Showing 3 changed files with 132 additions and 181 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import android.content.Context
import androidx.work.OneTimeWorkRequestBuilder
import androidx.work.WorkInfo
import androidx.work.WorkManager
import com.google.common.util.concurrent.ListenableFuture
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
Expand All @@ -14,7 +13,6 @@ import org.openedx.core.module.db.DownloadModelEntity
import org.openedx.core.module.db.DownloadedState
import org.openedx.core.module.download.FileDownloader
import java.io.File
import java.util.concurrent.ExecutionException

class DownloadWorkerController(
context: Context,
Expand All @@ -23,7 +21,6 @@ class DownloadWorkerController(
) {

private val workManager = WorkManager.getInstance(context)

private var downloadTaskList = listOf<DownloadModel>()

init {
Expand All @@ -46,16 +43,15 @@ class DownloadWorkerController(
}

private suspend fun updateList() {
downloadTaskList =
downloadDao.getAllDataFlow().first().map { it.mapToDomain() }.filter {
downloadTaskList = downloadDao.getAllDataFlow().first()
.map { it.mapToDomain() }
.filter {
it.downloadedState == DownloadedState.WAITING || it.downloadedState == DownloadedState.DOWNLOADING
}
}

suspend fun saveModels(downloadModels: List<DownloadModel>) {
downloadDao.insertDownloadModel(
downloadModels.map { DownloadModelEntity.createFrom(it) }
)
downloadDao.insertDownloadModel(downloadModels.map { DownloadModelEntity.createFrom(it) })
}

suspend fun removeModel(id: String) {
Expand All @@ -69,11 +65,9 @@ class DownloadWorkerController(

downloadModels.forEach { downloadModel ->
removeIds.add(downloadModel.id)

if (downloadModel.downloadedState == DownloadedState.DOWNLOADING) {
hasDownloading = true
}

try {
File(downloadModel.path).delete()
} catch (e: Exception) {
Expand All @@ -97,19 +91,14 @@ class DownloadWorkerController(
workManager.cancelAllWorkByTag(DownloadWorker.WORKER_TAG)
}


private fun isWorkScheduled(tag: String): Boolean {
val statuses: ListenableFuture<List<WorkInfo>> = workManager.getWorkInfosByTag(tag)
val statuses = workManager.getWorkInfosByTag(tag)
return try {
val workInfoList: List<WorkInfo> = statuses.get()
val workInfo = workInfoList.find {
(it.state == WorkInfo.State.RUNNING) or (it.state == WorkInfo.State.ENQUEUED)
val workInfo = statuses.get().find {
it.state == WorkInfo.State.RUNNING || it.state == WorkInfo.State.ENQUEUED
}
workInfo != null
} catch (e: ExecutionException) {
e.printStackTrace()
false
} catch (e: InterruptedException) {
} catch (e: Exception) {
e.printStackTrace()
false
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,91 +27,44 @@ class DownloadDialogManager(
}

private val uiState = MutableSharedFlow<DownloadDialogUIState>()
private val coroutineScope = CoroutineScope(Dispatchers.IO)

init {
CoroutineScope(Dispatchers.IO).launch {
uiState.collect { uiState ->
when {
uiState.isDownloadFailed -> {
val dialog = DownloadErrorDialogFragment.newInstance(
dialogType = DownloadErrorDialogType.DOWNLOAD_FAILED,
uiState = uiState
)
dialog.show(
uiState.fragmentManager,
DownloadErrorDialogFragment.DIALOG_TAG
)
}

uiState.isAllBlocksDownloaded -> {
val dialog = DownloadConfirmDialogFragment.newInstance(
dialogType = DownloadConfirmDialogType.REMOVE,
uiState = uiState
)
dialog.show(
uiState.fragmentManager,
DownloadConfirmDialogFragment.DIALOG_TAG
)
}

!networkConnection.isOnline() -> {
val dialog = DownloadErrorDialogFragment.newInstance(
dialogType = DownloadErrorDialogType.NO_CONNECTION,
uiState = uiState
)
dialog.show(
uiState.fragmentManager,
DownloadErrorDialogFragment.DIALOG_TAG
)
}

StorageManager.getFreeStorage() < uiState.sizeSum * DOWNLOAD_SIZE_FACTOR -> {
val dialog = DownloadStorageErrorDialogFragment.newInstance(
uiState = uiState
)
dialog.show(
uiState.fragmentManager,
DownloadStorageErrorDialogFragment.DIALOG_TAG
)
}

corePreferences.videoSettings.wifiDownloadOnly && !networkConnection.isWifiConnected() -> {
val dialog = DownloadErrorDialogFragment.newInstance(
dialogType = DownloadErrorDialogType.WIFI_REQUIRED,
uiState = uiState
)
dialog.show(
uiState.fragmentManager,
DownloadErrorDialogFragment.DIALOG_TAG
)
}

!corePreferences.videoSettings.wifiDownloadOnly && !networkConnection.isWifiConnected() -> {
val dialog = DownloadConfirmDialogFragment.newInstance(
dialogType = DownloadConfirmDialogType.DOWNLOAD_ON_CELLULAR,
uiState = uiState
)
dialog.show(
uiState.fragmentManager,
DownloadConfirmDialogFragment.DIALOG_TAG
)
}

uiState.sizeSum >= MAX_CELLULAR_SIZE -> {
val dialog = DownloadConfirmDialogFragment.newInstance(
dialogType = DownloadConfirmDialogType.CONFIRM,
uiState = uiState
)
dialog.show(
uiState.fragmentManager,
DownloadConfirmDialogFragment.DIALOG_TAG
)
}

else -> {
uiState.saveDownloadModels()
}
coroutineScope.launch {
uiState.collect { state ->
val dialog = when {
state.isDownloadFailed -> DownloadErrorDialogFragment.newInstance(
dialogType = DownloadErrorDialogType.DOWNLOAD_FAILED, uiState = state
)

state.isAllBlocksDownloaded -> DownloadConfirmDialogFragment.newInstance(
dialogType = DownloadConfirmDialogType.REMOVE, uiState = state
)

!networkConnection.isOnline() -> DownloadErrorDialogFragment.newInstance(
dialogType = DownloadErrorDialogType.NO_CONNECTION, uiState = state
)

StorageManager.getFreeStorage() < state.sizeSum * DOWNLOAD_SIZE_FACTOR -> DownloadStorageErrorDialogFragment.newInstance(
uiState = state
)

corePreferences.videoSettings.wifiDownloadOnly && !networkConnection.isWifiConnected() -> DownloadErrorDialogFragment.newInstance(
dialogType = DownloadErrorDialogType.WIFI_REQUIRED, uiState = state
)

!corePreferences.videoSettings.wifiDownloadOnly && !networkConnection.isWifiConnected() -> DownloadConfirmDialogFragment.newInstance(
dialogType = DownloadConfirmDialogType.DOWNLOAD_ON_CELLULAR, uiState = state
)

state.sizeSum >= MAX_CELLULAR_SIZE -> DownloadConfirmDialogFragment.newInstance(
dialogType = DownloadConfirmDialogType.CONFIRM, uiState = state
)

else -> null
}

dialog?.show(state.fragmentManager, dialog::class.java.simpleName) ?: state.saveDownloadModels()
}
}
}
Expand Down Expand Up @@ -141,7 +94,7 @@ class DownloadDialogManager(
fragmentManager: FragmentManager,
removeDownloadModels: () -> Unit,
) {
CoroutineScope(Dispatchers.IO).launch {
coroutineScope.launch {
uiState.emit(
DownloadDialogUIState(
downloadDialogItems = listOf(downloadDialogItem),
Expand All @@ -161,39 +114,44 @@ class DownloadDialogManager(
fragmentManager: FragmentManager,
) {
createDownloadItems(
downloadModel = downloadModel,
downloadModels = downloadModel,
fragmentManager = fragmentManager,
)
}

private fun createDownloadItems(
downloadModel: List<DownloadModel>,
downloadModels: List<DownloadModel>,
fragmentManager: FragmentManager,
) {
CoroutineScope(Dispatchers.IO).launch {
val courseIds = downloadModel.map { it.courseId }.distinct()
val blockIds = downloadModel.map { it.id }
coroutineScope.launch {
val courseIds = downloadModels.map { it.courseId }.distinct()
val blockIds = downloadModels.map { it.id }
val notDownloadedSubSections = mutableListOf<Block>()
val allDownloadDialogItems = mutableListOf<DownloadDialogItem>()

courseIds.forEach { courseId ->
val courseStructure = interactor.getCourseStructureFromCache(courseId)
val allSubSectionBlocks = courseStructure.blockData.filter { it.type == BlockType.SEQUENTIAL }
allSubSectionBlocks.forEach { subSectionsBlock ->
val verticalBlocks = courseStructure.blockData.filter { it.id in subSectionsBlock.descendants }

allSubSectionBlocks.forEach { subSectionBlock ->
val verticalBlocks = courseStructure.blockData.filter { it.id in subSectionBlock.descendants }
val blocks = courseStructure.blockData.filter {
it.id in verticalBlocks.flatMap { it.descendants } && it.id in blockIds
}
val size = blocks.sumOf { getFileSize(it) }
if (blocks.isNotEmpty()) notDownloadedSubSections.add(subSectionsBlock)
if (size > 0) {
val downloadDialogItem = DownloadDialogItem(
title = subSectionsBlock.displayName,
size = size
val totalSize = blocks.sumOf { getFileSize(it) }

if (blocks.isNotEmpty()) notDownloadedSubSections.add(subSectionBlock)
if (totalSize > 0) {
allDownloadDialogItems.add(
DownloadDialogItem(
title = subSectionBlock.displayName,
size = totalSize
)
)
allDownloadDialogItems.add(downloadDialogItem)
}
}
}

uiState.emit(
DownloadDialogUIState(
downloadDialogItems = allDownloadDialogItems,
Expand All @@ -203,8 +161,8 @@ class DownloadDialogManager(
fragmentManager = fragmentManager,
removeDownloadModels = {},
saveDownloadModels = {
CoroutineScope(Dispatchers.IO).launch {
workerController.saveModels(downloadModel)
coroutineScope.launch {
workerController.saveModels(downloadModels)
}
}
)
Expand All @@ -221,12 +179,12 @@ class DownloadDialogManager(
removeDownloadModels: (blockId: String) -> Unit,
saveDownloadModels: (blockId: String) -> Unit,
) {
CoroutineScope(Dispatchers.IO).launch {
coroutineScope.launch {
val courseStructure = interactor.getCourseStructure(courseId, false)
val downloadModelIds = interactor.getAllDownloadModels().map { it.id }

val downloadDialogItems = subSectionsBlocks.mapNotNull { subSectionsBlock ->
val verticalBlocks = courseStructure.blockData.filter { it.id in subSectionsBlock.descendants }
val downloadDialogItems = subSectionsBlocks.mapNotNull { subSectionBlock ->
val verticalBlocks = courseStructure.blockData.filter { it.id in subSectionBlock.descendants }
val blocks = verticalBlocks.flatMap { verticalBlock ->
courseStructure.blockData.filter {
it.id in verticalBlock.descendants &&
Expand All @@ -235,7 +193,7 @@ class DownloadDialogManager(
}
}
val size = blocks.sumOf { getFileSize(it) }
if (size > 0) DownloadDialogItem(title = subSectionsBlock.displayName, size = size) else null
if (size > 0) DownloadDialogItem(title = subSectionBlock.displayName, size = size) else null
}

uiState.emit(
Expand All @@ -252,12 +210,11 @@ class DownloadDialogManager(
}
}


private fun getFileSize(block: Block): Long {
return when {
block.type == BlockType.VIDEO -> block.downloadModel?.size ?: 0
block.isxBlock -> block.offlineDownload?.fileSize ?: 0
else -> 0
block.type == BlockType.VIDEO -> block.downloadModel?.size ?: 0L
block.isxBlock -> block.offlineDownload?.fileSize ?: 0L
else -> 0L
}
}
}
Loading

0 comments on commit 1daedbe

Please sign in to comment.