Rewrite SubmitPostService to run the service in a background thread.

This commit is contained in:
Alex Ning 2021-01-26 10:36:38 +08:00
parent 7ca511d3f1
commit b6c00df82f
2 changed files with 273 additions and 480 deletions

View File

@ -1,9 +1,7 @@
package ml.docilealligator.infinityforreddit.post; package ml.docilealligator.infinityforreddit.post;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.AsyncTask;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.json.JSONArray; import org.json.JSONArray;
@ -21,15 +19,14 @@ import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import ml.docilealligator.infinityforreddit.apis.RedditAPI;
import ml.docilealligator.infinityforreddit.Flair; import ml.docilealligator.infinityforreddit.Flair;
import ml.docilealligator.infinityforreddit.apis.RedditAPI;
import ml.docilealligator.infinityforreddit.utils.APIUtils; import ml.docilealligator.infinityforreddit.utils.APIUtils;
import ml.docilealligator.infinityforreddit.utils.JSONUtils; import ml.docilealligator.infinityforreddit.utils.JSONUtils;
import okhttp3.MediaType; import okhttp3.MediaType;
import okhttp3.MultipartBody; import okhttp3.MultipartBody;
import okhttp3.RequestBody; import okhttp3.RequestBody;
import retrofit2.Call; import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response; import retrofit2.Response;
import retrofit2.Retrofit; import retrofit2.Retrofit;
@ -38,33 +35,31 @@ public class SubmitPost {
Locale locale, String subredditName, String title, String content, Locale locale, String subredditName, String title, String content,
Flair flair, boolean isSpoiler, boolean isNSFW, String kind, Flair flair, boolean isSpoiler, boolean isNSFW, String kind,
SubmitPostListener submitPostListener) { SubmitPostListener submitPostListener) {
submitPost(oauthRetrofit, accessToken, locale, subredditName, title, content, submitPost(oauthRetrofit, accessToken, subredditName, title, content,
flair, isSpoiler, isNSFW, kind, null, submitPostListener); flair, isSpoiler, isNSFW, kind, null, submitPostListener);
} }
public static void submitImagePost(Retrofit oauthRetrofit, Retrofit uploadMediaRetrofit, public static void submitImagePost(Retrofit oauthRetrofit, Retrofit uploadMediaRetrofit,
String accessToken, Locale locale, String accessToken, String subredditName, String title, Bitmap image,
String subredditName, String title, Bitmap image, Flair flair, Flair flair, boolean isSpoiler, boolean isNSFW, SubmitPostListener submitPostListener) {
boolean isSpoiler, boolean isNSFW, SubmitPostListener submitPostListener) { try {
uploadImage(oauthRetrofit, uploadMediaRetrofit, accessToken, image, String imageUrlOrError = uploadImage(oauthRetrofit, uploadMediaRetrofit, accessToken, image);
new UploadImageListener() { if (imageUrlOrError != null && !imageUrlOrError.startsWith("Error: ")) {
@Override submitPost(oauthRetrofit, accessToken,
public void uploaded(String imageUrl) { subredditName, title, imageUrlOrError, flair, isSpoiler, isNSFW,
submitPost(oauthRetrofit, accessToken, locale,
subredditName, title, imageUrl, flair, isSpoiler, isNSFW,
APIUtils.KIND_IMAGE, null, submitPostListener); APIUtils.KIND_IMAGE, null, submitPostListener);
} else {
submitPostListener.submitFailed(imageUrlOrError);
} }
} catch (IOException | JSONException | XmlPullParserException e) {
@Override e.printStackTrace();
public void uploadFailed(@Nullable String errorMessage) { submitPostListener.submitFailed(e.getMessage());
submitPostListener.submitFailed(errorMessage);
} }
});
} }
public static void submitVideoPost(Retrofit oauthRetrofit, Retrofit uploadMediaRetrofit, public static void submitVideoPost(Retrofit oauthRetrofit, Retrofit uploadMediaRetrofit,
Retrofit uploadVideoRetrofit, String accessToken, Retrofit uploadVideoRetrofit, String accessToken,
Locale locale, String subredditName, String title, File buffer, String mimeType, String subredditName, String title, File buffer, String mimeType,
Bitmap posterBitmap, Flair flair, boolean isSpoiler, boolean isNSFW, Bitmap posterBitmap, Flair flair, boolean isSpoiler, boolean isNSFW,
SubmitPostListener submitPostListener) { SubmitPostListener submitPostListener) {
RedditAPI api = oauthRetrofit.create(RedditAPI.class); RedditAPI api = oauthRetrofit.create(RedditAPI.class);
@ -76,13 +71,11 @@ public class SubmitPost {
uploadImageParams.put(APIUtils.MIMETYPE_KEY, mimeType); uploadImageParams.put(APIUtils.MIMETYPE_KEY, mimeType);
Call<String> uploadImageCall = api.uploadImage(APIUtils.getOAuthHeader(accessToken), uploadImageParams); Call<String> uploadImageCall = api.uploadImage(APIUtils.getOAuthHeader(accessToken), uploadImageParams);
uploadImageCall.enqueue(new Callback<String>() { try {
@Override Response<String> uploadImageResponse = uploadImageCall.execute();
public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) { if (uploadImageResponse.isSuccessful()) {
if (response.isSuccessful()) { Map<String, RequestBody> nameValuePairsMap = parseJSONResponseFromAWS(uploadImageResponse.body());
new ParseJSONResponseFromAWSAsyncTask(response.body(), new ParseJSONResponseFromAWSAsyncTask.ParseJSONResponseFromAWSListener() {
@Override
public void parseSuccessful(Map<String, RequestBody> nameValuePairsMap) {
RequestBody fileBody = RequestBody.create(buffer, MediaType.parse("application/octet-stream")); RequestBody fileBody = RequestBody.create(buffer, MediaType.parse("application/octet-stream"));
MultipartBody.Part fileToUpload = MultipartBody.Part.createFormData("file", "post_video." + fileType, fileBody); MultipartBody.Part fileToUpload = MultipartBody.Part.createFormData("file", "post_video." + fileType, fileBody);
@ -93,80 +86,49 @@ public class SubmitPost {
uploadVideoToAWSApi = uploadVideoRetrofit.create(RedditAPI.class); uploadVideoToAWSApi = uploadVideoRetrofit.create(RedditAPI.class);
} }
Call<String> uploadMediaToAWS = uploadVideoToAWSApi.uploadMediaToAWS(nameValuePairsMap, fileToUpload); Call<String> uploadMediaToAWS = uploadVideoToAWSApi.uploadMediaToAWS(nameValuePairsMap, fileToUpload);
Response<String> uploadMediaToAWSResponse = uploadMediaToAWS.execute();
uploadMediaToAWS.enqueue(new Callback<String>() { if (uploadMediaToAWSResponse.isSuccessful()) {
@Override String url = parseXMLResponseFromAWS(uploadMediaToAWSResponse.body());
public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) { if (url == null) {
if (response.isSuccessful()) { submitPostListener.submitFailed(null);
new ParseXMLReponseFromAWSAsyncTask(response.body(), new ParseXMLReponseFromAWSAsyncTask.ParseXMLResponseFromAWSListener() { return;
@Override }
public void parseSuccessful(String url) { String imageUrlOrError = uploadImage(oauthRetrofit, uploadMediaRetrofit, accessToken, posterBitmap);
uploadImage(oauthRetrofit, uploadMediaRetrofit, accessToken, if (imageUrlOrError != null && !imageUrlOrError.startsWith("Error: ")) {
posterBitmap, new UploadImageListener() {
@Override
public void uploaded(String imageUrl) {
if (fileType.equals("gif")) { if (fileType.equals("gif")) {
submitPost(oauthRetrofit, accessToken, locale, submitPost(oauthRetrofit, accessToken,
subredditName, title, url, flair, isSpoiler, isNSFW, subredditName, title, url, flair, isSpoiler, isNSFW,
APIUtils.KIND_VIDEOGIF, imageUrl, submitPostListener); APIUtils.KIND_VIDEOGIF, imageUrlOrError, submitPostListener);
} else { } else {
submitPost(oauthRetrofit, accessToken, locale, submitPost(oauthRetrofit, accessToken,
subredditName, title, url, flair, isSpoiler, isNSFW, subredditName, title, url, flair, isSpoiler, isNSFW,
APIUtils.KIND_VIDEO, imageUrl, submitPostListener); APIUtils.KIND_VIDEO, imageUrlOrError, submitPostListener);
} }
}
@Override
public void uploadFailed(@Nullable String errorMessage) {
submitPostListener.submitFailed(errorMessage);
}
});
}
@Override
public void parseFailed() {
submitPostListener.submitFailed(null);
}
}).execute();
} else { } else {
submitPostListener.submitFailed(response.message()); submitPostListener.submitFailed(imageUrlOrError);
} }
}
@Override
public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) {
submitPostListener.submitFailed(t.getMessage());
}
});
}
@Override
public void parseFailed() {
submitPostListener.submitFailed(null);
}
}).execute();
} else { } else {
submitPostListener.submitFailed(response.message()); submitPostListener.submitFailed(uploadMediaToAWSResponse.code() + " " + uploadMediaToAWSResponse.message());
} }
} else {
submitPostListener.submitFailed(uploadImageResponse.code() + " " + uploadImageResponse.message());
} }
} catch (IOException | XmlPullParserException | JSONException e) {
@Override e.printStackTrace();
public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) { submitPostListener.submitFailed(e.getMessage());
submitPostListener.submitFailed(t.getMessage());
} }
});
} }
public static void submitCrosspost(Retrofit oauthRetrofit, String accessToken, public static void submitCrosspost(Retrofit oauthRetrofit, String accessToken,
Locale locale, String subredditName, String title, String crosspostFullname, String subredditName, String title, String crosspostFullname,
Flair flair, boolean isSpoiler, boolean isNSFW, String kind, Flair flair, boolean isSpoiler, boolean isNSFW, String kind,
SubmitPostListener submitPostListener) { SubmitPostListener submitPostListener) {
submitPost(oauthRetrofit, accessToken, locale, subredditName, title, crosspostFullname, submitPost(oauthRetrofit, accessToken, subredditName, title, crosspostFullname,
flair, isSpoiler, isNSFW, kind, null, submitPostListener); flair, isSpoiler, isNSFW, kind, null, submitPostListener);
} }
private static void submitPost(Retrofit oauthRetrofit, String accessToken, private static void submitPost(Retrofit oauthRetrofit, String accessToken,
Locale locale, String subredditName, String title, String content, String subredditName, String title, String content,
Flair flair, boolean isSpoiler, boolean isNSFW, String kind, Flair flair, boolean isSpoiler, boolean isNSFW, String kind,
@Nullable String posterUrl, SubmitPostListener submitPostListener) { @Nullable String posterUrl, SubmitPostListener submitPostListener) {
RedditAPI api = oauthRetrofit.create(RedditAPI.class); RedditAPI api = oauthRetrofit.create(RedditAPI.class);
@ -206,32 +168,23 @@ public class SubmitPost {
params.put(APIUtils.NSFW_KEY, Boolean.toString(isNSFW)); params.put(APIUtils.NSFW_KEY, Boolean.toString(isNSFW));
Call<String> submitPostCall = api.submit(APIUtils.getOAuthHeader(accessToken), params); Call<String> submitPostCall = api.submit(APIUtils.getOAuthHeader(accessToken), params);
submitPostCall.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull Call<String> call, @NonNull retrofit2.Response<String> response) {
if (response.isSuccessful()) {
try { try {
getSubmittedPost(response.body(), kind, oauthRetrofit, accessToken, Response<String> response = submitPostCall.execute();
locale, submitPostListener); if (response.isSuccessful()) {
} catch (JSONException e) { getSubmittedPost(response.body(), kind, oauthRetrofit, accessToken, submitPostListener);
e.printStackTrace();
submitPostListener.submitFailed(null);
}
} else { } else {
submitPostListener.submitFailed(response.message()); submitPostListener.submitFailed(response.message());
} }
} catch (IOException | JSONException e) {
e.printStackTrace();
submitPostListener.submitFailed(e.getMessage());
}
} }
@Override @Nullable
public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) { private static String uploadImage(Retrofit oauthRetrofit, Retrofit uploadMediaRetrofit,
submitPostListener.submitFailed(t.getMessage()); String accessToken, Bitmap image) throws IOException, JSONException, XmlPullParserException {
}
});
}
private static void uploadImage(Retrofit oauthRetrofit, Retrofit uploadMediaRetrofit,
String accessToken, Bitmap image,
UploadImageListener uploadImageListener) {
RedditAPI api = oauthRetrofit.create(RedditAPI.class); RedditAPI api = oauthRetrofit.create(RedditAPI.class);
Map<String, String> uploadImageParams = new HashMap<>(); Map<String, String> uploadImageParams = new HashMap<>();
@ -239,13 +192,10 @@ public class SubmitPost {
uploadImageParams.put(APIUtils.MIMETYPE_KEY, "image/jpeg"); uploadImageParams.put(APIUtils.MIMETYPE_KEY, "image/jpeg");
Call<String> uploadImageCall = api.uploadImage(APIUtils.getOAuthHeader(accessToken), uploadImageParams); Call<String> uploadImageCall = api.uploadImage(APIUtils.getOAuthHeader(accessToken), uploadImageParams);
uploadImageCall.enqueue(new Callback<String>() { Response<String> uploadImageResponse = uploadImageCall.execute();
@Override if (uploadImageResponse.isSuccessful()) {
public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) { Map<String, RequestBody> nameValuePairsMap = parseJSONResponseFromAWS(uploadImageResponse.body());
if (response.isSuccessful()) {
new ParseJSONResponseFromAWSAsyncTask(response.body(), new ParseJSONResponseFromAWSAsyncTask.ParseJSONResponseFromAWSListener() {
@Override
public void parseSuccessful(Map<String, RequestBody> nameValuePairsMap) {
ByteArrayOutputStream stream = new ByteArrayOutputStream(); ByteArrayOutputStream stream = new ByteArrayOutputStream();
image.compress(Bitmap.CompressFormat.JPEG, 100, stream); image.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] byteArray = stream.toByteArray(); byte[] byteArray = stream.toByteArray();
@ -255,54 +205,19 @@ public class SubmitPost {
RedditAPI uploadMediaToAWSApi = uploadMediaRetrofit.create(RedditAPI.class); RedditAPI uploadMediaToAWSApi = uploadMediaRetrofit.create(RedditAPI.class);
Call<String> uploadMediaToAWS = uploadMediaToAWSApi.uploadMediaToAWS(nameValuePairsMap, fileToUpload); Call<String> uploadMediaToAWS = uploadMediaToAWSApi.uploadMediaToAWS(nameValuePairsMap, fileToUpload);
Response<String> uploadMediaToAWSResponse = uploadMediaToAWS.execute();
uploadMediaToAWS.enqueue(new Callback<String>() { if (uploadMediaToAWSResponse.isSuccessful()) {
@Override return parseXMLResponseFromAWS(uploadMediaToAWSResponse.body());
public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) {
if (response.isSuccessful()) {
new ParseXMLReponseFromAWSAsyncTask(response.body(), new ParseXMLReponseFromAWSAsyncTask.ParseXMLResponseFromAWSListener() {
@Override
public void parseSuccessful(String url) {
uploadImageListener.uploaded(url);
}
@Override
public void parseFailed() {
uploadImageListener.uploadFailed(null);
}
}).execute();
} else { } else {
uploadImageListener.uploadFailed("Error: " + response.code()); return "Error: " + uploadMediaToAWSResponse.code();
} }
}
@Override
public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) {
uploadImageListener.uploadFailed(t.getMessage());
}
});
}
@Override
public void parseFailed() {
uploadImageListener.uploadFailed(null);
}
}).execute();
} else { } else {
uploadImageListener.uploadFailed(response.message()); return "Error: " + uploadImageResponse.message();
} }
} }
@Override
public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) {
uploadImageListener.uploadFailed(t.getMessage());
}
});
}
private static void getSubmittedPost(String response, String kind, Retrofit oauthRetrofit, private static void getSubmittedPost(String response, String kind, Retrofit oauthRetrofit,
String accessToken, Locale locale, String accessToken, SubmitPostListener submitPostListener) throws JSONException, IOException {
SubmitPostListener submitPostListener) throws JSONException {
JSONObject responseObject = new JSONObject(response).getJSONObject(JSONUtils.JSON_KEY); JSONObject responseObject = new JSONObject(response).getJSONObject(JSONUtils.JSON_KEY);
if (responseObject.getJSONArray(JSONUtils.ERRORS_KEY).length() != 0) { if (responseObject.getJSONArray(JSONUtils.ERRORS_KEY).length() != 0) {
JSONArray error = responseObject.getJSONArray(JSONUtils.ERRORS_KEY) JSONArray error = responseObject.getJSONArray(JSONUtils.ERRORS_KEY)
@ -328,11 +243,9 @@ public class SubmitPost {
RedditAPI api = oauthRetrofit.create(RedditAPI.class); RedditAPI api = oauthRetrofit.create(RedditAPI.class);
Call<String> getPostCall = api.getPostOauth(postId, APIUtils.getOAuthHeader(accessToken)); Call<String> getPostCall = api.getPostOauth(postId, APIUtils.getOAuthHeader(accessToken));
getPostCall.enqueue(new Callback<String>() { Response<String> getPostCallResponse = getPostCall.execute();
@Override if (getPostCallResponse.isSuccessful()) {
public void onResponse(@NonNull Call<String> call, @NonNull retrofit2.Response<String> response) { ParsePost.parsePost(getPostCallResponse.body(), new ParsePost.ParsePostListener() {
if (response.isSuccessful()) {
ParsePost.parsePost(response.body(), new ParsePost.ParsePostListener() {
@Override @Override
public void onParsePostSuccess(Post post) { public void onParsePostSuccess(Post post) {
submitPostListener.submitSuccessful(post); submitPostListener.submitSuccessful(post);
@ -344,15 +257,8 @@ public class SubmitPost {
} }
}); });
} else { } else {
submitPostListener.submitFailed(response.message()); submitPostListener.submitFailed(getPostCallResponse.message());
} }
}
@Override
public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) {
submitPostListener.submitFailed(t.getMessage());
}
});
} else { } else {
submitPostListener.submitSuccessful(null); submitPostListener.submitSuccessful(null);
} }
@ -364,75 +270,21 @@ public class SubmitPost {
void submitFailed(@Nullable String errorMessage); void submitFailed(@Nullable String errorMessage);
} }
private interface UploadImageListener { private static Map<String, RequestBody> parseJSONResponseFromAWS(String response) throws JSONException {
void uploaded(String imageUrl);
void uploadFailed(@Nullable String errorMessage);
}
private static class ParseJSONResponseFromAWSAsyncTask extends AsyncTask<Void, Void, Void> {
private String response;
private ParseJSONResponseFromAWSListener parseJSONResponseFromAWSListener;
private Map<String, RequestBody> nameValuePairsMap;
private boolean successful;
ParseJSONResponseFromAWSAsyncTask(String response, ParseJSONResponseFromAWSListener parseJSONResponseFromAWSListener) {
this.response = response;
this.parseJSONResponseFromAWSListener = parseJSONResponseFromAWSListener;
nameValuePairsMap = new HashMap<>();
successful = false;
}
@Override
protected Void doInBackground(Void... voids) {
try {
JSONObject responseObject = new JSONObject(response); JSONObject responseObject = new JSONObject(response);
JSONArray nameValuePairs = responseObject.getJSONObject(JSONUtils.ARGS_KEY).getJSONArray(JSONUtils.FIELDS_KEY); JSONArray nameValuePairs = responseObject.getJSONObject(JSONUtils.ARGS_KEY).getJSONArray(JSONUtils.FIELDS_KEY);
nameValuePairsMap = new HashMap<>(); Map<String, RequestBody> nameValuePairsMap = new HashMap<>();
for (int i = 0; i < nameValuePairs.length(); i++) { for (int i = 0; i < nameValuePairs.length(); i++) {
nameValuePairsMap.put(nameValuePairs.getJSONObject(i).getString(JSONUtils.NAME_KEY), nameValuePairsMap.put(nameValuePairs.getJSONObject(i).getString(JSONUtils.NAME_KEY),
APIUtils.getRequestBody(nameValuePairs.getJSONObject(i).getString(JSONUtils.VALUE_KEY))); APIUtils.getRequestBody(nameValuePairs.getJSONObject(i).getString(JSONUtils.VALUE_KEY)));
} }
successful = true; return nameValuePairsMap;
} catch (JSONException e) {
e.printStackTrace();
successful = false;
} }
return null; @Nullable
} private static String parseXMLResponseFromAWS(String response) throws XmlPullParserException, IOException {
@Override
protected void onPostExecute(Void aVoid) {
if (successful) {
parseJSONResponseFromAWSListener.parseSuccessful(nameValuePairsMap);
} else {
parseJSONResponseFromAWSListener.parseFailed();
}
}
interface ParseJSONResponseFromAWSListener {
void parseSuccessful(Map<String, RequestBody> nameValuePairsMap);
void parseFailed();
}
}
private static class ParseXMLReponseFromAWSAsyncTask extends AsyncTask<Void, Void, Void> {
private String response;
private ParseXMLResponseFromAWSListener parseXMLResponseFromAWSListener;
private String imageUrl;
private boolean successful;
ParseXMLReponseFromAWSAsyncTask(String response, ParseXMLResponseFromAWSListener parseXMLResponseFromAWSListener) {
this.response = response;
this.parseXMLResponseFromAWSListener = parseXMLResponseFromAWSListener;
successful = false;
}
@Override
protected Void doInBackground(Void... voids) {
try {
XmlPullParser xmlPullParser = XmlPullParserFactory.newInstance().newPullParser(); XmlPullParser xmlPullParser = XmlPullParserFactory.newInstance().newPullParser();
xmlPullParser.setInput(new StringReader(response)); xmlPullParser.setInput(new StringReader(response));
@ -445,34 +297,12 @@ public class SubmitPost {
} }
} else if (eventType == XmlPullParser.TEXT) { } else if (eventType == XmlPullParser.TEXT) {
if (isLocationTag) { if (isLocationTag) {
imageUrl = xmlPullParser.getText(); return xmlPullParser.getText();
successful = true;
return null;
} }
} }
eventType = xmlPullParser.next(); eventType = xmlPullParser.next();
} }
} catch (XmlPullParserException | IOException e) {
e.printStackTrace();
successful = false;
}
return null; return null;
} }
@Override
protected void onPostExecute(Void aVoid) {
if (successful) {
parseXMLResponseFromAWSListener.parseSuccessful(imageUrl);
} else {
parseXMLResponseFromAWSListener.parseFailed();
}
}
interface ParseXMLResponseFromAWSListener {
void parseSuccessful(String url);
void parseFailed();
}
}
} }

