package com.chrome.codereview; import android.app.AlertDialog; import android.app.LoaderManager; import android.content.Context; import android.content.DialogInterface; import android.content.Loader; import android.os.Bundle; import android.os.Parcelable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; import com.chrome.codereview.model.Comment; import com.chrome.codereview.model.FileDiff; import com.chrome.codereview.model.PatchSet; import com.chrome.codereview.model.PatchSetFile; import com.chrome.codereview.phone.UnifiedDiffAdapter; import com.chrome.codereview.tablet.SideBySideDiffAdapter; import com.chrome.codereview.utils.BaseListFragment; import com.chrome.codereview.utils.CachedLoader; import java.io.IOException; import java.util.ArrayList; import java.util.List; /** * Created by sergeyv on 29/4/14. */ public class DiffFragment extends BaseListFragment implements DiffAdapter.CommentActionListener { public static final int RESULT_REFRESH = 10; private static final String COMMENTS_ARG = "COMMENTS_ARG"; private static final String ISSUE_ID_ARG = "ISSUE_ID_ARG"; private static final String PATCH_SET_ID_ARG = "PATCH_SET_ID_ARG"; private static final String PATCH_ID_ARG = "PATCH_ID_ARG"; private static final int DIFF_LOADER_ID = 0; private static final int INLINE_DRAFT_LOADER_ID = 1; private static final int PATCH_SET_LOADER_ID = 1; private static final String KEY_COMMENT = "comment"; private static class DiffLoader extends CachedLoader<FileDiff> { private final int issueId; private final int patchSetId; private final int patchId; public DiffLoader(Context context, int issueId, int patchSetId, int patchId) { super(context); this.issueId = issueId; this.patchSetId = patchSetId; this.patchId = patchId; } @Override public FileDiff loadInBackground() { try { return serverCaller().loadDiff(issueId, patchSetId, patchId); } catch (IOException e) { e.printStackTrace(); } return null; } } private static class InlineDraftLoader extends CachedLoader<Void> { private int issueId; private int patchSetId; private int patchId; private Comment comment; public InlineDraftLoader(Context context, int issueId, int patchSetId, int patchId, Comment comment) { super(context); this.issueId = issueId; this.patchSetId = patchSetId; this.patchId = patchId; this.comment = comment; } @Override public Void loadInBackground() { try { serverCaller().inlineDraft(issueId, patchSetId, patchId, comment); } catch (IOException e) { e.printStackTrace(); } return null; } } private static class PatchSetLoader extends CachedLoader<PatchSet> { private int issueId; private int patchSetId; public PatchSetLoader(Context context, int issueId, int patchSetId) { super(context); this.issueId = issueId; this.patchSetId = patchSetId; } @Override public PatchSet loadInBackground() { return serverCaller().loadPatchSet(issueId, patchSetId); } } private class InlineDraftDialogListener implements DialogInterface.OnClickListener { private final int line; private final boolean left; private final EditText text; private final String messageId; private InlineDraftDialogListener(EditText text, String messageId, int line, boolean left) { this.line = line; this.left = left; this.text = text; this.messageId = messageId; } @Override public void onClick(DialogInterface dialog, int which) { Comment comment = Comment.createDraft(text.getText().toString(), line, left, messageId); restartInlineDraftLoader(comment); } } @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (isVisibleToUser && loadDiffInProgress) { startProgress(); return; } if (isVisibleToUser && loadDiffInProgress) { stopProgress(); return; } } private void restartInlineDraftLoader(Comment comment) { Bundle arg = new Bundle(); arg.putParcelable(KEY_COMMENT, comment); getLoaderManager().restartLoader(INLINE_DRAFT_LOADER_ID, arg, this.inlineDraftCallback); } private int issueId; private int patchSetId; private int patchId; private List<Comment> comments; private DiffAdapter diffAdapter; private boolean loadDiffInProgress; private LoaderManager.LoaderCallbacks<FileDiff> diffLoaderCallback = new LoaderManager.LoaderCallbacks<FileDiff>() { @Override public Loader<FileDiff> onCreateLoader(int id, Bundle args) { loadDiffInProgress = true; if (getUserVisibleHint()) { startProgress(); } return new DiffLoader(getActivity(), issueId, patchSetId, patchId); } @Override public void onLoadFinished(Loader<FileDiff> loader, FileDiff data) { loadDiffInProgress = false; if (getUserVisibleHint()) { stopProgress(); } if (data == null) { return; } boolean useSideBySideDiff = getResources().getBoolean(R.bool.use_side_by_side_diff); diffAdapter = useSideBySideDiff ? new SideBySideDiffAdapter(getActivity(), data, comments) : new UnifiedDiffAdapter(getActivity(), data, comments); diffAdapter.setCommentActionListener(DiffFragment.this); setListAdapter(diffAdapter); getListView().setOnItemClickListener(diffAdapter); } @Override public void onLoaderReset(Loader<FileDiff> loader) { } }; private LoaderManager.LoaderCallbacks<Void> inlineDraftCallback = new LoaderManager.LoaderCallbacks<Void>() { @Override public Loader<Void> onCreateLoader(int id, Bundle args) { startProgress(); return new InlineDraftLoader(getActivity(), issueId, patchSetId, patchId, (Comment) args.getParcelable(KEY_COMMENT)); } @Override public void onLoadFinished(Loader<Void> loader, Void data) { refresh(); getActivity().setResult(RESULT_REFRESH); } @Override public void onLoaderReset(Loader<Void> loader) { } }; private LoaderManager.LoaderCallbacks<PatchSet> patchSetLoaderCallback = new LoaderManager.LoaderCallbacks<PatchSet>() { @Override public Loader<PatchSet> onCreateLoader(int id, Bundle args) { return new PatchSetLoader(getActivity(), issueId, patchSetId); } @Override public void onLoadFinished(Loader<PatchSet> loader, PatchSet data) { stopProgress(); if (data == null) { return; } for (PatchSetFile file : data.files()) { if (file.id() == patchId) { comments = file.comments(); diffAdapter.resetComments(comments); return; } } } @Override public void onLoaderReset(Loader<PatchSet> loader) { } }; public void initialize(int issueId, PatchSet patchSet, int position) { Bundle args = new Bundle(); args.putInt(ISSUE_ID_ARG, issueId); args.putInt(PATCH_SET_ID_ARG, patchSet.id()); PatchSetFile patchSetFile = patchSet.files().get(position); args.putInt(PATCH_ID_ARG, patchSetFile.id()); args.putParcelableArrayList(COMMENTS_ARG, new ArrayList<Parcelable>(patchSetFile.comments())); this.setArguments(args); } @Override public void setArguments(Bundle args) { super.setArguments(args); comments = args.getParcelableArrayList(COMMENTS_ARG); issueId = args.getInt(ISSUE_ID_ARG, -1); patchSetId = args.getInt(PATCH_SET_ID_ARG, -1); patchId = args.getInt(PATCH_ID_ARG, -1); } @Override protected int getLayoutRes() { return R.layout.fragment_diff; } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putParcelableArrayList(DiffFragment.COMMENTS_ARG, new ArrayList<Parcelable>(comments)); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View layout = super.onCreateView(inflater, container, savedInstanceState, false); List<Comment> savedComments = null; if (savedInstanceState != null) { savedComments = savedInstanceState.getParcelableArrayList(COMMENTS_ARG); } if (savedComments != null) { comments = savedComments; } getLoaderManager().initLoader(DIFF_LOADER_ID, new Bundle(), this.diffLoaderCallback); return layout; } @Override protected void refresh() { getLoaderManager().restartLoader(PATCH_SET_LOADER_ID, new Bundle(), DiffFragment.this.patchSetLoaderCallback); } private void showWriteCommentDialog(String text, String messageId, int line, boolean left) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle(getString(R.string.new_comment, line)); EditText editText = new EditText(getActivity()); editText.setText(text); builder.setView(editText); builder.setNegativeButton(android.R.string.cancel, null); builder.setPositiveButton(android.R.string.ok, new InlineDraftDialogListener(editText, messageId, line, left)); builder.create().show(); } @Override public void removeDraft(Comment comment) { restartInlineDraftLoader(Comment.createDraft("", comment.line(), comment.left(), comment.messageId())); } @Override public void editDraft(Comment comment) { showWriteCommentDialog(comment.text(), comment.messageId(), comment.line(), comment.left()); } @Override public void doneComment(Comment comment) { restartInlineDraftLoader(Comment.createDraft(Comment.quote(comment) + "\nDone.", comment.line(), comment.left(), "")); } @Override public void replyComment(Comment comment) { showWriteCommentDialog(Comment.quote(comment), "", comment.line(), comment.left()); } @Override public void writeComment(int line, boolean left) { showWriteCommentDialog("", "", line, left); } }