From bd6299c3c85addd7ddb9838dc0b7b8e105bc820c Mon Sep 17 00:00:00 2001
From: Omer Habib <30689349+omerhabib26@users.noreply.github.com>
Date: Sat, 2 Mar 2024 00:04:36 +0500
Subject: [PATCH] feat: added option to delete downloaded videos from
AccountFragment (#1849)
- Allow user to delete downloaded videos from Accounts
- Added relevant analytics
- Show a progress dialog for 2 sec while deleting
- Reset whole app external directory folder
fix: LEARNER-9786
---
OpenEdXMobile/res/layout/fragment_account.xml | 23 ++++++++
OpenEdXMobile/res/values/video_strings.xml | 14 +++++
.../mobile/module/analytics/Analytics.java | 8 ++-
.../org/edx/mobile/module/db/IDatabase.java | 7 ++-
.../mobile/module/db/impl/IDatabaseImpl.java | 8 +++
.../DeleteAllDownloadedVideosEvent.java | 4 ++
.../edx/mobile/module/storage/IStorage.java | 9 +++-
.../edx/mobile/module/storage/Storage.java | 17 ++++++
.../java/org/edx/mobile/util/FileUtil.java | 12 +++++
.../org/edx/mobile/view/AccountFragment.kt | 54 +++++++++++++++++--
.../main/java/org/edx/mobile/view/Router.java | 2 +-
11 files changed, 150 insertions(+), 8 deletions(-)
create mode 100644 OpenEdXMobile/src/main/java/org/edx/mobile/module/storage/DeleteAllDownloadedVideosEvent.java
diff --git a/OpenEdXMobile/res/layout/fragment_account.xml b/OpenEdXMobile/res/layout/fragment_account.xml
index ecc2076cb0..00de694ce2 100755
--- a/OpenEdXMobile/res/layout/fragment_account.xml
+++ b/OpenEdXMobile/res/layout/fragment_account.xml
@@ -115,6 +115,29 @@
+
+
+
+
+
+
+
%d min
- %d mins
+
+
+ Delete all downloaded videos
+
+ Remove all downloaded videos from your device.
+
+ Are you sure you want to delete all downloaded videos from your device? This action cannot be undone.
+
+ Delete all videos
+
+ Deleting downloaded videos…
+
+ Downloaded videos deleted successfully.
+
diff --git a/OpenEdXMobile/src/main/java/org/edx/mobile/module/analytics/Analytics.java b/OpenEdXMobile/src/main/java/org/edx/mobile/module/analytics/Analytics.java
index 0d56b25721..89d0754a68 100644
--- a/OpenEdXMobile/src/main/java/org/edx/mobile/module/analytics/Analytics.java
+++ b/OpenEdXMobile/src/main/java/org/edx/mobile/module/analytics/Analytics.java
@@ -902,10 +902,12 @@ interface Values {
String DO_NOT_SELL_DATA_CLICKED = "edx.bi.app.profile.do_not_sell_data.clicked";
String SUBMIT_FEEDBACK_CLICKED = "edx.bi.app.profile.submit_feedback.clicked";
- // Video Download Quality
+ // Video Downloads & Quality
String PROFILE_VIDEO_DOWNLOAD_QUALITY_CLICKED = "edx.bi.app.profile.video_download_quality.clicked";
String COURSE_VIDEOS_VIDEO_DOWNLOAD_QUALITY_CLICKED = "edx.bi.app.course_videos.video_download_quality.clicked";
String VIDEO_DOWNLOAD_QUALITY_CHANGED = "edx.bi.app.video_download_quality.changed";
+ String DELETE_DOWNLOADED_VIDEOS_CLICKED = "edx.bi.app.delete_downloaded_videos.clicked";
+ String DELETE_DOWNLOADED_VIDEOS = "edx.bi.app.delete_downloaded_videos";
// Account Registration
String REGISTRATION_OPT_IN_TURNED_ON = "edx.bi.app.user.register.opt_in.on";
String REGISTRATION_OPT_IN_TURNED_OFF = "edx.bi.app.user.register.opt_in.off";
@@ -1124,10 +1126,12 @@ interface Events {
String DO_NOT_SELL_DATA_CLICKED = "Do Not Sell Data Clicked";
String SUBMIT_FEEDBACK_CLICKED = "Submit feedback clicked";
- // Video Download Quality
+ // Video Downloads & Quality
String PROFILE_VIDEO_DOWNLOAD_QUALITY_CLICKED = "Profile: Video Download Quality Clicked";
String COURSE_VIDEOS_VIDEO_DOWNLOAD_QUALITY_CLICKED = "Course Videos: Video Download Quality Clicked";
String VIDEO_DOWNLOAD_QUALITY_CHANGED = "Video Download Quality Changed";
+ String DELETE_DOWNLOADED_VIDEOS_CLICKED = "Delete Downloaded Videos Clicked";
+ String DELETE_DOWNLOADED_VIDEOS = "Delete Downloaded Videos";
// Account Registration
String REGISTRATION_OPT_IN_TURNED_ON = "Registration: Opt-in Turned On";
String REGISTRATION_OPT_IN_TURNED_OFF = "Registration: Opt-in Turned Off";
diff --git a/OpenEdXMobile/src/main/java/org/edx/mobile/module/db/IDatabase.java b/OpenEdXMobile/src/main/java/org/edx/mobile/module/db/IDatabase.java
index c4861dc62f..dd86af4de8 100644
--- a/OpenEdXMobile/src/main/java/org/edx/mobile/module/db/IDatabase.java
+++ b/OpenEdXMobile/src/main/java/org/edx/mobile/module/db/IDatabase.java
@@ -366,12 +366,17 @@ Integer updateDownloadCompleteInfoByDmId(long dmId, VideoModel de,
DataCallback callback);
/**
- * Returns list of all videos from the database.
+ * Returns list of all videos from the database for a specific username.
*
* @return
*/
List getAllVideos(String username, DataCallback> DataCallback);
+ /**
+ * Returns list of all videos from the database.
+ */
+ List getAllVideos(DataCallback> DataCallback);
+
/**
* Returns list of all videos from the database for a specific course.
*
diff --git a/OpenEdXMobile/src/main/java/org/edx/mobile/module/db/impl/IDatabaseImpl.java b/OpenEdXMobile/src/main/java/org/edx/mobile/module/db/impl/IDatabaseImpl.java
index 606f2556ba..b2dff83adf 100644
--- a/OpenEdXMobile/src/main/java/org/edx/mobile/module/db/impl/IDatabaseImpl.java
+++ b/OpenEdXMobile/src/main/java/org/edx/mobile/module/db/impl/IDatabaseImpl.java
@@ -580,6 +580,14 @@ public List getAllVideos(String username,
return enqueue(op);
}
+ @Override
+ public List getAllVideos(DataCallback> callback) {
+ DbOperationGetVideos op = new DbOperationGetVideos(false, DbStructure.Table.DOWNLOADS, null,
+ null, null, null);
+ op.setCallback(callback);
+ return enqueue(op);
+ }
+
@Override
public List getAllVideosByCourse(@NonNull String courseId,
@Nullable DataCallback> callback) {
diff --git a/OpenEdXMobile/src/main/java/org/edx/mobile/module/storage/DeleteAllDownloadedVideosEvent.java b/OpenEdXMobile/src/main/java/org/edx/mobile/module/storage/DeleteAllDownloadedVideosEvent.java
new file mode 100644
index 0000000000..7e29e6c97d
--- /dev/null
+++ b/OpenEdXMobile/src/main/java/org/edx/mobile/module/storage/DeleteAllDownloadedVideosEvent.java
@@ -0,0 +1,4 @@
+package org.edx.mobile.module.storage;
+
+public class DeleteAllDownloadedVideosEvent {
+}
diff --git a/OpenEdXMobile/src/main/java/org/edx/mobile/module/storage/IStorage.java b/OpenEdXMobile/src/main/java/org/edx/mobile/module/storage/IStorage.java
index cdac434bcf..2bd1e61155 100644
--- a/OpenEdXMobile/src/main/java/org/edx/mobile/module/storage/IStorage.java
+++ b/OpenEdXMobile/src/main/java/org/edx/mobile/module/storage/IStorage.java
@@ -42,11 +42,18 @@ public interface IStorage {
/**
* Removes all videos from the database as well as NativeDownloadManager.
- * This method fetches all ongoing downloads from the DB; iterates through the list
+ * This method fetches all downloads from the DB; iterates through the list
* and then calls the {@link #removeDownload(VideoModel)} method for each video
*/
void removeAllDownloads();
+ /**
+ * Removes all videos from the database as well as NativeDownloadManager.
+ * This method fetches all ongoing downloads from the DB; iterates through the list
+ * and then calls the {@link #removeDownload(VideoModel)} method for each video
+ */
+ void removeAllOnGoingDownloads();
+
/**
* This method fetches all unenrolledVideos from the DB.
* Iterates through the list and then calls the remove Download method for each video
diff --git a/OpenEdXMobile/src/main/java/org/edx/mobile/module/storage/Storage.java b/OpenEdXMobile/src/main/java/org/edx/mobile/module/storage/Storage.java
index 777c500016..decbf85a9d 100644
--- a/OpenEdXMobile/src/main/java/org/edx/mobile/module/storage/Storage.java
+++ b/OpenEdXMobile/src/main/java/org/edx/mobile/module/storage/Storage.java
@@ -163,6 +163,23 @@ public int removeDownloads(List modelList) {
@Override
public void removeAllDownloads() {
+ db.getAllVideos(new DataCallback>() {
+ @Override
+ public void onResult(List result) {
+ removeDownloadsFromApp(result, null);
+ FileUtil.resetAppDirectory(context);
+ EventBus.getDefault().post(new DeleteAllDownloadedVideosEvent());
+ }
+
+ @Override
+ public void onFail(Exception ex) {
+ logger.error(ex);
+ }
+ });
+ }
+
+ @Override
+ public void removeAllOnGoingDownloads() {
final String username = loginPrefs.getUsername();
final String sha1Username;
if (TextUtils.isEmpty(username)) {
diff --git a/OpenEdXMobile/src/main/java/org/edx/mobile/util/FileUtil.java b/OpenEdXMobile/src/main/java/org/edx/mobile/util/FileUtil.java
index 3885d77632..9f97df0c08 100644
--- a/OpenEdXMobile/src/main/java/org/edx/mobile/util/FileUtil.java
+++ b/OpenEdXMobile/src/main/java/org/edx/mobile/util/FileUtil.java
@@ -334,4 +334,16 @@ public static Uri getFileUriFromMediaStoreUri(@NonNull Context context, @NonNull
}
return null;
}
+
+ /**
+ * Deletes all the files and directories in the app's external storage directory.
+ *
+ * @param context The current context.
+ */
+ public static void resetAppDirectory(@NonNull Context context) {
+ final File externalAppDir = getExternalAppDir(context);
+ if (externalAppDir != null) {
+ deleteRecursive(externalAppDir);
+ }
+ }
}
diff --git a/OpenEdXMobile/src/main/java/org/edx/mobile/view/AccountFragment.kt b/OpenEdXMobile/src/main/java/org/edx/mobile/view/AccountFragment.kt
index 688b967cc3..080164b6fe 100644
--- a/OpenEdXMobile/src/main/java/org/edx/mobile/view/AccountFragment.kt
+++ b/OpenEdXMobile/src/main/java/org/edx/mobile/view/AccountFragment.kt
@@ -7,6 +7,8 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.URLUtil
+import android.widget.Toast
+import androidx.annotation.StringRes
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import com.bumptech.glide.Glide
@@ -38,6 +40,8 @@ import org.edx.mobile.module.analytics.InAppPurchasesAnalytics
import org.edx.mobile.module.prefs.FeaturesPrefs
import org.edx.mobile.module.prefs.LoginPrefs
import org.edx.mobile.module.prefs.UserPrefs
+import org.edx.mobile.module.storage.DeleteAllDownloadedVideosEvent
+import org.edx.mobile.module.storage.IStorage
import org.edx.mobile.user.UserAPI.AccountDataUpdatedCallback
import org.edx.mobile.user.UserService
import org.edx.mobile.util.AgreementUrlType
@@ -94,6 +98,9 @@ class AccountFragment : BaseFragment() {
@Inject
lateinit var iapAnalytics: InAppPurchasesAnalytics
+ @Inject
+ lateinit var storage: IStorage
+
private val courseViewModel: CourseViewModel by viewModels()
private val iapViewModel: InAppPurchasesViewModel by viewModels()
@@ -130,6 +137,7 @@ class AccountFragment : BaseFragment() {
initPurchases()
handleIntentBundle(arguments)
initVideoQuality()
+ initDeleteDownloadedVideos()
updateWifiSwitch()
updateSDCardSwitch()
initHelpFields()
@@ -171,7 +179,7 @@ class AccountFragment : BaseFragment() {
binding.btnRestorePurchases.setOnClickListener {
iapAnalytics.reset()
iapAnalytics.trackIAPEvent(Analytics.Events.IAP_RESTORE_PURCHASE_CLICKED)
- showLoader()
+ showLoader(R.string.title_checking_purchases)
lifecycleScope.launch {
courseViewModel.fetchEnrolledCourses(
type = CourseViewModel.CoursesRequestType.STALE,
@@ -266,9 +274,9 @@ class AccountFragment : BaseFragment() {
fullScreenLoader.show(childFragmentManager, FullscreenLoaderDialogFragment.TAG)
}
- private fun showLoader() {
+ private fun showLoader(@StringRes titleResId: Int) {
loaderDialog = AlertDialogFragment.newInstance(
- R.string.title_checking_purchases,
+ titleResId,
R.layout.alert_dialog_progress
)
loaderDialog?.isCancelable = false
@@ -301,6 +309,30 @@ class AccountFragment : BaseFragment() {
binding.tvVideoDownloadQuality.setText(videoQuality.titleResId)
}
+ private fun initDeleteDownloadedVideos() {
+ binding.btnDeleteVideos.setOnClickListener {
+ trackEvent(
+ Analytics.Events.DELETE_DOWNLOADED_VIDEOS_CLICKED,
+ Analytics.Values.DELETE_DOWNLOADED_VIDEOS_CLICKED
+ )
+ AlertDialogFragment.newInstance(
+ getString(R.string.delete_downloaded_videos_title),
+ getString(R.string.delete_downloaded_videos_dialog_description),
+ getString(R.string.label_delete),
+ { _, _ ->
+ showLoader(R.string.deleting_downloaded_videos_title)
+ storage.removeAllDownloads()
+ trackEvent(
+ Analytics.Events.DELETE_DOWNLOADED_VIDEOS,
+ Analytics.Values.DELETE_DOWNLOADED_VIDEOS
+ )
+ },
+ getString(R.string.label_cancel),
+ null
+ ).show(childFragmentManager, null)
+ }
+ }
+
private fun initHelpFields() {
if (URLUtil.isValidUrl(featuresPrefs.feedbackFormUrl)) {
binding.btnSubmitFeedback.setVisibility(true)
@@ -605,6 +637,22 @@ class AccountFragment : BaseFragment() {
environment.analyticsRegistry.trackEvent(eventName, biValue)
}
+ @Subscribe(sticky = true)
+ @Suppress("UNUSED_PARAMETER")
+ fun onEventMainThread(event: DeleteAllDownloadedVideosEvent) {
+ binding.root.postDelayed(
+ {
+ loaderDialog?.dismiss()
+ Toast.makeText(
+ requireContext(),
+ R.string.deleted_downloaded_videos_success,
+ Toast.LENGTH_LONG
+ ).show()
+ }, 2000
+ )
+
+ }
+
companion object {
@JvmStatic
fun newInstance(bundle: Bundle): AccountFragment {
diff --git a/OpenEdXMobile/src/main/java/org/edx/mobile/view/Router.java b/OpenEdXMobile/src/main/java/org/edx/mobile/view/Router.java
index b5cd63ca7e..6bf2f432f9 100644
--- a/OpenEdXMobile/src/main/java/org/edx/mobile/view/Router.java
+++ b/OpenEdXMobile/src/main/java/org/edx/mobile/view/Router.java
@@ -369,7 +369,7 @@ public void forceLogout(Context context, AnalyticsRegistry analyticsRegistry, No
*/
public void performManualLogout(Context context, AnalyticsRegistry analyticsRegistry, NotificationDelegate delegate) {
// Remove all ongoing downloads first which requires username
- storage.removeAllDownloads();
+ storage.removeAllOnGoingDownloads();
loginAPI.logOut();
forceLogout(context, analyticsRegistry, delegate);
SecurityUtil.clearUserData(context);