View messages in ViewMessageActivity. Use LinkResolverActivity to handle link clicking in CustomMarkwonView. Fixed cannot load user profile image after the post is deleted.

This commit is contained in:
Alex Ning 2019-08-18 16:03:58 +08:00
parent 1984332fdd
commit ffd1d6e204
30 changed files with 939 additions and 111 deletions

View File

@ -35,9 +35,9 @@
<map> <map>
<entry key="assetSourceType" value="FILE" /> <entry key="assetSourceType" value="FILE" />
<entry key="color" value="ffffff" /> <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="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> </map>
</option> </option>
</PersistentState> </PersistentState>

Binary file not shown.

Binary file not shown.

View File

@ -20,7 +20,13 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme" android:theme="@style/AppTheme"
android:usesCleartextTraffic="true"> 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:parentActivityName=".MainActivity"
android:theme="@style/AppTheme.NoActionBar" /> android:theme="@style/AppTheme.NoActionBar" />
<activity <activity

View File

@ -6,11 +6,8 @@ import android.net.Uri;
import android.util.AttributeSet; import android.util.AttributeSet;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.browser.customtabs.CustomTabsIntent;
import ml.docilealligator.infinityforreddit.R; import ml.docilealligator.infinityforreddit.LinkResolverActivity;
import ml.docilealligator.infinityforreddit.ViewSubredditDetailActivity;
import ml.docilealligator.infinityforreddit.ViewUserDetailActivity;
import ru.noties.markwon.SpannableConfiguration; import ru.noties.markwon.SpannableConfiguration;
import ru.noties.markwon.view.MarkwonView; import ru.noties.markwon.view.MarkwonView;
@ -26,30 +23,14 @@ public class CustomMarkwonView extends MarkwonView {
public void setMarkdown(@Nullable String markdown, Context context) { public void setMarkdown(@Nullable String markdown, Context context) {
SpannableConfiguration configuration = SpannableConfiguration.builder(context).linkResolver((view, link) -> { SpannableConfiguration configuration = SpannableConfiguration.builder(context).linkResolver((view, link) -> {
if(link.startsWith("/u/") || link.startsWith("u/")) { Intent intent = new Intent(context, LinkResolverActivity.class);
Intent intent = new Intent(context, ViewUserDetailActivity.class); Uri uri = Uri.parse(link);
intent.putExtra(ViewUserDetailActivity.EXTRA_USER_NAME_KEY, link.substring(3)); if(uri.getScheme() == null && uri.getHost() == null) {
context.startActivity(intent); intent.setData(LinkResolverActivity.getRedditUriByPath(link));
} 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);
} else { } else {
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); intent.setData(uri);
// 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));
} }
context.startActivity(intent);
}).build(); }).build();
super.setMarkdown(configuration, markdown); super.setMarkdown(configuration, markdown);

View File

@ -35,4 +35,5 @@ interface AppComponent {
void inject(EditCommentActivity editCommentActivity); void inject(EditCommentActivity editCommentActivity);
void inject(AccountPostsActivity accountPostsActivity); void inject(AccountPostsActivity accountPostsActivity);
void inject(PullNotificationWorker pullNotificationWorker); void inject(PullNotificationWorker pullNotificationWorker);
void inject(ViewMessageActivity viewMessageActivity);
} }

View File

