package what.whatandroid.inbox.conversation; import android.app.Activity; import android.os.AsyncTask; import android.os.Bundle; import android.os.Parcelable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentTransaction; import android.support.v4.app.LoaderManager; import android.support.v4.content.Loader; 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.ListView; import android.widget.ProgressBar; import android.widget.Toast; import api.inbox.conversation.Conversation; import api.soup.MySoup; import what.whatandroid.R; import what.whatandroid.callbacks.AddQuoteCallback; import what.whatandroid.callbacks.OnLoggedInCallback; import what.whatandroid.callbacks.SetTitleCallback; import what.whatandroid.comments.CommentsAdapter; import what.whatandroid.forums.thread.ReplyDialogFragment; import what.whatandroid.inbox.ConversationChangesPasser; /** * Fragment for displaying the list of messages in a conversation */ public class ConversationFragment extends Fragment implements OnLoggedInCallback, AddQuoteCallback, LoaderManager.LoaderCallbacks<Conversation>, ReplyDialogFragment.ReplyDialogListener, ManageConversationDialog.Listener { public static final String CONVERSATION = "what.whatandroid.conversationfragment.CONVERSATION"; private static final String SCROLL_STATE = "what.whatandroid.conversationfragment.SCROLL_STATE"; private ProgressBar loadingIndicator; /** * The conversation being viewed */ private Conversation conversation; /** * Adapter displaying the messages in the conversation */ private CommentsAdapter adapter; private ListView list; /** * Draft of the reply we're writing for this conversation */ private String replyDraft = ""; /** * Save the scroll position to return too instead of jumping to the * last post when restoring from an orientation change or such */ private Parcelable scrollState; /** * Callback to send the conversation changes bundle back too */ private ConversationChangesPasser changesPasser; /** * Create a conversation fragment displaying the messages in the * desired conversation * * @param id id of conversation to view */ public static ConversationFragment newInstance(int id){ ConversationFragment f = new ConversationFragment(); Bundle args = new Bundle(); args.putInt(CONVERSATION, id); f.setArguments(args); return f; } public ConversationFragment(){ //Required empty ctor } @Override public void onAttach(Activity activity){ super.onAttach(activity); try { changesPasser = (ConversationChangesPasser)activity; } catch (ClassCastException e){ throw new ClassCastException(activity.toString() + " must implement ConversationChangesPasser"); } } @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); setHasOptionsMenu(true); if (savedInstanceState != null){ replyDraft = savedInstanceState.getString(ReplyDialogFragment.DRAFT); scrollState = savedInstanceState.getParcelable(SCROLL_STATE); } } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){ View view = inflater.inflate(R.layout.fragment_list_view, container, false); list = (ListView)view.findViewById(R.id.list); loadingIndicator = (ProgressBar)view.findViewById(R.id.loading_indicator); adapter = new CommentsAdapter(getActivity()); list.setAdapter(adapter); if (MySoup.isLoggedIn()){ getLoaderManager().initLoader(0, getArguments(), this); } return view; } @Override public void onSaveInstanceState(Bundle outState){ super.onSaveInstanceState(outState); outState.putString(ReplyDialogFragment.DRAFT, replyDraft); outState.putParcelable(SCROLL_STATE, list.onSaveInstanceState()); } @Override public void onLoggedIn(){ if (isAdded()){ getLoaderManager().initLoader(0, getArguments(), this); } } @Override public void quote(String quote){ replyDraft += quote; showReplyDialog(); } @Override public void post(String message, String subject){ replyDraft = ""; new PostReplyTask().execute(message); } @Override public void saveDraft(String message, String subject){ replyDraft = message; } @Override public void manageConversation(int convId, boolean sticky, boolean unread, boolean delete){ Bundle changes = new Bundle(); changes.putInt(ConversationChangesPasser.CONVERSATION, convId); changes.putBoolean(ConversationChangesPasser.STICKY, sticky); changes.putBoolean(ConversationChangesPasser.UNREAD, unread); changes.putBoolean(ConversationChangesPasser.DELETED, delete); changesPasser.setChanges(changes); conversation.getResponse().setSticky(sticky); new ManageConversationTask(convId).execute(sticky, unread, delete); } @Override public void discard(){ replyDraft = ""; } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater){ inflater.inflate(R.menu.conversation, menu); } @Override public boolean onOptionsItemSelected(MenuItem item){ switch (item.getItemId()){ //Show the reply dialog so the user can write their post case R.id.action_reply: showReplyDialog(); return true; case R.id.action_manage: ManageConversationDialog dialog = ManageConversationDialog.newInstance(conversation.getResponse().getConvId().intValue(), conversation.getResponse().isSticky(), false); dialog.setTargetFragment(this, 0); dialog.show(getFragmentManager(), "dialog"); return true; default: break; } return false; } /** * Display the compose reply dialog so the user can write their response */ private void showReplyDialog(){ FragmentTransaction ft = getFragmentManager().beginTransaction(); Fragment prev = getFragmentManager().findFragmentByTag("dialog"); if (prev != null){ ft.remove(prev); } ft.addToBackStack(null); ReplyDialogFragment reply = ReplyDialogFragment.newInstance(replyDraft); reply.setTargetFragment(this, 0); reply.show(ft, "dialog"); } @Override public Loader<Conversation> onCreateLoader(int id, Bundle args){ loadingIndicator.setVisibility(View.VISIBLE); return new ConversationAsyncLoader(getActivity(), getArguments()); } @Override public void onLoadFinished(Loader<Conversation> loader, Conversation data){ loadingIndicator.setVisibility(View.GONE); if (data == null || data.getResponse() == null || !data.getStatus()){ Toast.makeText(getActivity(), "Could not load conversation", Toast.LENGTH_LONG).show(); } else { conversation = data; ((SetTitleCallback)getActivity()).setTitle(conversation.getResponse().getSubject()); if (adapter.isEmpty()){ adapter.addAll(data.getResponse().getMessages()); adapter.notifyDataSetChanged(); //If we've got a saved scroll state use that, otherwise jump //to the last post in the conversation since it's our first time here if (scrollState != null){ list.onRestoreInstanceState(scrollState); } else { list.setSelection(list.getCount() - 1); } } } } @Override public void onLoaderReset(Loader<Conversation> loader){ conversation = null; adapter.clear(); adapter.notifyDataSetChanged(); } private class PostReplyTask extends AsyncTask<String, Void, Boolean> { @Override protected Boolean doInBackground(String... params){ return conversation.reply(params[0]); } @Override protected void onPostExecute(Boolean status){ if (!status){ Toast.makeText(getActivity(), "Could not post reply", Toast.LENGTH_LONG).show(); } else { //Reload the new posts to show getLoaderManager().destroyLoader(0); getLoaderManager().initLoader(0, getArguments(), ConversationFragment.this); } } } /** * Manage the conversation in the background. Doesn't handle deletion as it doesn't * make sense to keep viewing the conversation if we delete it. Instead we tell * the inbox to delete the message and go back */ private class ManageConversationTask extends AsyncTask<Boolean, Void, Boolean> { private boolean delete; private int convId; public ManageConversationTask(int convId){ this.convId = convId; } /** * @param params {sticky, mark unread, delete}, true to set the corresponding value to true. */ @Override protected Boolean doInBackground(Boolean... params){ delete = params[2]; return Conversation.manage(convId, params[0], params[1], params[2]); } @Override protected void onPostExecute(Boolean status){ if (!status){ Toast.makeText(getActivity(), "Could not update conversation", Toast.LENGTH_LONG).show(); } else if (isAdded()){ if (delete){ Toast.makeText(getActivity(), "Conversation deleted", Toast.LENGTH_SHORT).show(); getActivity().onBackPressed(); } else { Toast.makeText(getActivity(), "Conversation updated", Toast.LENGTH_SHORT).show(); } } } } }