Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Download all videos together #234

Merged
merged 20 commits into from
Feb 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ad7e223
feat: ability to download all videos
dixidroid Feb 1, 2024
f01cbec
feat: confirmation dialog for downloading videos larger than 1 GB
dixidroid Feb 6, 2024
a085f30
refactor: renamed the Video tab to Videos
dixidroid Feb 6, 2024
68f66ef
refactor: hide All videos download element if there is no videos to d…
dixidroid Feb 6, 2024
336b389
fix: bug when unable to see all videos
dixidroid Feb 6, 2024
748afd8
fix: lags when updating downloading state
dixidroid Feb 6, 2024
ede212a
refactor: changed the type of allBlocks to HashMap in the BaseDownloa…
dixidroid Feb 6, 2024
2634bc4
feat: confirmation dialog when trying to remove all downloads
dixidroid Feb 7, 2024
46d3690
feat: show download progress on the download queue screen
dixidroid Feb 8, 2024
94e7102
feat: view downloads for subsection
dixidroid Feb 8, 2024
568a793
refactor: changed how all modules are download
dixidroid Feb 12, 2024
66e5f0e
refactor: optimized way to remove models
dixidroid Feb 13, 2024
912be12
refactor: changed the way the download progress is displayed
dixidroid Feb 14, 2024
a968960
feat: added confirmation dialogs
dixidroid Feb 14, 2024
88f78fa
feat: show Untitled title if the block has no title
dixidroid Feb 14, 2024
3478023
refactor: removed unused logs
dixidroid Feb 14, 2024
d5cd197
fix: after rebase
dixidroid Feb 15, 2024
655edf6
fix: after rebase
dixidroid Feb 15, 2024
3a7acdf
refactor: change the name of the Discussion tab to Discussions
dixidroid Feb 15, 2024
5adba97
fix: fixed issues after PR
dixidroid Feb 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<queries>
<intent>
Expand Down
12 changes: 9 additions & 3 deletions app/src/main/java/org/openedx/app/AppRouter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import org.openedx.core.presentation.course.CourseViewMode
import org.openedx.core.presentation.global.app_upgrade.AppUpgradeRouter
import org.openedx.core.presentation.global.app_upgrade.UpgradeRequiredFragment
import org.openedx.core.presentation.global.webview.WebContentFragment
import org.openedx.core.presentation.settings.VideoQualityFragment
import org.openedx.core.presentation.settings.VideoQualityType
import org.openedx.course.presentation.CourseRouter
import org.openedx.course.presentation.container.CourseContainerFragment
import org.openedx.course.presentation.container.NoAccessCourseContainerFragment
Expand All @@ -24,6 +26,7 @@ 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.course.settings.download.DownloadQueueFragment
import org.openedx.dashboard.presentation.DashboardRouter
import org.openedx.dashboard.presentation.program.ProgramFragment
import org.openedx.discovery.presentation.DiscoveryRouter
Expand All @@ -44,7 +47,6 @@ import org.openedx.profile.presentation.anothers_account.AnothersProfileFragment
import org.openedx.profile.presentation.delete.DeleteProfileFragment
import org.openedx.profile.presentation.edit.EditProfileFragment
import org.openedx.profile.presentation.profile.ProfileFragment
import org.openedx.profile.presentation.settings.video.VideoQualityFragment
import org.openedx.profile.presentation.settings.video.VideoSettingsFragment
import org.openedx.whatsnew.WhatsNewRouter
import org.openedx.whatsnew.presentation.whatsnew.WhatsNewFragment
Expand Down Expand Up @@ -72,6 +74,10 @@ class AppRouter : AuthRouter, DiscoveryRouter, DashboardRouter, CourseRouter, Di
replaceFragmentWithBackStack(fm, LogistrationFragment.newInstance(courseId))
}

override fun navigateToDownloadQueue(fm: FragmentManager, descendants: List<String>) {
replaceFragmentWithBackStack(fm, DownloadQueueFragment.newInstance(descendants))
}