@ -72,7 +72,6 @@ class CommentAndPostRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVie
private String mSingleCommentId; private String mSingleCommentId;
private boolean mIsSingleCommentThreadMode; private boolean mIsSingleCommentThreadMode;
private CommentRecyclerViewAdapterCallback mCommentRecyclerViewAdapterCallback; private CommentRecyclerViewAdapterCallback mCommentRecyclerViewAdapterCallback;
private LoadSubredditIconAsyncTask mLoadSubredditIconAsyncTask;
private boolean isInitiallyLoading; private boolean isInitiallyLoading;
private boolean isInitiallyLoadingFailed; private boolean isInitiallyLoadingFailed;
private boolean mHasMoreComments; private boolean mHasMoreComments;
@ -87,7 +86,6 @@ class CommentAndPostRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVie
RedditDataRoomDatabase redditDataRoomDatabase, RequestManager glide, RedditDataRoomDatabase redditDataRoomDatabase, RequestManager glide,
String accessToken, String accountName, Post post, Locale locale, String accessToken, String accountName, Post post, Locale locale,
String singleCommentId, boolean isSingleCommentThreadMode, String singleCommentId, boolean isSingleCommentThreadMode,
LoadSubredditIconAsyncTask loadSubredditIconAsyncTask,
CommentRecyclerViewAdapterCallback commentRecyclerViewAdapterCallback) { CommentRecyclerViewAdapterCallback commentRecyclerViewAdapterCallback) {
mActivity = activity; mActivity = activity;
mRetrofit = retrofit; mRetrofit = retrofit;
@ -102,7 +100,6 @@ class CommentAndPostRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVie
mLocale = locale; mLocale = locale;
mSingleCommentId = singleCommentId; mSingleCommentId = singleCommentId;
mIsSingleCommentThreadMode = isSingleCommentThreadMode; mIsSingleCommentThreadMode = isSingleCommentThreadMode;
mLoadSubredditIconAsyncTask = loadSubredditIconAsyncTask;
mCommentRecyclerViewAdapterCallback = commentRecyclerViewAdapterCallback; mCommentRecyclerViewAdapterCallback = commentRecyclerViewAdapterCallback;
isInitiallyLoading = true; isInitiallyLoading = true;
isInitiallyLoadingFailed = false; isInitiallyLoadingFailed = false;
@ -194,10 +191,10 @@ class CommentAndPostRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVie
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if(holder.getItemViewType() == VIEW_TYPE_POST_DETAIL) { if(holder.getItemViewType() == VIEW_TYPE_POST_DETAIL) {
((PostDetailViewHolder) holder).mTitleTextView.setText(mPost.getTitle()); ((PostDetailViewHolder) holder).mTitleTextView.setText(mPost.getTitle());
if(mPost.getSubredditNamePrefixed().startsWith("u/")) {
if(mPost.getSubredditNamePrefixed().equals("u/" + mPost.getAuthor())) {
if(mPost.getAuthorIconUrl() == null) { 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(mActivity != null && getItemCount() > 0) {
if(iconImageUrl == null || iconImageUrl.equals("")) { if(iconImageUrl == null || iconImageUrl.equals("")) {
mGlide.load(R.drawable.subreddit_default_icon) mGlide.load(R.drawable.subreddit_default_icon)
@ -229,10 +226,7 @@ class CommentAndPostRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVie
} }
} else { } else {
if(mPost.getSubredditIconUrl() == null) { if(mPost.getSubredditIconUrl() == null) {
if(mLoadSubredditIconAsyncTask != null) { new LoadSubredditIconAsyncTask(
mLoadSubredditIconAsyncTask.cancel(true);
}
mLoadSubredditIconAsyncTask = new LoadSubredditIconAsyncTask(
mRedditDataRoomDatabase.subredditDao(), mPost.getSubredditNamePrefixed().substring(2), mRedditDataRoomDatabase.subredditDao(), mPost.getSubredditNamePrefixed().substring(2),
mRetrofit, iconImageUrl -> { mRetrofit, iconImageUrl -> {
if(iconImageUrl == null || iconImageUrl.equals("")) { if(iconImageUrl == null || iconImageUrl.equals("")) {
@ -248,9 +242,7 @@ class CommentAndPostRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVie
} }
mPost.setSubredditIconUrl(iconImageUrl); mPost.setSubredditIconUrl(iconImageUrl);
}); }).execute();
mLoadSubredditIconAsyncTask.execute();
} else if(!mPost.getSubredditIconUrl().equals("")) { } else if(!mPost.getSubredditIconUrl().equals("")) {
mGlide.load(mPost.getSubredditIconUrl()) mGlide.load(mPost.getSubredditIconUrl())
.apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(72, 0))) .apply(RequestOptions.bitmapTransform(new RoundedCornersTransformation(72, 0)))
@ -713,10 +705,6 @@ class CommentAndPostRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerVie
} }
void initiallyLoading() { void initiallyLoading() {
if(mLoadSubredditIconAsyncTask != null) {
mLoadSubredditIconAsyncTask.cancel(true);
}
if(mVisibleComments.size() != 0) { if(mVisibleComments.size() != 0) {
int previousSize = mVisibleComments.size(); int previousSize = mVisibleComments.size();
mVisibleComments.clear(); mVisibleComments.clear();

View File

@ -87,7 +87,11 @@ public class CommentDataSource extends PageKeyedDataSource<String, CommentData>
hasPostLiveData.postValue(true); 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); initialLoadStateLiveData.postValue(NetworkState.LOADED);
} }
@ -147,13 +151,15 @@ public class CommentDataSource extends PageKeyedDataSource<String, CommentData>
} }
}).execute(); }).execute();
} else { } 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 @Override
public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) { 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"));
} }
}); });
} }

