package org.mian.gitnex.activities; import android.annotation.SuppressLint; import android.app.DatePickerDialog; import android.app.Dialog; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.ArrayAdapter; import androidx.annotation.NonNull; import com.google.gson.JsonElement; import org.gitnex.tea4j.models.Collaborators; import org.gitnex.tea4j.models.CreateIssue; import org.gitnex.tea4j.models.Labels; import org.gitnex.tea4j.models.Milestones; import org.mian.gitnex.R; import org.mian.gitnex.actions.AssigneesActions; import org.mian.gitnex.actions.LabelsActions; import org.mian.gitnex.adapters.AssigneesListAdapter; import org.mian.gitnex.adapters.LabelsListAdapter; import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.databinding.ActivityCreateIssueBinding; import org.mian.gitnex.databinding.CustomAssigneesSelectionDialogBinding; import org.mian.gitnex.databinding.CustomLabelsSelectionDialogBinding; import org.mian.gitnex.helpers.AlertDialogs; import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.Authorization; import org.mian.gitnex.helpers.StaticGlobalVariables; import org.mian.gitnex.helpers.TinyDB; import org.mian.gitnex.helpers.Toasty; import org.mian.gitnex.helpers.Version; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import java.util.Objects; import retrofit2.Call; import retrofit2.Callback; /** * Author M M Arif */ public class CreateIssueActivity extends BaseActivity implements View.OnClickListener, LabelsListAdapter.LabelsListAdapterListener, AssigneesListAdapter.AssigneesListAdapterListener { private ActivityCreateIssueBinding viewBinding; private CustomLabelsSelectionDialogBinding labelsBinding; private CustomAssigneesSelectionDialogBinding assigneesBinding; private View.OnClickListener onClickListener; private int resultLimit = StaticGlobalVariables.resultLimitOldGiteaInstances; private Dialog dialogLabels; private Dialog dialogAssignees; private String labelsSetter; private String assigneesSetter; private int milestoneId; private String loginUid; private String repoOwner; private String repoName; private LabelsListAdapter labelsAdapter; private AssigneesListAdapter assigneesAdapter; private List labelsIds = new ArrayList<>(); private List labelsList = new ArrayList<>(); private List milestonesList = new ArrayList<>(); private List assigneesList = new ArrayList<>(); private List assigneesListData = new ArrayList<>(); @SuppressLint("ClickableViewAccessibility") @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); viewBinding = ActivityCreateIssueBinding.inflate(getLayoutInflater()); setContentView(viewBinding.getRoot()); boolean connToInternet = AppUtil.hasNetworkConnection(appCtx); InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); loginUid = tinyDB.getString("loginUid"); String repoFullName = tinyDB.getString("repoFullName"); String[] parts = repoFullName.split("/"); repoOwner = parts[0]; repoName = parts[1]; // require gitea 1.12 or higher if(new Version(tinyDB.getString("giteaVersion")).higherOrEqual("1.12.0")) { resultLimit = StaticGlobalVariables.resultLimitNewGiteaInstances; } viewBinding.newIssueTitle.requestFocus(); assert imm != null; imm.showSoftInput(viewBinding.newIssueTitle, InputMethodManager.SHOW_IMPLICIT); viewBinding.newIssueDescription.setOnTouchListener((touchView, motionEvent) -> { touchView.getParent().requestDisallowInterceptTouchEvent(true); if ((motionEvent.getAction() & MotionEvent.ACTION_UP) != 0 && (motionEvent.getActionMasked() & MotionEvent.ACTION_UP) != 0) { touchView.getParent().requestDisallowInterceptTouchEvent(false); } return false; }); labelsAdapter = new LabelsListAdapter(labelsList, CreateIssueActivity.this, labelsIds); assigneesAdapter = new AssigneesListAdapter(ctx, assigneesList, CreateIssueActivity.this, assigneesListData); initCloseListener(); viewBinding.close.setOnClickListener(onClickListener); viewBinding.newIssueAssigneesList.setOnClickListener(this); viewBinding.newIssueLabels.setOnClickListener(this); viewBinding.newIssueDueDate.setOnClickListener(this); getMilestones(repoOwner, repoName, resultLimit); disableProcessButton(); viewBinding.newIssueLabels.setOnClickListener(newIssueLabels -> showLabels()); viewBinding.newIssueAssigneesList.setOnClickListener(newIssueAssigneesList -> showAssignees()); if(!connToInternet) { viewBinding.createNewIssueButton.setEnabled(false); } else { viewBinding.createNewIssueButton.setOnClickListener(this); } } @Override public void assigneesInterface(List data) { assigneesSetter = String.valueOf(data); viewBinding.newIssueAssigneesList.setText(assigneesSetter.replace("]", "").replace("[", "")); assigneesListData = data; } @Override public void labelsInterface(List data) { labelsSetter = String.valueOf(data); viewBinding.newIssueLabels.setText(labelsSetter.replace("]", "").replace("[", "")); } @Override public void labelsIdsInterface(List data) { labelsIds = data; } private void showAssignees() { dialogAssignees = new Dialog(ctx, R.style.ThemeOverlay_MaterialComponents_Dialog_Alert); if (dialogAssignees.getWindow() != null) { dialogAssignees.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); } assigneesBinding = CustomAssigneesSelectionDialogBinding.inflate(LayoutInflater.from(ctx)); View view = assigneesBinding.getRoot(); dialogAssignees.setContentView(view); assigneesBinding.cancel.setOnClickListener(assigneesBinding_ -> dialogAssignees.dismiss()); dialogAssignees.show(); AssigneesActions.getRepositoryAssignees(ctx, repoOwner, repoName, assigneesList, dialogAssignees, assigneesAdapter, assigneesBinding); } private void showLabels() { dialogLabels = new Dialog(ctx, R.style.ThemeOverlay_MaterialComponents_Dialog_Alert); if (dialogLabels.getWindow() != null) { dialogLabels.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); } labelsBinding = CustomLabelsSelectionDialogBinding.inflate(LayoutInflater.from(ctx)); View view = labelsBinding.getRoot(); dialogLabels.setContentView(view); labelsBinding.cancel.setOnClickListener(labelsBinding_ -> dialogLabels.dismiss()); dialogLabels.show(); LabelsActions.getRepositoryLabels(ctx, repoOwner, repoName, labelsList, dialogLabels, labelsAdapter, labelsBinding); } private void processNewIssue() { boolean connToInternet = AppUtil.hasNetworkConnection(appCtx); String newIssueTitleForm = Objects.requireNonNull(viewBinding.newIssueTitle.getText()).toString(); String newIssueDescriptionForm = Objects.requireNonNull(viewBinding.newIssueDescription.getText()).toString(); String newIssueDueDateForm = Objects.requireNonNull(viewBinding.newIssueDueDate.getText()).toString(); if(!connToInternet) { Toasty.error(ctx, getResources().getString(R.string.checkNetConnection)); return; } if (newIssueTitleForm.equals("")) { Toasty.error(ctx, getString(R.string.issueTitleEmpty)); return; } if (newIssueDueDateForm.equals("")) { newIssueDueDateForm = null; } else { newIssueDueDateForm = (AppUtil.customDateCombine(AppUtil.customDateFormat(newIssueDueDateForm))); } disableProcessButton(); createNewIssueFunc(repoOwner, repoName, loginUid, newIssueDescriptionForm, newIssueDueDateForm, milestoneId, newIssueTitleForm); } private void createNewIssueFunc(String repoOwner, String repoName, String loginUid, String newIssueDescriptionForm, String newIssueDueDateForm, int newIssueMilestoneIdForm, String newIssueTitleForm) { CreateIssue createNewIssueJson = new CreateIssue(loginUid, newIssueDescriptionForm, false, newIssueDueDateForm, newIssueMilestoneIdForm, newIssueTitleForm, assigneesListData, labelsIds); Call call3; call3 = RetrofitClient .getApiInterface(ctx) .createNewIssue(Authorization.get(ctx), repoOwner, repoName, createNewIssueJson); call3.enqueue(new Callback() { @Override public void onResponse(@NonNull Call call, @NonNull retrofit2.Response response2) { if(response2.code() == 201) { TinyDB tinyDb = TinyDB.getInstance(appCtx); tinyDb.putBoolean("resumeIssues", true); Toasty.success(ctx, getString(R.string.issueCreated)); enableProcessButton(); finish(); } else if(response2.code() == 401) { enableProcessButton(); AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle), getResources().getString(R.string.alertDialogTokenRevokedMessage), getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton), getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton)); } else { Toasty.error(ctx, getString(R.string.issueCreatedError)); enableProcessButton(); } } @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { Toasty.error(ctx, getString(R.string.genericServerResponseError)); enableProcessButton(); } }); } private void initCloseListener() { onClickListener = view -> finish(); } private void getMilestones(String repoOwner, String repoName, int resultLimit) { String msState = "open"; Call> call = RetrofitClient .getApiInterface(ctx) .getMilestones(Authorization.get(ctx), repoOwner, repoName, 1, resultLimit, msState); call.enqueue(new Callback>() { @Override public void onResponse(@NonNull Call> call, @NonNull retrofit2.Response> response) { if(response.isSuccessful()) { if(response.code() == 200) { List milestonesList_ = response.body(); milestonesList.add(new Milestones(0,getString(R.string.issueCreatedNoMilestone))); assert milestonesList_ != null; if(milestonesList_.size() > 0) { for (int i = 0; i < milestonesList_.size(); i++) { //Don't translate "open" is a enum if(milestonesList_.get(i).getState().equals("open")) { Milestones data = new Milestones( milestonesList_.get(i).getId(), milestonesList_.get(i).getTitle() ); milestonesList.add(data); } } } ArrayAdapter adapter = new ArrayAdapter<>(CreateIssueActivity.this, R.layout.list_spinner_items, milestonesList); viewBinding.newIssueMilestoneSpinner.setAdapter(adapter); enableProcessButton(); viewBinding.newIssueMilestoneSpinner.setOnItemClickListener ((parent, view, position, id) -> milestoneId = milestonesList.get(position).getId() ); } } } @Override public void onFailure(@NonNull Call> call, @NonNull Throwable t) { Toasty.error(ctx, getString(R.string.genericServerResponseError)); } }); } @Override public void onClick(View v) { if (v == viewBinding.newIssueDueDate) { final Calendar c = Calendar.getInstance(); int mYear = c.get(Calendar.YEAR); final int mMonth = c.get(Calendar.MONTH); final int mDay = c.get(Calendar.DAY_OF_MONTH); DatePickerDialog datePickerDialog = new DatePickerDialog(this, (view, year, monthOfYear, dayOfMonth) -> viewBinding.newIssueDueDate.setText(getString(R.string.setDueDate, year, (monthOfYear + 1), dayOfMonth)), mYear, mMonth, mDay); datePickerDialog.show(); } else if(v == viewBinding.createNewIssueButton) { processNewIssue(); } } private void disableProcessButton() { viewBinding.createNewIssueButton.setEnabled(false); } private void enableProcessButton() { viewBinding.createNewIssueButton.setEnabled(true); } }