From 2d002b42d9727d3a6bb185e760b3c3450ca51319 Mon Sep 17 00:00:00 2001 From: Alex Ning Date: Thu, 16 Jul 2020 17:42:07 +0800 Subject: [PATCH] Fix ANR when uploading large video files. Fix uploading gif as image. --- .../Activity/PostImageActivity.java | 33 +++- .../Activity/PostVideoActivity.java | 14 +- .../Event/SubmitVideoOrGifPostEvent.java | 13 ++ .../Event/SubmitVideoPostEvent.java | 13 -- .../infinityforreddit/Post/SubmitPost.java | 7 +- .../Service/SubmitPostService.java | 175 ++++++++++++------ app/src/main/res/values/strings.xml | 1 + 7 files changed, 176 insertions(+), 80 deletions(-) create mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/Event/SubmitVideoOrGifPostEvent.java delete mode 100644 app/src/main/java/ml/docilealligator/infinityforreddit/Event/SubmitVideoPostEvent.java diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/Activity/PostImageActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/Activity/PostImageActivity.java index f02ff6b5..ef6ba876 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/Activity/PostImageActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/Activity/PostImageActivity.java @@ -50,6 +50,7 @@ import ml.docilealligator.infinityforreddit.AsyncTask.LoadSubredditIconAsyncTask import ml.docilealligator.infinityforreddit.BottomSheetFragment.FlairBottomSheetFragment; import ml.docilealligator.infinityforreddit.CustomTheme.CustomThemeWrapper; import ml.docilealligator.infinityforreddit.Event.SubmitImagePostEvent; +import ml.docilealligator.infinityforreddit.Event.SubmitVideoOrGifPostEvent; import ml.docilealligator.infinityforreddit.Event.SwitchAccountEvent; import ml.docilealligator.infinityforreddit.Flair; import ml.docilealligator.infinityforreddit.Infinity; @@ -518,7 +519,12 @@ public class PostImageActivity extends BaseActivity implements FlairBottomSheetF intent.putExtra(SubmitPostService.EXTRA_FLAIR, flair); intent.putExtra(SubmitPostService.EXTRA_IS_SPOILER, isSpoiler); intent.putExtra(SubmitPostService.EXTRA_IS_NSFW, isNSFW); - intent.putExtra(SubmitPostService.EXTRA_POST_TYPE, SubmitPostService.EXTRA_POST_TYPE_IMAGE); + String mimeType = getContentResolver().getType(imageUri); + if (mimeType != null && mimeType.contains("gif")) { + intent.putExtra(SubmitPostService.EXTRA_POST_TYPE, SubmitPostService.EXTRA_POST_TYPE_VIDEO); + } else { + intent.putExtra(SubmitPostService.EXTRA_POST_TYPE, SubmitPostService.EXTRA_POST_TYPE_IMAGE); + } intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { @@ -643,4 +649,29 @@ public class PostImageActivity extends BaseActivity implements FlairBottomSheetF } } } + + @Subscribe + public void onSubmitVideoOrGifPostEvent(SubmitVideoOrGifPostEvent submitVideoOrGifPostEvent) { + isPosting = false; + mPostingSnackbar.dismiss(); + mMemu.findItem(R.id.action_send_post_image_activity).setEnabled(true); + mMemu.findItem(R.id.action_send_post_image_activity).getIcon().setAlpha(255); + + if (submitVideoOrGifPostEvent.postSuccess) { + Intent intent = new Intent(this, ViewUserDetailActivity.class); + intent.putExtra(ViewUserDetailActivity.EXTRA_USER_NAME_KEY, + mAccountName); + startActivity(intent); + finish(); + } else if (submitVideoOrGifPostEvent.errorProcessingVideoOrGif) { + Snackbar.make(coordinatorLayout, R.string.error_processing_image, Snackbar.LENGTH_SHORT).show(); + } else { + if (submitVideoOrGifPostEvent.errorMessage == null || submitVideoOrGifPostEvent.errorMessage.equals("")) { + Snackbar.make(coordinatorLayout, R.string.post_failed, Snackbar.LENGTH_SHORT).show(); + } else { + Snackbar.make(coordinatorLayout, submitVideoOrGifPostEvent.errorMessage.substring(0, 1).toUpperCase() + + submitVideoOrGifPostEvent.errorMessage.substring(1), Snackbar.LENGTH_SHORT).show(); + } + } + } } diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/Activity/PostVideoActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/Activity/PostVideoActivity.java index 2431e16d..e433d021 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/Activity/PostVideoActivity.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/Activity/PostVideoActivity.java @@ -51,7 +51,7 @@ import ml.docilealligator.infinityforreddit.AsyncTask.GetCurrentAccountAsyncTask import ml.docilealligator.infinityforreddit.AsyncTask.LoadSubredditIconAsyncTask; import ml.docilealligator.infinityforreddit.BottomSheetFragment.FlairBottomSheetFragment; import ml.docilealligator.infinityforreddit.CustomTheme.CustomThemeWrapper; -import ml.docilealligator.infinityforreddit.Event.SubmitVideoPostEvent; +import ml.docilealligator.infinityforreddit.Event.SubmitVideoOrGifPostEvent; import ml.docilealligator.infinityforreddit.Event.SwitchAccountEvent; import ml.docilealligator.infinityforreddit.Flair; import ml.docilealligator.infinityforreddit.Infinity; @@ -652,26 +652,26 @@ public class PostVideoActivity extends BaseActivity implements FlairBottomSheetF } @Subscribe - public void onSubmitVideoPostEvent(SubmitVideoPostEvent submitVideoPostEvent) { + public void onSubmitVideoPostEvent(SubmitVideoOrGifPostEvent submitVideoOrGifPostEvent) { isPosting = false; mPostingSnackbar.dismiss(); mMemu.findItem(R.id.action_send_post_video_activity).setEnabled(true); mMemu.findItem(R.id.action_send_post_video_activity).getIcon().setAlpha(255); - if (submitVideoPostEvent.postSuccess) { + if (submitVideoOrGifPostEvent.postSuccess) { Intent intent = new Intent(this, ViewUserDetailActivity.class); intent.putExtra(ViewUserDetailActivity.EXTRA_USER_NAME_KEY, mAccountName); startActivity(intent); finish(); - } else if (submitVideoPostEvent.errorProcessingVideo) { + } else if (submitVideoOrGifPostEvent.errorProcessingVideoOrGif) { Snackbar.make(coordinatorLayout, R.string.error_processing_video, Snackbar.LENGTH_SHORT).show(); } else { - if (submitVideoPostEvent.errorMessage == null || submitVideoPostEvent.errorMessage.equals("")) { + if (submitVideoOrGifPostEvent.errorMessage == null || submitVideoOrGifPostEvent.errorMessage.equals("")) { Snackbar.make(coordinatorLayout, R.string.post_failed, Snackbar.LENGTH_SHORT).show(); } else { - Snackbar.make(coordinatorLayout, submitVideoPostEvent.errorMessage.substring(0, 1).toUpperCase() - + submitVideoPostEvent.errorMessage.substring(1), Snackbar.LENGTH_SHORT).show(); + Snackbar.make(coordinatorLayout, submitVideoOrGifPostEvent.errorMessage.substring(0, 1).toUpperCase() + + submitVideoOrGifPostEvent.errorMessage.substring(1), Snackbar.LENGTH_SHORT).show(); } } } diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/Event/SubmitVideoOrGifPostEvent.java b/app/src/main/java/ml/docilealligator/infinityforreddit/Event/SubmitVideoOrGifPostEvent.java new file mode 100644 index 00000000..b6f96dc9 --- /dev/null +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/Event/SubmitVideoOrGifPostEvent.java @@ -0,0 +1,13 @@ +package ml.docilealligator.infinityforreddit.Event; + +public class SubmitVideoOrGifPostEvent { + public boolean postSuccess; + public boolean errorProcessingVideoOrGif; + public String errorMessage; + + public SubmitVideoOrGifPostEvent(boolean postSuccess, boolean errorProcessingVideoOrGif, String errorMessage) { + this.postSuccess = postSuccess; + this.errorProcessingVideoOrGif = errorProcessingVideoOrGif; + this.errorMessage = errorMessage; + } +} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/Event/SubmitVideoPostEvent.java b/app/src/main/java/ml/docilealligator/infinityforreddit/Event/SubmitVideoPostEvent.java deleted file mode 100644 index e30cb0e1..00000000 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/Event/SubmitVideoPostEvent.java +++ /dev/null @@ -1,13 +0,0 @@ -package ml.docilealligator.infinityforreddit.Event; - -public class SubmitVideoPostEvent { - public boolean postSuccess; - public boolean errorProcessingVideo; - public String errorMessage; - - public SubmitVideoPostEvent(boolean postSuccess, boolean errorProcessingVideo, String errorMessage) { - this.postSuccess = postSuccess; - this.errorProcessingVideo = errorProcessingVideo; - this.errorMessage = errorMessage; - } -} diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/Post/SubmitPost.java b/app/src/main/java/ml/docilealligator/infinityforreddit/Post/SubmitPost.java index 7b4762a6..01a0cb3d 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/Post/SubmitPost.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/Post/SubmitPost.java @@ -14,6 +14,7 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; import java.io.StringReader; import java.util.HashMap; @@ -22,8 +23,8 @@ import java.util.Map; import ml.docilealligator.infinityforreddit.API.RedditAPI; import ml.docilealligator.infinityforreddit.Flair; -import ml.docilealligator.infinityforreddit.Utils.JSONUtils; import ml.docilealligator.infinityforreddit.Utils.APIUtils; +import ml.docilealligator.infinityforreddit.Utils.JSONUtils; import okhttp3.MediaType; import okhttp3.MultipartBody; import okhttp3.RequestBody; @@ -63,7 +64,7 @@ public class SubmitPost { public static void submitVideoPost(Retrofit oauthRetrofit, Retrofit uploadMediaRetrofit, Retrofit uploadVideoRetrofit, String accessToken, - Locale locale, String subredditName, String title, byte[] buffer, String mimeType, + Locale locale, String subredditName, String title, File buffer, String mimeType, Bitmap posterBitmap, Flair flair, boolean isSpoiler, boolean isNSFW, SubmitPostListener submitPostListener) { RedditAPI api = oauthRetrofit.create(RedditAPI.class); @@ -82,7 +83,7 @@ public class SubmitPost { new ParseJSONResponseFromAWSAsyncTask(response.body(), new ParseJSONResponseFromAWSAsyncTask.ParseJSONResponseFromAWSListener() { @Override public void parseSuccessful(Map nameValuePairsMap) { - RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), buffer); + RequestBody fileBody = RequestBody.create(buffer, MediaType.parse("application/octet-stream")); MultipartBody.Part fileToUpload = MultipartBody.Part.createFormData("file", "post_video." + fileType, fileBody); RedditAPI uploadVideoToAWSApi; diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/Service/SubmitPostService.java b/app/src/main/java/ml/docilealligator/infinityforreddit/Service/SubmitPostService.java index eb8ffd3c..5457ef84 100644 --- a/app/src/main/java/ml/docilealligator/infinityforreddit/Service/SubmitPostService.java +++ b/app/src/main/java/ml/docilealligator/infinityforreddit/Service/SubmitPostService.java @@ -8,9 +8,9 @@ import android.content.Intent; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.AsyncTask; import android.os.Build; import android.os.IBinder; -import android.os.ParcelFileDescriptor; import android.widget.Toast; import androidx.annotation.NonNull; @@ -24,8 +24,11 @@ import com.bumptech.glide.request.transition.Transition; import org.greenrobot.eventbus.EventBus; -import java.io.FileInputStream; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import javax.inject.Inject; import javax.inject.Named; @@ -33,7 +36,7 @@ import javax.inject.Named; import ml.docilealligator.infinityforreddit.CustomTheme.CustomThemeWrapper; import ml.docilealligator.infinityforreddit.Event.SubmitImagePostEvent; import ml.docilealligator.infinityforreddit.Event.SubmitTextOrLinkPostEvent; -import ml.docilealligator.infinityforreddit.Event.SubmitVideoPostEvent; +import ml.docilealligator.infinityforreddit.Event.SubmitVideoOrGifPostEvent; import ml.docilealligator.infinityforreddit.Flair; import ml.docilealligator.infinityforreddit.Infinity; import ml.docilealligator.infinityforreddit.NotificationUtils; @@ -196,68 +199,128 @@ public class SubmitPostService extends Service { } private void submitVideoPost() { - try (ParcelFileDescriptor pfd = getContentResolver().openFileDescriptor(mediaUri, "r")) { - if (pfd != null) { - FileInputStream in = new FileInputStream(pfd.getFileDescriptor()); - byte[] buffer; - buffer = new byte[in.available()]; - while (in.read(buffer) != -1) ; - - Glide.with(this) - .asBitmap() - .load(mediaUri) - .into(new CustomTarget() { - @Override - public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition transition) { - String type = getContentResolver().getType(mediaUri); - if (type != null) { - SubmitPost.submitVideoPost(mOauthRetrofit, mUploadMediaRetrofit, mUploadVideoRetrofit, - mAccessToken, getResources().getConfiguration().locale, subredditName, title, - buffer, type, resource, flair, isSpoiler, isNSFW, - new SubmitPost.SubmitPostListener() { - @Override - public void submitSuccessful(Post post) { - EventBus.getDefault().post(new SubmitVideoPostEvent(true, false, null)); - Toast.makeText(SubmitPostService.this, R.string.video_is_processing, Toast.LENGTH_SHORT).show(); - - stopService(); - } - - @Override - public void submitFailed(@Nullable String errorMessage) { - EventBus.getDefault().post(new SubmitVideoPostEvent(false, false, errorMessage)); - - stopService(); - } - }); - } else { - EventBus.getDefault().post(new SubmitVideoPostEvent(false, true, null)); - } - } - - @Override - public void onLoadFailed(@Nullable Drawable errorDrawable) { - EventBus.getDefault().post(new SubmitVideoPostEvent(false, true, null)); - - stopService(); - } - - @Override - public void onLoadCleared(@Nullable Drawable placeholder) { - - } - }); + try { + InputStream in = getContentResolver().openInputStream(mediaUri); + String type = getContentResolver().getType(mediaUri); + String cacheFilePath; + if (type != null && type.contains("gif")) { + cacheFilePath = getExternalCacheDir() + "/" + mediaUri.getLastPathSegment() + ".gif"; } else { - EventBus.getDefault().post(new SubmitVideoPostEvent(false, true, null)); + cacheFilePath = getExternalCacheDir() + "/" + mediaUri.getLastPathSegment() + ".mp4"; } + + new CopyFileToCacheAsyncTask(in, cacheFilePath, + new CopyFileToCacheAsyncTask.CopyFileToCacheAsyncTaskListener() { + @Override + public void success() { + Glide.with(SubmitPostService.this) + .asBitmap() + .load(mediaUri) + .into(new CustomTarget() { + @Override + public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition transition) { + if (type != null) { + SubmitPost.submitVideoPost(mOauthRetrofit, mUploadMediaRetrofit, mUploadVideoRetrofit, + mAccessToken, getResources().getConfiguration().locale, subredditName, title, + new File(cacheFilePath), type, resource, flair, isSpoiler, isNSFW, + new SubmitPost.SubmitPostListener() { + @Override + public void submitSuccessful(Post post) { + EventBus.getDefault().post(new SubmitVideoOrGifPostEvent(true, false, null)); + if (type.contains("gif")) { + Toast.makeText(SubmitPostService.this, R.string.gif_is_processing, Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(SubmitPostService.this, R.string.video_is_processing, Toast.LENGTH_SHORT).show(); + } + + stopService(); + } + + @Override + public void submitFailed(@Nullable String errorMessage) { + EventBus.getDefault().post(new SubmitVideoOrGifPostEvent(false, false, errorMessage)); + + stopService(); + } + }); + } else { + EventBus.getDefault().post(new SubmitVideoOrGifPostEvent(false, true, null)); + } + } + + @Override + public void onLoadFailed(@Nullable Drawable errorDrawable) { + EventBus.getDefault().post(new SubmitVideoOrGifPostEvent(false, true, null)); + + stopService(); + } + + @Override + public void onLoadCleared(@Nullable Drawable placeholder) { + + } + }); + } + + @Override + public void failed() { + EventBus.getDefault().post(new SubmitVideoOrGifPostEvent(false, true, null)); + + stopService(); + } + }).execute(); } catch (IOException e) { e.printStackTrace(); - EventBus.getDefault().post(new SubmitVideoPostEvent(false, true, null)); + EventBus.getDefault().post(new SubmitVideoOrGifPostEvent(false, true, null)); stopService(); } } + private static class CopyFileToCacheAsyncTask extends AsyncTask { + + private InputStream fileInputStream; + private String destinationFilePath; + private CopyFileToCacheAsyncTaskListener copyFileToCacheAsyncTaskListener; + private boolean parseFailed = false; + + interface CopyFileToCacheAsyncTaskListener { + void success(); + void failed(); + } + + public CopyFileToCacheAsyncTask(InputStream fileInputStream, String destinationFilePath, CopyFileToCacheAsyncTaskListener copyFileToCacheAsyncTaskListener) { + this.fileInputStream = fileInputStream; + this.destinationFilePath = destinationFilePath; + this.copyFileToCacheAsyncTaskListener = copyFileToCacheAsyncTaskListener; + } + + @Override + protected Void doInBackground(Void... voids) { + try (OutputStream out = new FileOutputStream(destinationFilePath)) { + byte[] buf = new byte[2048]; + int len; + while ((len = fileInputStream.read(buf)) > 0) { + out.write(buf, 0, len); + } + } catch (IOException e) { + e.printStackTrace(); + parseFailed = true; + } + return null; + } + + @Override + protected void onPostExecute(Void aVoid) { + super.onPostExecute(aVoid); + if (parseFailed) { + copyFileToCacheAsyncTaskListener.failed(); + } else { + copyFileToCacheAsyncTaskListener.success(); + } + } + } + private void stopService() { stopForeground(true); stopSelf(); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fd9182f4..63f39e2f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -209,6 +209,7 @@ Video is processing. Please wait. Image is processing. Please wait. + Gif is processing. Please wait. Flair Spoiler