mirror of
				https://codeberg.org/Bazsalanszky/Infinity-For-Lemmy.git
				synced 2025-10-31 17:08:11 +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:
		| @@ -21,6 +21,11 @@ | ||||
|         android:theme="@style/AppTheme" | ||||
|         android:usesCleartextTraffic="true" | ||||
|         tools:replace="android:label"> | ||||
|         <service | ||||
|             android:name=".Service.DownloadRedditVideoService" | ||||
|             android:enabled="true" | ||||
|             android:exported="false"></service> | ||||
|  | ||||
|         <activity | ||||
|             android:name=".Activity.ViewImgurMediaActivity" | ||||
|             android:configChanges="orientation|screenSize|layoutDirection" | ||||
| @@ -265,7 +270,7 @@ | ||||
|             android:label="@string/comment_activity_label" | ||||
|             android:parentActivityName=".Activity.MainActivity" | ||||
|             android:theme="@style/AppTheme.NoActionBar" | ||||
|             android:windowSoftInputMode="adjustResize"/> | ||||
|             android:windowSoftInputMode="adjustResize" /> | ||||
|         <activity | ||||
|             android:name=".Activity.SearchResultActivity" | ||||
|             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); | ||||
|         try { | ||||
|             retrofit2.Response response = accessTokenCall.execute(); | ||||
|             retrofit2.Response<String> response = accessTokenCall.execute(); | ||||
|             if (response.isSuccessful() && response.body() != null) { | ||||
|                 JSONObject jsonObject = new JSONObject((String) response.body()); | ||||
|                 String newAccessToken = jsonObject.getString(APIUtils.ACCESS_TOKEN_KEY); | ||||
|   | ||||
| @@ -59,6 +59,7 @@ import butterknife.ButterKnife; | ||||
| import ml.docilealligator.infinityforreddit.FetchGfycatVideoLinks; | ||||
| import ml.docilealligator.infinityforreddit.Infinity; | ||||
| import ml.docilealligator.infinityforreddit.R; | ||||
| import ml.docilealligator.infinityforreddit.Service.DownloadRedditVideoService; | ||||
| import ml.docilealligator.infinityforreddit.Utils.SharedPreferencesUtils; | ||||
| import retrofit2.Retrofit; | ||||
|  | ||||
| @@ -94,11 +95,14 @@ public class ViewVideoActivity extends AppCompatActivity { | ||||
|  | ||||
|     private String videoDownloadUrl; | ||||
|     private String videoFileName; | ||||
|     private String subredditName; | ||||
|     private String id; | ||||
|     private boolean wasPlaying; | ||||
|     private boolean isDownloading = false; | ||||
|     private boolean isMute = false; | ||||
|     private String postTitle; | ||||
|     private long resumePosition = -1; | ||||
|     private int videoType; | ||||
|  | ||||
|     @Inject | ||||
|     @Named("gfycat") | ||||
| @@ -181,7 +185,7 @@ public class ViewVideoActivity extends AppCompatActivity { | ||||
|         TrackSelector trackSelector = new DefaultTrackSelector(videoTrackSelectionFactory); | ||||
|         player = ExoPlayerFactory.newSimpleInstance(this, trackSelector); | ||||
|         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 (savedInstanceState != null) { | ||||
|                 String videoUrl = savedInstanceState.getString(VIDEO_URI_STATE); | ||||
| @@ -243,7 +247,9 @@ public class ViewVideoActivity extends AppCompatActivity { | ||||
|             player.prepare(new ProgressiveMediaSource.Factory(dataSourceFactory).createMediaSource(mVideoUri)); | ||||
|         } else { | ||||
|             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. | ||||
|             dataSourceFactory = new DefaultHttpDataSourceFactory(Util.getUserAgent(this, "Infinity")); | ||||
|             // Prepare the player with the source. | ||||
| @@ -387,46 +393,59 @@ public class ViewVideoActivity extends AppCompatActivity { | ||||
|     private void download() { | ||||
|         isDownloading = false; | ||||
|  | ||||
|         DownloadManager.Request request = new DownloadManager.Request(Uri.parse(videoDownloadUrl)); | ||||
|         request.setTitle(videoFileName); | ||||
|         if (videoType != VIDEO_TYPE_NORMAL) { | ||||
|             DownloadManager.Request request = new DownloadManager.Request(Uri.parse(videoDownloadUrl)); | ||||
|             request.setTitle(videoFileName); | ||||
|  | ||||
|         request.allowScanningByMediaScanner(); | ||||
|         request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); | ||||
|             request.allowScanningByMediaScanner(); | ||||
|             request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); | ||||
|  | ||||
|         //Android Q support | ||||
|         if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { | ||||
|             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; | ||||
|                 } | ||||
|             //Android Q support | ||||
|             if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { | ||||
|                 request.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES, videoFileName); | ||||
|             } else { | ||||
|                 if (directory.isFile()) { | ||||
|                     if (!(directory.delete() && directory.mkdir())) { | ||||
|                 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 { | ||||
|                     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) { | ||||
|                 request.setDestinationInExternalPublicDir(Environment.DIRECTORY_PICTURES + "/Infinity/", videoFileName); | ||||
|             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); | ||||
|         } 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 { | ||||
|                 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(); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -879,7 +879,7 @@ public class PostRecyclerViewAdapter extends PagedListAdapter<Post, RecyclerView | ||||
|                             intent.setData(videoUri); | ||||
|                             intent.putExtra(ViewVideoActivity.EXTRA_VIDEO_DOWNLOAD_URL, post.getVideoDownloadUrl()); | ||||
|                             intent.putExtra(ViewVideoActivity.EXTRA_SUBREDDIT, subredditName); | ||||
|                             intent.putExtra(ViewVideoActivity.EXTRA_ID, fullName); | ||||
|                             intent.putExtra(ViewVideoActivity.EXTRA_ID, id); | ||||
|                             mActivity.startActivity(intent); | ||||
|                         }); | ||||
|  | ||||
| @@ -2130,7 +2130,7 @@ public class PostRecyclerViewAdapter extends PagedListAdapter<Post, RecyclerView | ||||
|                         intent.setData(Uri.parse(post.getVideoUrl())); | ||||
|                         intent.putExtra(ViewVideoActivity.EXTRA_VIDEO_DOWNLOAD_URL, post.getVideoDownloadUrl()); | ||||
|                         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()); | ||||
|                         mActivity.startActivity(intent); | ||||
|                     } 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.UserListingFragment; | ||||
| import ml.docilealligator.infinityforreddit.Fragment.ViewImgurVideoFragment; | ||||
| import ml.docilealligator.infinityforreddit.Service.DownloadRedditVideoService; | ||||
| import ml.docilealligator.infinityforreddit.Service.SubmitPostService; | ||||
| import ml.docilealligator.infinityforreddit.Settings.AdvancedPreferenceFragment; | ||||
| import ml.docilealligator.infinityforreddit.Settings.CustomizeMainPageTabsFragment; | ||||
| @@ -173,4 +174,6 @@ public interface AppComponent { | ||||
|     void inject(ViewImgurMediaActivity viewImgurMediaActivity); | ||||
|  | ||||
|     void inject(ViewImgurVideoFragment viewImgurVideoFragment); | ||||
|  | ||||
|     void inject(DownloadRedditVideoService downloadRedditVideoService); | ||||
| } | ||||
|   | ||||
| @@ -90,6 +90,16 @@ class AppModule { | ||||
|                 .build(); | ||||
|     } | ||||
|  | ||||
|     @Provides | ||||
|     @Named("download_reddit_video") | ||||
|     @Singleton | ||||
|     Retrofit provideDownloadRedditVideoRetrofit() { | ||||
|         return new Retrofit.Builder() | ||||
|                 .baseUrl("http://localhost/") | ||||
|                 .addConverterFactory(ScalarsConverterFactory.create()) | ||||
|                 .build(); | ||||
|     } | ||||
|  | ||||
|     @Provides | ||||
|     @Named("gfycat") | ||||
|     @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"; | ||||
|     static final String CHANNEL_ID_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 DOWNLOAD_REDDIT_VIDEO_NOTIFICATION_ID = 20000; | ||||
|  | ||||
|     private static final int SUMMARY_BASE_ID_UNREAD_MESSAGE = 0; | ||||
|     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.NotificationManager; | ||||
| import android.app.Service; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.graphics.Bitmap; | ||||
| import android.graphics.drawable.Drawable; | ||||
| @@ -110,22 +109,22 @@ public class SubmitPostService extends Service { | ||||
|         if (postType == EXTRA_POST_TEXT_OR_LINK) { | ||||
|             content = intent.getStringExtra(EXTRA_CONTENT); | ||||
|             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(); | ||||
|         } else if (postType == EXTRA_POST_TYPE_IMAGE) { | ||||
|             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(); | ||||
|         } else { | ||||
|             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(); | ||||
|         } | ||||
|  | ||||
|         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) | ||||
|                 .setContentTitle(getString(stringResId)) | ||||
|                 .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="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> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user