View File

@ -57,13 +57,13 @@ class CommentsListingRecyclerViewAdapter extends PagedListAdapter<CommentData, R
private static final DiffUtil.ItemCallback<CommentData> DIFF_CALLBACK = new DiffUtil.ItemCallback<CommentData>() { private static final DiffUtil.ItemCallback<CommentData> DIFF_CALLBACK = new DiffUtil.ItemCallback<CommentData>() {
@Override @Override
public boolean areItemsTheSame(@NonNull CommentData CommentData, @NonNull CommentData t1) { public boolean areItemsTheSame(@NonNull CommentData commentData, @NonNull CommentData t1) {
return CommentData.getId().equals(t1.getId()); return commentData.getId().equals(t1.getId());
} }
@Override @Override
public boolean areContentsTheSame(@NonNull CommentData CommentData, @NonNull CommentData t1) { public boolean areContentsTheSame(@NonNull CommentData commentData, @NonNull CommentData t1) {
return CommentData.getCommentContent().equals(t1.getCommentContent()); return commentData.getCommentContent().equals(t1.getCommentContent());
} }
}; };

View File

@ -22,8 +22,8 @@ import retrofit2.Retrofit;
class FetchMessages { class FetchMessages {
interface FetchMessagesListener { interface FetchMessagesListener {
void fetchSuccess(@Nullable ArrayList<Message> messages); void fetchSuccess(ArrayList<Message> messages, @Nullable String after);
void fetchFailed(boolean shouldRetry); void fetchFailed();
} }
static final String WHERE_INBOX = "inbox"; static final String WHERE_INBOX = "inbox";
@ -32,22 +32,21 @@ class FetchMessages {
static final String WHERE_COMMENTS = "comments"; static final String WHERE_COMMENTS = "comments";
static void fetchMessagesAsync(Retrofit oauthRetrofit, Locale locale, String accessToken, String where, static void fetchMessagesAsync(Retrofit oauthRetrofit, Locale locale, String accessToken, String where,
FetchMessagesListener fetchMessagesListener) { String after, FetchMessagesListener fetchMessagesListener) {
oauthRetrofit.create(RedditAPI.class).getMessages(RedditUtils.getOAuthHeader(accessToken), where) oauthRetrofit.create(RedditAPI.class).getMessages(RedditUtils.getOAuthHeader(accessToken), where, after)
.enqueue(new Callback<String>() { .enqueue(new Callback<String>() {
@Override @Override
public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) { public void onResponse(@NonNull Call<String> call, @NonNull Response<String> response) {
if(response.isSuccessful()) { if(response.isSuccessful()) {
new ParseMessageAsnycTask(response.body(), locale, new ParseMessageAsnycTask(response.body(), locale, fetchMessagesListener::fetchSuccess).execute();
fetchMessagesListener::fetchSuccess).execute();
} else { } else {
fetchMessagesListener.fetchFailed(true); fetchMessagesListener.fetchFailed();
} }
} }
@Override @Override
public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) { 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> { private static class ParseMessageAsnycTask extends AsyncTask<Void, Void, Void> {
interface ParseMessageAsyncTaskListener { interface ParseMessageAsyncTaskListener {
void parseSuccess(ArrayList<Message> messages); void parseSuccess(ArrayList<Message> messages, @Nullable String after);
} }
private String response; private String response;
private Locale locale; private Locale locale;
private ArrayList<Message> messages; private ArrayList<Message> messages;
private String after;
private ParseMessageAsyncTaskListener parseMessageAsyncTaskListener; private ParseMessageAsyncTaskListener parseMessageAsyncTaskListener;
ParseMessageAsnycTask(String response, Locale locale, ParseMessageAsyncTaskListener parseMessageAsnycTaskListener) { ParseMessageAsnycTask(String response, Locale locale, ParseMessageAsyncTaskListener parseMessageAsnycTaskListener) {
@ -122,13 +122,18 @@ class FetchMessages {
@Override @Override
protected Void doInBackground(Void... voids) { protected Void doInBackground(Void... voids) {
messages = parseMessage(response, locale); messages = parseMessage(response, locale);
try {
after = new JSONObject(response).getJSONObject(JSONUtils.DATA_KEY).getString(JSONUtils.AFTER_KEY);
} catch (JSONException e) {
e.printStackTrace();
}
return null; return null;
} }
@Override @Override
protected void onPostExecute(Void aVoid) { protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid); super.onPostExecute(aVoid);
parseMessageAsyncTaskListener.parseSuccess(messages); parseMessageAsyncTaskListener.parseSuccess(messages, after);
} }
} }
} }

View File

@ -85,11 +85,10 @@ public class LinkResolverActivity extends AppCompatActivity {
builder.setToolbarColor(getResources().getColor(R.color.colorPrimary)); builder.setToolbarColor(getResources().getColor(R.color.colorPrimary));
CustomTabsIntent customTabsIntent = builder.build(); CustomTabsIntent customTabsIntent = builder.build();
customTabsIntent.intent.setPackage(resolveInfos.get(0).activityInfo.packageName); customTabsIntent.intent.setPackage(resolveInfos.get(0).activityInfo.packageName);
String uriString = uri.toString(); if(uri.getScheme() == null) {
if(!uriString.startsWith("http://") || (!uriString.startsWith("https://"))) { uri = Uri.parse("http://" + uri.toString());
uriString = "http://" + uriString;
} }
customTabsIntent.launchUrl(this, Uri.parse(uriString)); customTabsIntent.launchUrl(this, uri);
} else { } else {
Intent intent = new Intent(Intent.ACTION_VIEW); Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(uri); intent.setData(uri);
@ -133,7 +132,7 @@ public class LinkResolverActivity extends AppCompatActivity {
return packagesSupportingCustomTabs; return packagesSupportingCustomTabs;
} }
static Uri getRedditUriByPath(String path) { public static Uri getRedditUriByPath(String path) {
return Uri.parse("https://www.reddit.com" + path); return Uri.parse("https://www.reddit.com" + path);
} }
} }

View File

@ -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.all_drawer_items_linear_layout_main_activity) LinearLayout allDrawerItemsLinearLayout;
@BindView(R.id.profile_linear_layout_main_activity) LinearLayout profileLinearLayout; @BindView(R.id.profile_linear_layout_main_activity) LinearLayout profileLinearLayout;
@BindView(R.id.subscriptions_linear_layout_main_activity) LinearLayout subscriptionLinearLayout; @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.upvoted_linear_layout_main_activity) LinearLayout upvotedLinearLayout;
@BindView(R.id.downvoted_linear_layout_main_activity) LinearLayout downvotedLinearLayout; @BindView(R.id.downvoted_linear_layout_main_activity) LinearLayout downvotedLinearLayout;
@BindView(R.id.hidden_linear_layout_main_activity) LinearLayout hiddenLinearLayout; @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); mAccountNameTextView.setText(R.string.anonymous_account);
profileLinearLayout.setVisibility(View.GONE); profileLinearLayout.setVisibility(View.GONE);
subscriptionLinearLayout.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); divider.setVisibility(View.GONE);
} }
@ -417,6 +424,12 @@ public class MainActivity extends AppCompatActivity implements SortTypeBottomShe
drawer.closeDrawers(); drawer.closeDrawers();
}); });
inboxLinearLayout.setOnClickListener(view -> {
Intent intent = new Intent(this, ViewMessageActivity.class);
startActivity(intent);
drawer.closeDrawers();
});
upvotedLinearLayout.setOnClickListener(view -> { upvotedLinearLayout.setOnClickListener(view -> {
Intent intent = new Intent(MainActivity.this, AccountPostsActivity.class); Intent intent = new Intent(MainActivity.this, AccountPostsActivity.class);
intent.putExtra(AccountPostsActivity.EXTRA_USER_WHERE, PostDataSource.USER_WHERE_UPVOTED); intent.putExtra(AccountPostsActivity.EXTRA_USER_WHERE, PostDataSource.USER_WHERE_UPVOTED);

View File

@ -104,7 +104,7 @@ class Message {
return formattedTime; return formattedTime;
} }
public boolean isWasComment() { public boolean wasComment() {
return wasComment; return wasComment;
} }

View File

@ -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"));
}
});
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -43,7 +43,7 @@ class ParseSubredditData {
jsonResponse = new JSONObject(response); jsonResponse = new JSONObject(response);
parseFailed = false; parseFailed = false;
} catch (JSONException e) { } catch (JSONException e) {
Log.i("subreddit json error", e.getMessage()); Log.i("subreddit json error", "message: " + e.getMessage());
parseSubredditDataListener.onParseSubredditDataFail(); parseSubredditDataListener.onParseSubredditDataFail();
} }
} }
@ -54,38 +54,9 @@ class ParseSubredditData {
JSONObject data = jsonResponse.getJSONObject(JSONUtils.DATA_KEY); JSONObject data = jsonResponse.getJSONObject(JSONUtils.DATA_KEY);
mNCurrentOnlineSubscribers = data.getInt(JSONUtils.ACTIVE_USER_COUNT_KEY); mNCurrentOnlineSubscribers = data.getInt(JSONUtils.ACTIVE_USER_COUNT_KEY);
subredditData = parseSubredditData(data); 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) { } catch (JSONException e) {
parseFailed = true; parseFailed = true;
Log.i("parse", "SubredditData error"); Log.i("parse", "SubredditData error");
parseSubredditDataListener.onParseSubredditDataFail();
} }
return null; return null;
} }
@ -114,7 +85,7 @@ class ParseSubredditData {
parseFailed = false; parseFailed = false;
subredditListingData = new ArrayList<>(); subredditListingData = new ArrayList<>();
} catch (JSONException e) { } catch (JSONException e) {
Log.i("subreddit json error", e.getMessage()); Log.i("subreddit json error", "message: " + e.getMessage());
parseFailed = true; parseFailed = true;
} }
} }

