/*******************************************************************************
* This file is part of RedReader.
*
* RedReader is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RedReader is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RedReader. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package org.quantumbadger.redreader.activities;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.preference.PreferenceManager;
import android.support.v7.widget.RecyclerView;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import org.quantumbadger.redreader.R;
import org.quantumbadger.redreader.account.RedditAccount;
import org.quantumbadger.redreader.account.RedditAccountManager;
import org.quantumbadger.redreader.adapters.GroupedRecyclerViewAdapter;
import org.quantumbadger.redreader.cache.CacheManager;
import org.quantumbadger.redreader.cache.CacheRequest;
import org.quantumbadger.redreader.cache.downloadstrategy.DownloadStrategyAlways;
import org.quantumbadger.redreader.common.AndroidApi;
import org.quantumbadger.redreader.common.Constants;
import org.quantumbadger.redreader.common.General;
import org.quantumbadger.redreader.common.PrefsUtility;
import org.quantumbadger.redreader.common.RRError;
import org.quantumbadger.redreader.common.RRThemeAttributes;
import org.quantumbadger.redreader.common.RRTime;
import org.quantumbadger.redreader.jsonwrap.JsonBufferedArray;
import org.quantumbadger.redreader.jsonwrap.JsonBufferedObject;
import org.quantumbadger.redreader.jsonwrap.JsonValue;
import org.quantumbadger.redreader.reddit.APIResponseHandler;
import org.quantumbadger.redreader.reddit.RedditAPI;
import org.quantumbadger.redreader.reddit.prepared.RedditChangeDataManager;
import org.quantumbadger.redreader.reddit.prepared.RedditParsedComment;
import org.quantumbadger.redreader.reddit.prepared.RedditPreparedMessage;
import org.quantumbadger.redreader.reddit.prepared.RedditRenderableComment;
import org.quantumbadger.redreader.reddit.prepared.RedditRenderableInboxItem;
import org.quantumbadger.redreader.reddit.things.RedditComment;
import org.quantumbadger.redreader.reddit.things.RedditMessage;
import org.quantumbadger.redreader.reddit.things.RedditThing;
import org.quantumbadger.redreader.views.RedditInboxItemView;
import org.quantumbadger.redreader.views.ScrollbarRecyclerViewManager;
import org.quantumbadger.redreader.views.liststatus.ErrorView;
import org.quantumbadger.redreader.views.liststatus.LoadingView;
import java.net.URI;
import java.util.UUID;
public final class InboxListingActivity extends BaseActivity {
private static final int OPTIONS_MENU_MARK_ALL_AS_READ = 0;
private static final int OPTIONS_MENU_SHOW_UNREAD_ONLY = 1;
private static final String PREF_ONLY_UNREAD = "inbox_only_show_unread";
private GroupedRecyclerViewAdapter adapter;
private LoadingView loadingView;
private LinearLayout notifications;
private CacheRequest request;
private boolean isModmail = false;
private boolean mOnlyShowUnread;
private RRThemeAttributes mTheme;
private RedditChangeDataManager mChangeDataManager;
private final Handler itemHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(final Message msg) {
adapter.appendToGroup(0, (GroupedRecyclerViewAdapter.Item)msg.obj);
}
};
private final class InboxItem extends GroupedRecyclerViewAdapter.Item {
private final int mListPosition;
private final RedditRenderableInboxItem mItem;
private InboxItem(int listPosition, RedditRenderableInboxItem item) {
this.mListPosition = listPosition;
this.mItem = item;
}
@Override
public Class getViewType() {
return RedditInboxItemView.class;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup) {
final RedditInboxItemView view = new RedditInboxItemView(InboxListingActivity.this, mTheme);
final RecyclerView.LayoutParams layoutParams
= new RecyclerView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
view.setLayoutParams(layoutParams);
return new RecyclerView.ViewHolder(view) {};
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder) {
((RedditInboxItemView)viewHolder.itemView).reset(
InboxListingActivity.this,
mChangeDataManager,
mTheme,
mItem,
mListPosition != 0);
}
@Override
public boolean isHidden() {
return false;
}
}
// TODO load more on scroll to bottom?
@Override
public void onCreate(Bundle savedInstanceState) {
PrefsUtility.applyTheme(this);
super.onCreate(savedInstanceState);
mTheme = new RRThemeAttributes(this);
mChangeDataManager = RedditChangeDataManager.getInstance(
RedditAccountManager.getInstance(this).getDefaultAccount());
final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
final String title;
isModmail = getIntent() != null && getIntent().getBooleanExtra("modmail", false);
mOnlyShowUnread = sharedPreferences.getBoolean(PREF_ONLY_UNREAD, false);
if(!isModmail) {
title = getString(R.string.mainmenu_inbox);
} else {
title = getString(R.string.mainmenu_modmail);
}
setTitle(title);
final LinearLayout outer = new LinearLayout(this);
outer.setOrientation(LinearLayout.VERTICAL);
loadingView = new LoadingView(this, getString(R.string.download_waiting), true, true);
notifications = new LinearLayout(this);
notifications.setOrientation(LinearLayout.VERTICAL);
notifications.addView(loadingView);
final ScrollbarRecyclerViewManager recyclerViewManager
= new ScrollbarRecyclerViewManager(this, null, false);
adapter = new GroupedRecyclerViewAdapter(1);
recyclerViewManager.getRecyclerView().setAdapter(adapter);
outer.addView(notifications);
outer.addView(recyclerViewManager.getOuterView());
makeFirstRequest(this);
setBaseActivityContentView(outer);
}
public void cancel() {
if(request != null) request.cancel();
}
private void makeFirstRequest(final Context context) {
final RedditAccount user = RedditAccountManager.getInstance(context).getDefaultAccount();
final CacheManager cm = CacheManager.getInstance(context);
final URI url;
if(!isModmail) {
if(mOnlyShowUnread) {
url = Constants.Reddit.getUri("/message/unread.json?mark=true&limit=100");
}else{
url = Constants.Reddit.getUri("/message/inbox.json?mark=true&limit=100");
}
} else {
url = Constants.Reddit.getUri("/message/moderator.json?limit=100");
}
// TODO parameterise limit
request = new CacheRequest(url, user, null, Constants.Priority.API_INBOX_LIST, 0,
DownloadStrategyAlways.INSTANCE, Constants.FileType.INBOX_LIST,
CacheRequest.DOWNLOAD_QUEUE_REDDIT_API, true, true, context) {
@Override
protected void onDownloadNecessary() {}
@Override
protected void onDownloadStarted() {}
@Override
protected void onCallbackException(final Throwable t) {
request = null;
BugReportActivity.handleGlobalError(context, t);
}
@Override
protected void onFailure(final @CacheRequest.RequestFailureType int type, final Throwable t, final Integer status, final String readableMessage) {
request = null;
if(loadingView != null) loadingView.setDone(R.string.download_failed);
final RRError error = General.getGeneralErrorForFailure(context, type, t, status, url.toString());
AndroidApi.UI_THREAD_HANDLER.post(new Runnable() {
@Override
public void run() {
notifications.addView(new ErrorView(InboxListingActivity.this, error));
}
});
if(t != null) t.printStackTrace();
}
@Override protected void onProgress(final boolean authorizationInProgress, final long bytesRead, final long totalBytes) {}
@Override
protected void onSuccess(final CacheManager.ReadableCacheFile cacheFile, final long timestamp, final UUID session, final boolean fromCache, final String mimetype) {
request = null;
}
@Override
public void onJsonParseStarted(final JsonValue value, final long timestamp, final UUID session, final boolean fromCache) {
if(loadingView != null) loadingView.setIndeterminate(R.string.download_downloading);
// TODO pref (currently 10 mins)
// TODO xml
if(fromCache && RRTime.since(timestamp) > 10 * 60 * 1000) {
AndroidApi.UI_THREAD_HANDLER.post(new Runnable() {
@Override
public void run() {
final TextView cacheNotif = new TextView(context);
cacheNotif.setText(context.getString(R.string.listing_cached, RRTime.formatDateTime(timestamp, context)));
final int paddingPx = General.dpToPixels(context, 6);
final int sidePaddingPx = General.dpToPixels(context, 10);
cacheNotif.setPadding(sidePaddingPx, paddingPx, sidePaddingPx, paddingPx);
cacheNotif.setTextSize(13f);
notifications.addView(cacheNotif);
adapter.notifyDataSetChanged();
}
});
}
// TODO {"error": 403} is received for unauthorized subreddits
try {
final JsonBufferedObject root = value.asObject();
final JsonBufferedObject data = root.getObject("data");
final JsonBufferedArray children = data.getArray("children");
int listPosition = 0;
for(JsonValue child : children) {
final RedditThing thing = child.asObject(RedditThing.class);
switch(thing.getKind()) {
case COMMENT:
final RedditComment comment = thing.asComment();
final RedditParsedComment parsedComment = new RedditParsedComment(comment);
final RedditRenderableComment renderableComment = new RedditRenderableComment(parsedComment, null, -100000, false);
itemHandler.sendMessage(General.handlerMessage(0, new InboxItem(listPosition, renderableComment)));
listPosition++;
break;
case MESSAGE:
final RedditPreparedMessage message = new RedditPreparedMessage(
InboxListingActivity.this, thing.asMessage(), timestamp);
itemHandler.sendMessage(General.handlerMessage(0, new InboxItem(listPosition, message)));
listPosition++;
if(message.src.replies != null && message.src.replies.getType() == JsonValue.TYPE_OBJECT) {
final JsonBufferedArray replies = message.src.replies.asObject().getObject("data").getArray("children");
for(JsonValue childMsgValue : replies) {
final RedditMessage childMsgRaw = childMsgValue.asObject(RedditThing.class).asMessage();
final RedditPreparedMessage childMsg = new RedditPreparedMessage(InboxListingActivity.this, childMsgRaw, timestamp);
itemHandler.sendMessage(General.handlerMessage(0, new InboxItem(listPosition, childMsg)));
listPosition++;
}
}
break;
default:
throw new RuntimeException("Unknown item in list.");
}
}
} catch (Throwable t) {
notifyFailure(CacheRequest.REQUEST_FAILURE_PARSE, t, null, "Parse failure");
return;
}
if(loadingView != null) loadingView.setDone(R.string.download_done);
}
};
cm.makeRequest(request);
}
@Override
public void onBackPressed() {
if(General.onBackPressed()) super.onBackPressed();
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
menu.add(0, OPTIONS_MENU_MARK_ALL_AS_READ, 0, R.string.mark_all_as_read);
menu.add(0, OPTIONS_MENU_SHOW_UNREAD_ONLY, 1, R.string.inbox_unread_only);
menu.getItem(1).setCheckable(true);
if(mOnlyShowUnread){
menu.getItem(1).setChecked(true);
}
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
switch(item.getItemId()) {
case OPTIONS_MENU_MARK_ALL_AS_READ:
RedditAPI.markAllAsRead(
CacheManager.getInstance(this),
new APIResponseHandler.ActionResponseHandler(this) {
@Override
protected void onSuccess() {
General.quickToast(context, R.string.mark_all_as_read_success);
}
@Override
protected void onCallbackException(final Throwable t) {
BugReportActivity.addGlobalError(new RRError("Mark all as Read failed", "Callback exception", t));
}
@Override
protected void onFailure(final @CacheRequest.RequestFailureType int type, final Throwable t, final Integer status, final String readableMessage) {
final RRError error = General.getGeneralErrorForFailure(context, type, t, status,
"Reddit API action: Mark all as Read");
AndroidApi.UI_THREAD_HANDLER.post(new Runnable() {
@Override
public void run() {
General.showResultDialog(InboxListingActivity.this, error);
}
});
}
@Override
protected void onFailure(final APIFailureType type) {
final RRError error = General.getGeneralErrorForFailure(context, type);
AndroidApi.UI_THREAD_HANDLER.post(new Runnable() {
@Override
public void run() {
General.showResultDialog(InboxListingActivity.this, error);
}
});
}
},
RedditAccountManager.getInstance(this).getDefaultAccount(),
this);
return true;
case OPTIONS_MENU_SHOW_UNREAD_ONLY: {
final boolean enabled = !item.isChecked();
item.setChecked(enabled);
mOnlyShowUnread = enabled;
PreferenceManager
.getDefaultSharedPreferences(this)
.edit()
.putBoolean(PREF_ONLY_UNREAD, enabled)
.apply();
General.recreateActivityNoAnimation(this);
return true;
}
default:
return super.onOptionsItemSelected(item);
}
}
}