aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.idea/assetWizardSettings.xml4
-rw-r--r--.idea/caches/build_file_checksums.serbin533 -> 533 bytes
-rw-r--r--.idea/caches/gradle_models.serbin267941 -> 267941 bytes
-rw-r--r--app/src/main/AndroidManifest.xml8
-rw-r--r--app/src/main/java/CustomView/CustomMarkwonView.java33
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/AppComponent.java1
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/CommentAndPostRecyclerViewAdapter.java22
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/CommentDataSource.java12
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/CommentsListingRecyclerViewAdapter.java8
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/FetchMessages.java25
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/LinkResolverActivity.java9
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/MainActivity.java13
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/Message.java2
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/MessageDataSource.java123
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/MessageDataSourceFactory.java47
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/MessageRecyclerViewAdapter.java195
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/MessageViewModel.java100
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/ParseSubredditData.java33
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/PostRecyclerViewAdapter.java3
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/PullNotificationWorker.java4
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/RedditAPI.java2
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/ViewMessageActivity.java230
-rw-r--r--app/src/main/java/ml/docilealligator/infinityforreddit/ViewPostDetailActivity.java8
-rw-r--r--app/src/main/res/drawable/ic_outline_inbox_24px.xml9
-rw-r--r--app/src/main/res/layout/activity_main.xml26
-rw-r--r--app/src/main/res/layout/activity_view_message.xml81
-rw-r--r--app/src/main/res/layout/item_message.xml34
-rw-r--r--app/src/main/res/menu/view_message_activity.xml9
-rw-r--r--app/src/main/res/values/colors.xml2
-rw-r--r--app/src/main/res/values/strings.xml7
30 files changed, 939 insertions, 111 deletions
diff --git a/.idea/assetWizardSettings.xml b/.idea/assetWizardSettings.xml
index 144d0da4..dd08719b 100644
--- a/.idea/assetWizardSettings.xml
+++ b/.idea/assetWizardSettings.xml
@@ -35,9 +35,9 @@
<map>
<entry key="assetSourceType" value="FILE" />
<entry key="color" value="ffffff" />
- <entry key="outputName" value="ic_outline_star_border_24px" />
+ <entry key="outputName" value="ic_outline_inbox_24px" />
<entry key="overrideSize" value="true" />
- <entry key="sourceFile" value="$USER_HOME$/Downloads/outline-star_border-24px.svg" />
+ <entry key="sourceFile" value="$USER_HOME$/Downloads/outline-inbox-24px.svg" />
</map>
</option>
</PersistentState>
diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser
index 38cb74bb..4b09346a 100644
--- a/.idea/caches/build_file_checksums.ser
+++ b/.idea/caches/build_file_checksums.ser
Binary files differ
diff --git a/.idea/caches/gradle_models.ser b/.idea/caches/gradle_models.ser
index 055c0fc8..e2b6be3f 100644
--- a/.idea/caches/gradle_models.ser
+++ b/.idea/caches/gradle_models.ser
Binary files differ
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 353588df..8e3f013e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -20,7 +20,13 @@
android:supportsRtl="true"
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true">
- <activity android:name=".AccountPostsActivity"
+ <activity
+ android:name=".ViewMessageActivity"
+ android:label="@string/view_message_activity"
+ android:parentActivityName=".MainActivity"
+ android:theme="@style/AppTheme.NoActionBar" />
+ <activity
+ android:name=".AccountPostsActivity"
android:parentActivityName=".MainActivity"
android:theme="@style/AppTheme.NoActionBar" />
<activity
diff --git a/app/src/main/java/CustomView/CustomMarkwonView.java b/app/src/main/java/CustomView/CustomMarkwonView.java
index d5828f13..940b6b76 100644
--- a/app/src/main/java/CustomView/CustomMarkwonView.java
+++ b/app/src/main/java/CustomView/CustomMarkwonView.java
@@ -6,11 +6,8 @@ import android.net.Uri;
import android.util.AttributeSet;
import androidx.annotation.Nullable;
-import androidx.browser.customtabs.CustomTabsIntent;
-import ml.docilealligator.infinityforreddit.R;
-import ml.docilealligator.infinityforreddit.ViewSubredditDetailActivity;
-import ml.docilealligator.infinityforreddit.ViewUserDetailActivity;
+import ml.docilealligator.infinityforreddit.LinkResolverActivity;
import ru.noties.markwon.SpannableConfiguration;
import ru.noties.markwon.view.MarkwonView;
@@ -26,30 +23,14 @@ public class CustomMarkwonView extends MarkwonView {
public void setMarkdown(@Nullable String markdown, Context context) {
SpannableConfiguration configuration = SpannableConfiguration.builder(context).linkResolver((view, link) -> {
- if(link.startsWith("/u/") || link.startsWith("u/")) {
- Intent intent = new Intent(context, ViewUserDetailActivity.class);
- intent.putExtra(ViewUserDetailActivity.EXTRA_USER_NAME_KEY, link.substring(3));
- context.startActivity(intent);
- } else if(link.startsWith("/r/") || link.startsWith("r/")) {
- Intent intent = new Intent(context, ViewSubredditDetailActivity.class);
- intent.putExtra(ViewSubredditDetailActivity.EXTRA_SUBREDDIT_NAME_KEY, link.substring(3));
- context.startActivity(intent);
+ Intent intent = new Intent(context, LinkResolverActivity.class);
+ Uri uri = Uri.parse(link);
+ if(uri.getScheme() == null && uri.getHost() == null) {
+ intent.setData(LinkResolverActivity.getRedditUriByPath(link));
} else {
- CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
- // add share action to menu list
- builder.addDefaultShareMenuItem();
- builder.setToolbarColor(context.getResources().getColor(R.color.colorPrimary));
- CustomTabsIntent customTabsIntent = builder.build();
- Uri uri = Uri.parse(link);
- if(uri.getHost() != null && uri.getHost().contains("reddit.com")) {
- customTabsIntent.intent.setPackage(context.getPackageName());
- }
- String uriString = uri.toString();
- if(!uriString.startsWith("http://") && (!uriString.startsWith("https://"))) {
- uriString = "http://" + uriString;
- }
- customTabsIntent.launchUrl(context, Uri.parse(uriString));
+ intent.setData(uri);
}
+ context.startActivity(intent);
}).build();
super.setMarkdown(configuration, markdown);
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/AppComponent.java b/app/src/main/java/ml/docilealligator/infinityforreddit/AppComponent.java
index 95e8f658..5a28dc40 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/AppComponent.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/AppComponent.java
@@ -35,4 +35,5 @@ interface AppComponent {
void inject(EditCommentActivity editCommentActivity);
void inject(AccountPostsActivity accountPostsActivity);
void inject(PullNotificationWorker pullNotificationWorker);
+ void inject(ViewMessageActivity viewMessageActivity);
}
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/CommentAndPostRecyclerViewAdapter.java b/app/src/main/java/ml/docilealligator/infinityforreddit/CommentAndPostRecyclerViewAdapter.java
index 89b2817b..347634aa 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/CommentAndPostRecyclerViewAdapter.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/CommentAndPostRecyclerViewAdapter.java
@@ -72,7 +72,6 @@ class CommentAndPostRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVie
private String mSingleCommentId;
private boolean mIsSingleCommentThreadMode;
private CommentRecyclerViewAdapterCallback mCommentRecyclerViewAdapterCallback;
- private LoadSubredditIconAsyncTask mLoadSubredditIconAsyncTask;
private boolean isInitiallyLoading;
private boolean isInitiallyLoadingFailed;
private boolean mHasMoreComments;
@@ -87,7 +86,6 @@ class CommentAndPostRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVie
RedditDataRoomDatabase redditDataRoomDatabase, RequestManager glide,
String accessToken, String accountName, Post post, Locale locale,
String singleCommentId, boolean isSingleCommentThreadMode,
- LoadSubredditIconAsyncTask loadSubredditIconAsyncTask,
CommentRecyclerViewAdapterCallback commentRecyclerViewAdapterCallback) {
mActivity = activity;
mRetrofit = retrofit;
@@ -102,7 +100,6 @@ class CommentAndPostRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVie
mLocale = locale;
mSingleCommentId = singleCommentId;
mIsSingleCommentThreadMode = isSingleCommentThreadMode;
- mLoadSubredditIconAsyncTask = loadSubredditIconAsyncTask;
mCommentRecyclerViewAdapterCallback = commentRecyclerViewAdapterCallback;
isInitiallyLoading = true;
isInitiallyLoadingFailed = false;
@@ -194,10 +191,10 @@ class CommentAndPostRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVie
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if(holder.getItemViewType() == VIEW_TYPE_POST_DETAIL) {
((PostDetailViewHolder) holder).mTitleTextView.setText(mPost.getTitle());
-
- if(mPost.getSubredditNamePrefixed().equals("u/" + mPost.getAuthor())) {
+ if(mPost.getSubredditNamePrefixed().startsWith("u/")) {
if(mPost.getAuthorIconUrl() == null) {
- new LoadUserDataAsyncTask(mRedditDataRoomDatabase.userDao(), mPost.getAuthor(), mOauthRetrofit, iconImageUrl -> {
+ String authorName = mPost.getAuthor().equals("[deleted]") ? mPost.getSubredditNamePrefixed().substring(2) : mPost.getAuthor();
+ new LoadUserDataAsyncTask(mRedditDataRoomDatabase.userDao(), authorName, mOauthRetrofit, iconImageUrl -> {
if(mActivity != null && getItemCount() > 0) {
if(iconImageUrl == null || iconImageUrl.equals("")) {
mGlide.load(R.drawable.subreddit_default_icon)
@@ -229,10 +226,7 @@ class CommentAndPostRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVie
}
} else {
if(mPost.getSubredditIconUrl() == null) {
- if(mLoadSubredditIconAsyncTask != null) {
- mLoadSubredditIconAsyncTask.cancel(true);
- }
- mLoadSubredditIconAsyncTask = new LoadSubredditIconAsyncTask(
+ new LoadSubredditIconAsyncTask(
mRedditDataRoomDatabase.subredditDao(), mPost.getSubredditNamePrefixed().substring(2),
mRetrofit, iconImageUrl -> {
if(iconImageUrl == null || iconImageUrl.equals("")) {
@@ -248,9 +242,7 @@ class CommentAndPostRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVie
}
mPost.setSubredditIconUrl(iconImageUrl);
- });
-
- mLoadSubredditIconAsyncTask.execute();
+ }).execute();
} else if(!mPost.getSubredditIconUrl().equals("")) {
mGlide.load(mPost.getSubredditIconUrl())
.apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(72, 0)))
@@ -713,10 +705,6 @@ class CommentAndPostRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVie
}
void initiallyLoading() {
- if(mLoadSubredditIconAsyncTask != null) {
- mLoadSubredditIconAsyncTask.cancel(true);
- }
-
if(mVisibleComments.size() != 0) {
int previousSize = mVisibleComments.size();
mVisibleComments.clear();
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/CommentDataSource.java b/app/src/main/java/ml/docilealligator/infinityforreddit/CommentDataSource.java
index dd33af70..377cc07c 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/CommentDataSource.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/CommentDataSource.java
@@ -87,7 +87,11 @@ public class CommentDataSource extends PageKeyedDataSource<String, CommentData>
hasPostLiveData.postValue(true);
}
- callback.onResult(comments, null, after);
+ if(after == null || after.equals("") || after.equals("null")) {
+ callback.onResult(comments, null, null);
+ } else {
+ callback.onResult(comments, null, after);
+ }
initialLoadStateLiveData.postValue(NetworkState.LOADED);
}
@@ -147,13 +151,15 @@ public class CommentDataSource extends PageKeyedDataSource<String, CommentData>
}
}).execute();
} else {
- paginationNetworkStateLiveData.postValue(new NetworkState(NetworkState.Status.FAILED, "Error parsing data"));
+ Log.i("Comments fetch error", "Error fetching data");
+ paginationNetworkStateLiveData.postValue(new NetworkState(NetworkState.Status.FAILED, "Error fetching data"));
}
}
@Override
public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) {
- paginationNetworkStateLiveData.postValue(new NetworkState(NetworkState.Status.FAILED, "Error parsing data"));
+ Log.i("Comments fetch error", "Error fetchin data");
+ paginationNetworkStateLiveData.postValue(new NetworkState(NetworkState.Status.FAILED, "Error fetching data"));
}
});
}
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/CommentsListingRecyclerViewAdapter.java b/app/src/main/java/ml/docilealligator/infinityforreddit/CommentsListingRecyclerViewAdapter.java
index c20920a9..06c4f232 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/CommentsListingRecyclerViewAdapter.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/CommentsListingRecyclerViewAdapter.java
@@ -57,13 +57,13 @@ class CommentsListingRecyclerViewAdapter extends PagedListAdapter<CommentData, R
private static final DiffUtil.ItemCallback<CommentData> DIFF_CALLBACK = new DiffUtil.ItemCallback<CommentData>() {
@Override
- public boolean areItemsTheSame(@NonNull CommentData CommentData, @NonNull CommentData t1) {
- return CommentData.getId().equals(t1.getId());
+ public boolean areItemsTheSame(@NonNull CommentData commentData, @NonNull CommentData t1) {
+ return commentData.getId().equals(t1.getId());
}
@Override
- public boolean areContentsTheSame(@NonNull CommentData CommentData, @NonNull CommentData t1) {
- return CommentData.getCommentContent().equals(t1.getCommentContent());
+ public boolean areContentsTheSame(@NonNull CommentData commentData, @NonNull CommentData t1) {
+ return commentData.getCommentContent().equals(t1.getCommentContent());
}
};
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/FetchMessages.java b/app/src/main/java/ml/docilealligator/infinityforreddit/FetchMessages.java
index 04d6f8de..424c1c83 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/FetchMessages.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/FetchMessages.java
@@ -22,8 +22,8 @@ import retrofit2.Retrofit;
class FetchMessages {
interface FetchMessagesListener {
- void fetchSuccess(@Nullable ArrayList<Message> messages);
- void fetchFailed(boolean shouldRetry);
+ void fetchSuccess(ArrayList<Message> messages, @Nullable String after);
+ void fetchFailed();
}
static final String WHERE_INBOX = "inbox";
@@ -32,22 +32,21 @@ class FetchMessages {
static final String WHERE_COMMENTS = "comments";
static void fetchMessagesAsync(Retrofit oauthRetrofit, Locale locale, String accessToken, String where,
- FetchMessagesListener fetchMessagesListener) {
- oauthRetrofit.create(RedditAPI.class).getMessages(RedditUtils.getOAuthHeader(accessToken), where)
+ String after, FetchMessagesListener fetchMessagesListener) {
+ oauthRetrofit.create(RedditAPI.class).getMessages(RedditUtils.getOAuthHeader(accessToken), where, after)
.enqueue(new Callback<String>() {
@Override
public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) {
if(response.isSuccessful()) {
- new ParseMessageAsnycTask(response.body(), locale,
- fetchMessagesListener::fetchSuccess).execute();
+ new ParseMessageAsnycTask(response.body(), locale, fetchMessagesListener::fetchSuccess).execute();
} else {
- fetchMessagesListener.fetchFailed(true);
+ fetchMessagesListener.fetchFailed();
}
}
@Override
public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) {
- fetchMessagesListener.fetchFailed(true);
+ fetchMessagesListener.fetchFailed();
}
});
}
@@ -104,12 +103,13 @@ class FetchMessages {
private static class ParseMessageAsnycTask extends AsyncTask<Void, Void, Void> {
interface ParseMessageAsyncTaskListener {
- void parseSuccess(ArrayList<Message> messages);
+ void parseSuccess(ArrayList<Message> messages, @Nullable String after);
}
private String response;
private Locale locale;
private ArrayList<Message> messages;
+ private String after;
private ParseMessageAsyncTaskListener parseMessageAsyncTaskListener;
ParseMessageAsnycTask(String response, Locale locale, ParseMessageAsyncTaskListener parseMessageAsnycTaskListener) {
@@ -122,13 +122,18 @@ class FetchMessages {
@Override
protected Void doInBackground(Void... voids) {
messages = parseMessage(response, locale);
+ try {
+ after = new JSONObject(response).getJSONObject(JSONUtils.DATA_KEY).getString(JSONUtils.AFTER_KEY);
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
- parseMessageAsyncTaskListener.parseSuccess(messages);
+ parseMessageAsyncTaskListener.parseSuccess(messages, after);
}
}
}
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/LinkResolverActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/LinkResolverActivity.java
index d95a210e..1c6ee055 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/LinkResolverActivity.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/LinkResolverActivity.java
@@ -85,11 +85,10 @@ public class LinkResolverActivity extends AppCompatActivity {
builder.setToolbarColor(getResources().getColor(R.color.colorPrimary));
CustomTabsIntent customTabsIntent = builder.build();
customTabsIntent.intent.setPackage(resolveInfos.get(0).activityInfo.packageName);
- String uriString = uri.toString();
- if(!uriString.startsWith("http://") || (!uriString.startsWith("https://"))) {
- uriString = "http://" + uriString;
+ if(uri.getScheme() == null) {
+ uri = Uri.parse("http://" + uri.toString());
}
- customTabsIntent.launchUrl(this, Uri.parse(uriString));
+ customTabsIntent.launchUrl(this, uri);
} else {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(uri);
@@ -133,7 +132,7 @@ public class LinkResolverActivity extends AppCompatActivity {
return packagesSupportingCustomTabs;
}
- static Uri getRedditUriByPath(String path) {
+ public static Uri getRedditUriByPath(String path) {
return Uri.parse("https://www.reddit.com" + path);
}
}
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/MainActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/MainActivity.java
index b9d79b94..f2c58214 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/MainActivity.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/MainActivity.java
@@ -83,6 +83,7 @@ public class MainActivity extends AppCompatActivity implements SortTypeBottomShe
@BindView(R.id.all_drawer_items_linear_layout_main_activity) LinearLayout allDrawerItemsLinearLayout;
@BindView(R.id.profile_linear_layout_main_activity) LinearLayout profileLinearLayout;
@BindView(R.id.subscriptions_linear_layout_main_activity) LinearLayout subscriptionLinearLayout;
+ @BindView(R.id.inbox_linear_layout_main_activity) LinearLayout inboxLinearLayout;
@BindView(R.id.upvoted_linear_layout_main_activity) LinearLayout upvotedLinearLayout;
@BindView(R.id.downvoted_linear_layout_main_activity) LinearLayout downvotedLinearLayout;
@BindView(R.id.hidden_linear_layout_main_activity) LinearLayout hiddenLinearLayout;
@@ -385,6 +386,12 @@ public class MainActivity extends AppCompatActivity implements SortTypeBottomShe
mAccountNameTextView.setText(R.string.anonymous_account);
profileLinearLayout.setVisibility(View.GONE);
subscriptionLinearLayout.setVisibility(View.GONE);
+ inboxLinearLayout.setVisibility(View.GONE);
+ upvotedLinearLayout.setVisibility(View.GONE);
+ downvotedLinearLayout.setVisibility(View.GONE);
+ hiddenLinearLayout.setVisibility(View.GONE);
+ savedLinearLayout.setVisibility(View.GONE);
+ gildedLinearLayout.setVisibility(View.GONE);
divider.setVisibility(View.GONE);
}
@@ -417,6 +424,12 @@ public class MainActivity extends AppCompatActivity implements SortTypeBottomShe
drawer.closeDrawers();
});
+ inboxLinearLayout.setOnClickListener(view -> {
+ Intent intent = new Intent(this, ViewMessageActivity.class);
+ startActivity(intent);
+ drawer.closeDrawers();
+ });
+
upvotedLinearLayout.setOnClickListener(view -> {
Intent intent = new Intent(MainActivity.this, AccountPostsActivity.class);
intent.putExtra(AccountPostsActivity.EXTRA_USER_WHERE, PostDataSource.USER_WHERE_UPVOTED);
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/Message.java b/app/src/main/java/ml/docilealligator/infinityforreddit/Message.java
index 1846a8ef..1df3b233 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/Message.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/Message.java
@@ -104,7 +104,7 @@ class Message {
return formattedTime;
}
- public boolean isWasComment() {
+ public boolean wasComment() {
return wasComment;
}
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/MessageDataSource.java b/app/src/main/java/ml/docilealligator/infinityforreddit/MessageDataSource.java
new file mode 100644
index 00000000..3b2af4fb
--- /dev/null
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/MessageDataSource.java
@@ -0,0 +1,123 @@
+package ml.docilealligator.infinityforreddit;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.lifecycle.MutableLiveData;
+import androidx.paging.PageKeyedDataSource;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+import retrofit2.Retrofit;
+
+class MessageDataSource extends PageKeyedDataSource<String, Message> {
+ private Retrofit oauthRetrofit;
+ private Locale locale;
+ private String accessToken;
+ private String where;
+
+ private MutableLiveData<NetworkState> paginationNetworkStateLiveData;
+ private MutableLiveData<NetworkState> initialLoadStateLiveData;
+ private MutableLiveData<Boolean> hasPostLiveData;
+
+ private LoadInitialParams<String> initialParams;
+ private LoadInitialCallback<String, Message> initialCallback;
+ private LoadParams<String> params;
+ private LoadCallback<String, Message> callback;
+
+ MessageDataSource(Retrofit oauthRetrofit, Locale locale, String accessToken, String where) {
+ this.oauthRetrofit = oauthRetrofit;
+ this.locale = locale;
+ this.accessToken = accessToken;
+ this.where = where;
+ paginationNetworkStateLiveData = new MutableLiveData<>();
+ initialLoadStateLiveData = new MutableLiveData<>();
+ hasPostLiveData = new MutableLiveData<>();
+ }
+
+ MutableLiveData<NetworkState> getPaginationNetworkStateLiveData() {
+ return paginationNetworkStateLiveData;
+ }
+
+ MutableLiveData<NetworkState> getInitialLoadStateLiveData() {
+ return initialLoadStateLiveData;
+ }
+
+ MutableLiveData<Boolean> hasPostLiveData() {
+ return hasPostLiveData;
+ }
+
+ void retry() {
+ loadInitial(initialParams, initialCallback);
+ }
+
+ void retryLoadingMore() {
+ loadAfter(params, callback);
+ }
+
+ @Override
+ public void loadInitial(@NonNull LoadInitialParams<String> params, @NonNull LoadInitialCallback<String, Message> callback) {
+ initialParams = params;
+ initialCallback = callback;
+
+ initialLoadStateLiveData.postValue(NetworkState.LOADING);
+
+ FetchMessages.fetchMessagesAsync(oauthRetrofit, locale, accessToken, where, null, new FetchMessages.FetchMessagesListener() {
+ @Override
+ public void fetchSuccess(ArrayList<Message> messages, @Nullable String after) {
+ if(messages.size() == 0) {
+ hasPostLiveData.postValue(false);
+ } else {
+ hasPostLiveData.postValue(true);
+ }
+
+ if(after == null || after.equals("") || after.equals("null")) {
+ callback.onResult(messages, null, null);
+ } else {
+ callback.onResult(messages, null, after);
+ }
+ initialLoadStateLiveData.postValue(NetworkState.LOADED);
+ }
+
+ @Override
+ public void fetchFailed() {
+ Log.i("Messages fetch error", "Error fetch messages");
+ initialLoadStateLiveData.postValue(new NetworkState(NetworkState.Status.FAILED, "Error fetch messages"));
+ }
+ });
+ }
+
+ @Override
+ public void loadBefore(@NonNull LoadParams<String> params, @NonNull LoadCallback<String, Message> callback) {
+
+ }
+
+ @Override
+ public void loadAfter(@NonNull LoadParams<String> params, @NonNull LoadCallback<String, Message> callback) {
+ this.params = params;
+ this.callback = callback;
+
+ paginationNetworkStateLiveData.postValue(NetworkState.LOADING);
+
+ FetchMessages.fetchMessagesAsync(oauthRetrofit, locale, accessToken, where, params.key, new FetchMessages.FetchMessagesListener() {
+ @Override
+ public void fetchSuccess(ArrayList<Message> messages, @Nullable String after) {
+ if(after == null || after.equals("") || after.equals("null")) {
+ callback.onResult(messages, null);
+ } else {
+ callback.onResult(messages, after);
+ }
+
+ paginationNetworkStateLiveData.postValue(NetworkState.LOADED);
+ }
+
+ @Override
+ public void fetchFailed() {
+ Log.i("Comments fetch error", "Error parsing data");
+ paginationNetworkStateLiveData.postValue(new NetworkState(NetworkState.Status.FAILED, "Error fetching data"));
+ }
+ });
+ }
+}
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/MessageDataSourceFactory.java b/app/src/main/java/ml/docilealligator/infinityforreddit/MessageDataSourceFactory.java
new file mode 100644
index 00000000..b1470bf7
--- /dev/null
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/MessageDataSourceFactory.java
@@ -0,0 +1,47 @@
+package ml.docilealligator.infinityforreddit;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.MutableLiveData;
+import androidx.paging.DataSource;
+
+import java.util.Locale;
+
+import retrofit2.Retrofit;
+
+class MessageDataSourceFactory extends DataSource.Factory {
+ private Retrofit oauthRetrofit;
+ private Locale locale;
+ private String accessToken;
+ private String where;
+
+ private MessageDataSource messageDataSource;
+ private MutableLiveData<MessageDataSource> messageDataSourceLiveData;
+
+ MessageDataSourceFactory(Retrofit oauthRetrofit, Locale locale, String accessToken, String where) {
+ this.oauthRetrofit = oauthRetrofit;
+ this.locale = locale;
+ this.accessToken = accessToken;
+ this.where = where;
+ messageDataSourceLiveData = new MutableLiveData<>();
+ }
+
+ @NonNull
+ @Override
+ public DataSource create() {
+ messageDataSource = new MessageDataSource(oauthRetrofit, locale, accessToken, where);
+ messageDataSourceLiveData.postValue(messageDataSource);
+ return messageDataSource;
+ }
+
+ public MutableLiveData<MessageDataSource> getMessageDataSourceLiveData() {
+ return messageDataSourceLiveData;
+ }
+
+ MessageDataSource getMessageDataSource() {
+ return messageDataSource;
+ }
+
+ void changeWhere(String where) {
+ this.where = where;
+ }
+}
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/MessageRecyclerViewAdapter.java b/app/src/main/java/ml/docilealligator/infinityforreddit/MessageRecyclerViewAdapter.java
new file mode 100644
index 00000000..3865123b
--- /dev/null
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/MessageRecyclerViewAdapter.java
@@ -0,0 +1,195 @@
+package ml.docilealligator.infinityforreddit;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.paging.PagedListAdapter;
+import androidx.recyclerview.widget.DiffUtil;
+import androidx.recyclerview.widget.RecyclerView;
+
+import CustomView.CustomMarkwonView;
+import butterknife.BindView;
+import butterknife.ButterKnife;
+
+class MessageRecyclerViewAdapter extends PagedListAdapter<Message, RecyclerView.ViewHolder> {
+ private static final int VIEW_TYPE_DATA = 0;
+ private static final int VIEW_TYPE_ERROR = 1;
+ private static final int VIEW_TYPE_LOADING = 2;
+
+ private Context mContext;
+ private Resources resources;
+
+ private NetworkState networkState;
+ private CommentsListingRecyclerViewAdapter.RetryLoadingMoreCallback mRetryLoadingMoreCallback;
+
+ interface RetryLoadingMoreCallback {
+ void retryLoadingMore();
+ }
+
+ MessageRecyclerViewAdapter(Context context) {
+ super(DIFF_CALLBACK);
+ mContext = context;
+ resources = context.getResources();
+ }
+
+ private static final DiffUtil.ItemCallback<Message> DIFF_CALLBACK = new DiffUtil.ItemCallback<Message>() {
+ @Override
+ public boolean areItemsTheSame(@NonNull Message message, @NonNull Message t1) {
+ return message.getId().equals(t1.getId());
+ }
+
+ @Override
+ public boolean areContentsTheSame(@NonNull Message message, @NonNull Message t1) {
+ return message.getBody().equals(t1.getBody());
+ }
+ };
+
+ @NonNull
+ @Override
+ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ if(viewType == VIEW_TYPE_DATA) {
+ return new DataViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_message, parent, false));
+ } else if(viewType == VIEW_TYPE_ERROR) {
+ return new ErrorViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_footer_error, parent, false));
+ } else {
+ return new LoadingViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_footer_loading, parent, false));
+ }
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
+ if(holder instanceof DataViewHolder) {
+ Message message = getItem(holder.getAdapterPosition());
+ if(message != null) {
+ if(message.isNew()) {
+ ((DataViewHolder) holder).itemView.setBackgroundColor(
+ resources.getColor(R.color.unreadMessageBackgroundColor));
+ }
+
+ if(message.wasComment()) {
+ ((DataViewHolder) holder).authorTextView.setTextColor(resources.getColor(R.color.colorPrimaryDarkDayNightTheme));
+ ((DataViewHolder) holder).titleTextView.setText(message.getTitle());
+ } else {
+ ((DataViewHolder) holder).titleTextView.setVisibility(View.GONE);
+ }
+
+ ((DataViewHolder) holder).authorTextView.setText(message.getAuthor());
+ String subject = message.getSubject().substring(0, 1).toUpperCase() + message.getSubject().substring(1);
+ ((DataViewHolder) holder).subjectTextView.setText(subject);
+ ((DataViewHolder) holder).contentCustomMarkwonView.setMarkdown(message.getBody(), mContext);
+
+ ((DataViewHolder) holder).itemView.setOnClickListener(view -> {
+ if(message.getContext() != null && !message.getContext().equals("")) {
+ Uri uri = LinkResolverActivity.getRedditUriByPath(message.getContext());
+ Intent intent = new Intent(mContext, LinkResolverActivity.class);
+ intent.setData(uri);
+ mContext.startActivity(intent);
+ }
+ });
+
+ ((DataViewHolder) holder).authorTextView.setOnClickListener(view -> {
+ if(message.wasComment()) {
+ Intent intent = new Intent(mContext, ViewUserDetailActivity.class);
+ intent.putExtra(ViewUserDetailActivity.EXTRA_USER_NAME_KEY, message.getAuthor());
+ mContext.startActivity(intent);
+ }
+ });
+
+ ((DataViewHolder) holder).contentCustomMarkwonView.setOnClickListener(view -> ((DataViewHolder) holder).itemView.performClick());
+ }
+ }
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ // Reached at the end
+ if (hasExtraRow() && position == getItemCount() - 1) {
+ if (networkState.getStatus() == NetworkState.Status.LOADING) {
+ return VIEW_TYPE_LOADING;
+ } else {
+ return VIEW_TYPE_ERROR;
+ }
+ } else {
+ return VIEW_TYPE_DATA;
+ }
+ }
+
+ @Override
+ public int getItemCount() {
+ if(hasExtraRow()) {
+ return super.getItemCount() + 1;
+ }
+ return super.getItemCount();
+ }
+
+ @Override
+ public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) {
+ super.onViewRecycled(holder);
+ if(holder instanceof DataViewHolder) {
+ ((DataViewHolder) holder).itemView.setBackgroundColor(resources.getColor(android.R.color.white));
+ ((DataViewHolder) holder).titleTextView.setVisibility(View.VISIBLE);
+ ((DataViewHolder) holder).authorTextView.setTextColor(resources.getColor(R.color.primaryTextColor));
+ }
+ }
+
+ private boolean hasExtraRow() {
+ return networkState != null && networkState.getStatus() != NetworkState.Status.SUCCESS;
+ }
+
+ void setNetworkState(NetworkState newNetworkState) {
+ NetworkState previousState = this.networkState;
+ boolean previousExtraRow = hasExtraRow();
+ this.networkState = newNetworkState;
+ boolean newExtraRow = hasExtraRow();
+ if (previousExtraRow != newExtraRow) {
+ if (previousExtraRow) {
+ notifyItemRemoved(super.getItemCount());
+ } else {
+ notifyItemInserted(super.getItemCount());
+ }
+ } else if (newExtraRow && !previousState.equals(newNetworkState)) {
+ notifyItemChanged(getItemCount() - 1);
+ }
+ }
+
+ class DataViewHolder extends RecyclerView.ViewHolder {
+ View itemView;
+ @BindView(R.id.author_text_view_item_message) TextView authorTextView;
+ @BindView(R.id.subject_text_view_item_message) TextView subjectTextView;
+ @BindView(R.id.title_text_view_item_message) TextView titleTextView;
+ @BindView(R.id.content_custom_markwon_view_item_message) CustomMarkwonView contentCustomMarkwonView;
+
+ DataViewHolder(View itemView) {
+ super(itemView);
+ ButterKnife.bind(this, itemView);
+ this.itemView = itemView;
+ }
+ }
+
+ class ErrorViewHolder extends RecyclerView.ViewHolder {
+ @BindView(R.id.error_text_view_item_footer_error) TextView errorTextView;
+ @BindView(R.id.retry_button_item_footer_error) Button retryButton;
+
+ ErrorViewHolder(View itemView) {
+ super(itemView);
+ ButterKnife.bind(this, itemView);
+ errorTextView.setText(R.string.load_comments_failed);
+ retryButton.setOnClickListener(view -> mRetryLoadingMoreCallback.retryLoadingMore());
+ }
+ }
+
+ class LoadingViewHolder extends RecyclerView.ViewHolder {
+ LoadingViewHolder(@NonNull View itemView) {
+ super(itemView);
+ ButterKnife.bind(this, itemView);
+ }
+ }
+}
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/MessageViewModel.java b/app/src/main/java/ml/docilealligator/infinityforreddit/MessageViewModel.java
new file mode 100644
index 00000000..4ad155d6
--- /dev/null
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/MessageViewModel.java
@@ -0,0 +1,100 @@
+package ml.docilealligator.infinityforreddit;
+
+import androidx.annotation.NonNull;
+import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MutableLiveData;
+import androidx.lifecycle.Transformations;
+import androidx.lifecycle.ViewModel;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.paging.LivePagedListBuilder;
+import androidx.paging.PagedList;
+
+import java.util.Locale;
+
+import retrofit2.Retrofit;
+
+public class MessageViewModel extends ViewModel {
+ private MessageDataSourceFactory messageDataSourceFactory;
+ private LiveData<NetworkState> paginationNetworkState;
+ private LiveData<NetworkState> initialLoadingState;
+ private LiveData<Boolean> hasMessageLiveData;
+ private LiveData<PagedList<Message>> messages;
+ private MutableLiveData<String> whereLiveData;
+
+ public MessageViewModel(Retrofit retrofit, Locale locale, String accessToken, String where) {
+ messageDataSourceFactory = new MessageDataSourceFactory(retrofit, locale, accessToken, where);
+
+ initialLoadingState = Transformations.switchMap(messageDataSourceFactory.getMessageDataSourceLiveData(),
+ MessageDataSource::getInitialLoadStateLiveData);
+ paginationNetworkState = Transformations.switchMap(messageDataSourceFactory.getMessageDataSourceLiveData(),
+ MessageDataSource::getPaginationNetworkStateLiveData);
+ hasMessageLiveData = Transformations.switchMap(messageDataSourceFactory.getMessageDataSourceLiveData(),
+ MessageDataSource::hasPostLiveData);
+
+ whereLiveData = new MutableLiveData<>();
+ whereLiveData.postValue(where);
+
+ PagedList.Config pagedListConfig =
+ (new PagedList.Config.Builder())
+ .setEnablePlaceholders(false)
+ .setPageSize(25)
+ .build();
+
+ messages = Transformations.switchMap(whereLiveData, newWhere -> {
+ messageDataSourceFactory.changeWhere(whereLiveData.getValue());
+ return (new LivePagedListBuilder(messageDataSourceFactory, pagedListConfig)).build();
+ });
+ }
+
+ LiveData<PagedList<Message>> getMessages() {
+ return messages;
+ }
+
+ LiveData<NetworkState> getPaginationNetworkState() {
+ return paginationNetworkState;
+ }
+
+ LiveData<NetworkState> getInitialLoadingState() {
+ return initialLoadingState;
+ }
+
+ LiveData<Boolean> hasMessage() {
+ return hasMessageLiveData;
+ }
+
+ void refresh() {
+ messageDataSourceFactory.getMessageDataSource().invalidate();
+ }
+
+ void retry() {
+ messageDataSourceFactory.getMessageDataSource().retry();
+ }
+
+ void retryLoadingMore() {
+ messageDataSourceFactory.getMessageDataSource().retryLoadingMore();
+ }
+
+ void changeWhere(String where) {
+ whereLiveData.postValue(where);
+ }
+
+ public static class Factory extends ViewModelProvider.NewInstanceFactory {
+ private Retrofit retrofit;
+ private Locale locale;
+ private String accessToken;
+ private String where;
+
+ public Factory(Retrofit retrofit, Locale locale, String accessToken, String where) {
+ this.retrofit = retrofit;
+ this.locale = locale;
+ this.accessToken = accessToken;
+ this.where = where;
+ }
+
+ @NonNull
+ @Override
+ public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
+ return (T) new MessageViewModel(retrofit, locale, accessToken, where);
+ }
+ }
+}
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/ParseSubredditData.java b/app/src/main/java/ml/docilealligator/infinityforreddit/ParseSubredditData.java
index 22e3787c..56d35248 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/ParseSubredditData.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/ParseSubredditData.java
@@ -43,7 +43,7 @@ class ParseSubredditData {
jsonResponse = new JSONObject(response);
parseFailed = false;
} catch (JSONException e) {
- Log.i("subreddit json error", e.getMessage());
+ Log.i("subreddit json error", "message: " + e.getMessage());
parseSubredditDataListener.onParseSubredditDataFail();
}
}
@@ -54,38 +54,9 @@ class ParseSubredditData {
JSONObject data = jsonResponse.getJSONObject(JSONUtils.DATA_KEY);
mNCurrentOnlineSubscribers = data.getInt(JSONUtils.ACTIVE_USER_COUNT_KEY);
subredditData = parseSubredditData(data);
- /*String id = data.getString(JSONUtils.EXTRA_NAME);
- String subredditFullName = data.getString(JSONUtils.DISPLAY_NAME);
- String description = data.getString(JSONUtils.PUBLIC_DESCRIPTION_KEY).trim();
-
- String bannerImageUrl;
- if(data.isNull(JSONUtils.BANNER_BACKGROUND_IMAGE_KEY)) {
- bannerImageUrl = "";
- } else {
- bannerImageUrl = data.getString(JSONUtils.BANNER_BACKGROUND_IMAGE_KEY);
- }
- if(bannerImageUrl.equals("") && !data.isNull(JSONUtils.BANNER_IMG_KEY)) {
- bannerImageUrl= data.getString(JSONUtils.BANNER_IMG_KEY);
- }
-
- String iconUrl;
- if(data.isNull(JSONUtils.COMMUNITY_ICON_KEY)) {
- iconUrl = "";
- } else {
- iconUrl = data.getString(JSONUtils.COMMUNITY_ICON_KEY);
- }
- if(iconUrl.equals("") && !data.isNull(JSONUtils.ICON_IMG_KEY)) {
- iconUrl = data.getString(JSONUtils.ICON_IMG_KEY);
- }
-
- int nSubscribers = data.getInt(JSONUtils.SUBSCRIBERS_KEY);
- int nCurrentOnlineSubscribers = data.getInt(JSONUtils.ACTIVE_USER_COUNT_KEY);
- subredditData = new SubredditData(id, subredditFullName, iconUrl, bannerImageUrl, description, nSubscribers);
- mNCurrentOnlineSubscribers = nCurrentOnlineSubscribers;*/
} catch (JSONException e) {
parseFailed = true;
Log.i("parse", "SubredditData error");
- parseSubredditDataListener.onParseSubredditDataFail();
}
return null;
}
@@ -114,7 +85,7 @@ class ParseSubredditData {
parseFailed = false;
subredditListingData = new ArrayList<>();
} catch (JSONException e) {
- Log.i("subreddit json error", e.getMessage());
+ Log.i("subreddit json error", "message: " + e.getMessage());
parseFailed = true;
}
}
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/PostRecyclerViewAdapter.java b/app/src/main/java/ml/docilealligator/infinityforreddit/PostRecyclerViewAdapter.java
index 239bf8c0..8d00a010 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/PostRecyclerViewAdapter.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/PostRecyclerViewAdapter.java
@@ -248,7 +248,8 @@ class PostRecyclerViewAdapter extends PagedListAdapter<Post, RecyclerView.ViewHo
});
} else {
if(post.getAuthorIconUrl() == null) {
- new LoadUserDataAsyncTask(mUserDao, post.getAuthor(), mRetrofit, iconImageUrl -> {
+ String authorName = post.getAuthor().equals("[deleted]") ? post.getSubredditNamePrefixed().substring(2) : post.getAuthor();
+ new LoadUserDataAsyncTask(mUserDao, authorName, mRetrofit, iconImageUrl -> {
if(mContext != null && getItemCount() > 0) {
if(iconImageUrl == null || iconImageUrl.equals("")) {
mGlide.load(R.drawable.subreddit_default_icon)
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/PullNotificationWorker.java b/app/src/main/java/ml/docilealligator/infinityforreddit/PullNotificationWorker.java
index 512096c7..57c13212 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/PullNotificationWorker.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/PullNotificationWorker.java
@@ -49,13 +49,13 @@ public class PullNotificationWorker extends Worker {
Account currentAccount = redditDataRoomDatabase.accountDao().getCurrentAccount();
Response<String> response = mOauthRetrofit.create(RedditAPI.class).getMessages(
RedditUtils.getOAuthHeader(currentAccount.getAccessToken()),
- FetchMessages.WHERE_COMMENTS).execute();
+ FetchMessages.WHERE_UNREAD, null).execute();
Log.i("workmanager", "has response");
if(response.isSuccessful()) {
String responseBody = response.body();
ArrayList<Message> messages = FetchMessages.parseMessage(responseBody, context.getResources().getConfiguration().locale);
- if(messages != null) {
+ if(messages != null && !messages.isEmpty()) {
NotificationManagerCompat notificationManager = NotificationUtils.getNotificationManager(context);
NotificationCompat.Builder summaryBuilder = NotificationUtils.buildSummaryNotification(context,
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/RedditAPI.java b/app/src/main/java/ml/docilealligator/infinityforreddit/RedditAPI.java
index ddd53c47..a187b521 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/RedditAPI.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/RedditAPI.java
@@ -158,5 +158,5 @@ public interface RedditAPI {
Call<String> selectFlair(@Path("subredditNamePrefixed") String subredditName, @HeaderMap Map<String, String> headers, @FieldMap Map<String, String> params);
@GET("/message/{where}.json?raw_json=1")
- Call<String> getMessages(@HeaderMap Map<String, String> headers, @Path("where") String where);
+ Call<String> getMessages(@HeaderMap Map<String, String> headers, @Path("where") String where, @Query("after") String after);
}
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/ViewMessageActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/ViewMessageActivity.java
new file mode 100644
index 00000000..b61438f4
--- /dev/null
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/ViewMessageActivity.java
@@ -0,0 +1,230 @@
+package ml.docilealligator.infinityforreddit;
+
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+import androidx.lifecycle.ViewModelProvider;
+import androidx.recyclerview.widget.DividerItemDecoration;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.RequestManager;
+import com.google.android.material.appbar.AppBarLayout;
+import com.google.android.material.appbar.CollapsingToolbarLayout;
+import com.lsjwzh.widget.materialloadingprogressbar.CircleProgressBar;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+import Account.Account;
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import retrofit2.Retrofit;
+
+public class ViewMessageActivity extends AppCompatActivity {
+
+ private static final String NULL_ACCESS_TOKEN_STATE = "NATS";
+ private static final String ACCESS_TOKEN_STATE = "ATS";
+
+ @BindView(R.id.collapsing_toolbar_layout_view_message_activity) CollapsingToolbarLayout collapsingToolbarLayout;
+ @BindView(R.id.appbar_layout_view_message_activity) AppBarLayout appBarLayout;
+ @BindView(R.id.toolbar_view_message_activity) Toolbar toolbar;
+ @BindView(R.id.progress_bar_view_message_activity) CircleProgressBar mProgressBar;
+ @BindView(R.id.recycler_view_view_message_activity) RecyclerView recyclerView;
+ @BindView(R.id.fetch_messages_info_linear_layout_view_message_activity) LinearLayout mFetchMessageInfoLinearLayout;
+ @BindView(R.id.fetch_messages_info_image_view_view_message_activity) ImageView mFetchMessageInfoImageView;
+ @BindView(R.id.fetch_messages_info_text_view_view_message_activity) TextView mFetchMessageInfoTextView;
+
+ private boolean mNullAccessToken = false;
+ private String mAccessToken;
+
+ private MessageRecyclerViewAdapter mAdapter;
+
+ private RequestManager mGlide;
+
+ MessageViewModel mMessageViewModel;
+
+ @Inject
+ @Named("oauth")
+ Retrofit mOauthRetrofit;
+
+ @Inject
+ RedditDataRoomDatabase mRedditDataRoomDatabase;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_view_message);
+
+ ButterKnife.bind(this);
+
+ ((Infinity) getApplication()).getAppComponent().inject(this);
+
+ mGlide = Glide.with(this);
+
+ Resources resources = getResources();
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1
+ && (resources.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT
+ || resources.getBoolean(R.bool.isTablet))) {
+ Window window = getWindow();
+ window.setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS, WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
+
+ boolean lightNavBar = false;
+ if((resources.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK) != Configuration.UI_MODE_NIGHT_YES) {
+ lightNavBar = true;
+ }
+ boolean finalLightNavBar = lightNavBar;
+
+ View decorView = window.getDecorView();
+ if(finalLightNavBar) {
+ decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+ }
+ appBarLayout.addOnOffsetChangedListener(new AppBarStateChangeListener() {
+ @Override
+ void onStateChanged(AppBarLayout appBarLayout, State state) {
+ if (state == State.COLLAPSED) {
+ if(finalLightNavBar) {
+ decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR | View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+ }
+ } else if (state == State.EXPANDED) {
+ if(finalLightNavBar) {
+ decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
+ }
+ }
+ }
+ });
+
+ int statusBarResourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
+ if (statusBarResourceId > 0) {
+ ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) toolbar.getLayoutParams();
+ params.topMargin = getResources().getDimensionPixelSize(statusBarResourceId);
+ toolbar.setLayoutParams(params);
+ }
+
+ int navBarResourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
+ if (navBarResourceId > 0) {
+ recyclerView.setPadding(0, 0, 0, resources.getDimensionPixelSize(navBarResourceId));
+ }
+ }
+
+ toolbar.setTitle(R.string.inbox);
+ setSupportActionBar(toolbar);
+
+ if(savedInstanceState != null) {
+ mNullAccessToken = savedInstanceState.getBoolean(NULL_ACCESS_TOKEN_STATE);
+ mAccessToken = savedInstanceState.getString(ACCESS_TOKEN_STATE);
+
+ if(!mNullAccessToken && mAccessToken == null) {
+ getCurrentAccountAndFetchMessage();
+ } else {
+ bindView();
+ }
+ } else {
+ getCurrentAccountAndFetchMessage();
+ }
+ }
+
+ private void getCurrentAccountAndFetchMessage() {
+ new GetCurrentAccountAsyncTask(mRedditDataRoomDatabase.accountDao(), new GetCurrentAccountAsyncTask.GetCurrentAccountAsyncTaskListener() {
+ @Override
+ public void success(Account account) {
+ if(account == null) {
+ mNullAccessToken = true;
+ } else {
+ mAccessToken = account.getAccessToken();
+
+ bindView();
+ }
+ }
+ }).execute();
+ }
+
+ private void bindView() {
+ mAdapter = new MessageRecyclerViewAdapter(this);
+ LinearLayoutManager layoutManager = new LinearLayoutManager(this);
+ recyclerView.setLayoutManager(layoutManager);
+ recyclerView.setAdapter(mAdapter);
+ DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(this, layoutManager.getOrientation());
+ recyclerView.addItemDecoration(dividerItemDecoration);
+
+ MessageViewModel.Factory factory = new MessageViewModel.Factory(mOauthRetrofit,
+ getResources().getConfiguration().locale, mAccessToken, FetchMessages.WHERE_INBOX);
+ mMessageViewModel = new ViewModelProvider(this, factory).get(MessageViewModel.class);
+ mMessageViewModel.getMessages().observe(this, messages -> mAdapter.submitList(messages));
+
+ mMessageViewModel.getInitialLoadingState().observe(this, networkState -> {
+ if(networkState.getStatus().equals(NetworkState.Status.SUCCESS)) {
+ mProgressBar.setVisibility(View.GONE);
+ } else if(networkState.getStatus().equals(NetworkState.Status.FAILED)) {
+ mFetchMessageInfoLinearLayout.setOnClickListener(view -> mMessageViewModel.retry());
+ showErrorView(R.string.load_messages_failed);
+ } else {
+ mFetchMessageInfoLinearLayout.setVisibility(View.GONE);
+ mProgressBar.setVisibility(View.VISIBLE);
+ }
+ });
+
+ mMessageViewModel.hasMessage().observe(this, hasMessage -> {
+ if(hasMessage) {
+ mFetchMessageInfoLinearLayout.setVisibility(View.GONE);
+ } else {
+ mFetchMessageInfoLinearLayout.setOnClickListener(view -> {
+ //Do nothing
+ });
+ showErrorView(R.string.no_messages);
+ }
+ });
+
+ mMessageViewModel.getPaginationNetworkState().observe(this, networkState -> {
+ mAdapter.setNetworkState(networkState);
+ });
+ }
+
+ private void showErrorView(int stringResId) {
+ mProgressBar.setVisibility(View.GONE);
+ mFetchMessageInfoLinearLayout.setVisibility(View.VISIBLE);
+ mFetchMessageInfoTextView.setText(stringResId);
+ mGlide.load(R.drawable.load_post_error_indicator).into(mFetchMessageInfoImageView);
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.view_message_activity, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+ if(item.getItemId() == R.id.action_refresh_view_message_activity) {
+ mMessageViewModel.refresh();
+ return true;
+ } else if(item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected void onSaveInstanceState(@NonNull Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putBoolean(NULL_ACCESS_TOKEN_STATE, mNullAccessToken);
+ outState.putString(ACCESS_TOKEN_STATE, mAccessToken);
+ }
+}
diff --git a/app/src/main/java/ml/docilealligator/infinityforreddit/ViewPostDetailActivity.java b/app/src/main/java/ml/docilealligator/infinityforreddit/ViewPostDetailActivity.java
index 1039fc96..9e7bdb9b 100644
--- a/app/src/main/java/ml/docilealligator/infinityforreddit/ViewPostDetailActivity.java
+++ b/app/src/main/java/ml/docilealligator/infinityforreddit/ViewPostDetailActivity.java
@@ -101,7 +101,6 @@ public class ViewPostDetailActivity extends AppCompatActivity implements FlairBo
private LinearLayoutManager mLinearLayoutManager;
private CommentAndPostRecyclerViewAdapter mAdapter;
- private LoadSubredditIconAsyncTask mLoadSubredditIconAsyncTask;
@BindView(R.id.coordinator_layout_view_post_detail) CoordinatorLayout mCoordinatorLayout;
@BindView(R.id.appbar_layout_view_post_detail_activity) AppBarLayout appBarLayout;
@@ -255,7 +254,7 @@ public class ViewPostDetailActivity extends AppCompatActivity implements FlairBo
}
mAdapter = new CommentAndPostRecyclerViewAdapter(ViewPostDetailActivity.this, mRetrofit,
mOauthRetrofit, mRedditDataRoomDatabase, mGlide, mAccessToken, mAccountName, mPost,
- mLocale, mSingleCommentId, isSingleCommentThreadMode, mLoadSubredditIconAsyncTask,
+ mLocale, mSingleCommentId, isSingleCommentThreadMode,
new CommentAndPostRecyclerViewAdapter.CommentRecyclerViewAdapterCallback() {
@Override
public void updatePost(Post post) {
@@ -331,7 +330,7 @@ public class ViewPostDetailActivity extends AppCompatActivity implements FlairBo
mAdapter = new CommentAndPostRecyclerViewAdapter(ViewPostDetailActivity.this, mRetrofit,
mOauthRetrofit, mRedditDataRoomDatabase, mGlide, mAccessToken, mAccountName, mPost,
- mLocale, mSingleCommentId, isSingleCommentThreadMode, mLoadSubredditIconAsyncTask,
+ mLocale, mSingleCommentId, isSingleCommentThreadMode,
new CommentAndPostRecyclerViewAdapter.CommentRecyclerViewAdapterCallback() {
@Override
public void updatePost(Post post) {
@@ -817,9 +816,6 @@ public class ViewPostDetailActivity extends AppCompatActivity implements FlairBo
EventBus.getDefault().unregister(this);
super.onDestroy();
Bridge.clear(this);
- if(mLoadSubredditIconAsyncTask != null) {
- mLoadSubredditIconAsyncTask.cancel(true);
- }
}
@Override
diff --git a/app/src/main/res/drawable/ic_outline_inbox_24px.xml b/app/src/main/res/drawable/ic_outline_inbox_24px.xml
new file mode 100644
index 00000000..97d34185
--- /dev/null
+++ b/app/src/main/res/drawable/ic_outline_inbox_24px.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M19,3L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM19,19L5,19v-3h3.56c0.69,1.19 1.97,2 3.45,2s2.75,-0.81 3.45,-2L19,16v3zM19,14h-4.99c0,1.1 -0.9,2 -2,2s-2,-0.9 -2,-2L5,14L5,5h14v9z"/>
+</vector>
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 407c2a1d..849253a1 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -91,6 +91,32 @@
</LinearLayout>
<LinearLayout
+ android:id="@+id/inbox_linear_layout_main_activity"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?attr/selectableItemBackground"
+ android:clickable="true"
+ android:focusable="true"
+ android:padding="16dp">
+
+ <ImageView
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_gravity="center_vertical"
+ android:layout_marginEnd="32dp"
+ android:src="@drawable/ic_outline_inbox_24px"
+ android:tint="@color/primaryTextColor"/>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:text="@string/inbox"
+ android:textColor="@color/primaryTextColor" />
+
+ </LinearLayout>
+
+ <LinearLayout
android:id="@+id/upvoted_linear_layout_main_activity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/app/src/main/res/layout/activity_view_message.xml b/app/src/main/res/layout/activity_view_message.xml
new file mode 100644
index 00000000..b05badf3
--- /dev/null
+++ b/app/src/main/res/layout/activity_view_message.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context=".ViewMessageActivity">
+
+ <com.google.android.material.appbar.AppBarLayout
+ android:id="@+id/appbar_layout_view_message_activity"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:theme="@style/AppTheme.AppBarOverlay">
+
+ <com.google.android.material.appbar.CollapsingToolbarLayout
+ android:id="@+id/collapsing_toolbar_layout_view_message_activity"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:layout_scrollFlags="scroll|enterAlways"
+ app:titleEnabled="false"
+ app:toolbarId="@+id/toolbar_view_message_activity">
+
+ <androidx.appcompat.widget.Toolbar
+ android:id="@+id/toolbar_view_message_activity"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ app:popupTheme="@style/AppTheme.PopupOverlay"
+ app:navigationIcon="?attr/homeAsUpIndicator" />
+
+ </com.google.android.material.appbar.CollapsingToolbarLayout>
+
+ </com.google.android.material.appbar.AppBarLayout>
+
+ <RelativeLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:layout_behavior="@string/appbar_scrolling_view_behavior" >
+
+ <com.lsjwzh.widget.materialloadingprogressbar.CircleProgressBar
+ android:id="@+id/progress_bar_view_message_activity"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerHorizontal="true"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ app:mlpb_progress_stoke_width="3dp"
+ app:mlpb_progress_color="@color/colorAccent"
+ app:mlpb_background_color="@color/circularProgressBarBackground" />
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/recycler_view_view_message_activity"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false"/>
+
+ <LinearLayout
+ android:id="@+id/fetch_messages_info_linear_layout_view_message_activity"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:visibility="gone">
+
+ <ImageView
+ android:id="@+id/fetch_messages_info_image_view_view_message_activity"
+ android:layout_width="150dp"
+ android:layout_height="wrap_content" />
+
+ <TextView
+ android:id="@+id/fetch_messages_info_text_view_view_message_activity"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:gravity="center" />
+
+ </LinearLayout>
+
+ </RelativeLayout>
+
+</androidx.coordinatorlayout.widget.CoordinatorLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/item_message.xml b/app/src/main/res/layout/item_message.xml
new file mode 100644
index 00000000..12fd25c4
--- /dev/null
+++ b/app/src/main/res/layout/item_message.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="16dp">
+
+ <TextView
+ android:id="@+id/author_text_view_item_message"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:textColor="@color/primaryTextColor" />
+
+ <TextView
+ android:id="@+id/subject_text_view_item_message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:textSize="16sp"
+ android:textColor="@color/primaryTextColor" />
+
+ <TextView
+ android:id="@+id/title_text_view_item_message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="8dp"
+ android:textColor="@color/primaryTextColor" />
+
+ <CustomView.CustomMarkwonView
+ android:id="@+id/content_custom_markwon_view_item_message"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/menu/view_message_activity.xml b/app/src/main/res/menu/view_message_activity.xml
new file mode 100644
index 00000000..ae1bdfdc
--- /dev/null
+++ b/app/src/main/res/menu/view_message_activity.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+ <item
+ android:id="@+id/action_refresh_view_message_activity"
+ android:orderInCategory="1"
+ android:title="@string/action_refresh"
+ app:showAsAction="never" />
+</menu> \ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index ee3bdeed..ef901a9e 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -47,4 +47,6 @@
<color name="cardViewBackgroundColor">#FFFFFF</color>
<color name="singleCommentThreadBackgroundColor">#B3E5F9</color>
+
+ <color name="unreadMessageBackgroundColor">#B3E5F9</color>
</resources>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 359315a2..1943b769 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -13,6 +13,7 @@
<string name="search_subreddits_activity_label">Subreddits</string>
<string name="edit_post_activity_label">Edit Post</string>
<string name="edit_comment_activity_label">Edit Comment</string>
+ <string name="view_message_activity">Inbox</string>
<string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string>
@@ -50,19 +51,23 @@
<string name="no_users">No users found</string>
<string name="no_storage_permission">No storage permission to save this file</string>
- <string name="load_comments_failed">Error loading comments.</string>
+ <string name="load_comments_failed">Error loading comments</string>
<string name="retry">Retry</string>
<string name="comments">Comments</string>
<string name="no_comments_yet">No comments yet. Write a comment?</string>
<string name="vote_failed">Vote failed</string>
<string name="refresh_post_failed">Error refreshing the post</string>
+ <string name="load_messages_failed">Error loading messages</string>
+ <string name="no_messages">Empty</string>
+
<string name="nsfw">NSFW</string>
<string name="karma_info">Karma: %1$d</string>
<string name="profile">Profile</string>
<string name="following">Following</string>
<string name="subscriptions">Subscriptions</string>
+ <string name="inbox">Inbox</string>
<string name="upvoted">Upvoted</string>
<string name="downvoted">Downvoted</string>
<string name="hidden">Hidden</string>