mirror of
https://codeberg.org/Bazsalanszky/Infinity-For-Lemmy.git
synced 2024-12-29 04:17:12 +01:00
Downloading Reddit videos with sound is now available. For Android Q and above, the downloaded videos are stored in Movies/Infinity.
This commit is contained in:
parent
ff5691aecb
commit
22576dc003
@ -21,6 +21,11 @@
|
|||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
tools:replace="android:label">
|
tools:replace="android:label">
|
||||||
|
<service
|
||||||
|
android:name=".Service.DownloadRedditVideoService"
|
||||||
|
android:enabled="true"
|
||||||
|
android:exported="false"></service>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".Activity.ViewImgurMediaActivity"
|
android:name=".Activity.ViewImgurMediaActivity"
|
||||||
android:configChanges="orientation|screenSize|layoutDirection"
|
android:configChanges="orientation|screenSize|layoutDirection"
|
||||||
@ -265,7 +270,7 @@
|
|||||||
android:label="@string/comment_activity_label"
|
android:label="@string/comment_activity_label"
|
||||||
android:parentActivityName=".Activity.MainActivity"
|
android:parentActivityName=".Activity.MainActivity"
|
||||||
android:theme="@style/AppTheme.NoActionBar"
|
android:theme="@style/AppTheme.NoActionBar"
|
||||||
android:windowSoftInputMode="adjustResize"/>
|
android:windowSoftInputMode="adjustResize" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".Activity.SearchResultActivity"
|
android:name=".Activity.SearchResultActivity"
|
||||||
android:label="@string/search_activity_label"
|
android:label="@string/search_activity_label"
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
package ml.docilealligator.infinityforreddit.API;
|
||||||
|
|
||||||
|
import okhttp3.ResponseBody;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.GET;
|
||||||
|
import retrofit2.http.Streaming;
|
||||||
|
import retrofit2.http.Url;
|
||||||
|
|
||||||
|
public interface DownloadRedditVideo {
|
||||||
|
@Streaming
|
||||||
|
@GET()
|
||||||
|
Call<ResponseBody> downloadFile(@Url String fileUrl);
|
||||||
|
}
|
@ -67,7 +67,7 @@ class AccessTokenAuthenticator implements Authenticator {
|
|||||||
|
|
||||||
Call<String> accessTokenCall = api.getAccessToken(APIUtils.getHttpBasicAuthHeader(), params);
|
Call<String> accessTokenCall = api.getAccessToken(APIUtils.getHttpBasicAuthHeader(), params);
|
||||||
try {
|
try {
|
||||||
retrofit2.Response response = accessTokenCall.execute();
|
retrofit2.Response<String> response = accessTokenCall.execute();
|
||||||
if (response.isSuccessful() && response.body() != null) {
|
if (response.isSuccessful() && response.body() != null) {
|
||||||
JSONObject jsonObject = new JSONObject((String) response.body());
|
JSONObject jsonObject = new JSONObject((String) response.body());
|
||||||
String newAccessToken = jsonObject.getString(APIUtils.ACCESS_TOKEN_KEY);
|
String newAccessToken = jsonObject.getString(APIUtils.ACCESS_TOKEN_KEY);
|
||||||
|
@ -59,6 +59,7 @@ import butterknife.ButterKnife;
|
|||||||
import ml.docilealligator.infinityforreddit.FetchGfycatVideoLinks;
|
import ml.docilealligator.infinityforreddit.FetchGfycatVideoLinks;
|
||||||
import ml.docilealligator.infinityforreddit.Infinity;
|
import ml.docilealligator.infinityforreddit.Infinity;
|
||||||
import ml.docilealligator.infinityforreddit.R;
|
import ml.docilealligator.infinityforreddit.R;
|
||||||
|
import ml.docilealligator.infinityforreddit.Service.DownloadRedditVideoService;
|
||||||
import ml.docilealligator.infinityforreddit.Utils.SharedPreferencesUtils;
|
import ml.docilealligator.infinityforreddit.Utils.SharedPreferencesUtils;
|
||||||
import retrofit2.Retrofit;
|
import retrofit2.Retrofit;
|
||||||
|
|
||||||
@ -94,11 +95,14 @@ public class ViewVideoActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
private String videoDownloadUrl;
|
private String videoDownloadUrl;
|
||||||
private String videoFileName;
|
private String videoFileName;
|
||||||
|
private String subredditName;
|
||||||
|
private String id;
|
||||||
private boolean wasPlaying;
|
private boolean wasPlaying;
|
||||||
private boolean isDownloading = false;
|
private boolean isDownloading = false;
|
||||||
private boolean isMute = false;
|
private boolean isMute = false;
|
||||||
private String postTitle;
|
private String postTitle;
|
||||||
private long resumePosition = -1;
|
private long resumePosition = -1;
|
||||||
|
private int videoType;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
@Named("gfycat")
|
@Named("gfycat")
|
||||||
@ -181,7 +185,7 @@ public class ViewVideoActivity extends AppCompatActivity {
|
|||||||
TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
|
TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory);
|
||||||
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector);
|
player = ExoPlayerFactory.newSimpleInstance(this, trackSelector);
|
||||||
videoPlayerView.setPlayer(player);
|
videoPlayerView.setPlayer(player);
|
||||||
int videoType = getIntent().getIntExtra(EXTRA_VIDEO_TYPE, VIDEO_TYPE_NORMAL);
|
videoType = getIntent().getIntExtra(EXTRA_VIDEO_TYPE, VIDEO_TYPE_NORMAL);
|
||||||
if (videoType == VIDEO_TYPE_GFYCAT || videoType == VIDEO_TYPE_REDGIFS) {
|
if (videoType == VIDEO_TYPE_GFYCAT || videoType == VIDEO_TYPE_REDGIFS) {
|
||||||
if (savedInstanceState != null) {
|
if (savedInstanceState != null) {
|
||||||
String videoUrl = savedInstanceState.getString(VIDEO_URI_STATE);
|
String videoUrl = savedInstanceState.getString(VIDEO_URI_STATE);
|
||||||
@ -243,7 +247,9 @@ public class ViewVideoActivity extends AppCompatActivity {
|
|||||||
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri));
|
player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri));
|
||||||
} else {
|
} else {
|
||||||
videoDownloadUrl = intent.getStringExtra(EXTRA_VIDEO_DOWNLOAD_URL);
|
videoDownloadUrl = intent.getStringExtra(EXTRA_VIDEO_DOWNLOAD_URL);
|
||||||
videoFileName = intent.getStringExtra(EXTRA_SUBREDDIT) + "-" + intent.getStringExtra(EXTRA_ID) + ".mp4";
|
subredditName = intent.getStringExtra(EXTRA_SUBREDDIT);
|
||||||
|
id = intent.getStringExtra(EXTRA_ID);
|
||||||
|
videoFileName = subredditName + "-" + id + ".mp4";
|
||||||
// Produces DataSource instances through which media data is loaded.
|
// Produces DataSource instances through which media data is loaded.
|
||||||
dataSourceFactory = new DefaultHttpDataSourceFactory(Util.getUserAgent(this, "Infinity"));
|
dataSourceFactory = new DefaultHttpDataSourceFactory(Util.getUserAgent(this, "Infinity"));
|
||||||
// Prepare the player with the source.
|
// Prepare the player with the source.
|
||||||
@ -387,46 +393,59 @@ public class ViewVideoActivity extends AppCompatActivity {
|
|||||||
private void download() {
|
private void download() {
|
||||||
isDownloading = false;
|
isDownloading = false;
|
||||||
|
|
||||||
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(videoDownloadUrl));
|
if (videoType != VIDEO_TYPE_NORMAL) {
|
||||||
request.setTitle(videoFileName);
|
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(videoDownloadUrl));
|
||||||
|
request.setTitle(videoFileName);
|
||||||
|
|
||||||
request.allowScanningByMediaScanner();
|
request.allowScanningByMediaScanner();
|
||||||
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
|
||||||
|
|
||||||
//Android Q support
|
//Android Q support
|
||||||
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||||
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES, videoFileName);
|
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES, videoFileName);
|
||||||
} else {
|
|
||||||
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString();
|
|
||||||
File directory = new File(path + "/Infinity/");
|
|
||||||
boolean saveToInfinityFolder = true;
|
|
||||||
if (!directory.exists()) {
|
|
||||||
if (!directory.mkdir()) {
|
|
||||||
saveToInfinityFolder = false;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (directory.isFile()) {
|
String path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES).toString();
|
||||||
if (!(directory.delete() && directory.mkdir())) {
|
File directory = new File(path + "/Infinity/");
|
||||||
|
boolean saveToInfinityFolder = true;
|
||||||
|
if (!directory.exists()) {
|
||||||
|
if (!directory.mkdir()) {
|
||||||
saveToInfinityFolder = false;
|
saveToInfinityFolder = false;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (directory.isFile()) {
|
||||||
|
if (!(directory.delete() && directory.mkdir())) {
|
||||||
|
saveToInfinityFolder = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (saveToInfinityFolder) {
|
||||||
|
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES + "/Infinity/", videoFileName);
|
||||||
|
} else {
|
||||||
|
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES, videoFileName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (saveToInfinityFolder) {
|
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
|
||||||
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES + "/Infinity/", videoFileName);
|
|
||||||
|
if (manager == null) {
|
||||||
|
Toast.makeText(this, R.string.download_failed, Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.enqueue(request);
|
||||||
|
} else {
|
||||||
|
Intent intent = new Intent(this, DownloadRedditVideoService.class);
|
||||||
|
intent.putExtra(DownloadRedditVideoService.EXTRA_VIDEO_URL, videoDownloadUrl);
|
||||||
|
intent.putExtra(DownloadRedditVideoService.EXTRA_POST_ID, id);
|
||||||
|
intent.putExtra(DownloadRedditVideoService.EXTRA_SUBREDDIT, subredditName);
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
startForegroundService(intent);
|
||||||
} else {
|
} else {
|
||||||
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES, videoFileName);
|
startService(intent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
|
|
||||||
|
|
||||||
if (manager == null) {
|
|
||||||
Toast.makeText(this, R.string.download_failed, Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
manager.enqueue(request);
|
|
||||||
Toast.makeText(this, R.string.download_started, Toast.LENGTH_SHORT).show();
|
Toast.makeText(this, R.string.download_started, Toast.LENGTH_SHORT).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -879,7 +879,7 @@ public class PostRecyclerViewAdapter extends PagedListAdapter<Post, RecyclerView
|
|||||||
intent.setData(videoUri);
|
intent.setData(videoUri);
|
||||||
intent.putExtra(ViewVideoActivity.EXTRA_VIDEO_DOWNLOAD_URL, post.getVideoDownloadUrl());
|
intent.putExtra(ViewVideoActivity.EXTRA_VIDEO_DOWNLOAD_URL, post.getVideoDownloadUrl());
|
||||||
intent.putExtra(ViewVideoActivity.EXTRA_SUBREDDIT, subredditName);
|
intent.putExtra(ViewVideoActivity.EXTRA_SUBREDDIT, subredditName);
|
||||||
intent.putExtra(ViewVideoActivity.EXTRA_ID, fullName);
|
intent.putExtra(ViewVideoActivity.EXTRA_ID, id);
|
||||||
mActivity.startActivity(intent);
|
mActivity.startActivity(intent);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -2130,7 +2130,7 @@ public class PostRecyclerViewAdapter extends PagedListAdapter<Post, RecyclerView
|
|||||||
intent.setData(Uri.parse(post.getVideoUrl()));
|
intent.setData(Uri.parse(post.getVideoUrl()));
|
||||||
intent.putExtra(ViewVideoActivity.EXTRA_VIDEO_DOWNLOAD_URL, post.getVideoDownloadUrl());
|
intent.putExtra(ViewVideoActivity.EXTRA_VIDEO_DOWNLOAD_URL, post.getVideoDownloadUrl());
|
||||||
intent.putExtra(ViewVideoActivity.EXTRA_SUBREDDIT, post.getSubredditName());
|
intent.putExtra(ViewVideoActivity.EXTRA_SUBREDDIT, post.getSubredditName());
|
||||||
intent.putExtra(ViewVideoActivity.EXTRA_ID, post.getFullName());
|
intent.putExtra(ViewVideoActivity.EXTRA_ID, post.getId());
|
||||||
intent.putExtra(ViewVideoActivity.EXTRA_POST_TITLE, post.getTitle());
|
intent.putExtra(ViewVideoActivity.EXTRA_POST_TITLE, post.getTitle());
|
||||||
mActivity.startActivity(intent);
|
mActivity.startActivity(intent);
|
||||||
} else if (post.getPostType() == Post.GIF_TYPE) {
|
} else if (post.getPostType() == Post.GIF_TYPE) {
|
||||||
|
@ -51,6 +51,7 @@ import ml.docilealligator.infinityforreddit.Fragment.SubredditListingFragment;
|
|||||||
import ml.docilealligator.infinityforreddit.Fragment.SubscribedSubredditsListingFragment;
|
import ml.docilealligator.infinityforreddit.Fragment.SubscribedSubredditsListingFragment;
|
||||||
import ml.docilealligator.infinityforreddit.Fragment.UserListingFragment;
|
import ml.docilealligator.infinityforreddit.Fragment.UserListingFragment;
|
||||||
import ml.docilealligator.infinityforreddit.Fragment.ViewImgurVideoFragment;
|
import ml.docilealligator.infinityforreddit.Fragment.ViewImgurVideoFragment;
|
||||||
|
import ml.docilealligator.infinityforreddit.Service.DownloadRedditVideoService;
|
||||||
import ml.docilealligator.infinityforreddit.Service.SubmitPostService;
|
import ml.docilealligator.infinityforreddit.Service.SubmitPostService;
|
||||||
import ml.docilealligator.infinityforreddit.Settings.AdvancedPreferenceFragment;
|
import ml.docilealligator.infinityforreddit.Settings.AdvancedPreferenceFragment;
|
||||||
import ml.docilealligator.infinityforreddit.Settings.CustomizeMainPageTabsFragment;
|
import ml.docilealligator.infinityforreddit.Settings.CustomizeMainPageTabsFragment;
|
||||||
@ -173,4 +174,6 @@ public interface AppComponent {
|
|||||||
void inject(ViewImgurMediaActivity viewImgurMediaActivity);
|
void inject(ViewImgurMediaActivity viewImgurMediaActivity);
|
||||||
|
|
||||||
void inject(ViewImgurVideoFragment viewImgurVideoFragment);
|
void inject(ViewImgurVideoFragment viewImgurVideoFragment);
|
||||||
|
|
||||||
|
void inject(DownloadRedditVideoService downloadRedditVideoService);
|
||||||
}
|
}
|
||||||
|
@ -90,6 +90,16 @@ class AppModule {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Named("download_reddit_video")
|
||||||
|
@Singleton
|
||||||
|
Retrofit provideDownloadRedditVideoRetrofit() {
|
||||||
|
return new Retrofit.Builder()
|
||||||
|
.baseUrl("http://localhost/")
|
||||||
|
.addConverterFactory(ScalarsConverterFactory.create())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Named("gfycat")
|
@Named("gfycat")
|
||||||
@Singleton
|
@Singleton
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
package ml.docilealligator.infinityforreddit.Event;
|
||||||
|
|
||||||
|
public class DownloadRedditVideoEvent {
|
||||||
|
public boolean isSuccessful;
|
||||||
|
|
||||||
|
public DownloadRedditVideoEvent(boolean isSuccessful) {
|
||||||
|
this.isSuccessful = isSuccessful;
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,10 @@ public class NotificationUtils {
|
|||||||
public static final String CHANNEL_SUBMIT_POST = "Submit Post";
|
public static final String CHANNEL_SUBMIT_POST = "Submit Post";
|
||||||
static final String CHANNEL_ID_NEW_MESSAGES = "new_messages";
|
static final String CHANNEL_ID_NEW_MESSAGES = "new_messages";
|
||||||
static final String CHANNEL_NEW_MESSAGES = "New Messages";
|
static final String CHANNEL_NEW_MESSAGES = "New Messages";
|
||||||
|
public static final String CHANNEL_ID_DOWNLOAD_REDDIT_VIDEO = "download_reddit_video";
|
||||||
|
public static final String CHANNEL_DOWNLOAD_REDDIT_VIDEO = "Download Reddit Video";
|
||||||
public static final int SUBMIT_POST_SERVICE_NOTIFICATION_ID = 10000;
|
public static final int SUBMIT_POST_SERVICE_NOTIFICATION_ID = 10000;
|
||||||
|
public static final int DOWNLOAD_REDDIT_VIDEO_NOTIFICATION_ID = 20000;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -0,0 +1,480 @@
|
|||||||
|
package ml.docilealligator.infinityforreddit.Service;
|
||||||
|
|
||||||
|
import android.app.Notification;
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.media.MediaCodec;
|
||||||
|
import android.media.MediaExtractor;
|
||||||
|
import android.media.MediaFormat;
|
||||||
|
import android.media.MediaMuxer;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
|
|
||||||
|
import org.greenrobot.eventbus.EventBus;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
|
|
||||||
|
import ml.docilealligator.infinityforreddit.CustomTheme.CustomThemeWrapper;
|
||||||
|
import ml.docilealligator.infinityforreddit.Event.DownloadRedditVideoEvent;
|
||||||
|
import ml.docilealligator.infinityforreddit.Infinity;
|
||||||
|
import ml.docilealligator.infinityforreddit.NotificationUtils;
|
||||||
|
import ml.docilealligator.infinityforreddit.R;
|
||||||
|
import okhttp3.ResponseBody;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.Callback;
|
||||||
|
import retrofit2.Response;
|
||||||
|
import retrofit2.Retrofit;
|
||||||
|
|
||||||
|
import static android.os.Environment.getExternalStoragePublicDirectory;
|
||||||
|
|
||||||
|
public class DownloadRedditVideoService extends Service {
|
||||||
|
public static final String EXTRA_VIDEO_URL = "EVU";
|
||||||
|
public static final String EXTRA_SUBREDDIT = "ES";
|
||||||
|
public static final String EXTRA_POST_ID = "EPI";
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
@Named("download_reddit_video")
|
||||||
|
Retrofit retrofit;
|
||||||
|
@Inject
|
||||||
|
CustomThemeWrapper mCustomThemeWrapper;
|
||||||
|
|
||||||
|
public DownloadRedditVideoService() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBinder onBind(Intent intent) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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('/')) + "/audio";
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
NotificationChannel serviceChannel = new NotificationChannel(
|
||||||
|
NotificationUtils.CHANNEL_ID_DOWNLOAD_REDDIT_VIDEO,
|
||||||
|
NotificationUtils.CHANNEL_DOWNLOAD_REDDIT_VIDEO,
|
||||||
|
NotificationManager.IMPORTANCE_LOW
|
||||||
|
);
|
||||||
|
|
||||||
|
NotificationManagerCompat manager = NotificationManagerCompat.from(this);
|
||||||
|
manager.createNotificationChannel(serviceChannel);
|
||||||
|
}
|
||||||
|
|
||||||
|
startForeground(NotificationUtils.DOWNLOAD_REDDIT_VIDEO_NOTIFICATION_ID, createNotification(R.string.downloading_reddit_video));
|
||||||
|
|
||||||
|
ml.docilealligator.infinityforreddit.API.DownloadRedditVideo downloadRedditVideo = retrofit.create(ml.docilealligator.infinityforreddit.API.DownloadRedditVideo.class);
|
||||||
|
downloadRedditVideo.downloadFile(videoUrl).enqueue(new Callback<ResponseBody>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> videoResponse) {
|
||||||
|
if (videoResponse.isSuccessful() && videoResponse.body() != null) {
|
||||||
|
String fileNameWithoutExtension = intent.getStringExtra(EXTRA_SUBREDDIT)
|
||||||
|
+ "-" + intent.getStringExtra(EXTRA_POST_ID);
|
||||||
|
|
||||||
|
downloadRedditVideo.downloadFile(audioUrl).enqueue(new Callback<ResponseBody>() {
|
||||||
|
@Override
|
||||||
|
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> audioResponse) {
|
||||||
|
File directory = getExternalCacheDir();
|
||||||
|
if (directory != null) {
|
||||||
|
String directoryPath = directory.getAbsolutePath() + "/";
|
||||||
|
if (audioResponse.isSuccessful() && audioResponse.body() != null) {
|
||||||
|
String videoFilePath = writeResponseBodyToDisk(videoResponse.body(), directoryPath + fileNameWithoutExtension+ "-cache.mp4");
|
||||||
|
if (videoFilePath != null) {
|
||||||
|
String audioFilePath = writeResponseBodyToDisk(audioResponse.body(), directoryPath + fileNameWithoutExtension + "-cache.mp3");
|
||||||
|
if (audioFilePath != null) {
|
||||||
|
String outputFilePath = directoryPath + fileNameWithoutExtension + ".mp4";
|
||||||
|
if(muxVideoAndAudio(videoFilePath, audioFilePath, outputFilePath)) {
|
||||||
|
new CopyFileAsyncTask(new File(outputFilePath), fileNameWithoutExtension + ".mp4",
|
||||||
|
getContentResolver(), new CopyFileAsyncTask.CopyFileAsyncTaskListener() {
|
||||||
|
@Override
|
||||||
|
public void successful() {
|
||||||
|
new File(videoFilePath).delete();
|
||||||
|
new File(audioFilePath).delete();
|
||||||
|
new File(outputFilePath).delete();
|
||||||
|
|
||||||
|
EventBus.getDefault().post(new DownloadRedditVideoEvent(true));
|
||||||
|
|
||||||
|
stopService();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failed() {
|
||||||
|
new File(videoFilePath).delete();
|
||||||
|
new File(audioFilePath).delete();
|
||||||
|
new File(outputFilePath).delete();
|
||||||
|
|
||||||
|
EventBus.getDefault().post(new DownloadRedditVideoEvent(false));
|
||||||
|
|
||||||
|
stopService();
|
||||||
|
}
|
||||||
|
}).execute();
|
||||||
|
} else {
|
||||||
|
EventBus.getDefault().post(new DownloadRedditVideoEvent(false));
|
||||||
|
|
||||||
|
stopService();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
new File(videoFilePath).delete();
|
||||||
|
|
||||||
|
EventBus.getDefault().post(new DownloadRedditVideoEvent(false));
|
||||||
|
|
||||||
|
stopService();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
EventBus.getDefault().post(new DownloadRedditVideoEvent(false));
|
||||||
|
|
||||||
|
stopService();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
//No audio
|
||||||
|
String videoFilePath = writeResponseBodyToDisk(videoResponse.body(), directoryPath + fileNameWithoutExtension+ ".mp4");
|
||||||
|
if (videoFilePath != null) {
|
||||||
|
new CopyFileAsyncTask(new File(videoFilePath), fileNameWithoutExtension + ".mp4",
|
||||||
|
getContentResolver(), new CopyFileAsyncTask.CopyFileAsyncTaskListener() {
|
||||||
|
@Override
|
||||||
|
public void successful() {
|
||||||
|
new File(videoFilePath).delete();
|
||||||
|
|
||||||
|
EventBus.getDefault().post(new DownloadRedditVideoEvent(true));
|
||||||
|
|
||||||
|
stopService();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void failed() {
|
||||||
|
new File(videoFilePath).delete();
|
||||||
|
|
||||||
|
EventBus.getDefault().post(new DownloadRedditVideoEvent(false));
|
||||||
|
|
||||||
|
stopService();
|
||||||
|
}
|
||||||
|
}).execute();
|
||||||
|
} else {
|
||||||
|
EventBus.getDefault().post(new DownloadRedditVideoEvent(false));
|
||||||
|
|
||||||
|
stopService();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
EventBus.getDefault().post(new DownloadRedditVideoEvent(false));
|
||||||
|
|
||||||
|
stopService();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {
|
||||||
|
EventBus.getDefault().post(new DownloadRedditVideoEvent(false));
|
||||||
|
|
||||||
|
stopService();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
EventBus.getDefault().post(new DownloadRedditVideoEvent(false));
|
||||||
|
|
||||||
|
stopService();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {
|
||||||
|
EventBus.getDefault().post(new DownloadRedditVideoEvent(false));
|
||||||
|
|
||||||
|
stopService();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return START_NOT_STICKY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Notification createNotification(int stringResId) {
|
||||||
|
return new NotificationCompat.Builder(this, NotificationUtils.CHANNEL_ID_DOWNLOAD_REDDIT_VIDEO)
|
||||||
|
.setContentTitle(getString(stringResId))
|
||||||
|
.setContentText(getString(R.string.please_wait))
|
||||||
|
.setSmallIcon(R.drawable.ic_notification)
|
||||||
|
.setColor(mCustomThemeWrapper.getColorPrimaryLightTheme())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String writeResponseBodyToDisk(ResponseBody body, String filePath) {
|
||||||
|
try {
|
||||||
|
File file = new File(filePath);
|
||||||
|
|
||||||
|
InputStream inputStream = null;
|
||||||
|
OutputStream outputStream = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] fileReader = new byte[4096];
|
||||||
|
|
||||||
|
long fileSize = body.contentLength();
|
||||||
|
long fileSizeDownloaded = 0;
|
||||||
|
|
||||||
|
inputStream = body.byteStream();
|
||||||
|
outputStream = new FileOutputStream(file);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int read = inputStream.read(fileReader);
|
||||||
|
|
||||||
|
if (read == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.write(fileReader, 0, read);
|
||||||
|
|
||||||
|
fileSizeDownloaded += read;
|
||||||
|
|
||||||
|
Log.i("asdfsadf", "file download: " + fileSizeDownloaded + " of " + fileSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
outputStream.flush();
|
||||||
|
|
||||||
|
return file.getPath();
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
} finally {
|
||||||
|
if (inputStream != null) {
|
||||||
|
inputStream.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outputStream != null) {
|
||||||
|
outputStream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean muxVideoAndAudio(String videoFilePath, String audioFilePath, String outputFilePath) {
|
||||||
|
try {
|
||||||
|
File file = new File(outputFilePath);
|
||||||
|
file.createNewFile();
|
||||||
|
MediaExtractor videoExtractor = new MediaExtractor();
|
||||||
|
videoExtractor.setDataSource(videoFilePath);
|
||||||
|
MediaExtractor audioExtractor = new MediaExtractor();
|
||||||
|
audioExtractor.setDataSource(audioFilePath);
|
||||||
|
MediaMuxer muxer = new MediaMuxer(outputFilePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
|
||||||
|
|
||||||
|
videoExtractor.selectTrack(0);
|
||||||
|
MediaFormat videoFormat = videoExtractor.getTrackFormat(0);
|
||||||
|
int videoTrack = muxer.addTrack(videoFormat);
|
||||||
|
|
||||||
|
audioExtractor.selectTrack(0);
|
||||||
|
MediaFormat audioFormat = audioExtractor.getTrackFormat(0);
|
||||||
|
int audioTrack = muxer.addTrack(audioFormat);
|
||||||
|
boolean sawEOS = false;
|
||||||
|
int offset = 100;
|
||||||
|
int sampleSize = 256 * 1024;
|
||||||
|
ByteBuffer videoBuf = ByteBuffer.allocate(sampleSize);
|
||||||
|
ByteBuffer audioBuf = ByteBuffer.allocate(sampleSize);
|
||||||
|
MediaCodec.BufferInfo videoBufferInfo = new MediaCodec.BufferInfo();
|
||||||
|
MediaCodec.BufferInfo audioBufferInfo = new MediaCodec.BufferInfo();
|
||||||
|
|
||||||
|
videoExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
|
||||||
|
audioExtractor.seekTo(0, MediaExtractor.SEEK_TO_CLOSEST_SYNC);
|
||||||
|
|
||||||
|
muxer.start();
|
||||||
|
|
||||||
|
while (!sawEOS)
|
||||||
|
{
|
||||||
|
videoBufferInfo.offset = offset;
|
||||||
|
videoBufferInfo.size = videoExtractor.readSampleData(videoBuf, offset);
|
||||||
|
|
||||||
|
|
||||||
|
if (videoBufferInfo.size < 0 || audioBufferInfo.size < 0)
|
||||||
|
{
|
||||||
|
// Log.d(TAG, "saw input EOS.");
|
||||||
|
sawEOS = true;
|
||||||
|
videoBufferInfo.size = 0;
|
||||||
|
} else {
|
||||||
|
videoBufferInfo.presentationTimeUs = videoExtractor.getSampleTime();
|
||||||
|
videoBufferInfo.flags = videoExtractor.getSampleFlags();
|
||||||
|
muxer.writeSampleData(videoTrack, videoBuf, videoBufferInfo);
|
||||||
|
videoExtractor.advance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean sawEOS2 = false;
|
||||||
|
while (!sawEOS2)
|
||||||
|
{
|
||||||
|
audioBufferInfo.offset = offset;
|
||||||
|
audioBufferInfo.size = audioExtractor.readSampleData(audioBuf, offset);
|
||||||
|
|
||||||
|
if (videoBufferInfo.size < 0 || audioBufferInfo.size < 0) {
|
||||||
|
sawEOS2 = true;
|
||||||
|
audioBufferInfo.size = 0;
|
||||||
|
} else {
|
||||||
|
audioBufferInfo.presentationTimeUs = audioExtractor.getSampleTime();
|
||||||
|
audioBufferInfo.flags = audioExtractor.getSampleFlags();
|
||||||
|
muxer.writeSampleData(audioTrack, audioBuf, audioBufferInfo);
|
||||||
|
audioExtractor.advance();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
muxer.stop();
|
||||||
|
muxer.release();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void stopService() {
|
||||||
|
stopForeground(true);
|
||||||
|
stopSelf();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CopyFileAsyncTask extends AsyncTask<Void, Void, Void> {
|
||||||
|
private File src;
|
||||||
|
private String destinationFileName;
|
||||||
|
private ContentResolver contentResolver;
|
||||||
|
private CopyFileAsyncTaskListener copyFileAsyncTaskListener;
|
||||||
|
private boolean successful;
|
||||||
|
|
||||||
|
interface CopyFileAsyncTaskListener {
|
||||||
|
void successful();
|
||||||
|
void failed();
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyFileAsyncTask(File src, String destinationFileName, ContentResolver contentResolver, CopyFileAsyncTaskListener copyFileAsyncTaskListener) {
|
||||||
|
this.src = src;
|
||||||
|
this.destinationFileName = destinationFileName;
|
||||||
|
this.contentResolver = contentResolver;
|
||||||
|
this.copyFileAsyncTaskListener = copyFileAsyncTaskListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Void... voids) {
|
||||||
|
if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
|
||||||
|
successful = copy(src, destinationFileName);
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
copyFileQ(src, destinationFileName);
|
||||||
|
successful = true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
successful = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Void aVoid) {
|
||||||
|
super.onPostExecute(aVoid);
|
||||||
|
if (successful) {
|
||||||
|
copyFileAsyncTaskListener.successful();
|
||||||
|
} else {
|
||||||
|
copyFileAsyncTaskListener.failed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||||
|
private void copyFileQ(File src, String outputFileName) throws IOException {
|
||||||
|
String relativeLocation = Environment.DIRECTORY_MOVIES + "/Infinity/";
|
||||||
|
|
||||||
|
ContentValues contentValues = new ContentValues();
|
||||||
|
contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, outputFileName);
|
||||||
|
contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, relativeLocation);
|
||||||
|
contentValues.put(MediaStore.Video.Media.IS_PENDING, 1);
|
||||||
|
|
||||||
|
OutputStream stream = null;
|
||||||
|
Uri uri = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Uri contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
uri = contentResolver.insert(contentUri, contentValues);
|
||||||
|
|
||||||
|
if (uri == null) {
|
||||||
|
throw new IOException("Failed to create new MediaStore record.");
|
||||||
|
}
|
||||||
|
|
||||||
|
stream = contentResolver.openOutputStream(uri);
|
||||||
|
|
||||||
|
if (stream == null) {
|
||||||
|
throw new IOException("Failed to get output stream.");
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStream in = new FileInputStream(src);
|
||||||
|
|
||||||
|
byte[] buf = new byte[1024];
|
||||||
|
int len;
|
||||||
|
while ((len = in.read(buf)) > 0) {
|
||||||
|
stream.write(buf, 0, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
contentValues.clear();
|
||||||
|
contentValues.put(MediaStore.Images.Media.IS_PENDING, 0);
|
||||||
|
contentResolver.update(uri, contentValues, null, null);
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (uri != null) {
|
||||||
|
// Don't leave an orphan entry in the MediaStore
|
||||||
|
contentResolver.delete(uri, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
if (stream != null) {
|
||||||
|
stream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean copy(File src, String outputFileName) {
|
||||||
|
File directory = getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
|
||||||
|
if (directory != null) {
|
||||||
|
String directoryPath = directory.getAbsolutePath() + "/Infinity/";;
|
||||||
|
File dst = new File(directoryPath, outputFileName);
|
||||||
|
|
||||||
|
try (InputStream in = new FileInputStream(src)) {
|
||||||
|
try (OutputStream out = new FileOutputStream(dst)) {
|
||||||
|
byte[] buf = new byte[1024];
|
||||||
|
int len;
|
||||||
|
while ((len = in.read(buf)) > 0) {
|
||||||
|
out.write(buf, 0, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
src.delete();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
src.delete();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
src.delete();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,6 @@ import android.app.Notification;
|
|||||||
import android.app.NotificationChannel;
|
import android.app.NotificationChannel;
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
@ -110,22 +109,22 @@ public class SubmitPostService extends Service {
|
|||||||
if (postType == EXTRA_POST_TEXT_OR_LINK) {
|
if (postType == EXTRA_POST_TEXT_OR_LINK) {
|
||||||
content = intent.getStringExtra(EXTRA_CONTENT);
|
content = intent.getStringExtra(EXTRA_CONTENT);
|
||||||
kind = intent.getStringExtra(EXTRA_KIND);
|
kind = intent.getStringExtra(EXTRA_KIND);
|
||||||
startForeground(NotificationUtils.SUBMIT_POST_SERVICE_NOTIFICATION_ID, createNotification(this, R.string.posting));
|
startForeground(NotificationUtils.SUBMIT_POST_SERVICE_NOTIFICATION_ID, createNotification(R.string.posting));
|
||||||
submitTextOrLinkPost();
|
submitTextOrLinkPost();
|
||||||
} else if (postType == EXTRA_POST_TYPE_IMAGE) {
|
} else if (postType == EXTRA_POST_TYPE_IMAGE) {
|
||||||
mediaUri = intent.getData();
|
mediaUri = intent.getData();
|
||||||
startForeground(NotificationUtils.SUBMIT_POST_SERVICE_NOTIFICATION_ID, createNotification(this, R.string.posting_image));
|
startForeground(NotificationUtils.SUBMIT_POST_SERVICE_NOTIFICATION_ID, createNotification(R.string.posting_image));
|
||||||
submitImagePost();
|
submitImagePost();
|
||||||
} else {
|
} else {
|
||||||
mediaUri = intent.getData();
|
mediaUri = intent.getData();
|
||||||
startForeground(NotificationUtils.SUBMIT_POST_SERVICE_NOTIFICATION_ID, createNotification(this, R.string.posting_video));
|
startForeground(NotificationUtils.SUBMIT_POST_SERVICE_NOTIFICATION_ID, createNotification(R.string.posting_video));
|
||||||
submitVideoPost();
|
submitVideoPost();
|
||||||
}
|
}
|
||||||
|
|
||||||
return START_NOT_STICKY;
|
return START_NOT_STICKY;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Notification createNotification(Context context, int stringResId) {
|
private Notification createNotification(int stringResId) {
|
||||||
return new NotificationCompat.Builder(this, NotificationUtils.CHANNEL_SUBMIT_POST)
|
return new NotificationCompat.Builder(this, NotificationUtils.CHANNEL_SUBMIT_POST)
|
||||||
.setContentTitle(getString(stringResId))
|
.setContentTitle(getString(stringResId))
|
||||||
.setContentText(getString(R.string.please_wait))
|
.setContentText(getString(R.string.please_wait))
|
||||||
|
@ -739,7 +739,7 @@
|
|||||||
<string name="fetching_video_info_please_wait">Fetching video info. Please wait.</string>
|
<string name="fetching_video_info_please_wait">Fetching video info. Please wait.</string>
|
||||||
|
|
||||||
<string name="error_fetching_imgur_media">Cannot load images</string>
|
<string name="error_fetching_imgur_media">Cannot load images</string>
|
||||||
<!-- TODO: Remove or change this placeholder text -->
|
|
||||||
<string name="hello_blank_fragment">Hello blank fragment</string>
|
<string name="downloading_reddit_video">Downloading video.</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user