aboutsummaryrefslogtreecommitdiff
path: root/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragment.java
diff options
context:
space:
mode:
Diffstat (limited to 'WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragment.java')
-rw-r--r--WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragment.java542
1 files changed, 542 insertions, 0 deletions
diff --git a/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragment.java b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragment.java
new file mode 100644
index 000000000..8ad48256c
--- /dev/null
+++ b/WordPress/src/main/java/org/wordpress/android/ui/notifications/NotificationsListFragment.java
@@ -0,0 +1,542 @@
+package org.wordpress.android.ui.notifications;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.NonNull;
+import android.support.annotation.StringRes;
+import android.support.design.widget.AppBarLayout;
+import android.support.v7.widget.LinearLayoutManager;
+import android.support.v7.widget.RecyclerView;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RadioGroup;
+import android.widget.TextView;
+
+import com.simperium.client.Bucket;
+import com.simperium.client.BucketObjectMissingException;
+
+import org.wordpress.android.GCMMessageService;
+import org.wordpress.android.R;
+import org.wordpress.android.WordPress;
+import org.wordpress.android.models.AccountHelper;
+import org.wordpress.android.models.Note;
+import org.wordpress.android.ui.ActivityLauncher;
+import org.wordpress.android.ui.RequestCodes;
+import org.wordpress.android.ui.main.WPMainActivity;
+import org.wordpress.android.ui.notifications.adapters.NotesAdapter;
+import org.wordpress.android.ui.notifications.utils.SimperiumUtils;
+import org.wordpress.android.util.AppLog;
+import org.wordpress.android.util.ToastUtils;
+import org.wordpress.android.util.ToastUtils.Duration;
+
+import de.greenrobot.event.EventBus;
+
+public class NotificationsListFragment extends Fragment
+ implements Bucket.Listener<Note>,
+ WPMainActivity.OnScrollToTopListener, RadioGroup.OnCheckedChangeListener {
+ public static final String NOTE_ID_EXTRA = "noteId";
+ public static final String NOTE_INSTANT_REPLY_EXTRA = "instantReply";
+ public static final String NOTE_INSTANT_LIKE_EXTRA = "instantLike";
+ public static final String NOTE_INSTANT_APPROVE_EXTRA = "instantApprove";
+ public static final String NOTE_MODERATE_ID_EXTRA = "moderateNoteId";
+ public static final String NOTE_MODERATE_STATUS_EXTRA = "moderateNoteStatus";
+
+ private static final String KEY_LIST_SCROLL_POSITION = "scrollPosition";
+
+ private NotesAdapter mNotesAdapter;
+ private LinearLayoutManager mLinearLayoutManager;
+ private RecyclerView mRecyclerView;
+ private ViewGroup mEmptyView;
+ private View mFilterView;
+ private RadioGroup mFilterRadioGroup;
+ private View mFilterDivider;
+
+ private int mRestoredScrollPosition;
+
+ private Bucket<Note> mBucket;
+
+ public static NotificationsListFragment newInstance() {
+ return new NotificationsListFragment();
+ }
+
+ /**
+ * For responding to tapping of notes
+ */
+ public interface OnNoteClickListener {
+ void onClickNote(String noteId);
+ }
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ View view = inflater.inflate(R.layout.notifications_fragment_notes_list, container, false);
+
+ mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view_notes);
+
+ mFilterRadioGroup = (RadioGroup)view.findViewById(R.id.notifications_radio_group);
+ mFilterRadioGroup.setOnCheckedChangeListener(this);
+ mFilterDivider = view.findViewById(R.id.notifications_filter_divider);
+ mEmptyView = (ViewGroup) view.findViewById(R.id.empty_view);
+ mFilterView = view.findViewById(R.id.notifications_filter);
+
+ mLinearLayoutManager = new LinearLayoutManager(getActivity());
+ mRecyclerView.setLayoutManager(mLinearLayoutManager);
+
+ return view;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+
+ if (savedInstanceState != null) {
+ setRestoredListPosition(savedInstanceState.getInt(KEY_LIST_SCROLL_POSITION, RecyclerView.NO_POSITION));
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ configureBucketAndAdapter();
+ refreshNotes();
+
+ // start listening to bucket change events
+ if (mBucket != null) {
+ mBucket.addListener(this);
+ }
+
+ // Removes app notifications from the system bar
+ new Thread(new Runnable() {
+ public void run() {
+ GCMMessageService.removeAllNotifications(getActivity());
+ }
+ }).start();
+
+ if (SimperiumUtils.isUserAuthorized()) {
+ SimperiumUtils.startBuckets();
+ AppLog.i(AppLog.T.NOTIFS, "Starting Simperium buckets");
+ }
+ }
+
+ @Override
+ public void onPause() {
+ // unregister the listener
+ if (mBucket != null) {
+ mBucket.removeListener(this);
+ }
+ super.onPause();
+ }
+
+ @Override
+ public void onDestroy() {
+ // Close Simperium cursor
+ if (mNotesAdapter != null) {
+ mNotesAdapter.closeCursor();
+ }
+
+ super.onDestroy();
+ }
+
+ // Sets up the notes bucket and list adapter
+ private void configureBucketAndAdapter() {
+ mBucket = SimperiumUtils.getNotesBucket();
+ if (mBucket != null) {
+ if (mNotesAdapter == null) {
+ mNotesAdapter = new NotesAdapter(getActivity(), mBucket);
+ mNotesAdapter.setOnNoteClickListener(mOnNoteClickListener);
+ }
+
+ if (mRecyclerView.getAdapter() == null) {
+ mRecyclerView.setAdapter(mNotesAdapter);
+ }
+ } else {
+ if (!AccountHelper.isSignedInWordPressDotCom()) {
+ // let user know that notifications require a wp.com account and enable sign-in
+ showEmptyView(R.string.notifications_account_required, 0, R.string.sign_in);
+ mFilterRadioGroup.setVisibility(View.GONE);
+ } else {
+ // failed for some other reason
+ showEmptyView(R.string.error_refresh_notifications);
+ }
+ }
+ }
+
+ private final OnNoteClickListener mOnNoteClickListener = new OnNoteClickListener() {
+ @Override
+ public void onClickNote(String noteId) {
+ if (!isAdded()) {
+ return;
+ }
+
+ if (TextUtils.isEmpty(noteId)) return;
+
+ // open the latest version of this note just in case it has changed - this can
+ // happen if the note was tapped from the list fragment after it was updated
+ // by another fragment (such as NotificationCommentLikeFragment)
+ openNoteForReply(getActivity(), noteId, false);
+ }
+ };
+
+ private static Intent getOpenNoteIntent(Activity activity,
+ String noteId) {
+ Intent detailIntent = new Intent(activity, NotificationsDetailActivity.class);
+ detailIntent.putExtra(NOTE_ID_EXTRA, noteId);
+ return detailIntent;
+ }
+
+ /**
+ * Open a note fragment based on the type of note
+ */
+ public static void openNoteForReply(Activity activity,
+ String noteId,
+ boolean shouldShowKeyboard) {
+ if (noteId == null || activity == null) {
+ return;
+ }
+
+ if (activity.isFinishing()) {
+ return;
+ }
+
+ Intent detailIntent = getOpenNoteIntent(activity, noteId);
+ detailIntent.putExtra(NOTE_INSTANT_REPLY_EXTRA, shouldShowKeyboard);
+ activity.startActivityForResult(detailIntent, RequestCodes.NOTE_DETAIL);
+ }
+
+ /**
+ * Open a note fragment based on the type of note, signaling to issue a like action immediately
+ */
+ public static void openNoteForLike(Activity activity,
+ String noteId) {
+ if (noteId == null || activity == null) {
+ return;
+ }
+
+ if (activity.isFinishing()) {
+ return;
+ }
+
+ Intent detailIntent = getOpenNoteIntent(activity, noteId);
+ detailIntent.putExtra(NOTE_INSTANT_LIKE_EXTRA, true);
+ activity.startActivityForResult(detailIntent, RequestCodes.NOTE_DETAIL);
+ }
+
+ /**
+ * Open a note fragment based on the type of note, signaling to issue a moderate:approve action immediately
+ */
+ public static void openNoteForApprove(Activity activity,
+ String noteId) {
+ if (noteId == null || activity == null) {
+ return;
+ }
+
+ if (activity.isFinishing()) {
+ return;
+ }
+
+ Intent detailIntent = getOpenNoteIntent(activity, noteId);
+ detailIntent.putExtra(NOTE_INSTANT_APPROVE_EXTRA, true);
+ activity.startActivityForResult(detailIntent, RequestCodes.NOTE_DETAIL);
+ }
+
+ private void setNoteIsHidden(String noteId, boolean isHidden) {
+ if (mNotesAdapter == null) return;
+
+ if (isHidden) {
+ mNotesAdapter.addHiddenNoteId(noteId);
+ } else {
+ // Scroll the row into view if it isn't visible so the animation can be seen
+ int notePosition = mNotesAdapter.getPositionForNote(noteId);
+ if (notePosition != RecyclerView.NO_POSITION &&
+ mLinearLayoutManager.findFirstCompletelyVisibleItemPosition() > notePosition) {
+ mLinearLayoutManager.scrollToPosition(notePosition);
+ }
+
+ mNotesAdapter.removeHiddenNoteId(noteId);
+ }
+ }
+
+ private void setNoteIsModerating(String noteId, boolean isModerating) {
+ if (mNotesAdapter == null) return;
+
+ if (isModerating) {
+ mNotesAdapter.addModeratingNoteId(noteId);
+ } else {
+ mNotesAdapter.removeModeratingNoteId(noteId);
+ }
+ }
+
+ private void showEmptyView(@StringRes int titleResId) {
+ showEmptyView(titleResId, 0, 0);
+ }
+
+ private void showEmptyView(@StringRes int titleResId, @StringRes int descriptionResId, @StringRes int buttonResId) {
+ if (isAdded() && mEmptyView != null) {
+ mEmptyView.setVisibility(View.VISIBLE);
+ mFilterDivider.setVisibility(View.GONE);
+ setFilterViewScrollable(false);
+ ((TextView) mEmptyView.findViewById(R.id.text_empty)).setText(titleResId);
+
+ TextView descriptionTextView = (TextView) mEmptyView.findViewById(R.id.text_empty_description);
+ if (descriptionResId > 0) {
+ descriptionTextView.setText(descriptionResId);
+ } else {
+ descriptionTextView.setVisibility(View.GONE);
+ }
+
+ TextView btnAction = (TextView)mEmptyView.findViewById(R.id.button_empty_action);
+ if (buttonResId > 0) {
+ btnAction.setText(buttonResId);
+ btnAction.setVisibility(View.VISIBLE);
+ } else {
+ btnAction.setVisibility(View.GONE);
+ }
+
+ btnAction.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ performActionForActiveFilter();
+ }
+ });
+ }
+ }
+
+ private void setFilterViewScrollable(boolean isScrollable) {
+ if (mFilterView != null && mFilterView.getLayoutParams() instanceof AppBarLayout.LayoutParams) {
+ AppBarLayout.LayoutParams params = (AppBarLayout.LayoutParams) mFilterView.getLayoutParams();
+ if (isScrollable) {
+ params.setScrollFlags(
+ AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL|
+ AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS
+ );
+ } else {
+ params.setScrollFlags(0);
+ }
+ }
+ }
+
+ private void hideEmptyView() {
+ if (isAdded() && mEmptyView != null) {
+ setFilterViewScrollable(true);
+ mEmptyView.setVisibility(View.GONE);
+ mFilterDivider.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private void refreshNotes() {
+ if (!isAdded() || mNotesAdapter == null) {
+ return;
+ }
+
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ // Filter the list according to the RadioGroup selection
+ int checkedId = mFilterRadioGroup.getCheckedRadioButtonId();
+ if (checkedId == R.id.notifications_filter_all) {
+ mNotesAdapter.queryNotes();
+ } else if (checkedId == R.id.notifications_filter_unread) {
+ mNotesAdapter.queryNotes(Note.Schema.UNREAD_INDEX, 1);
+ } else if (checkedId == R.id.notifications_filter_comments) {
+ mNotesAdapter.queryNotes(Note.Schema.TYPE_INDEX, Note.NOTE_COMMENT_TYPE);
+ } else if (checkedId == R.id.notifications_filter_follows) {
+ mNotesAdapter.queryNotes(Note.Schema.TYPE_INDEX, Note.NOTE_FOLLOW_TYPE);
+ } else if (checkedId == R.id.notifications_filter_likes) {
+ mNotesAdapter.queryNotes(Note.Schema.TYPE_INDEX, Note.NOTE_LIKE_TYPE);
+ } else {
+ mNotesAdapter.queryNotes();
+ }
+
+ restoreListScrollPosition();
+ if (mNotesAdapter.getCount() > 0) {
+ hideEmptyView();
+ } else {
+ showEmptyViewForCurrentFilter();
+ }
+ }
+ });
+ }
+
+ // Show different empty list message and action button based on the active filter
+ private void showEmptyViewForCurrentFilter() {
+ if (!AccountHelper.isSignedInWordPressDotCom()) return;
+
+ int i = mFilterRadioGroup.getCheckedRadioButtonId();
+ if (i == R.id.notifications_filter_all) {
+ showEmptyView(
+ R.string.notifications_empty_all,
+ R.string.notifications_empty_action_all,
+ R.string.notifications_empty_view_reader
+ );
+ } else if (i == R.id.notifications_filter_unread) {// User might not have a blog, if so just show the title
+ if (WordPress.getCurrentBlog() == null) {
+ showEmptyView(R.string.notifications_empty_unread);
+ } else {
+ showEmptyView(
+ R.string.notifications_empty_unread,
+ R.string.notifications_empty_action_unread,
+ R.string.new_post
+ );
+ }
+ } else if (i == R.id.notifications_filter_comments) {
+ showEmptyView(
+ R.string.notifications_empty_comments,
+ R.string.notifications_empty_action_comments,
+ R.string.notifications_empty_view_reader
+ );
+ } else if (i == R.id.notifications_filter_follows) {
+ showEmptyView(
+ R.string.notifications_empty_followers,
+ R.string.notifications_empty_action_followers_likes,
+ R.string.notifications_empty_view_reader
+ );
+ } else if (i == R.id.notifications_filter_likes) {
+ showEmptyView(
+ R.string.notifications_empty_likes,
+ R.string.notifications_empty_action_followers_likes,
+ R.string.notifications_empty_view_reader
+ );
+ } else {
+ showEmptyView(R.string.notifications_empty_list);
+ }
+ }
+
+ private void performActionForActiveFilter() {
+ if (mFilterRadioGroup == null || !isAdded()) return;
+
+ if (!AccountHelper.isSignedInWordPressDotCom()) {
+ ActivityLauncher.showSignInForResult(getActivity());
+ return;
+ }
+
+ int i = mFilterRadioGroup.getCheckedRadioButtonId();
+ if (i == R.id.notifications_filter_unread) {// Create a new post
+ ActivityLauncher.addNewBlogPostOrPageForResult(getActivity(), WordPress.getCurrentBlog(), false);
+ } else {// Switch to Reader tab
+ if (getActivity() instanceof WPMainActivity) {
+ ((WPMainActivity) getActivity()).setReaderTabActive();
+ }
+ }
+ }
+
+ private void restoreListScrollPosition() {
+ if (isAdded() && mRecyclerView != null && mRestoredScrollPosition != RecyclerView.NO_POSITION
+ && mRestoredScrollPosition < mNotesAdapter.getCount()) {
+ // Restore scroll position in list
+ mLinearLayoutManager.scrollToPosition(mRestoredScrollPosition);
+ mRestoredScrollPosition = RecyclerView.NO_POSITION;
+ }
+ }
+
+ @Override
+ public void onSaveInstanceState(@NonNull Bundle outState) {
+ if (outState.isEmpty()) {
+ outState.putBoolean("bug_19917_fix", true);
+ }
+
+ // Save list view scroll position
+ outState.putInt(KEY_LIST_SCROLL_POSITION, getScrollPosition());
+
+ super.onSaveInstanceState(outState);
+ }
+
+ private int getScrollPosition() {
+ if (!isAdded() || mRecyclerView == null) {
+ return RecyclerView.NO_POSITION;
+ }
+
+ return mLinearLayoutManager.findFirstVisibleItemPosition();
+ }
+
+ private void setRestoredListPosition(int listPosition) {
+ mRestoredScrollPosition = listPosition;
+ }
+
+ // Notification filter methods
+ @Override
+ public void onCheckedChanged(RadioGroup radioGroup, int checkedId) {
+ refreshNotes();
+ }
+
+ /**
+ * Simperium bucket listener methods
+ */
+ @Override
+ public void onSaveObject(Bucket<Note> bucket, final Note object) {
+ refreshNotes();
+ }
+
+ @Override
+ public void onDeleteObject(Bucket<Note> bucket, final Note object) {
+ refreshNotes();
+ }
+
+ @Override
+ public void onNetworkChange(Bucket<Note> bucket, final Bucket.ChangeType type, final String key) {
+ // Reset the note's local status when a remote change is received
+ if (type == Bucket.ChangeType.MODIFY) {
+ try {
+ Note note = bucket.get(key);
+ if (note.isCommentType()) {
+ note.setLocalStatus(null);
+ note.save();
+ }
+ } catch (BucketObjectMissingException e) {
+ AppLog.e(AppLog.T.NOTIFS, "Could not create note after receiving change.");
+ }
+ }
+
+ refreshNotes();
+ }
+
+ @Override
+ public void onBeforeUpdateObject(Bucket<Note> noteBucket, Note note) {
+ //noop
+ }
+
+ @Override
+ public void onScrollToTop() {
+ if (isAdded() && getScrollPosition() > 0) {
+ mLinearLayoutManager.smoothScrollToPosition(mRecyclerView, null, 0);
+ }
+ }
+
+ @Override
+ public void onStop() {
+ EventBus.getDefault().unregister(this);
+ super.onStop();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ EventBus.getDefault().registerSticky(this);
+ }
+
+ @SuppressWarnings("unused")
+ public void onEventMainThread(NotificationEvents.NoteModerationStatusChanged event) {
+ setNoteIsModerating(event.mNoteId, event.mIsModerating);
+
+ EventBus.getDefault().removeStickyEvent(NotificationEvents.NoteModerationStatusChanged.class);
+ }
+
+ @SuppressWarnings("unused")
+ public void onEventMainThread(NotificationEvents.NoteVisibilityChanged event) {
+ setNoteIsHidden(event.mNoteId, event.mIsHidden);
+
+ EventBus.getDefault().removeStickyEvent(NotificationEvents.NoteVisibilityChanged.class);
+ }
+
+ @SuppressWarnings("unused")
+ public void onEventMainThread(NotificationEvents.NoteModerationFailed event) {
+ if (isAdded()) {
+ ToastUtils.showToast(getActivity(), R.string.error_moderate_comment, Duration.LONG);
+ }
+
+ EventBus.getDefault().removeStickyEvent(NotificationEvents.NoteModerationFailed.class);
+ }
+}