package com.moez.QKSMS.ui.conversationlist; import android.app.LoaderManager; import android.content.Context; import android.content.CursorLoader; import android.content.Loader; import android.content.SharedPreferences; import android.database.Cursor; import android.graphics.PorterDuff; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; 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 android.widget.ImageView; import butterknife.Bind; import butterknife.ButterKnife; import com.melnykov.fab.FloatingActionButton; import com.moez.QKSMS.QKSMSApp; import com.moez.QKSMS.R; import com.moez.QKSMS.common.BlockedConversationHelper; import com.moez.QKSMS.common.DialogHelper; import com.moez.QKSMS.common.LiveViewManager; import com.moez.QKSMS.common.utils.ColorUtils; import com.moez.QKSMS.data.Contact; import com.moez.QKSMS.data.Conversation; import com.moez.QKSMS.data.ConversationLegacy; import com.moez.QKSMS.enums.QKPreference; import com.moez.QKSMS.transaction.SmsHelper; import com.moez.QKSMS.ui.MainActivity; import com.moez.QKSMS.ui.ThemeManager; import com.moez.QKSMS.ui.base.QKFragment; import com.moez.QKSMS.ui.base.RecyclerCursorAdapter; import com.moez.QKSMS.ui.compose.ComposeActivity; import com.moez.QKSMS.ui.dialog.conversationdetails.ConversationDetailsDialog; import com.moez.QKSMS.ui.messagelist.MessageListActivity; import com.moez.QKSMS.ui.settings.SettingsFragment; import java.util.Observable; import java.util.Observer; public class ConversationListFragment extends QKFragment implements LoaderManager.LoaderCallbacks<Cursor>, RecyclerCursorAdapter.ItemClickListener<Conversation>, RecyclerCursorAdapter.MultiSelectListener, Observer { public static final String TAG = "ConversationListFragment"; @Bind(R.id.empty_state) View mEmptyState; @Bind(R.id.empty_state_icon) ImageView mEmptyStateIcon; @Bind(R.id.conversations_list) RecyclerView mRecyclerView; @Bind(R.id.fab) FloatingActionButton mFab; private ConversationListAdapter mAdapter; private LinearLayoutManager mLayoutManager; private ConversationDetailsDialog mConversationDetailsDialog; private SharedPreferences mPrefs; private MenuItem mBlockedItem; private boolean mShowBlocked = false; private boolean mViewHasLoaded = false; // This does not hold the current position of the list, rather the position the list is pending being set to private int mPosition; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPrefs = PreferenceManager.getDefaultSharedPreferences(mContext); setHasOptionsMenu(true); mAdapter = new ConversationListAdapter(mContext); mAdapter.setItemClickListener(this); mAdapter.setMultiSelectListener(this); mLayoutManager = new LinearLayoutManager(mContext); mConversationDetailsDialog = new ConversationDetailsDialog(mContext, getFragmentManager()); LiveViewManager.registerView(QKPreference.THEME, this, key -> { if (!mViewHasLoaded) { return; } mFab.setColorNormal(ThemeManager.getColor()); mFab.setColorPressed(ColorUtils.lighten(ThemeManager.getColor())); mFab.getDrawable().setColorFilter(ThemeManager.getTextOnColorPrimary(), PorterDuff.Mode.SRC_ATOP); mEmptyStateIcon.setColorFilter(ThemeManager.getTextOnBackgroundPrimary()); }); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_conversations, null); ButterKnife.bind(this, view); mEmptyStateIcon.setColorFilter(ThemeManager.getTextOnBackgroundPrimary()); mRecyclerView.setHasFixedSize(true); mRecyclerView.setLayoutManager(mLayoutManager); mRecyclerView.setAdapter(mAdapter); mFab.setColorNormal(ThemeManager.getColor()); mFab.setColorPressed(ColorUtils.lighten(ThemeManager.getColor())); mFab.attachToRecyclerView(mRecyclerView); mFab.setColorFilter(ThemeManager.getTextOnColorPrimary()); mFab.setOnClickListener(v -> { if (mAdapter.isInMultiSelectMode()) { mAdapter.disableMultiSelectMode(true); } else { mContext.startActivity(ComposeActivity.class); } }); mViewHasLoaded = true; initLoaderManager(); BlockedConversationHelper.FutureBlockedConversationObservable.getInstance().addObserver(this); return view; } /** * Returns the weighting for unread vs. read conversations that are selected, to decide * which options we should show in the multi selction toolbar */ private int getUnreadWeight() { int unreadWeight = 0; for (Conversation conversation : mAdapter.getSelectedItems().values()) { unreadWeight += conversation.hasUnreadMessages() ? 1 : -1; } return unreadWeight; } /** * Returns the weighting for blocked vs. unblocked conversations that are selected */ private int getBlockedWeight() { int blockedWeight = 0; for (Conversation conversation : mAdapter.getSelectedItems().values()) { blockedWeight += BlockedConversationHelper.isConversationBlocked(mPrefs, conversation.getThreadId()) ? 1 : -1; } return blockedWeight; } /** * Returns whether or not any of the selected conversations have errors */ private boolean doSomeHaveErrors() { for (Conversation conversation : mAdapter.getSelectedItems().values()) { if (conversation.hasError()) { return true; } } return false; } public void inflateToolbar(Menu menu, MenuInflater inflater, Context context) { if (mAdapter.isInMultiSelectMode()) { inflater.inflate(R.menu.conversations_selection, menu); mContext.setTitle(getString(R.string.title_conversations_selected, mAdapter.getSelectedItems().size())); menu.findItem(R.id.menu_block).setVisible(mPrefs.getBoolean(SettingsFragment.BLOCKED_ENABLED, false)); menu.findItem(R.id.menu_mark_read).setIcon(getUnreadWeight() >= 0 ? R.drawable.ic_mark_read : R.drawable.ic_mark_unread); menu.findItem(R.id.menu_mark_read).setTitle(getUnreadWeight() >= 0 ? R.string.menu_mark_read : R.string.menu_mark_unread); menu.findItem(R.id.menu_block).setTitle(getBlockedWeight() > 0 ? R.string.menu_unblock_conversations : R.string.menu_block_conversations); menu.findItem(R.id.menu_delete_failed).setVisible(doSomeHaveErrors()); } else { inflater.inflate(R.menu.conversations, menu); mContext.setTitle(mShowBlocked ? R.string.title_blocked : R.string.title_conversation_list); mBlockedItem = menu.findItem(R.id.menu_blocked); BlockedConversationHelper.bindBlockedMenuItem(mContext, mPrefs, mBlockedItem, mShowBlocked); } super.onCreateOptionsMenu(menu, inflater); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_blocked: setShowingBlocked(!mShowBlocked); return true; case R.id.menu_delete: DialogHelper.showDeleteConversationsDialog((MainActivity) mContext, mAdapter.getSelectedItems().keySet()); mAdapter.disableMultiSelectMode(true); return true; case R.id.menu_mark_read: for (long threadId : mAdapter.getSelectedItems().keySet()) { if (getUnreadWeight() >= 0) { new ConversationLegacy(mContext, threadId).markRead(); } else { new ConversationLegacy(mContext, threadId).markUnread(); } } mAdapter.disableMultiSelectMode(true); return true; case R.id.menu_block: for (long threadId : mAdapter.getSelectedItems().keySet()) { if (getBlockedWeight() > 0) { BlockedConversationHelper.unblockConversation(mPrefs, threadId); } else { BlockedConversationHelper.blockConversation(mPrefs, threadId); } } mAdapter.disableMultiSelectMode(true); initLoaderManager(); return true; case R.id.menu_delete_failed: DialogHelper.showDeleteFailedMessagesDialog((MainActivity) mContext, mAdapter.getSelectedItems().keySet()); mAdapter.disableMultiSelectMode(true); return true; case R.id.menu_done: mAdapter.disableMultiSelectMode(true); return true; } return super.onOptionsItemSelected(item); } public boolean isShowingBlocked() { return mShowBlocked; } public void setShowingBlocked(boolean showBlocked) { mShowBlocked = showBlocked; mContext.setTitle(mShowBlocked ? R.string.title_blocked : R.string.title_conversation_list); BlockedConversationHelper.bindBlockedMenuItem(mContext, mPrefs, mBlockedItem, mShowBlocked); initLoaderManager(); } @Override public void onItemClick(Conversation conversation, View view) { if (mAdapter.isInMultiSelectMode()) { mAdapter.toggleSelection(conversation.getThreadId(), conversation); } else { MessageListActivity.launch(mContext, conversation.getThreadId(), -1, null, true); } } @Override public void onItemLongClick(final Conversation conversation, View view) { mAdapter.toggleSelection(conversation.getThreadId(), conversation); } public void setPosition(int position) { mPosition = position; if (mLayoutManager != null && mAdapter != null) { mLayoutManager.scrollToPosition(Math.min(mPosition, mAdapter.getCount() - 1)); } } public int getPosition() { return mLayoutManager.findFirstVisibleItemPosition(); } private void initLoaderManager() { getLoaderManager().restartLoader(QKSMSApp.LOADER_CONVERSATIONS, null, this); } @Override public void onDestroy() { super.onDestroy(); BlockedConversationHelper.FutureBlockedConversationObservable.getInstance().deleteObserver(this); if (null == mRecyclerView) { return; } try { for (int i = 0; i < mRecyclerView.getChildCount(); i++) { View child = mRecyclerView.getChildAt(i); RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(child); if (holder instanceof ConversationListViewHolder) { Contact.removeListener((ConversationListViewHolder) holder); } } } catch (Exception ignored) { // } } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { if (id == QKSMSApp.LOADER_CONVERSATIONS) { return new CursorLoader(mContext, SmsHelper.CONVERSATIONS_CONTENT_PROVIDER, Conversation.ALL_THREADS_PROJECTION, BlockedConversationHelper.getCursorSelection(mPrefs, mShowBlocked), BlockedConversationHelper.getBlockedConversationArray(mPrefs), "date DESC"); } else { return null; } } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { if (loader.getId() == QKSMSApp.LOADER_CONVERSATIONS) { if (mAdapter != null) { // Swap the new cursor in. (The framework will take care of closing the, old cursor once we return.) mAdapter.changeCursor(data); if (mPosition != 0) { mRecyclerView.scrollToPosition(Math.min(mPosition, data.getCount() - 1)); mPosition = 0; } } mEmptyState.setVisibility(data != null && data.getCount() > 0 ? View.GONE : View.VISIBLE); } } @Override public void onLoaderReset(Loader<Cursor> loader) { if (mAdapter != null && loader.getId() == QKSMSApp.LOADER_CONVERSATIONS) { mAdapter.changeCursor(null); } } @Override public void onMultiSelectStateChanged(boolean enabled) { mContext.invalidateOptionsMenu(); mFab.setImageResource(enabled ? R.drawable.ic_accept : R.drawable.ic_add); } @Override public void onItemAdded(long id) { mContext.invalidateOptionsMenu(); } @Override public void onItemRemoved(long id) { mContext.invalidateOptionsMenu(); } /** * This should be called when there's a future blocked conversation, and it's received */ @Override public void update(Observable observable, Object data) { initLoaderManager(); } }