override fun navigateToRestorePassword(fm: FragmentManager) {
replaceFragmentWithBackStack(fm, RestorePasswordFragment())
}
Expand Down Expand Up @@ -325,8 +331,8 @@ class AppRouter : AuthRouter, DiscoveryRouter, DashboardRouter, CourseRouter, Di
replaceFragmentWithBackStack(fm, VideoSettingsFragment())
}

override fun navigateToVideoQuality(fm: FragmentManager) {
replaceFragmentWithBackStack(fm, VideoQualityFragment())
override fun navigateToVideoQuality(fm: FragmentManager, videoQualityType: VideoQualityType) {
replaceFragmentWithBackStack(fm, VideoQualityFragment.newInstance(videoQualityType.name))
}

override fun navigateToDeleteAccount(fm: FragmentManager) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import org.openedx.app.BuildConfig
import org.openedx.core.data.model.User
import org.openedx.core.data.storage.CorePreferences
import org.openedx.core.data.storage.InAppReviewPreferences
import org.openedx.core.domain.model.VideoQuality
import org.openedx.core.domain.model.VideoSettings
import org.openedx.profile.data.model.Account
import org.openedx.profile.data.storage.ProfilePreferences
Expand All @@ -23,7 +24,9 @@ class PreferencesManager(context: Context) : CorePreferences, ProfilePreferences
}.apply()
}

private fun getString(key: String): String = sharedPreferences.getString(key, "") ?: ""
private fun getString(key: String, defValue: String = ""): String {
return sharedPreferences.getString(key, defValue) ?: defValue
}

private fun saveLong(key: String, value: Long) {
sharedPreferences.edit().apply {
Expand All @@ -39,7 +42,9 @@ class PreferencesManager(context: Context) : CorePreferences, ProfilePreferences
}.apply()
}

private fun getBoolean(key: String): Boolean = sharedPreferences.getBoolean(key, false)
private fun getBoolean(key: String, defValue: Boolean = false): Boolean {
return sharedPreferences.getBoolean(key, defValue)
}

