package org.wordpress.android.ui.notifications;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.NonNull;
import android.support.v13.app.FragmentStatePagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.ProgressBar;
import org.wordpress.android.R;
import org.wordpress.android.WordPress;
import org.wordpress.android.analytics.AnalyticsTracker;
import org.wordpress.android.datasets.NotificationsTable;
import org.wordpress.android.fluxc.model.CommentStatus;
import org.wordpress.android.fluxc.model.SiteModel;
import org.wordpress.android.fluxc.store.AccountStore;
import org.wordpress.android.fluxc.store.SiteStore;
import org.wordpress.android.models.Note;
import org.wordpress.android.push.GCMMessageService;
import org.wordpress.android.ui.ActivityLauncher;
import org.wordpress.android.ui.WPWebViewActivity;
import org.wordpress.android.ui.comments.CommentActions;
import org.wordpress.android.ui.comments.CommentDetailFragment;
import org.wordpress.android.ui.notifications.adapters.NotesAdapter;
import org.wordpress.android.ui.notifications.blocks.NoteBlockRangeType;
import org.wordpress.android.ui.notifications.services.NotificationsUpdateService;
import org.wordpress.android.ui.notifications.utils.NotificationsActions;
import org.wordpress.android.ui.notifications.utils.NotificationsUtils;
import org.wordpress.android.ui.prefs.AppPrefs;
import org.wordpress.android.ui.reader.ReaderActivityLauncher;
import org.wordpress.android.ui.reader.ReaderPostDetailFragment;
import org.wordpress.android.ui.stats.StatsAbstractFragment;
import org.wordpress.android.ui.stats.StatsTimeframe;
import org.wordpress.android.ui.stats.StatsViewAllActivity;
import org.wordpress.android.ui.stats.StatsViewType;
import org.wordpress.android.util.AppLog;
import org.wordpress.android.util.StringUtils;
import org.wordpress.android.util.ToastUtils;
import org.wordpress.android.widgets.WPSwipeSnackbar;
import org.wordpress.android.widgets.WPViewPager;
import org.wordpress.android.widgets.WPViewPagerTransformer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import javax.inject.Inject;
import de.greenrobot.event.EventBus;
import static org.wordpress.android.models.Note.NOTE_COMMENT_LIKE_TYPE;
import static org.wordpress.android.models.Note.NOTE_COMMENT_TYPE;
import static org.wordpress.android.models.Note.NOTE_FOLLOW_TYPE;
import static org.wordpress.android.models.Note.NOTE_LIKE_TYPE;
public class NotificationsDetailActivity extends AppCompatActivity implements
CommentActions.OnNoteCommentActionListener {
private static final String ARG_TITLE = "activityTitle";
private static final String DOMAIN_WPCOM = "wordpress.com";
@Inject AccountStore mAccountStore;
@Inject SiteStore mSiteStore;
private String mNoteId;
private WPViewPager mViewPager;
private ViewPager.OnPageChangeListener mOnPageChangeListener;
private NotificationDetailFragmentAdapter mAdapter;
private boolean mIsReaderSwipeToNavigateShown;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((WordPress) getApplication()).component().inject(this);
AppLog.i(AppLog.T.NOTIFS, "Creating NotificationsDetailActivity");
setContentView(R.layout.notifications_detail_activity);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
if (savedInstanceState == null) {
mNoteId = getIntent().getStringExtra(NotificationsListFragment.NOTE_ID_EXTRA);
} else {
if (savedInstanceState.containsKey(ARG_TITLE) && getSupportActionBar() != null) {
getSupportActionBar().setTitle(StringUtils.notNullStr(savedInstanceState.getString(ARG_TITLE)));
}
mNoteId = savedInstanceState.getString(NotificationsListFragment.NOTE_ID_EXTRA);
}
//set up the viewpager and adapter for lateral navigation
mViewPager = (WPViewPager) findViewById(R.id.viewpager);
mViewPager.setPageTransformer(false,
new WPViewPagerTransformer(WPViewPagerTransformer.TransformType.SLIDE_OVER));
mIsReaderSwipeToNavigateShown = AppPrefs.isReaderSwipeToNavigateShown();
Note note = NotificationsTable.getNoteById(mNoteId);
updateUIAndNote(note == null);
// Hide the keyboard, unless we arrived here from the 'Reply' action in a push notification
if (!getIntent().getBooleanExtra(NotificationsListFragment.NOTE_INSTANT_REPLY_EXTRA, false)) {
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
}
}
private void updateUIAndNote(boolean doRefresh) {
if (mNoteId == null) {
showErrorToastAndFinish();
return;
}
if (doRefresh) {
setProgressVisible(true);
// here start the service and wait for it
NotificationsUpdateService.startService(this, mNoteId);
return;
}
Note note = NotificationsTable.getNoteById(mNoteId);
if (note == null) {
// no note found
showErrorToastAndFinish();
return;
}
NotesAdapter.FILTERS filter = NotesAdapter.FILTERS.FILTER_ALL;
if (getIntent().hasExtra(NotificationsListFragment.NOTE_CURRENT_LIST_FILTER_EXTRA)) {
filter = (NotesAdapter.FILTERS) getIntent().getSerializableExtra(NotificationsListFragment.NOTE_CURRENT_LIST_FILTER_EXTRA);
}
mAdapter = buildNoteListAdapterAndSetPosition(note, filter);
resetOnPageChangeListener();
//set title
setActionBarTitleForNote(note);
markNoteAsRead(note);
// If `note.getTimestamp()` is not the most recent seen note, the server will discard the value.
NotificationsActions.updateSeenTimestamp(note);
// analytics tracking
Map<String, String> properties = new HashMap<>();
properties.put("notification_type", note.getType());
AnalyticsTracker.track(AnalyticsTracker.Stat.NOTIFICATIONS_OPENED_NOTIFICATION_DETAILS, properties);
setProgressVisible(false);
}
private void resetOnPageChangeListener() {
if (mOnPageChangeListener != null) {
mViewPager.removeOnPageChangeListener(mOnPageChangeListener);
} else {
mOnPageChangeListener = new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
AnalyticsTracker.track(AnalyticsTracker.Stat.NOTIFICATION_SWIPE_PAGE_CHANGED);
if (!mIsReaderSwipeToNavigateShown) {
AppPrefs.setNotificationsSwipeToNavigateShown(true);
}
//change the action bar title for the current note
Note currentNote = mAdapter.getNoteAtPosition(position);
if (currentNote != null) {
setActionBarTitleForNote(currentNote);
markNoteAsRead(currentNote);
}
}
@Override
public void onPageScrollStateChanged(int state) {
}
};
}
mViewPager.addOnPageChangeListener(mOnPageChangeListener);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
if (getSupportActionBar() != null && getSupportActionBar().getTitle() != null) {
outState.putString(ARG_TITLE, getSupportActionBar().getTitle().toString());
}
outState.putString(NotificationsListFragment.NOTE_ID_EXTRA, mNoteId);
super.onSaveInstanceState(outState);
}
@Override
protected void onStart() {
super.onStart();
EventBus.getDefault().registerSticky(this);
// If the user hasn't used swipe yet and if the adapter is initialised and have at least 2 notifications,
// show a hint to promote swipe usage on the ViewPager
if (!AppPrefs.isNotificationsSwipeToNavigateShown() && mAdapter != null && mAdapter.getCount() > 1) {
WPSwipeSnackbar.show(mViewPager);
}
}
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
private void showErrorToastAndFinish() {
AppLog.e(AppLog.T.NOTIFS, "Note could not be found.");
ToastUtils.showToast(this, R.string.error_notification_open);
finish();
}
private void markNoteAsRead(Note note) {
GCMMessageService.removeNotificationWithNoteIdFromSystemBar(this, note.getId());
// mark the note as read if it's unread
if (note.isUnread()) {
NotificationsActions.markNoteAsRead(note);
note.setRead();
NotificationsTable.saveNote(note);
EventBus.getDefault().post(new NotificationEvents.NotificationsChanged());
}
}
private void setActionBarTitleForNote(Note note) {
if (getSupportActionBar() != null) {
String title = note.getTitle();
if (TextUtils.isEmpty(title)) {
//set a default title if title is not set within the note
switch (note.getType()) {
case NOTE_FOLLOW_TYPE:
title = getString(R.string.follows);
break;
case NOTE_COMMENT_LIKE_TYPE:
title = getString(R.string.comment_likes);
break;
case NOTE_LIKE_TYPE:
title = getString(R.string.like);
break;
case NOTE_COMMENT_TYPE:
title = getString(R.string.comment);
break;
}
}
// Force change the Action Bar title for 'new_post' notifications.
if (note.isNewPostType()) {
title = getString(R.string.reader_title_post_detail);
}
getSupportActionBar().setTitle(title);
}
}
private NotificationDetailFragmentAdapter buildNoteListAdapterAndSetPosition(Note note,
NotesAdapter.FILTERS filter) {
NotificationDetailFragmentAdapter adapter;
ArrayList<Note> notes = NotificationsTable.getLatestNotes();
ArrayList<Note> filteredNotes = new ArrayList<>();
//apply filter to the list so we show the same items that the list show vertically, but horizontally
NotesAdapter.buildFilteredNotesList(filteredNotes, notes, filter);
adapter = new NotificationDetailFragmentAdapter(getFragmentManager(), filteredNotes);
mViewPager.setAdapter(adapter);
mViewPager.setCurrentItem(NotificationsUtils.findNoteInNoteArray(filteredNotes, note.getId()));
return adapter;
}
/**
* Tries to pick the correct fragment detail type for a given note
* Defaults to NotificationDetailListFragment
*/
private Fragment getDetailFragmentForNote(Note note, int idForFragmentContainer) {
if (note == null)
return null;
Fragment fragment;
if (note.isCommentType()) {
// show comment detail for comment notifications
boolean isInstantReply = getIntent().getBooleanExtra(NotificationsListFragment.NOTE_INSTANT_REPLY_EXTRA,
false);
fragment = CommentDetailFragment.newInstance(note.getId(),
getIntent().getStringExtra(NotificationsListFragment.NOTE_PREFILLED_REPLY_EXTRA),
idForFragmentContainer);
if (isInstantReply) {
((CommentDetailFragment) fragment).enableShouldFocusReplyField();
}
} else if (note.isAutomattcherType()) {
// show reader post detail for automattchers about posts - note that comment
// automattchers are handled by note.isCommentType() above
boolean isPost = (note.getSiteId() != 0 && note.getPostId() != 0 && note.getCommentId() == 0);
if (isPost) {
fragment = ReaderPostDetailFragment.newInstance(note.getSiteId(), note.getPostId());
} else {
fragment = NotificationsDetailListFragment.newInstance(note.getId());
}
} else if (note.isNewPostType()) {
fragment = ReaderPostDetailFragment.newInstance(note.getSiteId(), note.getPostId());
} else {
fragment = NotificationsDetailListFragment.newInstance(note.getId());
}
return fragment;
}
public void showBlogPreviewActivity(long siteId) {
if (isFinishing()) return;
ReaderActivityLauncher.showReaderBlogPreview(this, siteId);
}
public void showPostActivity(long siteId, long postId) {
if (isFinishing()) return;
ReaderActivityLauncher.showReaderPostDetail(this, siteId, postId);
}
public void showStatsActivityForSite(long siteId, NoteBlockRangeType rangeType) {
SiteModel site = mSiteStore.getSiteBySiteId(siteId);
if (site == null) {
// One way the site can be null: new site created, receive a notification from this site,
// but the site list is not yet updated in the app.
ToastUtils.showToast(this, R.string.blog_not_found);
return;
}
showStatsActivityForSite(site, rangeType);
}
private void showStatsActivityForSite(@NonNull SiteModel site, NoteBlockRangeType rangeType) {
if (isFinishing()) return;
if (rangeType == NoteBlockRangeType.FOLLOW) {
Intent intent = new Intent(this, StatsViewAllActivity.class);
intent.putExtra(StatsAbstractFragment.ARGS_VIEW_TYPE, StatsViewType.FOLLOWERS);
intent.putExtra(StatsAbstractFragment.ARGS_TIMEFRAME, StatsTimeframe.DAY);
intent.putExtra(StatsAbstractFragment.ARGS_SELECTED_DATE, "");
intent.putExtra(WordPress.SITE, site);
intent.putExtra(StatsViewAllActivity.ARG_STATS_VIEW_ALL_TITLE, getString(R.string.stats_view_followers));
startActivity(intent);
} else {
ActivityLauncher.viewBlogStats(this, site);
}
}
public void showWebViewActivityForUrl(String url) {
if (isFinishing() || url == null) return;
if (url.contains(DOMAIN_WPCOM)) {
WPWebViewActivity.openUrlByUsingGlobalWPCOMCredentials(this, url);
} else {
WPWebViewActivity.openURL(this, url);
}
}
public void showReaderPostLikeUsers(long blogId, long postId) {
if (isFinishing()) return;
ReaderActivityLauncher.showReaderLikingUsers(this, blogId, postId);
}
public void showReaderCommentsList(long siteId, long postId, long commentId) {
if (isFinishing()) return;
ReaderActivityLauncher.showReaderComments(this, siteId, postId, commentId);
}
private void setProgressVisible(boolean visible) {
final ProgressBar progress =
(ProgressBar) findViewById(R.id.progress_loading);
if (progress != null) {
progress.setVisibility(visible ? View.VISIBLE : View.GONE);
}
}
@Override
public void onModerateCommentForNote(Note note, CommentStatus newStatus) {
Intent resultIntent = new Intent();
resultIntent.putExtra(NotificationsListFragment.NOTE_MODERATE_ID_EXTRA, note.getId());
resultIntent.putExtra(NotificationsListFragment.NOTE_MODERATE_STATUS_EXTRA, newStatus.toString());
setResult(RESULT_OK, resultIntent);
finish();
}
@SuppressWarnings("unused")
public void onEventMainThread(final NotificationEvents.NotificationsRefreshCompleted event) {
setProgressVisible(false);
updateUIAndNote(false);
}
@SuppressWarnings("unused")
public void onEventMainThread(NotificationEvents.NotificationsRefreshError error) {
setProgressVisible(false);
if (mNoteId == null) {
showErrorToastAndFinish();
return;
}
Note note = NotificationsTable.getNoteById(mNoteId);
if (note == null) {
// no note found
showErrorToastAndFinish();
return;
}
}
private class NotificationDetailFragmentAdapter extends FragmentStatePagerAdapter {
final private ArrayList<Note> mNoteList;
NotificationDetailFragmentAdapter(FragmentManager fm, ArrayList<Note> notes) {
super(fm);
mNoteList = (ArrayList<Note>)notes.clone();
}
@Override
public Fragment getItem(int position) {
return getDetailFragmentForNote(mNoteList.get(position), position);
}
@Override
public int getCount() {
return mNoteList.size();
}
@Override
public void restoreState(Parcelable state, ClassLoader loader) {
// work around "Fragment no longer exists for key" Android bug
// by catching the IllegalStateException
// https://code.google.com/p/android/issues/detail?id=42601
try {
AppLog.d(AppLog.T.NOTIFS, "notifications pager > adapter restoreState");
super.restoreState(state, loader);
} catch (IllegalStateException e) {
AppLog.e(AppLog.T.NOTIFS, e);
}
}
@Override
public Parcelable saveState() {
AppLog.d(AppLog.T.NOTIFS, "notifications pager > adapter saveState");
Bundle bundle = (Bundle) super.saveState();
if (bundle == null) {
bundle = new Bundle();
}
// This is a possible solution to https://github.com/wordpress-mobile/WordPress-Android/issues/5456
// See https://issuetracker.google.com/issues/37103380#comment77 for more details
bundle.putParcelableArray("states", null);
return bundle;
}
boolean isValidPosition(int position) {
return (position >= 0 && position < getCount());
}
public Note getNoteAtPosition(int position){
if (isValidPosition(position))
return mNoteList.get(position);
else
return null;
}
}
}