View File

@ -6,11 +6,15 @@ import android.app.NotificationManager;
import android.app.Service; import android.app.Service;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri; import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build; import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder; import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
@ -19,8 +23,6 @@ import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat; import androidx.core.app.NotificationManagerCompat;
import com.bumptech.glide.Glide; import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.CustomTarget;
import com.bumptech.glide.request.transition.Transition;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
@ -29,22 +31,24 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named; import javax.inject.Named;
import ml.docilealligator.infinityforreddit.Flair;
import ml.docilealligator.infinityforreddit.Infinity;
import ml.docilealligator.infinityforreddit.R;
import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper; import ml.docilealligator.infinityforreddit.customtheme.CustomThemeWrapper;
import ml.docilealligator.infinityforreddit.events.SubmitCrosspostEvent; import ml.docilealligator.infinityforreddit.events.SubmitCrosspostEvent;
import ml.docilealligator.infinityforreddit.events.SubmitImagePostEvent; import ml.docilealligator.infinityforreddit.events.SubmitImagePostEvent;
import ml.docilealligator.infinityforreddit.events.SubmitTextOrLinkPostEvent; import ml.docilealligator.infinityforreddit.events.SubmitTextOrLinkPostEvent;
import ml.docilealligator.infinityforreddit.events.SubmitVideoOrGifPostEvent; import ml.docilealligator.infinityforreddit.events.SubmitVideoOrGifPostEvent;
import ml.docilealligator.infinityforreddit.Flair;
import ml.docilealligator.infinityforreddit.Infinity;
import ml.docilealligator.infinityforreddit.utils.NotificationUtils;
import ml.docilealligator.infinityforreddit.post.Post; import ml.docilealligator.infinityforreddit.post.Post;
import ml.docilealligator.infinityforreddit.post.SubmitPost; import ml.docilealligator.infinityforreddit.post.SubmitPost;
import ml.docilealligator.infinityforreddit.R;
import ml.docilealligator.infinityforreddit.utils.APIUtils; import ml.docilealligator.infinityforreddit.utils.APIUtils;
import ml.docilealligator.infinityforreddit.utils.NotificationUtils;
import retrofit2.Retrofit; import retrofit2.Retrofit;
public class SubmitPostService extends Service { public class SubmitPostService extends Service {
@ -61,6 +65,7 @@ public class SubmitPostService extends Service {
public static final int EXTRA_POST_TYPE_IMAGE = 1; public static final int EXTRA_POST_TYPE_IMAGE = 1;
public static final int EXTRA_POST_TYPE_VIDEO = 2; public static final int EXTRA_POST_TYPE_VIDEO = 2;
public static final int EXTRA_POST_TYPE_CROSSPOST = 3; public static final int EXTRA_POST_TYPE_CROSSPOST = 3;
private static final String EXTRA_MEDIA_URI = "EU";
@Inject @Inject
@Named("oauth") @Named("oauth")
Retrofit mOauthRetrofit; Retrofit mOauthRetrofit;
@ -72,15 +77,7 @@ public class SubmitPostService extends Service {
Retrofit mUploadVideoRetrofit; Retrofit mUploadVideoRetrofit;
@Inject @Inject
CustomThemeWrapper mCustomThemeWrapper; CustomThemeWrapper mCustomThemeWrapper;
private String mAccessToken; private ServiceHandler serviceHandler;
private String subredditName;
private String title;
private Flair flair;
private boolean isSpoiler;
private boolean isNSFW;
private String content;
private String kind;
private Uri mediaUri;
public SubmitPostService() { public SubmitPostService() {
} }
@ -90,18 +87,60 @@ public class SubmitPostService extends Service {
return null; return null;
} }
// Handler that receives messages from the thread
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
Bundle bundle = msg.getData();
String accessToken = bundle.getString(EXTRA_ACCESS_TOKEN);
String subredditName = bundle.getString(EXTRA_SUBREDDIT_NAME);
String title = bundle.getString(EXTRA_TITLE);
Flair flair = bundle.getParcelable(EXTRA_FLAIR);
boolean isSpoiler = bundle.getBoolean(EXTRA_IS_SPOILER, false);
boolean isNSFW = bundle.getBoolean(EXTRA_IS_NSFW, false);
int postType = bundle.getInt(EXTRA_POST_TYPE, EXTRA_POST_TEXT_OR_LINK);
if (postType == EXTRA_POST_TEXT_OR_LINK) {
String content = bundle.getString(EXTRA_CONTENT);
String kind = bundle.getString(EXTRA_KIND);
submitTextOrLinkPost(accessToken, subredditName, title, content, flair, isSpoiler, isNSFW, kind);
} else if (postType == EXTRA_POST_TYPE_CROSSPOST) {
String content = bundle.getString(EXTRA_CONTENT);
submitCrosspost(accessToken, subredditName, title, content, flair, isSpoiler, isNSFW);
} else if (postType == EXTRA_POST_TYPE_IMAGE) {
Uri mediaUri = Uri.parse(bundle.getString(EXTRA_MEDIA_URI));
submitImagePost(accessToken, mediaUri, subredditName, title, flair, isSpoiler, isNSFW);
} else {
Uri mediaUri = Uri.parse(bundle.getString(EXTRA_MEDIA_URI));
submitVideoPost(accessToken, mediaUri, subredditName, title, flair, isSpoiler, isNSFW);
}
}
}
@Override
public void onCreate() {
((Infinity) getApplication()).getAppComponent().inject(this);
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work doesn't disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
serviceHandler = new ServiceHandler(thread.getLooper());
}
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
((Infinity) getApplication()).getAppComponent().inject(this); ((Infinity) getApplication()).getAppComponent().inject(this);
mAccessToken = intent.getStringExtra(EXTRA_ACCESS_TOKEN);
subredditName = intent.getStringExtra(EXTRA_SUBREDDIT_NAME);
title = intent.getStringExtra(EXTRA_TITLE);
flair = intent.getParcelableExtra(EXTRA_FLAIR);
isSpoiler = intent.getBooleanExtra(EXTRA_IS_SPOILER, false);
isNSFW = intent.getBooleanExtra(EXTRA_IS_NSFW, false);
int postType = intent.getIntExtra(EXTRA_POST_TYPE, EXTRA_POST_TEXT_OR_LINK);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel( NotificationChannel serviceChannel = new NotificationChannel(
NotificationUtils.CHANNEL_SUBMIT_POST, NotificationUtils.CHANNEL_SUBMIT_POST,
@ -113,25 +152,26 @@ public class SubmitPostService extends Service {
manager.createNotificationChannel(serviceChannel); manager.createNotificationChannel(serviceChannel);
} }
int randomNotificationIdOffset = new Random().nextInt(10000);
int postType = intent.getIntExtra(EXTRA_POST_TYPE, EXTRA_POST_TEXT_OR_LINK);
Bundle bundle = intent.getExtras();
if (postType == EXTRA_POST_TEXT_OR_LINK) { if (postType == EXTRA_POST_TEXT_OR_LINK) {
content = intent.getStringExtra(EXTRA_CONTENT); startForeground(NotificationUtils.SUBMIT_POST_SERVICE_NOTIFICATION_ID + randomNotificationIdOffset, createNotification(R.string.posting));
kind = intent.getStringExtra(EXTRA_KIND);
startForeground(NotificationUtils.SUBMIT_POST_SERVICE_NOTIFICATION_ID, createNotification(R.string.posting));
submitTextOrLinkPost();
} else if (postType == EXTRA_POST_TYPE_CROSSPOST) { } else if (postType == EXTRA_POST_TYPE_CROSSPOST) {
content = intent.getStringExtra(EXTRA_CONTENT); startForeground(NotificationUtils.SUBMIT_POST_SERVICE_NOTIFICATION_ID + randomNotificationIdOffset, createNotification(R.string.posting));
startForeground(NotificationUtils.SUBMIT_POST_SERVICE_NOTIFICATION_ID, createNotification(R.string.posting));
submitCrosspost();
} else if (postType == EXTRA_POST_TYPE_IMAGE) { } else if (postType == EXTRA_POST_TYPE_IMAGE) {
mediaUri = intent.getData(); bundle.putString(EXTRA_MEDIA_URI, intent.getData().toString());
startForeground(NotificationUtils.SUBMIT_POST_SERVICE_NOTIFICATION_ID, createNotification(R.string.posting_image)); startForeground(NotificationUtils.SUBMIT_POST_SERVICE_NOTIFICATION_ID + randomNotificationIdOffset, createNotification(R.string.posting_image));
submitImagePost();
} else { } else {
mediaUri = intent.getData(); bundle.putString(EXTRA_MEDIA_URI, intent.getData().toString());
startForeground(NotificationUtils.SUBMIT_POST_SERVICE_NOTIFICATION_ID, createNotification(R.string.posting_video)); startForeground(NotificationUtils.SUBMIT_POST_SERVICE_NOTIFICATION_ID + randomNotificationIdOffset, createNotification(R.string.posting_video));
submitVideoPost();
} }
Message msg = serviceHandler.obtainMessage();
msg.setData(bundle);
serviceHandler.sendMessage(msg);
return START_NOT_STICKY; return START_NOT_STICKY;
} }
@ -144,8 +184,8 @@ public class SubmitPostService extends Service {
.build(); .build();
} }
private void submitTextOrLinkPost() { private void submitTextOrLinkPost(String accessToken, String subredditName, String title, String content, Flair flair, boolean isSpoiler, boolean isNSFW, String kind) {
SubmitPost.submitTextOrLinkPost(mOauthRetrofit, mAccessToken, getResources().getConfiguration().locale, SubmitPost.submitTextOrLinkPost(mOauthRetrofit, accessToken, getResources().getConfiguration().locale,
subredditName, title, content, flair, isSpoiler, isNSFW, kind, new SubmitPost.SubmitPostListener() { subredditName, title, content, flair, isSpoiler, isNSFW, kind, new SubmitPost.SubmitPostListener() {
@Override @Override
public void submitSuccessful(Post post) { public void submitSuccessful(Post post) {
@ -163,9 +203,9 @@ public class SubmitPostService extends Service {
}); });
} }
private void submitCrosspost() { private void submitCrosspost(String accessToken, String subredditName, String title, String content, Flair flair, boolean isSpoiler, boolean isNSFW) {
SubmitPost.submitCrosspost(mOauthRetrofit, mAccessToken, getResources().getConfiguration().locale, SubmitPost.submitCrosspost(mOauthRetrofit, accessToken, subredditName, title, content, flair, isSpoiler,
subredditName, title, content, flair, isSpoiler, isNSFW, APIUtils.KIND_CROSSPOST, new SubmitPost.SubmitPostListener() { isNSFW, APIUtils.KIND_CROSSPOST, new SubmitPost.SubmitPostListener() {
@Override @Override
public void submitSuccessful(Post post) { public void submitSuccessful(Post post) {
EventBus.getDefault().post(new SubmitCrosspostEvent(true, post, null)); EventBus.getDefault().post(new SubmitCrosspostEvent(true, post, null));
@ -182,16 +222,10 @@ public class SubmitPostService extends Service {
}); });
} }
private void submitImagePost() { private void submitImagePost(String accessToken, Uri mediaUri, String subredditName, String title, Flair flair, boolean isSpoiler, boolean isNSFW) {
Glide.with(this) try {
.asBitmap() Bitmap resource = Glide.with(this).asBitmap().load(mediaUri).submit().get();
.load(mediaUri) SubmitPost.submitImagePost(mOauthRetrofit, mUploadMediaRetrofit, accessToken, subredditName, title, resource,
.into(new CustomTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
SubmitPost.submitImagePost(mOauthRetrofit, mUploadMediaRetrofit, mAccessToken,
getResources().getConfiguration().locale, subredditName, title, resource,
flair, isSpoiler, isNSFW, new SubmitPost.SubmitPostListener() { flair, isSpoiler, isNSFW, new SubmitPost.SubmitPostListener() {
@Override @Override
public void submitSuccessful(Post post) { public void submitSuccessful(Post post) {
@ -208,23 +242,15 @@ public class SubmitPostService extends Service {
stopService(); stopService();
} }
}); });
} } catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
EventBus.getDefault().post(new SubmitImagePostEvent(false, getString(R.string.error_processing_image))); EventBus.getDefault().post(new SubmitImagePostEvent(false, getString(R.string.error_processing_image)));
stopService(); stopService();
} }
@Override
public void onLoadCleared(@Nullable Drawable placeholder) {
}
});
} }
private void submitVideoPost() { private void submitVideoPost(String accessToken, Uri mediaUri, String subredditName, String title,
Flair flair, boolean isSpoiler, boolean isNSFW) {
try { try {
InputStream in = getContentResolver().openInputStream(mediaUri); InputStream in = getContentResolver().openInputStream(mediaUri);
String type = getContentResolver().getType(mediaUri); String type = getContentResolver().getType(mediaUri);
@ -235,21 +261,14 @@ public class SubmitPostService extends Service {
cacheFilePath = getExternalCacheDir() + "/" + mediaUri.getLastPathSegment() + ".mp4"; cacheFilePath = getExternalCacheDir() + "/" + mediaUri.getLastPathSegment() + ".mp4";
} }
new CopyFileToCacheAsyncTask(in, cacheFilePath, copyFileToCache(in, cacheFilePath);
new CopyFileToCacheAsyncTask.CopyFileToCacheAsyncTaskListener() {
@Override Bitmap resource = Glide.with(this).asBitmap().load(mediaUri).submit().get();
public void success() {
Glide.with(SubmitPostService.this)
.asBitmap()
.load(mediaUri)
.into(new CustomTarget<Bitmap>() {
@Override
public void onResourceReady(@NonNull Bitmap resource, @Nullable Transition<? super Bitmap> transition) {
if (type != null) { if (type != null) {
SubmitPost.submitVideoPost(mOauthRetrofit, mUploadMediaRetrofit, mUploadVideoRetrofit, SubmitPost.submitVideoPost(mOauthRetrofit, mUploadMediaRetrofit, mUploadVideoRetrofit,
mAccessToken, getResources().getConfiguration().locale, subredditName, title, accessToken, subredditName, title, new File(cacheFilePath), type, resource, flair,
new File(cacheFilePath), type, resource, flair, isSpoiler, isNSFW, isSpoiler, isNSFW, new SubmitPost.SubmitPostListener() {
new SubmitPost.SubmitPostListener() {
@Override @Override
public void submitSuccessful(Post post) { public void submitSuccessful(Post post) {
EventBus.getDefault().post(new SubmitVideoOrGifPostEvent(true, false, null)); EventBus.getDefault().post(new SubmitVideoOrGifPostEvent(true, false, null));
@ -271,31 +290,10 @@ public class SubmitPostService extends Service {
}); });
} else { } else {
EventBus.getDefault().post(new SubmitVideoOrGifPostEvent(false, true, null)); EventBus.getDefault().post(new SubmitVideoOrGifPostEvent(false, true, null));
}
}
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
EventBus.getDefault().post(new SubmitVideoOrGifPostEvent(false, true, null));
stopService(); stopService();
} }
} catch (IOException | InterruptedException | ExecutionException e) {
@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(); e.printStackTrace();
EventBus.getDefault().post(new SubmitVideoOrGifPostEvent(false, true, null)); EventBus.getDefault().post(new SubmitVideoOrGifPostEvent(false, true, null));
@ -303,48 +301,13 @@ public class SubmitPostService extends Service {
} }
} }
private static class CopyFileToCacheAsyncTask extends AsyncTask<Void, Void, Void> { private static void copyFileToCache(InputStream fileInputStream, String destinationFilePath) throws IOException {
OutputStream out = new FileOutputStream(destinationFilePath);
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]; byte[] buf = new byte[2048];
int len; int len;
while ((len = fileInputStream.read(buf)) > 0) { while ((len = fileInputStream.read(buf)) > 0) {
out.write(buf, 0, len); 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() { private void stopService() {