override fun clear() {
sharedPreferences.edit().apply {
Expand Down Expand Up @@ -90,13 +95,22 @@ class PreferencesManager(context: Context) : CorePreferences, ProfilePreferences

override var videoSettings: VideoSettings
set(value) {
val videoSettingsJson = Gson().toJson(value)
saveString(VIDEO_SETTINGS, videoSettingsJson)
saveBoolean(VIDEO_SETTINGS_WIFI_DOWNLOAD_ONLY, value.wifiDownloadOnly)
saveString(VIDEO_SETTINGS_STREAMING_QUALITY, value.videoStreamingQuality.name)
saveString(VIDEO_SETTINGS_DOWNLOAD_QUALITY, value.videoDownloadQuality.name)
}
get() {
val videoSettingsString = getString(VIDEO_SETTINGS)
return Gson().fromJson(videoSettingsString, VideoSettings::class.java)
?: VideoSettings.default
val wifiDownloadOnly = getBoolean(VIDEO_SETTINGS_WIFI_DOWNLOAD_ONLY, defValue = true)
val streamingQualityString =
getString(VIDEO_SETTINGS_STREAMING_QUALITY, defValue = VideoQuality.AUTO.name)
val downloadQualityString =
getString(VIDEO_SETTINGS_DOWNLOAD_QUALITY, defValue = VideoQuality.AUTO.name)

return VideoSettings(
wifiDownloadOnly = wifiDownloadOnly,
videoStreamingQuality = VideoQuality.valueOf(streamingQualityString),
videoDownloadQuality = VideoQuality.valueOf(downloadQualityString)
)
}

override var lastWhatsNewVersion: String
Expand Down Expand Up @@ -132,9 +146,11 @@ class PreferencesManager(context: Context) : CorePreferences, ProfilePreferences
private const val EXPIRES_IN = "expires_in"
private const val USER = "user"
private const val ACCOUNT = "account"
private const val VIDEO_SETTINGS = "video_settings"
private const val LAST_WHATS_NEW_VERSION = "last_whats_new_version"
private const val LAST_REVIEW_VERSION = "last_review_version"
private const val APP_WAS_POSITIVE_RATED = "app_was_positive_rated"
private const val VIDEO_SETTINGS_WIFI_DOWNLOAD_ONLY = "video_settings_wifi_download_only"
private const val VIDEO_SETTINGS_STREAMING_QUALITY = "video_settings_streaming_quality"
private const val VIDEO_SETTINGS_DOWNLOAD_QUALITY = "video_settings_download_quality"
}
}
4 changes: 4 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 @@ -42,6 +42,8 @@ import org.openedx.core.system.notifier.AppUpgradeNotifier
import org.openedx.core.system.notifier.CourseNotifier
import org.openedx.course.domain.interactor.CourseInteractor
import org.openedx.dashboard.notifier.DashboardNotifier
import org.openedx.core.system.notifier.DownloadNotifier
import org.openedx.core.system.notifier.VideoNotifier
import org.openedx.course.presentation.CourseAnalytics
import org.openedx.course.presentation.CourseRouter
import org.openedx.dashboard.presentation.dashboard.DashboardAnalytics
Expand Down Expand Up @@ -79,7 +81,9 @@ val appModule = module {
single { DiscussionNotifier() }
single { ProfileNotifier() }
single { AppUpgradeNotifier() }
single { DownloadNotifier() }
single { DashboardNotifier() }
single { VideoNotifier() }

single { AppRouter() }
single<AuthRouter> { get<AppRouter>() }
Expand Down
16 changes: 14 additions & 2 deletions app/src/main/java/org/openedx/app/di/ScreenModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import org.openedx.auth.presentation.signin.SignInViewModel
import org.openedx.auth.presentation.signup.SignUpViewModel
import org.openedx.core.Validator
import org.openedx.core.presentation.dialog.selectorbottomsheet.SelectDialogViewModel
import org.openedx.core.presentation.settings.VideoQualityViewModel
import org.openedx.course.data.repository.CourseRepository
import org.openedx.course.domain.interactor.CourseInteractor
import org.openedx.course.presentation.container.CourseContainerViewModel
Expand All @@ -27,6 +28,7 @@ import org.openedx.course.presentation.unit.video.EncodedVideoUnitViewModel
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.course.settings.download.DownloadQueueViewModel
import org.openedx.dashboard.data.repository.DashboardRepository
import org.openedx.dashboard.domain.interactor.DashboardInteractor
import org.openedx.dashboard.presentation.dashboard.DashboardViewModel
Expand All @@ -52,7 +54,6 @@ import org.openedx.profile.presentation.anothers_account.AnothersProfileViewMode
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.openedx.whatsnew.presentation.whatsnew.WhatsNewViewModel

Expand Down Expand Up @@ -120,7 +121,7 @@ val screenModule = module {
}
viewModel { (account: Account) -> EditProfileViewModel(get(), get(), get(), get(), account) }
viewModel { VideoSettingsViewModel(get(), get()) }
viewModel { VideoQualityViewModel(get(), get()) }
viewModel { (qualityType: String) -> VideoQualityViewModel(get(), get(), qualityType) }
viewModel { DeleteProfileViewModel(get(), get(), get(), get()) }
viewModel { (username: String) -> AnothersProfileViewModel(get(), get(), username) }

Expand Down Expand Up @@ -210,6 +211,7 @@ val screenModule = module {
get(),
get(),
get(),
get(),
get()
)
}
Expand Down Expand Up @@ -293,6 +295,16 @@ val screenModule = module {
get()
)
}

viewModel { (descendants: List<String>) ->
DownloadQueueViewModel(
descendants,
get(),
get(),
get(),
get()
)
}
viewModel { HtmlUnitViewModel(get(), get(), get(), get()) }

