Rewrite DownloadRedditVideoService to run the service in a background thread.

This commit is contained in:
Alex Ning 2020-12-22 15:56:55 +08:00
parent 664141b324
commit f4d061cecc
5 changed files with 260 additions and 319 deletions

View File

@ -11,7 +11,6 @@ import androidx.annotation.Nullable;
import com.evernote.android.state.StateSaver; import com.evernote.android.state.StateSaver;
import com.livefront.bridge.Bridge; import com.livefront.bridge.Bridge;
import com.livefront.bridge.SavedStateHandler; import com.livefront.bridge.SavedStateHandler;
import com.melegy.redscreenofdeath.RedScreenOfDeath;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
@ -27,7 +26,7 @@ public class Infinity extends Application {
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
RedScreenOfDeath.init(this); //RedScreenOfDeath.init(this);
mAppComponent = DaggerAppComponent.builder() mAppComponent = DaggerAppComponent.builder()
.appModule(new AppModule(this)) .appModule(new AppModule(this))

View File

@ -45,7 +45,8 @@ public abstract class RedditDataRoomDatabase extends RoomDatabase {
RedditDataRoomDatabase.class, "reddit_data") RedditDataRoomDatabase.class, "reddit_data")
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, .addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5,
MIGRATION_5_6, MIGRATION_6_7, MIGRATION_7_8, MIGRATION_8_9, MIGRATION_5_6, MIGRATION_6_7, MIGRATION_7_8, MIGRATION_8_9,
MIGRATION_9_10, MIGRATION_10_11, MIGRATION_11_12, MIGRATION_12_13) MIGRATION_9_10, MIGRATION_10_11, MIGRATION_11_12, MIGRATION_12_13,
MIGRATION_13_14)
.build(); .build();
} }
} }

View File

