package ml.docilealligator.infinityforreddit.comment; import android.os.AsyncTask; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.lifecycle.MutableLiveData; import androidx.paging.PageKeyedDataSource; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import ml.docilealligator.infinityforreddit.NetworkState; import ml.docilealligator.infinityforreddit.thing.SortType; import ml.docilealligator.infinityforreddit.account.Account; import ml.docilealligator.infinityforreddit.apis.RedditAPI; import ml.docilealligator.infinityforreddit.post.PostPagingSource; import ml.docilealligator.infinityforreddit.utils.APIUtils; import ml.docilealligator.infinityforreddit.utils.JSONUtils; import retrofit2.Call; import retrofit2.Callback; import retrofit2.Response; import retrofit2.Retrofit; public class CommentDataSource extends PageKeyedDataSource { private final Retrofit retrofit; @Nullable private final String accessToken; @NonNull private final String accountName; private final String username; private final SortType sortType; private final boolean areSavedComments; private final MutableLiveData paginationNetworkStateLiveData; private final MutableLiveData initialLoadStateLiveData; private final MutableLiveData hasPostLiveData; private LoadParams params; private LoadCallback callback; CommentDataSource(Retrofit retrofit, @Nullable String accessToken, @NonNull String accountName, String username, SortType sortType, boolean areSavedComments) { this.retrofit = retrofit; this.accessToken = accessToken; this.accountName = accountName; this.username = username; this.sortType = sortType; this.areSavedComments = areSavedComments; paginationNetworkStateLiveData = new MutableLiveData<>(); initialLoadStateLiveData = new MutableLiveData<>(); hasPostLiveData = new MutableLiveData<>(); } MutableLiveData getPaginationNetworkStateLiveData() { return paginationNetworkStateLiveData; } MutableLiveData getInitialLoadStateLiveData() { return initialLoadStateLiveData; } MutableLiveData hasPostLiveData() { return hasPostLiveData; } void retryLoadingMore() { loadAfter(params, callback); } @Override public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback callback) { initialLoadStateLiveData.postValue(NetworkState.LOADING); RedditAPI api = retrofit.create(RedditAPI.class); Call commentsCall; if (areSavedComments) { commentsCall = api.getUserSavedCommentsOauth(username, PostPagingSource.USER_WHERE_SAVED, null, sortType.getType(), sortType.getTime(), APIUtils.getOAuthHeader(accessToken)); } else { if (accountName.equals(Account.ANONYMOUS_ACCOUNT)) { commentsCall = api.getUserComments(username, null, sortType.getType(), sortType.getTime()); } else { commentsCall = api.getUserCommentsOauth(APIUtils.getOAuthHeader(accessToken), username, null, sortType.getType(), sortType.getTime()); } } commentsCall.enqueue(new Callback<>() { @Override public void onResponse(@NonNull Call call, @NonNull Response response) { if (response.isSuccessful()) { new ParseCommentAsyncTask(response.body(), new ParseCommentAsyncTask.ParseCommentAsyncTaskListener() { @Override public void parseSuccessful(ArrayList comments, String after) { if (comments.size() == 0) { hasPostLiveData.postValue(false); } else { hasPostLiveData.postValue(true); } if (after == null || after.equals("") || after.equals("null")) { callback.onResult(comments, null, null); } else { callback.onResult(comments, null, after); } initialLoadStateLiveData.postValue(NetworkState.LOADED); } @Override public void parseFailed() { initialLoadStateLiveData.postValue(new NetworkState(NetworkState.Status.FAILED, "Error parsing data")); } }).execute(); } else { initialLoadStateLiveData.postValue(new NetworkState(NetworkState.Status.FAILED, "Error parsing data")); } } @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { initialLoadStateLiveData.postValue(new NetworkState(NetworkState.Status.FAILED, "Error parsing data")); } }); } @Override public void loadBefore(@NonNull LoadParams params, @NonNull LoadCallback callback) { } @Override public void loadAfter(@NonNull LoadParams params, @NonNull LoadCallback callback) { this.params = params; this.callback = callback; paginationNetworkStateLiveData.postValue(NetworkState.LOADING); RedditAPI api = retrofit.create(RedditAPI.class); Call commentsCall; if (areSavedComments) { commentsCall = api.getUserSavedCommentsOauth(username, PostPagingSource.USER_WHERE_SAVED, params.key, sortType.getType(), sortType.getTime(), APIUtils.getOAuthHeader(accessToken)); } else { if (accountName.equals(Account.ANONYMOUS_ACCOUNT)) { commentsCall = api.getUserComments(username, params.key, sortType.getType(), sortType.getTime()); } else { commentsCall = api.getUserCommentsOauth(APIUtils.getOAuthHeader(accessToken), username, params.key, sortType.getType(), sortType.getTime()); } } commentsCall.enqueue(new Callback<>() { @Override public void onResponse(@NonNull Call call, @NonNull Response response) { if (response.isSuccessful()) { new ParseCommentAsyncTask(response.body(), new ParseCommentAsyncTask.ParseCommentAsyncTaskListener() { @Override public void parseSuccessful(ArrayList comments, String after) { if (after == null || after.equals("") || after.equals("null")) { callback.onResult(comments, null); } else { callback.onResult(comments, after); } paginationNetworkStateLiveData.postValue(NetworkState.LOADED); } @Override public void parseFailed() { paginationNetworkStateLiveData.postValue(new NetworkState(NetworkState.Status.FAILED, "Error parsing data")); } }).execute(); } else { paginationNetworkStateLiveData.postValue(new NetworkState(NetworkState.Status.FAILED, "Error fetching data")); } } @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { paginationNetworkStateLiveData.postValue(new NetworkState(NetworkState.Status.FAILED, "Error fetching data")); } }); } private static class ParseCommentAsyncTask extends AsyncTask, ArrayList> { private String after; private JSONArray commentsJSONArray; private boolean parseFailed; private final ParseCommentAsyncTaskListener parseCommentAsyncTaskListener; ParseCommentAsyncTask(String response, ParseCommentAsyncTaskListener parseCommentAsyncTaskListener) { this.parseCommentAsyncTaskListener = parseCommentAsyncTaskListener; try { JSONObject data = new JSONObject(response).getJSONObject(JSONUtils.DATA_KEY); commentsJSONArray = data.getJSONArray(JSONUtils.CHILDREN_KEY); after = data.getString(JSONUtils.AFTER_KEY); parseFailed = false; } catch (JSONException e) { parseFailed = true; e.printStackTrace(); } } @Override protected ArrayList doInBackground(Void... voids) { if (parseFailed) { return null; } ArrayList comments = new ArrayList<>(); for (int i = 0; i < commentsJSONArray.length(); i++) { try { JSONObject commentJSON = commentsJSONArray.getJSONObject(i).getJSONObject(JSONUtils.DATA_KEY); comments.add(ParseComment.parseSingleComment(commentJSON, 0)); } catch (JSONException ignored) { } } return comments; } @Override protected void onPostExecute(ArrayList commentData) { super.onPostExecute(commentData); if (commentData != null) { parseCommentAsyncTaskListener.parseSuccessful(commentData, after); } else { parseCommentAsyncTaskListener.parseFailed(); } } interface ParseCommentAsyncTaskListener { void parseSuccessful(ArrayList comments, String after); void parseFailed(); } } }