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

Binary file not shown.

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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.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);

View File

@ -104,7 +104,7 @@ class Message {
return formattedTime;
}
public boolean isWasComment() {
public boolean 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);
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;
}
}

View File

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

View File

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

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

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

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="singleCommentThreadBackgroundColor">#B3E5F9</color>
<color name="unreadMessageBackgroundColor">#B3E5F9</color>
</resources>

View File

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