@ -99,7 +99,7 @@ public class DownloadMediaService extends Service {
Bundle intent = msg.getData(); Bundle intent = msg.getData();
downloadFinished = false; downloadFinished = false;
String fileUrl = intent.getString(EXTRA_URL); String fileUrl = intent.getString(EXTRA_URL);
final String[] fileName = {intent.getString(EXTRA_FILE_NAME)}; String fileName = intent.getString(EXTRA_FILE_NAME);
String subredditName = intent.getString(EXTRA_SUBREDDIT_NAME); String subredditName = intent.getString(EXTRA_SUBREDDIT_NAME);
int mediaType = intent.getInt(EXTRA_MEDIA_TYPE, EXTRA_MEDIA_TYPE_IMAGE); int mediaType = intent.getInt(EXTRA_MEDIA_TYPE, EXTRA_MEDIA_TYPE_IMAGE);
String mimeType = mediaType == EXTRA_MEDIA_TYPE_VIDEO ? "video/*" : "image/*"; String mimeType = mediaType == EXTRA_MEDIA_TYPE_VIDEO ? "video/*" : "image/*";
@ -152,7 +152,7 @@ public class DownloadMediaService extends Service {
null, ERROR_CANNOT_GET_DESTINATION_DIRECTORY); null, ERROR_CANNOT_GET_DESTINATION_DIRECTORY);
return; return;
} }
destinationFileUriString = directoryPath + fileName[0]; destinationFileUriString = directoryPath + fileName;
} else { } else {
downloadFinished(mediaType, randomNotificationIdOffset, mimeType, downloadFinished(mediaType, randomNotificationIdOffset, mimeType,
null, ERROR_CANNOT_GET_DESTINATION_DIRECTORY); null, ERROR_CANNOT_GET_DESTINATION_DIRECTORY);
@ -191,16 +191,16 @@ public class DownloadMediaService extends Service {
return; return;
} }
} }
DocumentFile checkForDuplicates = dir.findFile(fileName[0]); DocumentFile checkForDuplicates = dir.findFile(fileName);
int extensionPosition = fileName[0].lastIndexOf('.'); int extensionPosition = fileName.lastIndexOf('.');
String extension = fileName[0].substring(extensionPosition); String extension = fileName.substring(extensionPosition);
int num = 1; int num = 1;
while (checkForDuplicates != null) { while (checkForDuplicates != null) {
fileName[0] = fileName[0].substring(0, extensionPosition) + " (" + num + ")" + extension; fileName = fileName.substring(0, extensionPosition) + " (" + num + ")" + extension;
checkForDuplicates = dir.findFile(fileName[0]); checkForDuplicates = dir.findFile(fileName);
num++; num++;
} }
picFile = dir.createFile(mimeType, fileName[0]); picFile = dir.createFile(mimeType, fileName);
if (picFile == null) { if (picFile == null) {
downloadFinished(mediaType, randomNotificationIdOffset, mimeType, downloadFinished(mediaType, randomNotificationIdOffset, mimeType,
null, ERROR_CANNOT_GET_DESTINATION_DIRECTORY); null, ERROR_CANNOT_GET_DESTINATION_DIRECTORY);
@ -219,7 +219,7 @@ public class DownloadMediaService extends Service {
try { try {
if (response != null && response.body() != null) { if (response != null && response.body() != null) {
Uri destinationFileUri = writeResponseBodyToDisk(response.body(), isDefaultDestination, destinationFileUriString, Uri destinationFileUri = writeResponseBodyToDisk(response.body(), isDefaultDestination, destinationFileUriString,
fileName[0], mediaType); fileName, mediaType);
downloadFinished(mediaType, randomNotificationIdOffset, downloadFinished(mediaType, randomNotificationIdOffset,
mimeType, destinationFileUri, NO_ERROR); mimeType, destinationFileUri, NO_ERROR);
} }
@ -348,7 +348,6 @@ public class DownloadMediaService extends Service {
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate();
((Infinity) getApplication()).getAppComponent().inject(this); ((Infinity) getApplication()).getAppComponent().inject(this);
notificationManager = NotificationManagerCompat.from(this); notificationManager = NotificationManagerCompat.from(this);
// Start up the thread running the service. Note that we create a // Start up the thread running the service. Note that we create a

View File

@ -15,13 +15,17 @@ import android.media.MediaFormat;
import android.media.MediaMuxer; import android.media.MediaMuxer;
import android.media.MediaScannerConnection; import android.media.MediaScannerConnection;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.provider.MediaStore; import android.provider.MediaStore;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat; import androidx.core.app.NotificationManagerCompat;
import androidx.documentfile.provider.DocumentFile; import androidx.documentfile.provider.DocumentFile;
@ -35,6 +39,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Random;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
@ -49,8 +54,6 @@ import ml.docilealligator.infinityforreddit.utils.NotificationUtils;
import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils; import ml.docilealligator.infinityforreddit.utils.SharedPreferencesUtils;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.ResponseBody; import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response; import retrofit2.Response;
import retrofit2.Retrofit; import retrofit2.Retrofit;
@ -79,101 +82,82 @@ public class DownloadRedditVideoService extends Service {
SharedPreferences sharedPreferences; SharedPreferences sharedPreferences;
@Inject @Inject
CustomThemeWrapper customThemeWrapper; CustomThemeWrapper customThemeWrapper;
String resultFile; private ServiceHandler serviceHandler;
private NotificationManagerCompat notificationManager; private NotificationManagerCompat notificationManager;
private NotificationCompat.Builder builder; private NotificationCompat.Builder builder;
public DownloadRedditVideoService() { public DownloadRedditVideoService() {
} }
@Override private final class ServiceHandler extends Handler {
public IBinder onBind(Intent intent) { public ServiceHandler(Looper looper) {
return null; super(looper);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
((Infinity) getApplication()).getAppComponent().inject(this);
String videoUrl = intent.getStringExtra(EXTRA_VIDEO_URL);
String audioUrl = videoUrl.substring(0, videoUrl.lastIndexOf('/')) + "/DASH_audio.mp4";
String subredditName = intent.getStringExtra(EXTRA_SUBREDDIT);
final String[] fileNameWithoutExtension = {subredditName + "-" + intent.getStringExtra(EXTRA_POST_ID)};
notificationManager = NotificationManagerCompat.from(this);
builder = new NotificationCompat.Builder(this, NotificationUtils.CHANNEL_ID_DOWNLOAD_REDDIT_VIDEO);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel;
serviceChannel = new NotificationChannel(
NotificationUtils.CHANNEL_ID_DOWNLOAD_REDDIT_VIDEO,
NotificationUtils.CHANNEL_DOWNLOAD_REDDIT_VIDEO,
NotificationManager.IMPORTANCE_LOW
);
notificationManager.createNotificationChannel(serviceChannel);
} }
@Override
public void handleMessage(Message msg) {
Bundle intent = msg.getData();
String videoUrl = intent.getString(EXTRA_VIDEO_URL);
String audioUrl = videoUrl.substring(0, videoUrl.lastIndexOf('/')) + "/DASH_audio.mp4";
String subredditName = intent.getString(EXTRA_SUBREDDIT);
String fileNameWithoutExtension = subredditName + "-" + intent.getString(EXTRA_POST_ID);
int randomNotificationIdOffset = msg.arg1;
startForeground( final DownloadProgressResponseBody.ProgressListener progressListener = new DownloadProgressResponseBody.ProgressListener() {
NotificationUtils.DOWNLOAD_REDDIT_VIDEO_NOTIFICATION_ID, long time = 0;
createNotification(fileNameWithoutExtension[0] + ".mp4")
);
final DownloadProgressResponseBody.ProgressListener progressListener = new DownloadProgressResponseBody.ProgressListener() { @Override public void update(long bytesRead, long contentLength, boolean done) {
long time = 0; if (!done) {
if (contentLength != -1) {
@Override public void update(long bytesRead, long contentLength, boolean done) { long currentTime = System.currentTimeMillis();
if (!done) { if (currentTime - time > 1000) {
if (contentLength != -1) { time = currentTime;
long currentTime = System.currentTimeMillis(); updateNotification(0, (int) ((100 * bytesRead) / contentLength),
if (currentTime - time > 1000) { randomNotificationIdOffset, null);
time = currentTime; }
updateNotification(0, (int) ((100 * bytesRead) / contentLength), null);
} }
} }
} }
} };
};
OkHttpClient client = new OkHttpClient.Builder() OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(chain -> { .addNetworkInterceptor(chain -> {
okhttp3.Response originalResponse = chain.proceed(chain.request()); okhttp3.Response originalResponse = chain.proceed(chain.request());
return originalResponse.newBuilder() return originalResponse.newBuilder()
.body(new DownloadProgressResponseBody(originalResponse.body(), progressListener)) .body(new DownloadProgressResponseBody(originalResponse.body(), progressListener))
.build(); .build();
}) })
.build(); .build();
retrofit = retrofit.newBuilder().client(client).build(); retrofit = retrofit.newBuilder().client(client).build();
DownloadFile downloadFile = retrofit.create(DownloadFile.class); DownloadFile downloadFile = retrofit.create(DownloadFile.class);
boolean separateDownloadFolder = sharedPreferences.getBoolean(SharedPreferencesUtils.SEPARATE_FOLDER_FOR_EACH_SUBREDDIT, false); boolean separateDownloadFolder = sharedPreferences.getBoolean(SharedPreferencesUtils.SEPARATE_FOLDER_FOR_EACH_SUBREDDIT, false);
File directory = getExternalCacheDir(); File externalCacheDirectory = getExternalCacheDir();
String destinationFileName = fileNameWithoutExtension[0] + ".mp4"; if (externalCacheDirectory != null) {
if (directory != null) { String destinationFileName = fileNameWithoutExtension + ".mp4";
String directoryPath = directory.getAbsolutePath() + "/";
downloadFile.downloadFile(videoUrl).enqueue(new Callback<ResponseBody>() { try {
@Override Response<ResponseBody> videoResponse = downloadFile.downloadFile(videoUrl).execute();
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> videoResponse) {
if (videoResponse.isSuccessful() && videoResponse.body() != null) { if (videoResponse.isSuccessful() && videoResponse.body() != null) {
String externalCacheDirectoryPath = externalCacheDirectory.getAbsolutePath() + "/";
String destinationFileDirectory = sharedPreferences.getString(SharedPreferencesUtils.VIDEO_DOWNLOAD_LOCATION, ""); String destinationFileDirectory = sharedPreferences.getString(SharedPreferencesUtils.VIDEO_DOWNLOAD_LOCATION, "");
String destinationFileUriString; String destinationFileUriString;
boolean isDefaultDestination; boolean isDefaultDestination;
if (destinationFileDirectory.equals("")) { if (destinationFileDirectory.equals("")) {
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
File directory = getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES); File destinationDirectory = getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
if (directory != null) { if (destinationDirectory != null) {
String directoryPath = separateDownloadFolder ? directory.getAbsolutePath() + "/Infinity/" + subredditName + "/" : directory.getAbsolutePath() + "/Infinity/"; String destinationDirectoryPath = separateDownloadFolder ? destinationDirectory.getAbsolutePath() + "/Infinity/" + subredditName + "/" : destinationDirectory.getAbsolutePath() + "/Infinity/";
File infinityDir = new File(directoryPath); File infinityDir = new File(destinationDirectoryPath);
if (!infinityDir.exists() && !infinityDir.mkdir()) { if (!infinityDir.exists() && !infinityDir.mkdir()) {
downloadFinished(null, ERROR_CANNOT_GET_DESTINATION_DIRECTORY); downloadFinished(null, ERROR_CANNOT_GET_DESTINATION_DIRECTORY, randomNotificationIdOffset);
return; return;
} }
destinationFileUriString = directoryPath + destinationFileName; destinationFileUriString = destinationDirectoryPath + destinationFileName;
} else { } else {
downloadFinished(null, ERROR_CANNOT_GET_DESTINATION_DIRECTORY); downloadFinished(null, ERROR_CANNOT_GET_DESTINATION_DIRECTORY, randomNotificationIdOffset);
return; return;
} }
} else { } else {
@ -187,272 +171,104 @@ public class DownloadRedditVideoService extends Service {
if (separateDownloadFolder) { if (separateDownloadFolder) {
dir = DocumentFile.fromTreeUri(DownloadRedditVideoService.this, Uri.parse(destinationFileDirectory)); dir = DocumentFile.fromTreeUri(DownloadRedditVideoService.this, Uri.parse(destinationFileDirectory));
if (dir == null) { if (dir == null) {
downloadFinished(null, ERROR_CANNOT_GET_DESTINATION_DIRECTORY); downloadFinished(null, ERROR_CANNOT_GET_DESTINATION_DIRECTORY, randomNotificationIdOffset);
return; return;
} }
dir = dir.findFile(subredditName); dir = dir.findFile(subredditName);
if (dir == null) { if (dir == null) {
dir = DocumentFile.fromTreeUri(DownloadRedditVideoService.this, Uri.parse(destinationFileDirectory)).createDirectory(subredditName); dir = DocumentFile.fromTreeUri(DownloadRedditVideoService.this, Uri.parse(destinationFileDirectory)).createDirectory(subredditName);
if (dir == null) { if (dir == null) {
downloadFinished(null, ERROR_CANNOT_GET_DESTINATION_DIRECTORY); downloadFinished(null, ERROR_CANNOT_GET_DESTINATION_DIRECTORY, randomNotificationIdOffset);
return; return;
} }
} }
} else { } else {
dir = DocumentFile.fromTreeUri(DownloadRedditVideoService.this, Uri.parse(destinationFileDirectory)); dir = DocumentFile.fromTreeUri(DownloadRedditVideoService.this, Uri.parse(destinationFileDirectory));
if (dir == null) { if (dir == null) {
downloadFinished(null, ERROR_CANNOT_GET_DESTINATION_DIRECTORY); downloadFinished(null, ERROR_CANNOT_GET_DESTINATION_DIRECTORY, randomNotificationIdOffset);
return; return;
} }
} }
DocumentFile checkForDuplicates = dir.findFile(destinationFileName); DocumentFile checkForDuplicates = dir.findFile(destinationFileName);
int num = 1; int num = 1;
while (checkForDuplicates != null) { while (checkForDuplicates != null) {
fileNameWithoutExtension[0] = fileNameWithoutExtension[0] + " (" + num + ")"; fileNameWithoutExtension = fileNameWithoutExtension + " (" + num + ")";
checkForDuplicates = dir.findFile(fileNameWithoutExtension[0] + ".mp4"); checkForDuplicates = dir.findFile(fileNameWithoutExtension + ".mp4");
num++; num++;
} }
picFile = dir.createFile("video/*", fileNameWithoutExtension[0] + ".mp4"); picFile = dir.createFile("video/*", fileNameWithoutExtension + ".mp4");
if (picFile == null) { if (picFile == null) {
downloadFinished(null, ERROR_CANNOT_GET_DESTINATION_DIRECTORY); downloadFinished(null, ERROR_CANNOT_GET_DESTINATION_DIRECTORY, randomNotificationIdOffset);
return; return;
} }
destinationFileUriString = picFile.getUri().toString(); destinationFileUriString = picFile.getUri().toString();
} }
updateNotification(R.string.downloading_reddit_video_audio_track, 0, null); updateNotification(R.string.downloading_reddit_video_audio_track, 0,
downloadFile.downloadFile(audioUrl).enqueue(new Callback<ResponseBody>() { randomNotificationIdOffset, null);
@Override
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> audioResponse) {
if (audioResponse.isSuccessful() && audioResponse.body() != null) {
String videoFilePath = directoryPath + fileNameWithoutExtension[0] + "-cache.mp4";
String audioFilePath = directoryPath + fileNameWithoutExtension[0] + "-cache.mp3";
String outputFilePath = directoryPath + fileNameWithoutExtension[0] + ".mp4";
new SaveTempMuxAndCopyAsyncTask(videoResponse.body(),
audioResponse.body(), videoFilePath, audioFilePath, outputFilePath,
destinationFileName, destinationFileUriString, isDefaultDestination,
getContentResolver(),
new SaveTempMuxAndCopyAsyncTask.SaveTempMuxAndCopyAsyncTaskListener() {
@Override
public void finished(Uri destinationFileUri, int errorCode) {
new File(videoFilePath).delete();
new File(audioFilePath).delete();
new File(outputFilePath).delete();
downloadFinished(destinationFileUri, errorCode);
}
@Override Response<ResponseBody> audioResponse = downloadFile.downloadFile(audioUrl).execute();
public void updateProgressNotification(int stringResId) { if (audioResponse.isSuccessful() && audioResponse.body() != null) {
updateNotification(stringResId, -1, null); String videoFilePath = externalCacheDirectoryPath + fileNameWithoutExtension + "-cache.mp4";
} String audioFilePath = externalCacheDirectoryPath + fileNameWithoutExtension + "-cache.mp3";
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); String outputFilePath = externalCacheDirectoryPath + fileNameWithoutExtension + ".mp4";
} else {
String videoFilePath = directoryPath + fileNameWithoutExtension[0] + "-cache.mp4";
new SaveTempMuxAndCopyAsyncTask(videoResponse.body(),
null, videoFilePath, null, null,
destinationFileName, destinationFileUriString, isDefaultDestination,
getContentResolver(),
new SaveTempMuxAndCopyAsyncTask.SaveTempMuxAndCopyAsyncTaskListener() {
@Override
public void finished(Uri destinationFileUri, int errorCode) {
new File(videoFilePath).delete();
downloadFinished(destinationFileUri, errorCode);
}
@Override String savedVideoFilePath = writeResponseBodyToDisk(videoResponse.body(), videoFilePath);
public void updateProgressNotification(int stringResId) { if (savedVideoFilePath == null) {
updateNotification(stringResId, -1, null); downloadFinished(null, ERROR_VIDEO_FILE_CANNOT_SAVE, randomNotificationIdOffset);
} return;
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); }
} String savedAudioFilePath = writeResponseBodyToDisk(audioResponse.body(), audioFilePath);
if (savedAudioFilePath == null) {
downloadFinished(null, ERROR_AUDIO_FILE_CANNOT_SAVE, randomNotificationIdOffset);
return;
} }
@Override updateNotification(R.string.downloading_reddit_video_muxing, -1,
public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) { randomNotificationIdOffset, null);
String videoFilePath = directoryPath + fileNameWithoutExtension[0] + "-cache.mp4"; if (!muxVideoAndAudio(videoFilePath, audioFilePath, outputFilePath)) {
new SaveTempMuxAndCopyAsyncTask(videoResponse.body(), downloadFinished(null, ERROR_MUX_FAILED, randomNotificationIdOffset);
null, videoFilePath, null, null, return;
destinationFileName, destinationFileUriString, isDefaultDestination,
getContentResolver(),
new SaveTempMuxAndCopyAsyncTask.SaveTempMuxAndCopyAsyncTaskListener() {
@Override
public void finished(Uri destinationFileUri, int errorCode) {
new File(videoFilePath).delete();
downloadFinished(destinationFileUri, errorCode);
}
@Override
public void updateProgressNotification(int stringResId) {
updateNotification(stringResId, -1, null);
}
}).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} }
});
updateNotification(R.string.downloading_reddit_video_save_file_to_public_dir, -1,
randomNotificationIdOffset, null);
try {
Uri destinationFileUri = copyToDestination(outputFilePath, destinationFileUriString, destinationFileName, isDefaultDestination);
new File(videoFilePath).delete();
new File(audioFilePath).delete();
new File(outputFilePath).delete();
downloadFinished(destinationFileUri, NO_ERROR, randomNotificationIdOffset);
} catch (IOException e) {
e.printStackTrace();
downloadFinished(null, ERROR_MUXED_VIDEO_FILE_CANNOT_SAVE, randomNotificationIdOffset);
}
} else {
String videoFilePath = externalCacheDirectoryPath + fileNameWithoutExtension + "-cache.mp4";
updateNotification(R.string.downloading_reddit_video_save_file_to_public_dir, -1,
randomNotificationIdOffset, null);
try {
Uri destinationFileUri = copyToDestination(videoFilePath, destinationFileUriString, destinationFileName, isDefaultDestination);
new File(videoFilePath).delete();
downloadFinished(destinationFileUri, NO_ERROR, randomNotificationIdOffset);
} catch (IOException e) {
e.printStackTrace();
downloadFinished(null, ERROR_MUXED_VIDEO_FILE_CANNOT_SAVE, randomNotificationIdOffset);
}
}
} else { } else {
downloadFinished(null, ERROR_VIDEO_FILE_CANNOT_DOWNLOAD); downloadFinished(null, ERROR_VIDEO_FILE_CANNOT_DOWNLOAD, randomNotificationIdOffset);
} }
}
@Override
public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {
downloadFinished(null, ERROR_VIDEO_FILE_CANNOT_DOWNLOAD);
}
});
} else {
downloadFinished(null, ERROR_CANNOT_GET_CACHE_DIRECTORY);
}
return START_NOT_STICKY;
}
private void downloadFinished(Uri destinationFileUri, int errorCode) {
if (errorCode != NO_ERROR) {
switch (errorCode) {
case ERROR_CANNOT_GET_CACHE_DIRECTORY:
updateNotification(R.string.downloading_reddit_video_failed_cannot_get_cache_directory, -1, null);
break;
case ERROR_VIDEO_FILE_CANNOT_DOWNLOAD:
updateNotification(R.string.downloading_reddit_video_failed_cannot_download_video, -1, null);
break;
case ERROR_VIDEO_FILE_CANNOT_SAVE:
updateNotification(R.string.downloading_reddit_video_failed_cannot_save_video, -1, null);
break;
case ERROR_AUDIO_FILE_CANNOT_SAVE:
updateNotification(R.string.downloading_reddit_video_failed_cannot_save_audio, -1, null);
break;
case ERROR_MUX_FAILED:
updateNotification(R.string.downloading_reddit_video_failed_cannot_mux, -1, null);
break;
case ERROR_MUXED_VIDEO_FILE_CANNOT_SAVE:
updateNotification(R.string.downloading_reddit_video_failed_cannot_save_mux_video, -1, null);
break;
case ERROR_CANNOT_GET_DESTINATION_DIRECTORY:
updateNotification(R.string.downloading_media_failed_cannot_save_to_destination_directory, -1, null);
break;
}
EventBus.getDefault().post(new DownloadRedditVideoEvent(false));
} else {
MediaScannerConnection.scanFile(
this, new String[]{destinationFileUri.toString()}, null,
(path, uri) -> {
Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setDataAndType(destinationFileUri, "video/*");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
updateNotification(R.string.downloading_reddit_video_finished, -1, pendingIntent);
EventBus.getDefault().post(new DownloadRedditVideoEvent(true));
}
);
}
stopForeground(false);
}
private Notification createNotification(String fileName) {
builder.setContentTitle(fileName).setContentText(getString(R.string.downloading_reddit_video)).setProgress(100, 0, false);
return builder.setSmallIcon(R.drawable.ic_notification)
.setColor(customThemeWrapper.getColorPrimaryLightTheme())
.build();
}
private void updateNotification(int contentStringResId, int progress, PendingIntent pendingIntent) {
if (notificationManager != null) {
if (progress < 0) {
builder.setProgress(0, 0, false);
} else {
builder.setProgress(100, progress, false);
}
if (contentStringResId != 0) {
builder.setContentText(getString(contentStringResId));
}
if (pendingIntent != null) {
builder.setContentIntent(pendingIntent);
}
notificationManager.notify(NotificationUtils.DOWNLOAD_REDDIT_VIDEO_NOTIFICATION_ID, builder.build());
}
}
private static class SaveTempMuxAndCopyAsyncTask extends AsyncTask<Void, Integer, Void> {
private ResponseBody videoResponse;
private ResponseBody audioResponse;
private String videoFilePath;
private String audioFilePath;
private String outputFilePath;
private String destinationFileName;
@NonNull
private String destinationFileUriString;
private boolean isDefaultDestination;
private ContentResolver contentResolver;
private SaveTempMuxAndCopyAsyncTaskListener saveTempMuxAndCopyAsyncTaskListener;
private int errorCode = NO_ERROR;
public SaveTempMuxAndCopyAsyncTask(ResponseBody videoResponse, ResponseBody audioResponse,
String videoFilePath, String audioFilePath, String outputFilePath,
String destinationFileName, @NonNull String destinationFileUriString,
boolean isDefaultDestination, ContentResolver contentResolver,
SaveTempMuxAndCopyAsyncTaskListener saveTempMuxAndCopyAsyncTaskListener) {
this.videoResponse = videoResponse;
this.audioResponse = audioResponse;
this.videoFilePath = videoFilePath;
this.audioFilePath = audioFilePath;
this.outputFilePath = outputFilePath;
this.destinationFileName = destinationFileName;
this.destinationFileUriString = destinationFileUriString;
this.isDefaultDestination = isDefaultDestination;
this.contentResolver = contentResolver;
this.saveTempMuxAndCopyAsyncTaskListener = saveTempMuxAndCopyAsyncTaskListener;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
saveTempMuxAndCopyAsyncTaskListener.updateProgressNotification(values[0]);
}
@Override
protected Void doInBackground(Void... voids) {
String savedVideoFilePath = writeResponseBodyToDisk(videoResponse, videoFilePath);
if (savedVideoFilePath == null) {
errorCode = ERROR_VIDEO_FILE_CANNOT_SAVE;
return null;
}
if (audioResponse != null) {
String savedAudioFilePath = writeResponseBodyToDisk(audioResponse, audioFilePath);
if (savedAudioFilePath == null) {
errorCode = ERROR_AUDIO_FILE_CANNOT_SAVE;
return null;
}
publishProgress(R.string.downloading_reddit_video_muxing);
if (!muxVideoAndAudio(videoFilePath, audioFilePath, outputFilePath)) {
errorCode = ERROR_MUX_FAILED;
return null;
}
publishProgress(R.string.downloading_reddit_video_save_file_to_public_dir);
try {
copyToDestination(outputFilePath);
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
errorCode = ERROR_MUXED_VIDEO_FILE_CANNOT_SAVE; downloadFinished(null, ERROR_VIDEO_FILE_CANNOT_DOWNLOAD, randomNotificationIdOffset);
} }
} else { } else {
publishProgress(R.string.downloading_reddit_video_save_file_to_public_dir); downloadFinished(null, ERROR_CANNOT_GET_CACHE_DIRECTORY, randomNotificationIdOffset);
try {
copyToDestination(videoFilePath);
} catch (IOException e) {
e.printStackTrace();
errorCode = ERROR_MUXED_VIDEO_FILE_CANNOT_SAVE;
}
} }
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
saveTempMuxAndCopyAsyncTaskListener.finished(Uri.parse(destinationFileUriString), errorCode);
} }
private String writeResponseBodyToDisk(ResponseBody body, String filePath) { private String writeResponseBodyToDisk(ResponseBody body, String filePath) {
@ -576,7 +392,9 @@ public class DownloadRedditVideoService extends Service {
return true; return true;
} }
private void copyToDestination(String srcPath) throws IOException { private Uri copyToDestination(String srcPath, String destinationFileUriString, String destinationFileName,
boolean isDefaultDestination) throws IOException {
ContentResolver contentResolver = getContentResolver();
if (isDefaultDestination) { if (isDefaultDestination) {
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
InputStream in = new FileInputStream(srcPath); InputStream in = new FileInputStream(srcPath);
@ -623,7 +441,7 @@ public class DownloadRedditVideoService extends Service {
contentValues.clear(); contentValues.clear();
contentValues.put(MediaStore.Video.Media.IS_PENDING, 0); contentValues.put(MediaStore.Video.Media.IS_PENDING, 0);
contentResolver.update(uri, contentValues, null, null); contentResolver.update(uri, contentValues, null, null);
destinationFileUriString = uri.toString(); return uri;
} catch (IOException e) { } catch (IOException e) {
if (uri != null) { if (uri != null) {
// Don't leave an orphan entry in the MediaStore // Don't leave an orphan entry in the MediaStore
@ -651,11 +469,136 @@ public class DownloadRedditVideoService extends Service {
stream.write(buf, 0, len); stream.write(buf, 0, len);
} }
} }
return Uri.parse(destinationFileUriString);
}
}
@Override
public void onCreate() {
((Infinity) getApplication()).getAppComponent().inject(this);
notificationManager = NotificationManagerCompat.from(this);
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work doesn't disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
serviceHandler = new ServiceHandler(thread.getLooper());
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
builder = new NotificationCompat.Builder(DownloadRedditVideoService.this, NotificationUtils.CHANNEL_ID_DOWNLOAD_REDDIT_VIDEO);
String subredditName = intent.getStringExtra(EXTRA_SUBREDDIT);
String fileNameWithoutExtension = subredditName + "-" + intent.getStringExtra(EXTRA_POST_ID);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel;
serviceChannel = new NotificationChannel(
NotificationUtils.CHANNEL_ID_DOWNLOAD_REDDIT_VIDEO,
NotificationUtils.CHANNEL_DOWNLOAD_REDDIT_VIDEO,
NotificationManager.IMPORTANCE_LOW
);
notificationManager.createNotificationChannel(serviceChannel);
} }
interface SaveTempMuxAndCopyAsyncTaskListener { int randomNotificationIdOffset = new Random().nextInt(10000);
void finished(Uri destinationFileUri, int errorCode); startForeground(
void updateProgressNotification(int stringResId); NotificationUtils.DOWNLOAD_REDDIT_VIDEO_NOTIFICATION_ID + randomNotificationIdOffset,
createNotification(fileNameWithoutExtension + ".mp4")
);
Message msg = serviceHandler.obtainMessage();
Bundle bundle = intent.getExtras();
msg.setData(bundle);
msg.arg1 = randomNotificationIdOffset;
serviceHandler.sendMessage(msg);
return START_NOT_STICKY;
}
private void downloadFinished(Uri destinationFileUri, int errorCode, int randomNotificationIdOffset) {
if (errorCode != NO_ERROR) {
switch (errorCode) {
case ERROR_CANNOT_GET_CACHE_DIRECTORY:
updateNotification(R.string.downloading_reddit_video_failed_cannot_get_cache_directory, -1,
randomNotificationIdOffset, null);
break;
case ERROR_VIDEO_FILE_CANNOT_DOWNLOAD:
updateNotification(R.string.downloading_reddit_video_failed_cannot_download_video, -1,
randomNotificationIdOffset, null);
break;
case ERROR_VIDEO_FILE_CANNOT_SAVE:
updateNotification(R.string.downloading_reddit_video_failed_cannot_save_video, -1,
randomNotificationIdOffset, null);
break;
case ERROR_AUDIO_FILE_CANNOT_SAVE:
updateNotification(R.string.downloading_reddit_video_failed_cannot_save_audio, -1,
randomNotificationIdOffset, null);
break;
case ERROR_MUX_FAILED:
updateNotification(R.string.downloading_reddit_video_failed_cannot_mux, -1,
randomNotificationIdOffset, null);
break;
case ERROR_MUXED_VIDEO_FILE_CANNOT_SAVE:
updateNotification(R.string.downloading_reddit_video_failed_cannot_save_mux_video, -1,
randomNotificationIdOffset, null);
break;
case ERROR_CANNOT_GET_DESTINATION_DIRECTORY:
updateNotification(R.string.downloading_media_failed_cannot_save_to_destination_directory, -1,
randomNotificationIdOffset, null);
break;
}
EventBus.getDefault().post(new DownloadRedditVideoEvent(false));
} else {
MediaScannerConnection.scanFile(
this, new String[]{destinationFileUri.toString()}, null,
(path, uri) -> {
Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setDataAndType(destinationFileUri, "video/*");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
updateNotification(R.string.downloading_reddit_video_finished, -1, randomNotificationIdOffset, pendingIntent);
EventBus.getDefault().post(new DownloadRedditVideoEvent(true));
}
);
}
stopForeground(false);
}
private Notification createNotification(String fileName) {
builder.setContentTitle(fileName).setContentText(getString(R.string.downloading_reddit_video)).setProgress(100, 0, false);
return builder.setSmallIcon(R.drawable.ic_notification)
.setColor(customThemeWrapper.getColorPrimaryLightTheme())
.build();
}
private void updateNotification(int contentStringResId, int progress, int randomNotificationIdOffset, PendingIntent pendingIntent) {
if (notificationManager != null) {
if (progress < 0) {
builder.setProgress(0, 0, false);
} else {
builder.setProgress(100, progress, false);
}
if (contentStringResId != 0) {
builder.setContentText(getString(contentStringResId));
}
if (pendingIntent != null) {
builder.setContentIntent(pendingIntent);
}
notificationManager.notify(NotificationUtils.DOWNLOAD_REDDIT_VIDEO_NOTIFICATION_ID + randomNotificationIdOffset, builder.build());
} }
} }
} }

View File

@ -26,7 +26,6 @@ public class NotificationUtils {
public static final int DOWNLOAD_VIDEO_NOTIFICATION_ID = 30000; public static final int DOWNLOAD_VIDEO_NOTIFICATION_ID = 30000;
public static final int DOWNLOAD_IMAGE_NOTIFICATION_ID = 40000; public static final int DOWNLOAD_IMAGE_NOTIFICATION_ID = 40000;
public static final int DOWNLOAD_GIF_NOTIFICATION_ID = 50000; public static final int DOWNLOAD_GIF_NOTIFICATION_ID = 50000;
public static final int PULL_NOTIFICATION_ALARM_RECEIVER_REQUEST_CODE = 12;
private static final int SUMMARY_BASE_ID_UNREAD_MESSAGE = 0; private static final int SUMMARY_BASE_ID_UNREAD_MESSAGE = 0;
private static final int NOTIFICATION_BASE_ID_UNREAD_MESSAGE = 1; private static final int NOTIFICATION_BASE_ID_UNREAD_MESSAGE = 1;