From f042b892cd14cff5a097b67cd9d69281c981dd9d Mon Sep 17 00:00:00 2001 From: Docile-Alligator <25734209+Docile-Alligator@users.noreply.github.com> Date: Wed, 18 Sep 2024 14:04:00 -0400 Subject: Add an option to download all media in an imgur album in ViewImgurMediaActivity. --- .../activities/ViewImgurMediaActivity.java | 15 +++ .../activities/ViewRedditGalleryActivity.java | 2 +- .../PostOptionsBottomSheetFragment.java | 2 +- .../services/DownloadMediaService.java | 102 +++++++++++++++++---- .../main/res/menu/view_imgur_media_activity.xml | 9 ++ app/src/main/res/values/strings.xml | 7 +- 6 files changed, 117 insertions(+), 20 deletions(-) create mode 100644 app/src/main/res/menu/view_imgur_media_activity.xml diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewImgurMediaActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewImgurMediaActivity.java index 25c78a31..77ba0fa7 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewImgurMediaActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewImgurMediaActivity.java @@ -1,5 +1,8 @@ package ml.docilealligator.infinityforreddit.activities; +import android.app.job.JobInfo; +import android.app.job.JobScheduler; +import android.content.Context; import android.content.SharedPreferences; import android.graphics.Typeface; import android.graphics.drawable.ColorDrawable; @@ -48,6 +51,7 @@ import ml.docilealligator.infinityforreddit.font.TitleFontFamily; import ml.docilealligator.infinityforreddit.font.TitleFontStyle; import ml.docilealligator.infinityforreddit.fragments.ViewImgurImageFragment; import ml.docilealligator.infinityforreddit.fragments.ViewImgurVideoFragment; +import ml.docilealligator.infinityforreddit.services.DownloadMediaService; import ml.docilealligator.infinityforreddit.utils.APIUtils; import ml.docilealligator.infinityforreddit.utils.JSONUtils; import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils; @@ -297,6 +301,10 @@ public class ViewImgurMediaActivity extends AppCompatActivity implements SetAsWa @Override public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.view_imgur_media_activity, menu); + for (int i = 0; i < menu.size(); i++) { + Utils.setTitleWithCustomFontToMenuItem(typeface, menu.getItem(i), null); + } return true; } @@ -305,6 +313,13 @@ public class ViewImgurMediaActivity extends AppCompatActivity implements SetAsWa if (item.getItemId() == android.R.id.home) { finish(); return true; + } else if (item.getItemId() == R.id.action_download_all_imgur_album_media_view_imgur_media_activity) { + //TODO: contentEstimatedBytes + JobInfo jobInfo = DownloadMediaService.constructImgurAlbumDownloadAllMediaJobInfo(this, 5000000L * images.size(), images); + ((JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE)).schedule(jobInfo); + + Toast.makeText(this, R.string.download_started, Toast.LENGTH_SHORT).show(); + return true; } return false; diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewRedditGalleryActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewRedditGalleryActivity.java index 2dfe1ddc..b2d233b3 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewRedditGalleryActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/activities/ViewRedditGalleryActivity.java @@ -223,7 +223,7 @@ public class ViewRedditGalleryActivity extends AppCompatActivity implements SetA return true; } else if (item.getItemId() == R.id.action_download_all_gallery_media_view_reddit_gallery_activity) { //TODO: contentEstimatedBytes - JobInfo jobInfo = DownloadMediaService.constructGalleryDownloadAllImagesJobInfo(this, 5000000, post); + JobInfo jobInfo = DownloadMediaService.constructGalleryDownloadAllMediaJobInfo(this, 5000000L * gallery.size(), post); ((JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE)).schedule(jobInfo); Toast.makeText(this, R.string.download_started, Toast.LENGTH_SHORT).show(); diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/bottomsheetfragments/PostOptionsBottomSheetFragment.java b/app/src/main/java/ml/docilealligator/infinityforreddit/bottomsheetfragments/PostOptionsBottomSheetFragment.java index 80e736fc..6e52eb3b 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/bottomsheetfragments/PostOptionsBottomSheetFragment.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/bottomsheetfragments/PostOptionsBottomSheetFragment.java @@ -136,7 +136,7 @@ public class PostOptionsBottomSheetFragment extends LandscapeExpandedRoundedBott if (mPost.getPostType() == Post.GALLERY_TYPE) { binding.downloadAllTextViewPostOptionsBottomSheetFragment.setVisibility(View.VISIBLE); binding.downloadAllTextViewPostOptionsBottomSheetFragment.setOnClickListener(view -> { - JobInfo jobInfo = DownloadMediaService.constructGalleryDownloadAllImagesJobInfo(mBaseActivity, 5000000, mPost); + JobInfo jobInfo = DownloadMediaService.constructGalleryDownloadAllMediaJobInfo(mBaseActivity, 5000000, mPost); ((JobScheduler) mBaseActivity.getSystemService(Context.JOB_SCHEDULER_SERVICE)).schedule(jobInfo); dismiss(); diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/services/DownloadMediaService.java b/app/src/main/java/ml/docilealligator/infinityforreddit/services/DownloadMediaService.java index a8908795..7d9b3ac9 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/services/DownloadMediaService.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/services/DownloadMediaService.java @@ -20,7 +20,6 @@ import android.os.Build; import android.os.Environment; import android.os.PersistableBundle; import android.provider.MediaStore; -import android.util.Log; import androidx.core.app.NotificationChannelCompat; import androidx.core.app.NotificationCompat; @@ -35,6 +34,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; +import java.util.List; import java.util.Random; import java.util.concurrent.Executor; @@ -69,6 +69,7 @@ public class DownloadMediaService extends JobService { public static final String EXTRA_IS_NSFW = "EIN"; public static final String EXTRA_REDGIFS_ID = "EGI"; public static final String EXTRA_STREAMABLE_SHORT_CODE = "ESSC"; + public static final String EXTRA_IS_ALL_GALLERY_MEDIA = "EIAGM"; public static final int EXTRA_MEDIA_TYPE_IMAGE = 0; public static final int EXTRA_MEDIA_TYPE_GIF = 1; public static final int EXTRA_MEDIA_TYPE_VIDEO = 2; @@ -82,7 +83,8 @@ public class DownloadMediaService extends JobService { private static final int ERROR_FILE_CANNOT_DOWNLOAD = 1; private static final int ERROR_FILE_CANNOT_SAVE = 2; private static final int ERROR_FILE_CANNOT_FETCH_REDGIFS_VIDEO_LINK = 3; - private static final int ERROR_FILE_CANNOT_FETCH_STREAMABLE_VIDEO_LINK = 4; + private static final int ERROR_CANNOT_FETCH_STREAMABLE_VIDEO_LINK = 4; + private static final int ERROR_INVALID_ARGUMENT = 5; private static int JOB_ID = 20000; @@ -125,13 +127,13 @@ public class DownloadMediaService extends JobService { extras.putString(EXTRA_FILE_NAME, post.getSubredditName() + "-" + post.getId() + ".jpg"); extras.putString(EXTRA_SUBREDDIT_NAME, post.getSubredditName()); - extras.putInt(EXTRA_IS_NSFW, post.isNSFW() ? 1 : 0); + extras.putBoolean(EXTRA_IS_NSFW, post.isNSFW()); } else if (post.getPostType() == Post.GIF_TYPE) { extras.putString(EXTRA_URL, post.getVideoUrl()); extras.putInt(EXTRA_MEDIA_TYPE, EXTRA_MEDIA_TYPE_GIF); extras.putString(EXTRA_FILE_NAME, post.getSubredditName() + "-" + post.getId() + ".gif"); extras.putString(EXTRA_SUBREDDIT_NAME, post.getSubredditName()); - extras.putInt(EXTRA_IS_NSFW, post.isNSFW() ? 1 : 0); + extras.putBoolean(EXTRA_IS_NSFW, post.isNSFW()); } else if (post.getPostType() == Post.VIDEO_TYPE) { if (post.isStreamable()) { if (post.isLoadRedgifsOrStreamableVideoSuccess()) { @@ -160,7 +162,7 @@ public class DownloadMediaService extends JobService { extras.putInt(EXTRA_MEDIA_TYPE, EXTRA_MEDIA_TYPE_VIDEO); extras.putString(EXTRA_SUBREDDIT_NAME, post.getSubredditName()); - extras.putInt(EXTRA_IS_NSFW, post.isNSFW() ? 1 : 0); + extras.putBoolean(EXTRA_IS_NSFW, post.isNSFW()); } else if (post.getPostType() == Post.GALLERY_TYPE) { Post.Gallery media = post.getGallery().get(galleryIndex); if (media.mediaType == Post.Gallery.TYPE_VIDEO) { @@ -168,13 +170,13 @@ public class DownloadMediaService extends JobService { extras.putInt(EXTRA_MEDIA_TYPE, EXTRA_MEDIA_TYPE_VIDEO); extras.putString(EXTRA_FILE_NAME, media.fileName); extras.putString(EXTRA_SUBREDDIT_NAME, post.getSubredditName()); - extras.putInt(EXTRA_IS_NSFW, post.isNSFW() ? 1 : 0); + extras.putBoolean(EXTRA_IS_NSFW, post.isNSFW()); } else { extras.putString(EXTRA_URL, media.hasFallback() ? media.fallbackUrl : media.url); // Retrieve original instead of the one additionally compressed by reddit extras.putInt(EXTRA_MEDIA_TYPE, media.mediaType == Post.Gallery.TYPE_GIF ? EXTRA_MEDIA_TYPE_GIF: EXTRA_MEDIA_TYPE_IMAGE); extras.putString(EXTRA_FILE_NAME, media.fileName); extras.putString(EXTRA_SUBREDDIT_NAME, post.getSubredditName()); - extras.putInt(EXTRA_IS_NSFW, post.isNSFW() ? 1 : 0); + extras.putBoolean(EXTRA_IS_NSFW, post.isNSFW()); } } @@ -193,11 +195,11 @@ public class DownloadMediaService extends JobService { } } - public static JobInfo constructGalleryDownloadAllImagesJobInfo(Context context, long contentEstimatedBytes, Post post) { + public static JobInfo constructGalleryDownloadAllMediaJobInfo(Context context, long contentEstimatedBytes, Post post) { PersistableBundle extras = new PersistableBundle(); if (post.getPostType() == Post.GALLERY_TYPE) { extras.putString(EXTRA_SUBREDDIT_NAME, post.getSubredditName()); - extras.putInt(EXTRA_IS_NSFW, post.isNSFW() ? 1 : 0); + extras.putBoolean(EXTRA_IS_NSFW, post.isNSFW()); ArrayList gallery = post.getGallery(); @@ -234,6 +236,7 @@ public class DownloadMediaService extends JobService { extras.putString(EXTRA_ALL_GALLERY_IMAGE_URLS, concatUrlsBuilder.toString()); extras.putString(EXTRA_ALL_GALLERY_IMAGE_MEDIA_TYPES, concatMediaTypesBuilder.toString()); extras.putString(EXTRA_ALL_GALLERY_IMAGE_FILE_NAMES, concatFileNamesBuilder.toString()); + extras.putBoolean(EXTRA_IS_ALL_GALLERY_MEDIA, true); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { @@ -276,6 +279,58 @@ public class DownloadMediaService extends JobService { } } + public static JobInfo constructImgurAlbumDownloadAllMediaJobInfo(Context context, long contentEstimatedBytes, List imgurMedia) { + PersistableBundle extras = new PersistableBundle(); + + StringBuilder concatUrlsBuilder = new StringBuilder(); + StringBuilder concatMediaTypesBuilder = new StringBuilder(); + StringBuilder concatFileNamesBuilder = new StringBuilder(); + + for (int i = 0; i < imgurMedia.size(); i++) { + ImgurMedia media = imgurMedia.get(i); + + if (media.getType() == ImgurMedia.TYPE_VIDEO) { + concatUrlsBuilder.append(media.getLink()).append(" "); + concatMediaTypesBuilder.append(EXTRA_MEDIA_TYPE_VIDEO).append(" "); + concatFileNamesBuilder.append(media.getFileName()).append(" "); + } else { + concatUrlsBuilder.append(media.getLink()).append(" "); // Retrieve original instead of the one additionally compressed by reddit + concatMediaTypesBuilder.append(EXTRA_MEDIA_TYPE_IMAGE).append(" "); + concatFileNamesBuilder.append(media.getFileName()).append(" "); + } + } + + if (concatUrlsBuilder.length() > 0) { + concatUrlsBuilder.deleteCharAt(concatUrlsBuilder.length() - 1); + } + + if (concatMediaTypesBuilder.length() > 0) { + concatMediaTypesBuilder.deleteCharAt(concatMediaTypesBuilder.length() - 1); + } + + if (concatFileNamesBuilder.length() > 0) { + concatFileNamesBuilder.deleteCharAt(concatFileNamesBuilder.length() - 1); + } + + extras.putString(EXTRA_ALL_GALLERY_IMAGE_URLS, concatUrlsBuilder.toString()); + extras.putString(EXTRA_ALL_GALLERY_IMAGE_MEDIA_TYPES, concatMediaTypesBuilder.toString()); + extras.putString(EXTRA_ALL_GALLERY_IMAGE_FILE_NAMES, concatFileNamesBuilder.toString()); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { + return new JobInfo.Builder(JOB_ID++, new ComponentName(context, DownloadMediaService.class)) + .setUserInitiated(true) + .setRequiredNetwork(new NetworkRequest.Builder().clearCapabilities().build()) + .setEstimatedNetworkBytes(0, contentEstimatedBytes + 500) + .setExtras(extras) + .build(); + } else { + return new JobInfo.Builder(JOB_ID++, new ComponentName(context, DownloadMediaService.class)) + .setOverrideDeadline(0) + .setExtras(extras) + .build(); + } + } + public static JobInfo constructJobInfo(Context context, long contentEstimatedBytes, PersistableBundle extras) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { return new JobInfo.Builder(JOB_ID++, new ComponentName(context, DownloadMediaService.class)) @@ -296,7 +351,6 @@ public class DownloadMediaService extends JobService { public void onCreate() { ((Infinity) getApplication()).getAppComponent().inject(this); notificationManager = NotificationManagerCompat.from(this); - Log.i("asdfasdf", "s " + JOB_ID); } @Override @@ -314,7 +368,10 @@ public class DownloadMediaService extends JobService { notificationManager.createNotificationChannel(serviceChannel); int randomNotificationIdOffset = new Random().nextInt(10000); - String notificationTitle = extras.containsKey(EXTRA_FILE_NAME) ? extras.getString(EXTRA_FILE_NAME) : getString(R.string.download_all_gallery_media_notification_title); + String notificationTitle = extras.containsKey(EXTRA_FILE_NAME) ? + extras.getString(EXTRA_FILE_NAME) : + (extras.getBoolean(EXTRA_IS_ALL_GALLERY_MEDIA, false) ? + getString(R.string.download_all_gallery_media_notification_title) : getString(R.string.download_all_imgur_album_media_notification_title)); switch (extras.getInt(EXTRA_MEDIA_TYPE, EXTRA_MEDIA_TYPE_IMAGE)) { case EXTRA_MEDIA_TYPE_GIF: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { @@ -352,7 +409,7 @@ public class DownloadMediaService extends JobService { mExecutor.execute(() -> { String subredditName = extras.getString(EXTRA_SUBREDDIT_NAME); - boolean isNsfw = extras.getInt(EXTRA_IS_NSFW, 0) == 1; + boolean isNsfw = extras.getBoolean(EXTRA_IS_NSFW, false); if (extras.containsKey(EXTRA_ALL_GALLERY_IMAGE_URLS)) { // Download all images in a gallery post @@ -452,6 +509,15 @@ public class DownloadMediaService extends JobService { // Only Redgifs and Streamble video can go inside this if clause. String redgifsId = intent.getString(EXTRA_REDGIFS_ID, null); String streamableShortCode = intent.getString(EXTRA_STREAMABLE_SHORT_CODE, null); + + if (redgifsId == null && streamableShortCode == null) { + downloadFinished(params, builder, mediaType, randomNotificationIdOffset, mimeType, + null, + ERROR_INVALID_ARGUMENT, + multipleDownloads); + return false; + } + fileUrl = VideoLinkFetcher.fetchVideoLinkSync(mRedgifsRetrofit, mStreamableApiProvider, mCurrentAccountSharedPreferences, redgifsId == null ? ViewVideoActivity.VIDEO_TYPE_STREAMABLE : ViewVideoActivity.VIDEO_TYPE_REDGIFS, redgifsId, streamableShortCode); @@ -459,7 +525,7 @@ public class DownloadMediaService extends JobService { if (fileUrl == null) { downloadFinished(params, builder, mediaType, randomNotificationIdOffset, mimeType, null, - redgifsId == null ? ERROR_FILE_CANNOT_FETCH_STREAMABLE_VIDEO_LINK : ERROR_FILE_CANNOT_FETCH_REDGIFS_VIDEO_LINK, + redgifsId == null ? ERROR_CANNOT_FETCH_STREAMABLE_VIDEO_LINK : ERROR_FILE_CANNOT_FETCH_REDGIFS_VIDEO_LINK, multipleDownloads); return false; } @@ -783,11 +849,15 @@ public class DownloadMediaService extends JobService { -1, randomNotificationIdOffset, null, null); break; case ERROR_FILE_CANNOT_FETCH_REDGIFS_VIDEO_LINK: - updateNotification(builder, mediaType, R.string.downloading_media_failed_cannot_fetch_redgifs_url, + updateNotification(builder, mediaType, R.string.download_media_failed_cannot_fetch_redgifs_url, + -1, randomNotificationIdOffset, null, null); + break; + case ERROR_CANNOT_FETCH_STREAMABLE_VIDEO_LINK: + updateNotification(builder, mediaType, R.string.download_media_failed_cannot_fetch_streamable_url, -1, randomNotificationIdOffset, null, null); break; - case ERROR_FILE_CANNOT_FETCH_STREAMABLE_VIDEO_LINK: - updateNotification(builder, mediaType, R.string.downloading_media_failed_cannot_fetch_streamable_url, + case ERROR_INVALID_ARGUMENT: + updateNotification(builder, mediaType, R.string.download_media_failed_invalid_argument, -1, randomNotificationIdOffset, null, null); break; } diff --git a/app/src/main/res/menu/view_imgur_media_activity.xml b/app/src/main/res/menu/view_imgur_media_activity.xml new file mode 100644 index 00000000..e7fca0e9 --- /dev/null +++ b/app/src/main/res/menu/view_imgur_media_activity.xml @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 481aefe5..3650e686 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -100,6 +100,7 @@ Add to Home screen Reset FAB position Download All Gallery Media + Download All Imgur Album Media Error occurred when parsing the JSON response Error Retrieving the token @@ -1051,9 +1052,11 @@ Download failed: cannot access destination directory Download failed Download failed: cannot save the file to destination directory - Download failed: cannot fetch Redgifs video url - Download failed: cannot fetch Streamable video url + Download failed: cannot fetch Redgifs video url + Download failed: cannot fetch Streamable video url + Invalid argument All Gallery Media + All Imgur Album Media Downloaded. Some media failed to download. Wallpaper set -- cgit v1.2.3