package org.wikipedia.readinglist; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; import android.support.v7.app.AppCompatActivity; import android.support.v7.view.ActionMode; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import com.squareup.otto.Subscribe; import org.wikipedia.Constants; import org.wikipedia.R; import org.wikipedia.WikipediaApp; import org.wikipedia.analytics.ReadingListsFunnel; import org.wikipedia.concurrency.CallbackTask; import org.wikipedia.history.SearchActionModeCallback; import org.wikipedia.readinglist.sync.ReadingListSyncEvent; import org.wikipedia.readinglist.sync.ReadingListSynchronizer; import org.wikipedia.settings.Prefs; import org.wikipedia.util.FeedbackUtil; import org.wikipedia.util.ResourceUtil; import org.wikipedia.views.DrawableItemDecoration; import org.wikipedia.views.SearchEmptyView; import org.wikipedia.views.TextInputDialog; import java.util.List; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.Unbinder; public class ReadingListsFragment extends Fragment { private Unbinder unbinder; @BindView(R.id.reading_list_list) RecyclerView readingListView; @BindView(R.id.empty_container) View emptyContainer; @BindView(R.id.search_empty_view) SearchEmptyView searchEmptyView; private ReadingLists readingLists = new ReadingLists(); private ReadingListsFunnel funnel = new ReadingListsFunnel(); private EventBusMethods eventBusMethods = new EventBusMethods(); private ReadingListAdapter adapter = new ReadingListAdapter(); private ReadingListItemCallback listItemCallback = new ReadingListItemCallback(); private ReadingListsSearchCallback searchActionModeCallback = new ReadingListsSearchCallback(); @Nullable private ActionMode actionMode; @NonNull public static ReadingListsFragment newInstance() { return new ReadingListsFragment(); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); } @Override public void onDestroy() { super.onDestroy(); WikipediaApp.getInstance().getRefWatcher().watch(this); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_reading_lists, container, false); unbinder = ButterKnife.bind(this, view); searchEmptyView.setEmptyText(R.string.search_reading_lists_no_results); readingListView.setLayoutManager(new LinearLayoutManager(getContext())); readingListView.setAdapter(adapter); readingListView.addItemDecoration(new DrawableItemDecoration(getContext(), ResourceUtil.getThemedAttributeId(getContext(), R.attr.list_separator_drawable), true)); WikipediaApp.getInstance().getBus().register(eventBusMethods); updateLists(); return view; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setHasOptionsMenu(true); } @Override public void onDestroyView() { WikipediaApp.getInstance().getBus().unregister(eventBusMethods); readingListView.setAdapter(null); unbinder.unbind(); unbinder = null; super.onDestroyView(); } @Override public void onResume() { super.onResume(); updateLists(); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.menu_reading_lists, menu); } @Override public void onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); MenuItem sortByNameItem = menu.findItem(R.id.menu_sort_by_name); MenuItem sortByRecentItem = menu.findItem(R.id.menu_sort_by_recent); int sortMode = Prefs.getReadingListSortMode(ReadingLists.SORT_BY_NAME_ASC); sortByNameItem.setTitle(sortMode == ReadingLists.SORT_BY_NAME_ASC ? R.string.reading_list_sort_by_name_desc : R.string.reading_list_sort_by_name); sortByRecentItem.setTitle(sortMode == ReadingLists.SORT_BY_RECENT_DESC ? R.string.reading_list_sort_by_recent_desc : R.string.reading_list_sort_by_recent); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_sort_by_name: setSortMode(ReadingLists.SORT_BY_NAME_ASC, ReadingLists.SORT_BY_NAME_DESC); return true; case R.id.menu_sort_by_recent: setSortMode(ReadingLists.SORT_BY_RECENT_DESC, ReadingLists.SORT_BY_RECENT_ASC); return true; case R.id.menu_search_lists: ((AppCompatActivity) getActivity()) .startSupportActionMode(searchActionModeCallback); return true; default: return super.onOptionsItemSelected(item); } } @Override public void setUserVisibleHint(boolean visible) { super.setUserVisibleHint(visible); if (!isAdded()) { return; } if (visible) { updateLists(); } else if (actionMode != null) { actionMode.finish(); } } private void updateLists() { updateLists(null); } private void updateLists(@Nullable final String searchQuery) { ReadingList.DAO.queryMruLists(searchQuery, new CallbackTask.DefaultCallback<List<ReadingList>>() { @Override public void success(List<ReadingList> rows) { if (getActivity() == null) { return; } readingLists.set(rows); sortLists(); updateEmptyState(searchQuery); maybeDeleteListFromIntent(); } }); } private void updateEmptyState(@Nullable String searchQuery) { if (TextUtils.isEmpty(searchQuery)) { searchEmptyView.setVisibility(View.GONE); emptyContainer.setVisibility(readingLists.isEmpty() ? View.VISIBLE : View.GONE); } else { searchEmptyView.setVisibility(readingLists.isEmpty() ? View.VISIBLE : View.GONE); emptyContainer.setVisibility(View.GONE); } } private class ReadingListItemHolder extends RecyclerView.ViewHolder { private ReadingListItemView itemView; ReadingListItemHolder(ReadingListItemView itemView) { super(itemView); this.itemView = itemView; } public void bindItem(ReadingList readingList) { itemView.setReadingList(readingList); } public ReadingListItemView getView() { return itemView; } } private final class ReadingListAdapter extends RecyclerView.Adapter<ReadingListItemHolder> { @Override public int getItemCount() { return readingLists.size(); } @Override public ReadingListItemHolder onCreateViewHolder(ViewGroup parent, int pos) { ReadingListItemView view = new ReadingListItemView(getContext()); return new ReadingListItemHolder(view); } @Override public void onBindViewHolder(ReadingListItemHolder holder, int pos) { holder.bindItem(readingLists.get(pos)); } @Override public void onViewAttachedToWindow(ReadingListItemHolder holder) { super.onViewAttachedToWindow(holder); holder.getView().setCallback(listItemCallback); } @Override public void onViewDetachedFromWindow(ReadingListItemHolder holder) { holder.getView().setCallback(null); super.onViewDetachedFromWindow(holder); } } private class ReadingListItemCallback implements ReadingListItemView.Callback { @Override public void onClick(@NonNull ReadingList readingList) { if (actionMode != null) { actionMode.finish(); } startActivity(ReadingListActivity.newIntent(getContext(), readingList)); } @Override public void onRename(@NonNull final ReadingList readingList) { ReadingListTitleDialog.readingListTitleDialog(getContext(), readingList.getTitle(), readingLists.getTitlesExcept(readingList.getTitle()), new ReadingListTitleDialog.Callback() { @Override public void onSuccess(@NonNull CharSequence text) { ReadingList.DAO.renameAndSaveListInfo(readingList, text.toString()); updateLists(); ReadingListSynchronizer.instance().bumpRevAndSync(); funnel.logModifyList(readingList, readingLists.size()); } }).show(); } @Override public void onEditDescription(@NonNull final ReadingList readingList) { TextInputDialog.newInstance(getContext(), new TextInputDialog.DefaultCallback() { @Override public void onShow(@NonNull TextInputDialog dialog) { dialog.setHint(R.string.reading_list_description_hint); dialog.setText(readingList.getDescription()); } @Override public void onSuccess(@NonNull CharSequence text) { readingList.setDescription(text.toString()); ReadingList.DAO.saveListInfo(readingList); updateLists(); ReadingListSynchronizer.instance().bumpRevAndSync(); funnel.logModifyList(readingList, readingLists.size()); } }).show(); } @Override public void onDelete(@NonNull ReadingList readingList) { deleteList(readingList); } } private void maybeDeleteListFromIntent() { if (getActivity().getIntent().hasExtra(Constants.INTENT_EXTRA_DELETE_READING_LIST)) { String titleToDelete = getActivity().getIntent() .getStringExtra(Constants.INTENT_EXTRA_DELETE_READING_LIST); getActivity().getIntent().removeExtra(Constants.INTENT_EXTRA_DELETE_READING_LIST); deleteList(readingLists.get(titleToDelete)); } } private void deleteList(@Nullable ReadingList readingList) { if (readingList != null) { showDeleteListUndoSnackbar(readingList); ReadingList.DAO.removeList(readingList); ReadingListSynchronizer.instance().bumpRevAndSync(); funnel.logDeleteList(readingList, readingLists.size()); updateLists(); } } private void showDeleteListUndoSnackbar(final ReadingList readingList) { Snackbar snackbar = FeedbackUtil.makeSnackbar(getActivity(), String.format(getString(R.string.reading_list_deleted), readingList.getTitle()), FeedbackUtil.LENGTH_DEFAULT); snackbar.setAction(R.string.reading_list_item_delete_undo, new View.OnClickListener() { @Override public void onClick(View v) { ReadingList.DAO.addList(readingList); ReadingListSynchronizer.instance().bumpRevAndSync(); updateLists(); } }); snackbar.show(); } private void setSortMode(int sortModeAsc, int sortModeDesc) { int sortMode = Prefs.getReadingListSortMode(ReadingLists.SORT_BY_NAME_ASC); if (sortMode != sortModeAsc) { sortMode = sortModeAsc; } else { sortMode = sortModeDesc; } Prefs.setReadingListSortMode(sortMode); sortLists(); getActivity().supportInvalidateOptionsMenu(); } private void sortLists() { readingLists.sort(Prefs.getReadingListSortMode(ReadingLists.SORT_BY_NAME_ASC)); adapter.notifyDataSetChanged(); } private class ReadingListsSearchCallback extends SearchActionModeCallback { @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { actionMode = mode; return super.onCreateActionMode(mode, menu); } @Override protected void onQueryChange(String s) { updateLists(s); } @Override public void onDestroyActionMode(ActionMode mode) { super.onDestroyActionMode(mode); actionMode = null; updateLists(); } @Override protected String getSearchHintString() { return getContext().getResources().getString(R.string.search_hint_search_my_lists); } } private class EventBusMethods { @Subscribe public void on(ReadingListSyncEvent event) { readingListView.post(new Runnable() { @Override public void run() { if (isAdded()) { updateLists(); } } }); } } }