viewModel { ProgramViewModel(get(), get(), get(), get(), get(), get(), get()) }
Expand Down
5 changes: 4 additions & 1 deletion core/src/main/java/org/openedx/core/AppDataConstants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@ object AppDataConstants {

const val VIDEO_FORMAT_M3U8 = ".m3u8"
const val VIDEO_FORMAT_MP4 = ".mp4"
}

// Equal 1GB
const val DOWNLOADS_CONFIRMATION_SIZE = 1024 * 1024 * 1024L
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import org.openedx.core.R

data class VideoSettings(
val wifiDownloadOnly: Boolean,
val videoQuality: VideoQuality
val videoStreamingQuality: VideoQuality,
val videoDownloadQuality: VideoQuality
) {
companion object {
val default = VideoSettings(true, VideoQuality.AUTO)
val default = VideoSettings(true, VideoQuality.AUTO, VideoQuality.AUTO)
}
}

Expand Down
18 changes: 18 additions & 0 deletions core/src/main/java/org/openedx/core/extension/LongExt.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.openedx.core.extension

import kotlin.math.log10
import kotlin.math.pow

fun Long.toFileSize(round: Int = 2): String {
try {
if (this <= 0) return "0"
val units = arrayOf("B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB")
val digitGroups = (log10(this.toDouble()) / log10(1024.0)).toInt()
return String.format(
"%." + round + "f", this / 1024.0.pow(digitGroups.toDouble())
) + " " + units[digitGroups]
} catch (e: Exception) {
println(e.toString())
}
return ""
}
26 changes: 21 additions & 5 deletions core/src/main/java/org/openedx/core/module/DownloadWorker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import androidx.core.app.NotificationCompat
import androidx.work.CoroutineWorker
import androidx.work.ForegroundInfo
import androidx.work.WorkerParameters
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import org.koin.java.KoinJavaComponent.inject
import org.openedx.core.R
import org.openedx.core.module.db.DownloadDao
Expand All @@ -19,19 +21,22 @@ 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 org.openedx.core.system.notifier.DownloadNotifier
import org.openedx.core.system.notifier.DownloadProgressChanged
import java.io.File

class DownloadWorker(
val context: Context,
parameters: WorkerParameters
) : CoroutineWorker(context, parameters) {
) : CoroutineWorker(context, parameters), CoroutineScope {

private val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as
NotificationManager

private val notificationBuilder = NotificationCompat.Builder(context, CHANNEL_ID)

private val notifier by inject<DownloadNotifier>(DownloadNotifier::class.java)
private val downloadDao: DownloadDao by inject(DownloadDao::class.java)

private var downloadEnqueue = listOf<DownloadModel>()
Expand All @@ -43,6 +48,7 @@ class DownloadWorker(
)

private var currentDownload: DownloadModel? = null
private var lastUpdateTime = 0L

private val fileDownloader by inject<FileDownloader>(FileDownloader::class.java)

Expand Down Expand Up @@ -79,14 +85,24 @@ class DownloadWorker(

private fun updateProgress() {
fileDownloader.progressListener = object : CurrentProgress {
override fun progress(value: Long) {
if (!fileDownloader.isCanceled) {
override fun progress(value: Long, size: Long) {
val progress = 100 * value / size
// Update no more than 5 times per sec
if (!fileDownloader.isCanceled &&
(System.currentTimeMillis() - lastUpdateTime > 200)
) {
lastUpdateTime = System.currentTimeMillis()

currentDownload?.let {
launch {
notifier.send(DownloadProgressChanged(it.id, value, size))
}

notificationManager.notify(
NOTIFICATION_ID,
notificationBuilder
.setSmallIcon(R.drawable.core_ic_check_in_box)
.setProgress(100, value.toInt(), false)
.setProgress(100, progress.toInt(), false)
.setPriority(NotificationManager.IMPORTANCE_LOW)
.setContentText(context.getString(R.string.core_downloading_in_progress))
.setContentTitle(it.title)
Expand Down Expand Up @@ -152,4 +168,4 @@ class DownloadWorker(
private const val NOTIFICATION_ID = 10
}

}
}
Loading
Loading