package com.chrome.codereview; import android.app.AlertDialog; import android.app.LoaderManager; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.Loader; import android.net.Uri; import android.os.Bundle; 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 android.widget.EditText; import android.widget.ExpandableListView; import android.widget.Toast; import com.chrome.codereview.model.Issue; import com.chrome.codereview.model.PatchSet; import com.chrome.codereview.model.PatchSetFile; import com.chrome.codereview.model.PublishData; import com.chrome.codereview.model.Reviewer; import com.chrome.codereview.model.TryBotResult; import com.chrome.codereview.utils.BaseFragment; import com.chrome.codereview.utils.CachedLoader; import com.chrome.codereview.utils.ViewUtils; import com.google.android.gms.auth.GoogleAuthException; import org.apache.http.auth.AuthenticationException; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.StringTokenizer; /** * Created by sergeyv on 18/4/14. */ public class IssueDetailsFragment extends BaseFragment implements DialogInterface.OnClickListener, ExpandableListView.OnChildClickListener { public static final String EXTRA_ISSUE_ID = "EXTRA_ISSUE_ID"; public static final int REQUEST_CODE_DIFF = 1; private static final int ISSUE_LOADER_ID = 0; private static final int COMMIT_LOADER_ID = 2; private static final String PUBLISH_DATA_ARG = "publishData"; private static class IssueLoader extends CachedLoader<Issue> { private int issueId; private final PublishData publishData; public IssueLoader(Context context, int issueId, PublishData publishData) { super(context); this.issueId = issueId; this.publishData = publishData; } @Override public Issue loadInBackground() { Issue issue = null; try { issue = serverCaller().publishAndReloadIssue(issueId, publishData); } catch (GoogleAuthException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (AuthenticationException e) { e.printStackTrace(); } return issue; } } private static class CommitLoader extends CachedLoader<Boolean> { private final int issuedId; private final int patchSetId; private final boolean commit; public CommitLoader(Context context, int issuedId, int patchSetId, boolean commit) { super(context); this.issuedId = issuedId; this.patchSetId = patchSetId; this.commit = commit; } @Override public Boolean loadInBackground() { try { serverCaller().checkCQBit(issuedId, patchSetId, commit); } catch (IOException e) { e.printStackTrace(); } catch (GoogleAuthException e) { e.printStackTrace(); } catch (AuthenticationException e) { e.printStackTrace(); } return true; } } private LoaderManager.LoaderCallbacks<Issue> issueLoaderCallback = new LoaderManager.LoaderCallbacks<Issue>() { @Override public Loader<Issue> onCreateLoader(int id, Bundle args) { enableMenuButtons(false); startProgress(); PublishData publishData = args != null ? (PublishData) args.getParcelable(PUBLISH_DATA_ARG) : null; return new IssueLoader(getActivity(), issueId, publishData); } @Override public void onLoadFinished(Loader<Issue> loader, Issue issue) { enableMenuButtons(true); IssueDetailsFragment.this.issue = issue; issueDetailsAdapter.setIssue(issue); stopProgress(); if (issue == null) { return; } getActivity().getActionBar().setTitle(issue.subject()); commitItem.setIcon(issue.isInCQ() ? R.drawable.ic_action_stop : R.drawable.ic_action_play); } @Override public void onLoaderReset(Loader<Issue> loader) { issue = null; issueDetailsAdapter.setIssue(null); } }; private LoaderManager.LoaderCallbacks<Boolean> commitLoaderCallback = new LoaderManager.LoaderCallbacks<Boolean>() { @Override public Loader<Boolean> onCreateLoader(int id, Bundle args) { startProgress(); commitItem.setEnabled(false); int patchSetId = issue.patchSets().get(issue.patchSets().size() - 1).id(); return new CommitLoader(getActivity(), issueId, patchSetId, !issue.isInCQ()); } @Override public void onLoadFinished(Loader<Boolean> loader, Boolean data) { getLoaderManager().restartLoader(ISSUE_LOADER_ID, null, issueLoaderCallback); } @Override public void onLoaderReset(Loader<Boolean> loader) { } }; private int issueId; private Issue issue; private AlertDialog publishDialog; private IssueDetailsAdapter issueDetailsAdapter; private MenuItem commitItem; private MenuItem publishItem; private boolean menuItemState; @Override protected int getLayoutRes() { return R.layout.fragment_issue_detail; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { setHasOptionsMenu(true); issueId = getActivity().getIntent().getIntExtra(EXTRA_ISSUE_ID, -1); issueDetailsAdapter = new IssueDetailsAdapter(this); View layout = super.onCreateView(inflater, container, savedInstanceState); ExpandableListView listView = (ExpandableListView) layout.findViewById(android.R.id.list); listView.setOnChildClickListener(this); listView.setAdapter(issueDetailsAdapter); if (issueId != -1) { getActivity().getActionBar().setTitle(getString(R.string.issue) + " " + issueId); } return layout; } public void setIssueId(int issueId) { this.issueId = issueId; if (issueId == -1) { issueDetailsAdapter.setIssue(null); } refresh(); } @Override protected void refresh() { if (issueId != -1) { getLoaderManager().restartLoader(ISSUE_LOADER_ID, new Bundle(), this.issueLoaderCallback); } } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.issue_detail, menu); publishItem = menu.getItem(0); commitItem = menu.getItem(1); enableMenuButtons(menuItemState); } private String getTextFromPublishDialog(int id) { return ((EditText) publishDialog.findViewById(id)).getText().toString(); } private String getReviewerString(String userText) { StringTokenizer tokenizer = new StringTokenizer(userText, ", "); List<String> mails = new ArrayList<String>(10); Map<String, Reviewer> nameToReviewer = new HashMap<String, Reviewer>(); for (Reviewer reviewer: issue.reviewers()) { nameToReviewer.put(reviewer.name(), reviewer); } while (tokenizer.hasMoreTokens()) { String name = tokenizer.nextToken(); if (nameToReviewer.containsKey(name)) { mails.add(nameToReviewer.get(name).email()); } else { mails.add(name + "@chromium.org"); } } return TextUtils.join(", ", mails); } @Override public void onClick(DialogInterface dialog, int which) { String prefix = dialog.BUTTON_NEUTRAL == which ? "lgtm.\n" : ""; String message = prefix + getTextFromPublishDialog(R.id.publish_message); String subject = getTextFromPublishDialog(R.id.publish_subject); String cc = getTextFromPublishDialog(R.id.publish_cc); String reviewers = getReviewerString(getTextFromPublishDialog(R.id.publish_reviewers)); PublishData publishData = new PublishData(issueId, message, subject, cc, reviewers); Bundle bundle = new Bundle(); bundle.putParcelable(PUBLISH_DATA_ARG, publishData); getLoaderManager().restartLoader(ISSUE_LOADER_ID, bundle, this.issueLoaderCallback); } @Override public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) { Object patchSetObject = issueDetailsAdapter.getGroup(groupPosition); if (!(patchSetObject instanceof PatchSet)) { return false; } PatchSet patchSet = (PatchSet) patchSetObject; PatchSetFile file = (PatchSetFile) issueDetailsAdapter.getChild(groupPosition, childPosition); if (file != null) { DiffActivity.startDiffActivity(this, REQUEST_CODE_DIFF, issueId, patchSet, file.id()); } else { showTryBotResultsDialog(patchSet); } return true; } public void showPublishDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle(R.string.publish_action); LayoutInflater inflater = getActivity().getLayoutInflater(); View publishView = inflater.inflate(R.layout.publish_dialog, null); builder.setView(publishView); if (issue != null) { ViewUtils.setText(publishView, R.id.publish_subject, issue.subject()); publishView.findViewById(R.id.publish_message).requestFocus(); ViewUtils.setText(publishView, R.id.publish_reviewers, TextUtils.join(", ", issue.reviewers())); ViewUtils.setText(publishView, R.id.publish_cc, issue.ccdString()); } builder.setPositiveButton(R.string.publish_action, this); builder.setNeutralButton(R.string.quick_lgtm, this); builder.setNegativeButton(android.R.string.cancel, null); publishDialog = builder.create(); publishDialog.show(); } public void showTryBotResultsDialog(PatchSet patchSet) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); final TryBotsResultsAdapter adapter = new TryBotsResultsAdapter(getActivity(), patchSet); builder.setAdapter(adapter, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { TryBotResult result = adapter.getItem(which); Intent intent = new Intent(Intent.ACTION_VIEW); intent.setData(Uri.parse(result.url())); startActivity(intent); } }); builder.setTitle(getActivity().getString(R.string.try_bots_dialog_title)); builder.create().show(); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_publish: showPublishDialog(); return true; case R.id.action_commit: if (issue != null) { getLoaderManager().restartLoader(COMMIT_LOADER_ID, null, commitLoaderCallback); } else { Toast.makeText(getActivity(), getString(R.string.fail_to_commit), Toast.LENGTH_LONG).show(); } return true; } return super.onOptionsItemSelected(item); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == DiffFragment.RESULT_REFRESH) { getLoaderManager().restartLoader(ISSUE_LOADER_ID, null, issueLoaderCallback); } } private void enableMenuButtons(boolean enabled) { menuItemState = enabled; if (publishItem == null || commitItem == null) { return; } publishItem.setEnabled(enabled); commitItem.setEnabled(enabled); } }