View File

@ -248,7 +248,8 @@ class PostRecyclerViewAdapter extends PagedListAdapter<Post, RecyclerView.ViewHo
}); });
} else { } else {
if(post.getAuthorIconUrl() == null) { 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(mContext != null && getItemCount() > 0) {
if(iconImageUrl == null || iconImageUrl.equals("")) { if(iconImageUrl == null || iconImageUrl.equals("")) {
mGlide.load(R.drawable.subreddit_default_icon) mGlide.load(R.drawable.subreddit_default_icon)

View File

@ -49,13 +49,13 @@ public class PullNotificationWorker extends Worker {
Account currentAccount = redditDataRoomDatabase.accountDao().getCurrentAccount(); Account currentAccount = redditDataRoomDatabase.accountDao().getCurrentAccount();
Response<String> response = mOauthRetrofit.create(RedditAPI.class).getMessages( Response<String> response = mOauthRetrofit.create(RedditAPI.class).getMessages(
RedditUtils.getOAuthHeader(currentAccount.getAccessToken()), RedditUtils.getOAuthHeader(currentAccount.getAccessToken()),
FetchMessages.WHERE_COMMENTS).execute(); FetchMessages.WHERE_UNREAD, null).execute();
Log.i("workmanager", "has response"); Log.i("workmanager", "has response");
if(response.isSuccessful()) { if(response.isSuccessful()) {
String responseBody = response.body(); String responseBody = response.body();
ArrayList<Message> messages = FetchMessages.parseMessage(responseBody, context.getResources().getConfiguration().locale); ArrayList<Message> messages = FetchMessages.parseMessage(responseBody, context.getResources().getConfiguration().locale);
if(messages != null) { if(messages != null && !messages.isEmpty()) {
NotificationManagerCompat notificationManager = NotificationUtils.getNotificationManager(context); NotificationManagerCompat notificationManager = NotificationUtils.getNotificationManager(context);
NotificationCompat.Builder summaryBuilder = NotificationUtils.buildSummaryNotification(context, NotificationCompat.Builder summaryBuilder = NotificationUtils.buildSummaryNotification(context,

View File

@ -158,5 +158,5 @@ public interface RedditAPI {
Call<String> selectFlair(@Path("subredditNamePrefixed") String subredditName, @HeaderMap Map<String, String> headers, @FieldMap Map<String, String> params); Call<String> selectFlair(@Path("subredditNamePrefixed") String subredditName, @HeaderMap Map<String, String> headers, @FieldMap Map<String, String> params);
@GET("/message/{where}.json?raw_json=1") @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);
} }

View File

@ -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);
}
}

View File

@ -101,7 +101,6 @@ public class ViewPostDetailActivity extends AppCompatActivity implements FlairBo
private LinearLayoutManager mLinearLayoutManager; private LinearLayoutManager mLinearLayoutManager;
private CommentAndPostRecyclerViewAdapter mAdapter; private CommentAndPostRecyclerViewAdapter mAdapter;
private LoadSubredditIconAsyncTask mLoadSubredditIconAsyncTask;
@BindView(R.id.coordinator_layout_view_post_detail) CoordinatorLayout mCoordinatorLayout; @BindView(R.id.coordinator_layout_view_post_detail) CoordinatorLayout mCoordinatorLayout;
@BindView(R.id.appbar_layout_view_post_detail_activity) AppBarLayout appBarLayout; @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, mAdapter = new CommentAndPostRecyclerViewAdapter(ViewPostDetailActivity.this, mRetrofit,
mOauthRetrofit, mRedditDataRoomDatabase, mGlide, mAccessToken, mAccountName, mPost, mOauthRetrofit, mRedditDataRoomDatabase, mGlide, mAccessToken, mAccountName, mPost,
mLocale, mSingleCommentId, isSingleCommentThreadMode, mLoadSubredditIconAsyncTask, mLocale, mSingleCommentId, isSingleCommentThreadMode,
new CommentAndPostRecyclerViewAdapter.CommentRecyclerViewAdapterCallback() { new CommentAndPostRecyclerViewAdapter.CommentRecyclerViewAdapterCallback() {
@Override @Override
public void updatePost(Post post) { public void updatePost(Post post) {
@ -331,7 +330,7 @@ public class ViewPostDetailActivity extends AppCompatActivity implements FlairBo
mAdapter = new CommentAndPostRecyclerViewAdapter(ViewPostDetailActivity.this, mRetrofit, mAdapter = new CommentAndPostRecyclerViewAdapter(ViewPostDetailActivity.this, mRetrofit,
mOauthRetrofit, mRedditDataRoomDatabase, mGlide, mAccessToken, mAccountName, mPost, mOauthRetrofit, mRedditDataRoomDatabase, mGlide, mAccessToken, mAccountName, mPost,
mLocale, mSingleCommentId, isSingleCommentThreadMode, mLoadSubredditIconAsyncTask, mLocale, mSingleCommentId, isSingleCommentThreadMode,
new CommentAndPostRecyclerViewAdapter.CommentRecyclerViewAdapterCallback() { new CommentAndPostRecyclerViewAdapter.CommentRecyclerViewAdapterCallback() {
@Override @Override
public void updatePost(Post post) { public void updatePost(Post post) {
@ -817,9 +816,6 @@ public class ViewPostDetailActivity extends AppCompatActivity implements FlairBo
EventBus.getDefault().unregister(this); EventBus.getDefault().unregister(this);
super.onDestroy(); super.onDestroy();
Bridge.clear(this); Bridge.clear(this);
if(mLoadSubredditIconAsyncTask != null) {
mLoadSubredditIconAsyncTask.cancel(true);
}
} }
@Override @Override

View File

@ -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>

View File

@ -90,6 +90,32 @@
</LinearLayout> </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 <LinearLayout
android:id="@+id/upvoted_linear_layout_main_activity" android:id="@+id/upvoted_linear_layout_main_activity"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -47,4 +47,6 @@
<color name="cardViewBackgroundColor">#FFFFFF</color> <color name="cardViewBackgroundColor">#FFFFFF</color>
<color name="singleCommentThreadBackgroundColor">#B3E5F9</color> <color name="singleCommentThreadBackgroundColor">#B3E5F9</color>
<color name="unreadMessageBackgroundColor">#B3E5F9</color>
</resources> </resources>

View File

@ -13,6 +13,7 @@
<string name="search_subreddits_activity_label">Subreddits</string> <string name="search_subreddits_activity_label">Subreddits</string>
<string name="edit_post_activity_label">Edit Post</string> <string name="edit_post_activity_label">Edit Post</string>
<string name="edit_comment_activity_label">Edit Comment</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_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close 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_users">No users found</string>
<string name="no_storage_permission">No storage permission to save this file</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="retry">Retry</string>
<string name="comments">Comments</string> <string name="comments">Comments</string>
<string name="no_comments_yet">No comments yet. Write a comment?</string> <string name="no_comments_yet">No comments yet. Write a comment?</string>
<string name="vote_failed">Vote failed</string> <string name="vote_failed">Vote failed</string>
<string name="refresh_post_failed">Error refreshing the post</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="nsfw">NSFW</string>
<string name="karma_info">Karma: %1$d</string> <string name="karma_info">Karma: %1$d</string>
<string name="profile">Profile</string> <string name="profile">Profile</string>
<string name="following">Following</string> <string name="following">Following</string>
<string name="subscriptions">Subscriptions</string> <string name="subscriptions">Subscriptions</string>
<string name="inbox">Inbox</string>
<string name="upvoted">Upvoted</string> <string name="upvoted">Upvoted</string>
<string name="downvoted">Downvoted</string> <string name="downvoted">Downvoted</string>
<string name="hidden">Hidden</string> <string name="hidden">Hidden</string>