package com.alorma.github.ui.activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.view.ViewCompat;
import android.support.v7.widget.LinearLayoutManager;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.alorma.github.R;
import com.alorma.github.cache.CacheWrapper;
import com.alorma.github.emoji.EmojisActivity;
import com.alorma.github.sdk.bean.dto.request.CreateMilestoneRequestDTO;
import com.alorma.github.sdk.bean.dto.request.IssueRequest;
import com.alorma.github.sdk.bean.dto.response.Contributor;
import com.alorma.github.sdk.bean.dto.response.Issue;
import com.alorma.github.sdk.bean.dto.response.Milestone;
import com.alorma.github.sdk.bean.dto.response.MilestoneState;
import com.alorma.github.sdk.bean.info.IssueInfo;
import com.alorma.github.sdk.bean.info.RepoInfo;
import com.alorma.github.sdk.services.issues.CreateMilestoneClient;
import com.alorma.github.sdk.services.issues.GetMilestonesClient;
import com.alorma.github.sdk.services.issues.GithubIssueLabelsClient;
import com.alorma.github.sdk.services.issues.PostNewIssueClient;
import com.alorma.github.sdk.services.repo.GetRepoContributorsClient;
import com.alorma.github.ui.ErrorHandler;
import com.alorma.github.ui.activity.base.RepositoryThemeActivity;
import com.alorma.github.ui.adapter.users.UsersAdapterSpinner;
import com.alorma.github.ui.utils.DialogUtils;
import com.alorma.github.utils.AttributesUtils;
import com.mikepenz.iconics.IconicsDrawable;
import com.mikepenz.iconics.typeface.IIcon;
import com.mikepenz.octicons_typeface_library.Octicons;
import core.User;
import core.issues.Label;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
public class NewIssueActivity extends RepositoryThemeActivity {
public static final String REPO_INFO = "REPO_INFO";
private static final int EMOJI_CODE = 1554;
private static final int NEW_ISSUE_REQUEST = 575;
private boolean creatingIssue = false;
private RepoInfo repoInfo;
private EditText editTitle;
private TextView editBody;
private TextView userTextView;
private TextView milestoneTextView;
private TextView labelsTextView;
private Integer[] positionsSelectedLabels;
private boolean editBodyHasFocus;
private boolean issuePublished = false;
private IssueRequest issueRequest;
public static Intent createLauncherIntent(Context context, RepoInfo info) {
Bundle bundle = new Bundle();
bundle.putParcelable(REPO_INFO, info);
Intent intent = new Intent(context, NewIssueActivity.class);
intent.putExtras(bundle);
return intent;
}
@Override
public int getToolbarId() {
return super.getToolbarId();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_new_issue);
if (getIntent().getExtras() != null) {
repoInfo = getIntent().getExtras().getParcelable(REPO_INFO);
findViews();
setTitle(getString(R.string.new_issue_title, repoInfo.name));
issueRequest = CacheWrapper.getIssueRequest(repoInfo.owner + "/" + repoInfo.name);
if (issueRequest != null) {
setupFromCache(issueRequest);
} else {
issueRequest = new IssueRequest();
}
if (getSupportActionBar() != null) {
getSupportActionBar().setBackgroundDrawable(new ColorDrawable(AttributesUtils.getPrimaryColor(this)));
}
} else {
finish();
}
}
private void setupFromCache(IssueRequest issueRequest) {
if (issueRequest != null) {
if (issueRequest.title != null) {
editTitle.setText(issueRequest.title);
}
if (issueRequest.body != null) {
editBody.setText(issueRequest.body);
}
if (issueRequest.labels != null) {
StringBuilder builder = new StringBuilder();
for (CharSequence label : issueRequest.labels) {
builder.append(label);
builder.append(",");
}
String s = builder.toString();
if (s.endsWith(",")) {
s = s.substring(0, s.length() - 1);
}
labelsTextView.setText(s);
}
if (issueRequest.assignee != null) {
userTextView.setText(issueRequest.assignee);
}
if (issueRequest.milestone != null && issueRequest.milestoneName != null) {
milestoneTextView.setText(issueRequest.milestoneName);
}
}
}
private void findViews() {
editTitle = (EditText) findViewById(R.id.editTitle);
editBody = (TextView) findViewById(R.id.editBody);
editBody.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String hint = getString(R.string.add_issue_body);
Intent intent =
ContentEditorActivity.createLauncherIntent(v.getContext(), repoInfo, 0, hint, editBody.getText().toString(), false, false);
startActivityForResult(intent, NEW_ISSUE_REQUEST);
}
});
if (repoInfo.permissions != null && !repoInfo.permissions.push) {
findViewById(R.id.pushAccessLayout).setVisibility(View.GONE);
} else {
userTextView = (TextView) findViewById(R.id.assignee);
userTextView = (TextView) findViewById(R.id.assignee);
milestoneTextView = (TextView) findViewById(R.id.milestone);
labelsTextView = (TextView) findViewById(R.id.labels);
userTextView.setCompoundDrawables(pushAccessInfoIcon(Octicons.Icon.oct_person), null, null, null);
milestoneTextView.setCompoundDrawables(pushAccessInfoIcon(Octicons.Icon.oct_milestone), null, null, null);
labelsTextView.setCompoundDrawables(pushAccessInfoIcon(Octicons.Icon.oct_tag), null, null, null);
View.OnClickListener pushAccessListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.assignee:
openAssignee();
break;
case R.id.milestone:
openMilestone();
break;
case R.id.labels:
openLabels();
break;
}
}
};
userTextView.setOnClickListener(pushAccessListener);
milestoneTextView.setOnClickListener(pushAccessListener);
labelsTextView.setOnClickListener(pushAccessListener);
if (getToolbar() != null) {
ViewCompat.setElevation(getToolbar(), 8);
}
}
editBody.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
editBodyHasFocus = hasFocus;
invalidateOptionsMenu();
}
});
}
public Drawable pushAccessInfoIcon(IIcon icon) {
return new IconicsDrawable(this, icon).actionBar().color(AttributesUtils.getPrimaryColor(this));
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.new_issue, menu);
MenuItem item = menu.findItem(R.id.action_send);
if (item != null) {
IconicsDrawable githubIconDrawable = new IconicsDrawable(this, Octicons.Icon.oct_plus);
githubIconDrawable.actionBar();
githubIconDrawable.colorRes(R.color.white);
item.setIcon(githubIconDrawable);
}
return true;
}
private IssueRequest checkDataAndCreateIssue() {
if (editTitle.length() <= 0) {
editTitle.setError(getString(R.string.issue_title_mandatory));
return null;
}
if (issueRequest == null) {
issueRequest = new IssueRequest();
}
issueRequest.title = editTitle.getText().toString();
issueRequest.body = editBody.getText().toString();
creatingIssue = true;
return issueRequest;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem item = menu.findItem(R.id.action_send);
if (item != null) {
item.setEnabled(!creatingIssue);
}
if (editBodyHasFocus) {
menu.add(0, R.id.action_add_emoji, 100, R.string.add_emoji);
MenuItem emojiMenu = menu.findItem(R.id.action_add_emoji);
emojiMenu.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
Drawable emojiIcon = new IconicsDrawable(this, Octicons.Icon.oct_octoface).actionBar().color(Color.WHITE);
emojiMenu.setIcon(emojiIcon);
}
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case R.id.action_send:
issueRequest = checkDataAndCreateIssue();
invalidateOptionsMenu();
createIssue(issueRequest);
showProgressDialog(R.string.creating_issue);
break;
case R.id.action_add_emoji:
Intent intent = new Intent(this, EmojisActivity.class);
startActivityForResult(intent, EMOJI_CODE);
break;
}
return true;
}
@Override
protected void close(boolean navigateUp) {
super.close(navigateUp);
if (!issuePublished && issueRequest != null) {
issueRequest.title = editTitle.getText().toString();
CacheWrapper.setNewIssueRequest(repoInfo.owner + "/" + repoInfo.name, issueRequest);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK && data != null) {
String content = data.getStringExtra(ContentEditorActivity.CONTENT);
issueRequest.body = content;
editBody.setText(content);
}
}
private void createIssue(IssueRequest issue) {
issuePublished = true;
issueRequest.milestoneName = null;
PostNewIssueClient postNewIssueClient = new PostNewIssueClient(repoInfo, issue);
postNewIssueClient.observable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Issue>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
hideProgressDialog();
creatingIssue = false;
ErrorHandler.onError(NewIssueActivity.this, "Creating issue", e);
invalidateOptionsMenu();
Toast.makeText(NewIssueActivity.this, R.string.create_issue_error, Toast.LENGTH_SHORT).show();
}
@Override
public void onNext(Issue issue) {
hideProgressDialog();
if (issue != null) {
IssueInfo issueInfo = new IssueInfo();
issueInfo.repoInfo = repoInfo;
issueInfo.num = issue.number;
Intent launcherIntent = IssueDetailActivity.createLauncherIntent(NewIssueActivity.this, issueInfo);
startActivity(launcherIntent);
setResult(RESULT_OK);
CacheWrapper.clearIssueRequest(repoInfo.owner + "/" + repoInfo.name);
finish();
}
}
});
}
/**
* Assignee
*/
private void openAssignee() {
GetRepoContributorsClient contributorsClient = new GetRepoContributorsClient(repoInfo);
contributorsClient.observable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<Contributor>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(List<Contributor> contributors) {
onContributorsLoaded(contributors);
}
});
}
public void onContributorsLoaded(List<Contributor> contributors) {
final List<User> users = new ArrayList<>();
String owner = repoInfo.owner;
boolean exist = false;
if (contributors != null) {
for (Contributor contributor : contributors) {
exist = contributor.author.getLogin().equals(owner);
users.add(contributor.author);
}
}
if (!exist) {
User user = new User();
user.setLogin(owner);
users.add(user);
}
Collections.reverse(users);
UsersAdapterSpinner assigneesAdapter = new UsersAdapterSpinner(getLayoutInflater());
assigneesAdapter.addAll(users);
assigneesAdapter.setCallback(item -> {
if (dialog != null) {
dialog.dismiss();
setAssigneeUser(item);
}
});
MaterialDialog.Builder builder = new DialogUtils().builder(NewIssueActivity.this);
builder.adapter(assigneesAdapter, new LinearLayoutManager(this));
builder.negativeText(R.string.no_assignee);
builder.onNegative((dialog1, which) -> setAssigneeUser(null));
dialog = builder.show();
}
private void setAssigneeUser(User user) {
if (userTextView != null) {
if (user != null) {
issueRequest.assignee = user.getLogin();
userTextView.setText(user.getLogin());
} else {
userTextView.setText(null);
}
}
}
/**
* Milestone
*/
private void openMilestone() {
GetMilestonesClient milestonesClient = new GetMilestonesClient(repoInfo, MilestoneState.open, true);
milestonesClient.observable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<Milestone>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(List<Milestone> milestones) {
onMilestonesLoaded(milestones);
}
});
}
public void onMilestonesLoaded(final List<Milestone> milestones) {
if (milestones.size() == 0) {
showCreateMilestone();
} else {
String[] itemsMilestones = new String[milestones.size()];
for (int i = 0; i < milestones.size(); i++) {
itemsMilestones[i] = milestones.get(i).title;
}
MaterialDialog.Builder builder = new DialogUtils().builder(NewIssueActivity.this)
.title(R.string.select_milestone)
.items(itemsMilestones)
.itemsCallbackSingleChoice(-1, (materialDialog, view, i, text) -> {
addMilestone(milestones.get(i));
return false;
})
.forceStacking(true)
.widgetColor(AttributesUtils.getPrimaryColor(this))
.negativeText(R.string.add_milestone);
builder.onNeutral((dialog1, which) -> clearMilestone());
builder.onNegative((dialog1, which) -> showCreateMilestone());
builder.show();
}
}
private void showCreateMilestone() {
MaterialDialog.Builder builder = new DialogUtils().builder(this);
builder.title(R.string.add_milestone);
builder.content(R.string.add_milestone_description);
builder.input(R.string.add_milestone_hint, 0, (materialDialog, milestoneName) -> {
createMilestone(milestoneName.toString());
}).negativeText(R.string.cancel);
dialog = builder.show();
}
private void createMilestone(String milestoneName) {
CreateMilestoneRequestDTO createMilestoneRequestDTO = new CreateMilestoneRequestDTO(milestoneName);
CreateMilestoneClient createMilestoneClient = new CreateMilestoneClient(repoInfo, createMilestoneRequestDTO);
createMilestoneClient.observable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Milestone>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
hideProgressDialog();
}
@Override
public void onNext(Milestone milestone) {
addMilestone(milestone);
}
});
}
private void addMilestone(Milestone milestone) {
issueRequest.milestone = milestone.number;
issueRequest.milestoneName = milestone.title;
milestoneTextView.setText(milestone.title);
}
private void clearMilestone() {
milestoneTextView.setText(null);
}
/**
* Labels
*/
private void openLabels() {
GithubIssueLabelsClient labelsClient = new GithubIssueLabelsClient(repoInfo, true);
labelsClient.observable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<Label>>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(List<Label> labels) {
onLabelsLoaded(labels);
}
});
}
public void onLabelsLoaded(List<Label> labels) {
if (labels != null) {
List<String> items = new ArrayList<>();
for (Label label : labels) {
items.add(label.name);
}
MaterialDialog.Builder builder = new DialogUtils().builder(NewIssueActivity.this);
builder.items(items.toArray(new String[items.size()]));
builder.alwaysCallMultiChoiceCallback();
builder.itemsCallbackMultiChoice(positionsSelectedLabels, new MaterialDialog.ListCallbackMultiChoice() {
@Override
public boolean onSelection(MaterialDialog materialDialog, Integer[] integers, CharSequence[] charSequences) {
List<String> labels = new ArrayList<>();
for (CharSequence charSequence : charSequences) {
labels.add(charSequence.toString());
}
issueRequest.labels = labels.toArray(new String[labels.size()]);
positionsSelectedLabels = integers;
return true;
}
});
builder.forceStacking(true);
builder.positiveText(R.string.ok);
// builder.neutralText(R.string.add_new_label);
builder.negativeText(R.string.clear_labels);
builder.callback(new MaterialDialog.ButtonCallback() {
@Override
public void onPositive(MaterialDialog dialog) {
super.onPositive(dialog);
setLabels(issueRequest.labels);
}
@Override
public void onNegative(MaterialDialog dialog) {
super.onNegative(dialog);
issueRequest.labels = null;
positionsSelectedLabels = null;
setLabels(null);
}
});
dialog = builder.show();
}
}
private void setLabels(CharSequence[] selectedLabels) {
if (selectedLabels != null) {
StringBuilder builder = new StringBuilder();
for (CharSequence selectedLabel : selectedLabels) {
builder.append(selectedLabel);
builder.append(", ");
}
if (selectedLabels.length > 0) {
String labels = builder.toString();
int lastIndexOf = labels.lastIndexOf(", ");
labels = labels.substring(0, lastIndexOf);
labelsTextView.setText(labels);
}
} else {
labelsTextView.setText(null);
}
}
}