New popup for labels/assignees (#723)

Preventing lists to store duplicate collaborators (#726)

Using login instead of id.

Merge branch 'new-popup-labels-assigness' of https://codeberg.org/gitnex/GitNex into new-popup-labels-assigness

To prevent lists storing duplicate Collaborators

Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/726

Add current logged user to list

Change to collection

Fix depricated handler calls

Change to List and better naming

Move to actions

Merge branch 'master' into new-popup-labels-assigness

Add assignees popup and remove multi select

update to view binding

add/remove labels in edit issue

remove org call

Add org members to assignees list

Add assignees adapter

Add color to labels

Refactor and add new labels popup

Clean up build libs

Co-authored-by: opyale <opyale@noreply.codeberg.org>
Co-authored-by: M M Arif <mmarif@swatian.com>
Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/723
Reviewed-by: opyale <opyale@noreply.codeberg.org>
This commit is contained in:
M M Arif 2020-10-09 17:47:52 +02:00
parent 1e30c37d7c
commit 57f0d23ef0
32 changed files with 1304 additions and 2013 deletions

View File

@ -72,13 +72,11 @@ Thanks to all the open source libraries, contributors and donators.
- Retrofit
- Gson
- Okhttp
- ViHtarb/tooltip
- Picasso
- Markwon
- Prettytime
- Amulyakhare/textdrawable
- Vdurmont/emoji-java
- Abumoallim/android-multi-select-dialog
- Pes/materialcolorpicker
- Hendraanggrian/socialview
- HamidrezaAmz/BreadcrumbsView

View File

@ -66,7 +66,6 @@ dependencies {
testImplementation "junit:junit:4.13"
androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation "com.github.vihtarb:tooltip:0.2.0"
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
implementation "com.google.code.gson:gson:2.8.6"
implementation "com.squareup.picasso:picasso:2.71828"

View File

@ -31,15 +31,9 @@
android:name=".activities.RepoStargazersActivity"
android:theme="@style/AppTheme.NoActionBar" />
<activity android:name=".activities.AdminGetUsersActivity" />
<activity
android:name=".activities.AddRemoveAssigneesActivity"
android:theme="@style/Theme.AppCompat.Light.Dialog" />
<activity android:name=".activities.CreateReleaseActivity" />
<activity android:name=".activities.EditIssueActivity" />
<activity android:name=".activities.CreateNewUserActivity" />
<activity
android:name=".activities.AddRemoveLabelsActivity"
android:theme="@style/Theme.AppCompat.Light.Dialog" />
<activity android:name=".activities.ProfileEmailActivity" />
<activity android:name=".activities.AddCollaboratorToRepositoryActivity" />
<activity android:name=".activities.CreateTeamByOrgActivity" />

View File

@ -0,0 +1,122 @@
package org.mian.gitnex.actions;
import android.app.Dialog;
import android.content.Context;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import org.mian.gitnex.R;
import org.mian.gitnex.adapters.AssigneesListAdapter;
import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.databinding.CustomAssigneesSelectionDialogBinding;
import org.mian.gitnex.helpers.Authorization;
import org.mian.gitnex.helpers.TinyDB;
import org.mian.gitnex.helpers.Toasty;
import org.mian.gitnex.models.Collaborators;
import org.mian.gitnex.models.Issues;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
/**
* Author M M Arif
*/
public class AssigneesActions {
public static void getCurrentIssueAssignees(Context ctx, String instanceUrl, String loginUid, String instanceToken, String repoOwner, String repoName, int issueIndex, List<String> currentAssignees) {
Call<Issues> callSingleIssueLabels = RetrofitClient
.getInstance(instanceUrl, ctx)
.getApiInterface()
.getIssueByIndex(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex);
callSingleIssueLabels.enqueue(new Callback<Issues>() {
@Override
public void onResponse(@NonNull Call<Issues> call, @NonNull retrofit2.Response<Issues> response) {
if(response.code() == 200) {
Issues issueAssigneesList = response.body();
assert issueAssigneesList != null;
if (issueAssigneesList.getAssignees() != null) {
if(issueAssigneesList.getAssignees().size() > 0) {
for(int i = 0; i < issueAssigneesList.getAssignees().size(); i++) {
currentAssignees.add(issueAssigneesList.getAssignees().get(i).getLogin());
}
}
}
}
}
@Override
public void onFailure(@NonNull Call<Issues> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
}
});
}
public static void getRepositoryAssignees(Context ctx, String instanceUrl, String instanceToken, String repoOwner, String repoName, List<Collaborators> assigneesList, Dialog dialogAssignees, AssigneesListAdapter assigneesAdapter, CustomAssigneesSelectionDialogBinding assigneesBinding) {
TinyDB tinyDB = new TinyDB(ctx);
Call<List<Collaborators>> call = RetrofitClient
.getInstance(instanceUrl, ctx)
.getApiInterface()
.getCollaborators(instanceToken, repoOwner, repoName);
call.enqueue(new Callback<List<Collaborators>>() {
@Override
public void onResponse(@NonNull Call<List<Collaborators>> call, @NonNull retrofit2.Response<List<Collaborators>> response) {
assigneesList.clear();
List<Collaborators> assigneesList_ = response.body();
assigneesBinding.progressBar.setVisibility(View.GONE);
assigneesBinding.dialogFrame.setVisibility(View.VISIBLE);
if (response.code() == 200) {
assert assigneesList_ != null;
if(assigneesList_.size() > 0) {
dialogAssignees.show();
assigneesList.add(new Collaborators(tinyDB.getString("userFullname"), tinyDB.getString("loginUid"), tinyDB.getString("userAvatar")));
assigneesList.addAll(assigneesList_);
}
else {
dialogAssignees.dismiss();
Toasty.warning(ctx, ctx.getResources().getString(R.string.noAssigneesFound));
}
assigneesBinding.assigneesRecyclerView.setAdapter(assigneesAdapter);
}
else {
Toasty.error(ctx, ctx.getResources().getString(R.string.genericError));
}
}
@Override
public void onFailure(@NonNull Call<List<Collaborators>> call, @NonNull Throwable t) {
Toasty.error(ctx, ctx.getResources().getString(R.string.genericServerResponseError));
}
});
}
}

View File

@ -0,0 +1,116 @@
package org.mian.gitnex.actions;
import android.app.Dialog;
import android.content.Context;
import android.util.Log;
import android.view.View;
import androidx.annotation.NonNull;
import org.mian.gitnex.R;
import org.mian.gitnex.adapters.LabelsListAdapter;
import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.databinding.CustomLabelsSelectionDialogBinding;
import org.mian.gitnex.helpers.Authorization;
import org.mian.gitnex.helpers.Toasty;
import org.mian.gitnex.models.Labels;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
/**
* Author M M Arif
*/
public class LabelsActions {
public static void getCurrentIssueLabels(Context ctx, String instanceUrl, String loginUid, String instanceToken, String repoOwner, String repoName, int issueIndex, List<Integer> currentLabelsIds) {
Call<List<Labels>> callSingleIssueLabels = RetrofitClient
.getInstance(instanceUrl, ctx)
.getApiInterface()
.getIssueLabels(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex);
callSingleIssueLabels.enqueue(new Callback<List<Labels>>() {
@Override
public void onResponse(@NonNull Call<List<Labels>> call, @NonNull retrofit2.Response<List<Labels>> response) {
if(response.code() == 200) {
List<Labels> issueLabelsList = response.body();
assert issueLabelsList != null;
if(issueLabelsList.size() > 0) {
for (int i = 0; i < issueLabelsList.size(); i++) {
currentLabelsIds.add(issueLabelsList.get(i).getId());
}
}
}
}
@Override
public void onFailure(@NonNull Call<List<Labels>> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
}
});
}
public static void getRepositoryLabels(Context ctx, String instanceUrl, String instanceToken, String repoOwner, String repoName, List<Labels> labelsList, Dialog dialogLabels, LabelsListAdapter labelsAdapter, CustomLabelsSelectionDialogBinding labelsBinding) {
Call<List<Labels>> call = RetrofitClient
.getInstance(instanceUrl, ctx)
.getApiInterface()
.getlabels(instanceToken, repoOwner, repoName);
call.enqueue(new Callback<List<Labels>>() {
@Override
public void onResponse(@NonNull Call<List<Labels>> call, @NonNull retrofit2.Response<List<Labels>> response) {
labelsList.clear();
List<Labels> labelsList_ = response.body();
labelsBinding.progressBar.setVisibility(View.GONE);
labelsBinding.dialogFrame.setVisibility(View.VISIBLE);
if (response.code() == 200) {
assert labelsList_ != null;
if(labelsList_.size() > 0) {
dialogLabels.show();
labelsList.addAll(labelsList_);
}
else {
dialogLabels.dismiss();
Toasty.warning(ctx, ctx.getResources().getString(R.string.noLabelsFound));
}
labelsBinding.labelsRecyclerView.setAdapter(labelsAdapter);
}
else {
Toasty.error(ctx, ctx.getResources().getString(R.string.genericError));
}
}
@Override
public void onFailure(@NonNull Call<List<Labels>> call, @NonNull Throwable t) {
Toasty.error(ctx, ctx.getResources().getString(R.string.genericServerResponseError));
}
});
}
}

View File

@ -1,294 +0,0 @@
package org.mian.gitnex.activities;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull;
import com.google.gson.JsonElement;
import org.mian.gitnex.R;
import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.helpers.AlertDialogs;
import org.mian.gitnex.helpers.Authorization;
import org.mian.gitnex.helpers.MultiSelectDialog;
import org.mian.gitnex.helpers.TinyDB;
import org.mian.gitnex.helpers.Toasty;
import org.mian.gitnex.models.Collaborators;
import org.mian.gitnex.models.Issues;
import org.mian.gitnex.models.MultiSelectModel;
import org.mian.gitnex.models.UpdateIssueAssignees;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
/**
* Author M M Arif
*/
public class AddRemoveAssigneesActivity extends BaseActivity {
private ArrayList<MultiSelectModel> listOfCollaborators = new ArrayList<>();
private ArrayList<Integer> issueAssigneesIds = new ArrayList<>();
private Boolean assigneesFlag = false;
private MultiSelectDialog multiSelectDialogAssignees;
final Context ctx = this;
private Context appCtx;
@Override
protected int getLayoutResourceId(){
return R.layout.activity_add_remove_assignees;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
appCtx = getApplicationContext();
//supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().getDecorView().setBackground(new ColorDrawable(Color.TRANSPARENT));
TinyDB tinyDb = new TinyDB(appCtx);
final String instanceUrl = tinyDb.getString("instanceUrl");
final String loginUid = tinyDb.getString("loginUid");
String repoFullName = tinyDb.getString("repoFullName");
String[] parts = repoFullName.split("/");
final String repoOwner = parts[0];
final String repoName = parts[1];
final String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
final int issueIndex = Integer.parseInt(tinyDb.getString("issueNumber"));
getAssignees(instanceUrl, instanceToken, repoOwner, repoName, issueIndex, loginUid);
}
private void getAssignees(final String instanceUrl, final String instanceToken, final String repoOwner, final String repoName, final int issueIndex, final String loginUid) {
final TinyDB tinyDb = new TinyDB(appCtx);
Call<List<Collaborators>> call = RetrofitClient
.getInstance(instanceUrl, ctx)
.getApiInterface()
.getCollaborators(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName);
call.enqueue(new Callback<List<Collaborators>>() {
@Override
public void onResponse(@NonNull final Call<List<Collaborators>> call, @NonNull final retrofit2.Response<List<Collaborators>> response) {
if(response.isSuccessful()) {
if(response.code() == 200) {
final List<Collaborators> collaboratorsList_ = response.body();
assert collaboratorsList_ != null;
if(collaboratorsList_.size() > 0) {
for (int i = 0; i < collaboratorsList_.size(); i++) {
listOfCollaborators.add(new MultiSelectModel(collaboratorsList_.get(i).getId(), collaboratorsList_.get(i).getUsername().trim()));
}
}
// get current issue assignees
Call<Issues> callSingleIssueAssignees = RetrofitClient
.getInstance(instanceUrl, ctx)
.getApiInterface()
.getIssueByIndex(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex);
callSingleIssueAssignees.enqueue(new Callback<Issues>() {
@Override
public void onResponse(@NonNull Call<Issues> call, @NonNull retrofit2.Response<Issues> response) {
if(response.code() == 200) {
Issues issueAssigneesList = response.body();
assert issueAssigneesList != null;
if (issueAssigneesList.getAssignees() != null) {
if (issueAssigneesList.getAssignees().size() > 0) {
for (int i = 0; i < issueAssigneesList.getAssignees().size(); i++) {
issueAssigneesIds.add(issueAssigneesList.getAssignees().get(i).getId());
if(issueAssigneesList.getAssignees().get(i).getUsername().equals(loginUid)) {
listOfCollaborators.add(new MultiSelectModel(issueAssigneesList.getAssignees().get(i).getId(), issueAssigneesList.getAssignees().get(i).getUsername().trim()));
}
}
assigneesFlag = true;
}
}
else {
listOfCollaborators.add(new MultiSelectModel(tinyDb.getInt("userId"), loginUid));
}
if(assigneesFlag) {
multiSelectDialogAssignees = new MultiSelectDialog()
.title(getResources().getString(R.string.newIssueSelectAssigneesListTitle))
.titleSize(25)
.positiveText(getResources().getString(R.string.saveButton))
.negativeText(getResources().getString(R.string.cancelButton))
.setMinSelectionLimit(0)
.preSelectIDsList(issueAssigneesIds)
.setMaxSelectionLimit(listOfCollaborators.size())
.multiSelectList(listOfCollaborators)
.onSubmit(new MultiSelectDialog.SubmitCallbackListener() {
@Override
public void onSelected(List<Integer> selectedIds, List<String> selectedNames, String dataString) {
Log.i("selectedNames", String.valueOf(selectedNames));
updateIssueAssignees(instanceUrl, Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, loginUid, issueIndex, selectedNames);
tinyDb.putBoolean("singleIssueUpdate", true);
CloseActivity();
}
@Override
public void onCancel() {
CloseActivity();
}
});
}
else {
multiSelectDialogAssignees = new MultiSelectDialog()
.title(getResources().getString(R.string.newIssueSelectAssigneesListTitle))
.titleSize(25)
.positiveText(getResources().getString(R.string.saveButton))
.negativeText(getResources().getString(R.string.cancelButton))
.setMinSelectionLimit(0)
.setMaxSelectionLimit(listOfCollaborators.size())
.multiSelectList(listOfCollaborators)
.onSubmit(new MultiSelectDialog.SubmitCallbackListener() {
@Override
public void onSelected(List<Integer> selectedIds, List<String> selectedNames, String dataString) {
updateIssueAssignees(instanceUrl, Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, loginUid, issueIndex, selectedNames);
tinyDb.putBoolean("singleIssueUpdate", true);
CloseActivity();
}
@Override
public void onCancel() {
CloseActivity();
}
});
}
multiSelectDialogAssignees.show(getSupportFragmentManager(), "issueMultiSelectDialog");
}
}
@Override
public void onFailure(@NonNull Call<Issues> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
}
});
// get current issue assignees
}
else if(response.code() == 401) {
AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle),
getResources().getString(R.string.alertDialogTokenRevokedMessage),
getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton),
getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
}
else if(response.code() == 403) {
Toasty.error(ctx, ctx.getString(R.string.authorizeError));
}
else if(response.code() == 404) {
Toasty.warning(ctx, ctx.getString(R.string.apiNotFound));
}
else {
Toasty.error(ctx, getString(R.string.genericError));
}
}
}
@Override
public void onFailure(@NonNull Call<List<Collaborators>> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
}
});
}
private void CloseActivity() {
this.finish();
}
private void updateIssueAssignees(final String instanceUrl, final String instanceToken, String repoOwner, String repoName, String loginUid, int issueIndex, List<String> issueAssigneesList) {
UpdateIssueAssignees updateAssigneeJson = new UpdateIssueAssignees(issueAssigneesList);
Call<JsonElement> call3;
call3 = RetrofitClient
.getInstance(instanceUrl, ctx)
.getApiInterface()
.patchIssueAssignees(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex, updateAssigneeJson);
call3.enqueue(new Callback<JsonElement>() {
@Override
public void onResponse(@NonNull Call<JsonElement> call, @NonNull retrofit2.Response<JsonElement> response2) {
if(response2.code() == 201) {
Toasty.success(ctx, ctx.getString(R.string.assigneesUpdated));
}
else if(response2.code() == 401) {
AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle),
getResources().getString(R.string.alertDialogTokenRevokedMessage),
getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton),
getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
}
else if(response2.code() == 403) {
Toasty.error(ctx, ctx.getString(R.string.authorizeError));
}
else if(response2.code() == 404) {
Toasty.warning(ctx, ctx.getString(R.string.apiNotFound));
}
else {
Toasty.error(ctx, getString(R.string.genericError));
}
}
@Override
public void onFailure(@NonNull Call<JsonElement> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
}
});
}
}

View File

@ -1,308 +0,0 @@
package org.mian.gitnex.activities;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.NonNull;
import com.google.gson.JsonElement;
import org.mian.gitnex.R;
import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.helpers.AlertDialogs;
import org.mian.gitnex.helpers.Authorization;
import org.mian.gitnex.helpers.MultiSelectDialog;
import org.mian.gitnex.helpers.TinyDB;
import org.mian.gitnex.helpers.Toasty;
import org.mian.gitnex.models.Labels;
import org.mian.gitnex.models.MultiSelectModel;
import java.util.ArrayList;
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
/**
* Author M M Arif
*/
public class AddRemoveLabelsActivity extends BaseActivity {
private ArrayList<MultiSelectModel> listOfLabels = new ArrayList<>();
private ArrayList<Integer> issueLabelIds = new ArrayList<>();
private Boolean labelsFlag = false;
private MultiSelectDialog multiSelectDialogLabels;
final Context ctx = this;
private Context appCtx;
@Override
protected int getLayoutResourceId(){
return R.layout.activity_add_remove_labels;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
appCtx = getApplicationContext();
//supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().getDecorView().setBackground(new ColorDrawable(Color.TRANSPARENT));
TinyDB tinyDb = new TinyDB(appCtx);
final String instanceUrl = tinyDb.getString("instanceUrl");
final String loginUid = tinyDb.getString("loginUid");
String repoFullName = tinyDb.getString("repoFullName");
String[] parts = repoFullName.split("/");
final String repoOwner = parts[0];
final String repoName = parts[1];
final String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
final int issueIndex = Integer.parseInt(tinyDb.getString("issueNumber"));
getLabels(instanceUrl, instanceToken, repoOwner, repoName, issueIndex, loginUid);
}
private void getLabels(final String instanceUrl, final String instanceToken, final String repoOwner, final String repoName, final int issueIndex, final String loginUid) {
final TinyDB tinyDb = new TinyDB(appCtx);
Call<List<Labels>> call = RetrofitClient
.getInstance(instanceUrl, ctx)
.getApiInterface()
.getlabels(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName);
call.enqueue(new Callback<List<Labels>>() {
@Override
public void onResponse(@NonNull Call<List<Labels>> call, @NonNull retrofit2.Response<List<Labels>> response) {
if(response.isSuccessful()) {
if(response.code() == 200) {
List<Labels> labelsList_ = response.body();
assert labelsList_ != null;
if(labelsList_.size() > 0) {
for (int i = 0; i < labelsList_.size(); i++) {
listOfLabels.add(new MultiSelectModel(labelsList_.get(i).getId(), labelsList_.get(i).getName().trim()));
}
}
// get current issue labels
Call<List<Labels>> callSingleIssueLabels = RetrofitClient
.getInstance(instanceUrl, ctx)
.getApiInterface()
.getIssueLabels(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex);
callSingleIssueLabels.enqueue(new Callback<List<Labels>>() {
@Override
public void onResponse(@NonNull Call<List<Labels>> call, @NonNull retrofit2.Response<List<Labels>> response) {
if(response.code() == 200) {
List<Labels> issueLabelsList = response.body();
assert issueLabelsList != null;
if(issueLabelsList.size() > 0) {
for (int i = 0; i < issueLabelsList.size(); i++) {
issueLabelIds.add(issueLabelsList.get(i).getId());
}
labelsFlag = true;
}
if(labelsFlag) {
multiSelectDialogLabels = new MultiSelectDialog()
.title(getResources().getString(R.string.newIssueSelectLabelsListTitle))
.titleSize(25)
.positiveText(getResources().getString(R.string.saveButton))
.negativeText(getResources().getString(R.string.cancelButton))
.setMinSelectionLimit(0)
.preSelectIDsList(issueLabelIds)
.setMaxSelectionLimit(listOfLabels.size())
.multiSelectList(listOfLabels)
.onSubmit(new MultiSelectDialog.SubmitCallbackListener() {
@Override
public void onSelected(List<Integer> selectedIds, List<String> selectedNames, String dataString) {
String labelIds = selectedIds.toString();
int[] integers;
if (selectedIds.size() > 0) {
String[] items = labelIds.replaceAll("\\[", "").replaceAll("\\]", "").replaceAll("\\s", "").split(",");
integers = new int[items.length];
for (int i = 0; i < integers.length; i++) {
integers[i] = Integer.parseInt(items[i]);
}
}
else {
integers = new int[0];
}
updateIssueLabels(instanceUrl, Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex, integers, loginUid);
tinyDb.putBoolean("singleIssueUpdate", true);
CloseActivity();
}
@Override
public void onCancel() {
CloseActivity();
}
});
}
else {
multiSelectDialogLabels = new MultiSelectDialog()
.title(getResources().getString(R.string.newIssueSelectLabelsListTitle))
.titleSize(25)
.positiveText(getResources().getString(R.string.saveButton))
.negativeText(getResources().getString(R.string.cancelButton))
.setMinSelectionLimit(0)
.setMaxSelectionLimit(listOfLabels.size())
.multiSelectList(listOfLabels)
.onSubmit(new MultiSelectDialog.SubmitCallbackListener() {
@Override
public void onSelected(List<Integer> selectedIds, List<String> selectedNames, String dataString) {
String labelIds = selectedIds.toString();
int[] integers;
if (selectedIds.size() > 0) {
String[] items = labelIds.replaceAll("\\[", "").replaceAll("\\]", "").replaceAll("\\s", "").split(",");
integers = new int[items.length];
for (int i = 0; i < integers.length; i++) {
integers[i] = Integer.parseInt(items[i]);
}
}
else {
integers = new int[0];
}
updateIssueLabels(instanceUrl, Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex, integers, loginUid);
tinyDb.putBoolean("singleIssueUpdate", true);
CloseActivity();
}
@Override
public void onCancel() {
CloseActivity();
}
});
}
multiSelectDialogLabels.show(getSupportFragmentManager(), "issueMultiSelectDialog");
}
}
@Override
public void onFailure(@NonNull Call<List<Labels>> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
}
});
// get current issue labels
}
else if(response.code() == 401) {
AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle),
getResources().getString(R.string.alertDialogTokenRevokedMessage),
getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton),
getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
}
else if(response.code() == 403) {
Toasty.error(ctx, ctx.getString(R.string.authorizeError));
}
else if(response.code() == 404) {
Toasty.warning(ctx, ctx.getString(R.string.apiNotFound));
}
else {
Toasty.error(ctx, getString(R.string.genericError));
}
}
}
@Override
public void onFailure(@NonNull Call<List<Labels>> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
}
});
}
private void updateIssueLabels(final String instanceUrl, final String instanceToken, String repoOwner, String repoName, int issueIndex, int[] issueLabels, String loginUid) {
Labels patchIssueLabels = new Labels(issueLabels);
Call<JsonElement> call = RetrofitClient
.getInstance(instanceUrl, ctx)
.getApiInterface()
.updateIssueLabels(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex, patchIssueLabels);
call.enqueue(new Callback<JsonElement>() {
@Override
public void onResponse(@NonNull Call<JsonElement> call, @NonNull retrofit2.Response<JsonElement> response) {
if(response.code() == 200) {
Toasty.success(ctx, ctx.getString(R.string.labelsUpdated));
}
else if(response.code() == 401) {
AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle),
getResources().getString(R.string.alertDialogTokenRevokedMessage),
getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton),
getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
}
else if(response.code() == 403) {
Toasty.error(ctx, ctx.getString(R.string.authorizeError));
}
else if(response.code() == 404) {
Toasty.warning(ctx, ctx.getString(R.string.apiNotFound));
}
else {
Toasty.error(ctx, getString(R.string.genericError));
}
}
@Override
public void onFailure(@NonNull Call<JsonElement> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
}
});
}
private void CloseActivity() {
this.finish();
}
}

View File

@ -1,29 +1,29 @@
package org.mian.gitnex.activities;
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.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.google.gson.JsonElement;
import com.hendraanggrian.appcompat.socialview.Mention;
import com.hendraanggrian.appcompat.widget.MentionArrayAdapter;
import com.hendraanggrian.appcompat.widget.SocialAutoCompleteTextView;
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.MultiSelectDialog;
import org.mian.gitnex.helpers.StaticGlobalVariables;
import org.mian.gitnex.helpers.TinyDB;
import org.mian.gitnex.helpers.Toasty;
@ -32,45 +32,50 @@ import org.mian.gitnex.models.Collaborators;
import org.mian.gitnex.models.CreateIssue;
import org.mian.gitnex.models.Labels;
import org.mian.gitnex.models.Milestones;
import org.mian.gitnex.models.MultiSelectModel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;
import java.util.Objects;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
/**
* Author M M Arif
*/
public class CreateIssueActivity extends BaseActivity implements View.OnClickListener {
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;
MultiSelectDialog multiSelectDialog;
MultiSelectDialog multiSelectDialogLabels;
private TextView assigneesList;
private TextView newIssueLabels;
private TextView newIssueDueDate;
private Spinner newIssueMilestoneSpinner;
private EditText newIssueTitle;
private SocialAutoCompleteTextView newIssueDescription;
private Button createNewIssueButton;
private TextView labelsIdHolder;
private boolean assigneesFlag;
private boolean labelsFlag;
final Context ctx = this;
private Context appCtx;
private TinyDB tinyDb;
private int resultLimit = StaticGlobalVariables.resultLimitOldGiteaInstances;
private Dialog dialogLabels;
private Dialog dialogAssignees;
private String labelsSetter;
private String assigneesSetter;
private int milestoneId;
List<Milestones> milestonesList = new ArrayList<>();
ArrayList<MultiSelectModel> listOfAssignees = new ArrayList<>();
ArrayList<MultiSelectModel> listOfLabels= new ArrayList<>();
private ArrayAdapter<Mention> defaultMentionAdapter;
private String instanceUrl;
private String loginUid;
private String instanceToken;
private String repoOwner;
private String repoName;
private LabelsListAdapter labelsAdapter;
private AssigneesListAdapter assigneesAdapter;
private List<Integer> labelsIds = new ArrayList<>();
private List<Labels> labelsList = new ArrayList<>();
private List<Milestones> milestonesList = new ArrayList<>();
private List<Collaborators> assigneesList = new ArrayList<>();
private List<String> assigneesListData = new ArrayList<>();
@Override
protected int getLayoutResourceId(){
protected int getLayoutResourceId() {
return R.layout.activity_create_issue;
}
@ -79,20 +84,24 @@ public class CreateIssueActivity extends BaseActivity implements View.OnClickLis
super.onCreate(savedInstanceState);
appCtx = getApplicationContext();
tinyDb = new TinyDB(appCtx);
viewBinding = ActivityCreateIssueBinding.inflate(getLayoutInflater());
View view = viewBinding.getRoot();
setContentView(view);
boolean connToInternet = AppUtil.hasNetworkConnection(appCtx);
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
TinyDB tinyDb = new TinyDB(appCtx);
final String instanceUrl = tinyDb.getString("instanceUrl");
final String loginUid = tinyDb.getString("loginUid");
instanceUrl = tinyDb.getString("instanceUrl");
loginUid = tinyDb.getString("loginUid");
final String loginFullName = tinyDb.getString("userFullname");
String repoFullName = tinyDb.getString("repoFullName");
String[] parts = repoFullName.split("/");
final String repoOwner = parts[0];
final String repoName = parts[1];
final String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
repoOwner = parts[0];
repoName = parts[1];
instanceToken = "token " + tinyDb.getString(loginUid + "-token");
// require gitea 1.12 or higher
if(new Version(tinyDb.getString("giteaVersion")).higherOrEqual("1.12.0")) {
@ -100,71 +109,113 @@ public class CreateIssueActivity extends BaseActivity implements View.OnClickLis
resultLimit = StaticGlobalVariables.resultLimitNewGiteaInstances;
}
ImageView closeActivity = findViewById(R.id.close);
assigneesList = findViewById(R.id.newIssueAssigneesList);
newIssueLabels = findViewById(R.id.newIssueLabels);
newIssueDueDate = findViewById(R.id.newIssueDueDate);
createNewIssueButton = findViewById(R.id.createNewIssueButton);
newIssueTitle = findViewById(R.id.newIssueTitle);
newIssueDescription = findViewById(R.id.newIssueDescription);
labelsIdHolder = findViewById(R.id.labelsIdHolder);
newIssueTitle.requestFocus();
viewBinding.newIssueTitle.requestFocus();
assert imm != null;
imm.showSoftInput(newIssueTitle, InputMethodManager.SHOW_IMPLICIT);
imm.showSoftInput(viewBinding.newIssueTitle, InputMethodManager.SHOW_IMPLICIT);
defaultMentionAdapter = new MentionArrayAdapter<>(this);
loadCollaboratorsList();
newIssueDescription.setMentionAdapter(defaultMentionAdapter);
labelsAdapter = new LabelsListAdapter(labelsList, CreateIssueActivity.this, labelsIds);
assigneesAdapter = new AssigneesListAdapter(ctx, assigneesList, CreateIssueActivity.this, assigneesListData);
initCloseListener();
closeActivity.setOnClickListener(onClickListener);
viewBinding.close.setOnClickListener(onClickListener);
assigneesList.setOnClickListener(this);
newIssueLabels.setOnClickListener(this);
newIssueDueDate.setOnClickListener(this);
viewBinding.newIssueAssigneesList.setOnClickListener(this);
viewBinding.newIssueLabels.setOnClickListener(this);
viewBinding.newIssueDueDate.setOnClickListener(this);
newIssueMilestoneSpinner = findViewById(R.id.newIssueMilestoneSpinner);
getMilestones(instanceUrl, instanceToken, repoOwner, repoName, loginUid, resultLimit);
getLabels(instanceUrl, instanceToken, repoOwner, repoName, loginUid);
getCollaborators(instanceUrl, instanceToken, repoOwner, repoName, loginUid, loginFullName);
disableProcessButton();
viewBinding.newIssueLabels.setOnClickListener(newIssueLabels ->
showLabels()
);
viewBinding.newIssueAssigneesList.setOnClickListener(newIssueAssigneesList ->
showAssignees()
);
if(!connToInternet) {
createNewIssueButton.setEnabled(false);
viewBinding.createNewIssueButton.setEnabled(false);
}
else {
createNewIssueButton.setOnClickListener(this);
viewBinding.createNewIssueButton.setOnClickListener(this);
}
}
@Override
public void assigneesInterface(List<String> data) {
assigneesSetter = String.valueOf(data);
viewBinding.newIssueAssigneesList.setText(assigneesSetter.replace("]", "").replace("[", ""));
assigneesListData = data;
}
@Override
public void labelsInterface(List<String> data) {
labelsSetter = String.valueOf(data);
viewBinding.newIssueLabels.setText(labelsSetter.replace("]", "").replace("[", ""));
}
@Override
public void labelsIdsInterface(List<Integer> 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()
);
AssigneesActions.getRepositoryAssignees(ctx, instanceUrl, instanceToken, 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()
);
LabelsActions.getRepositoryLabels(ctx, instanceUrl, instanceToken, repoOwner, repoName, labelsList, dialogLabels, labelsAdapter, labelsBinding);
}
private void processNewIssue() {
boolean connToInternet = AppUtil.hasNetworkConnection(appCtx);
TinyDB tinyDb = new TinyDB(appCtx);
final String instanceUrl = tinyDb.getString("instanceUrl");
final String loginUid = tinyDb.getString("loginUid");
String repoFullName = tinyDb.getString("repoFullName");
String[] parts = repoFullName.split("/");
final String repoOwner = parts[0];
final String repoName = parts[1];
final String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
Milestones mModel = (Milestones) newIssueMilestoneSpinner.getSelectedItem();
int newIssueMilestoneIdForm = mModel.getId();
String newIssueTitleForm = newIssueTitle.getText().toString();
String newIssueDescriptionForm = newIssueDescription.getText().toString();
String newIssueAssigneesListForm = assigneesList.getText().toString();
//String newIssueLabelsForm = newIssueLabels.getText().toString();
String newIssueDueDateForm = newIssueDueDate.getText().toString();
String newIssueLabelsIdHolderForm = labelsIdHolder.getText().toString();
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) {
@ -177,101 +228,25 @@ public class CreateIssueActivity extends BaseActivity implements View.OnClickLis
Toasty.error(ctx, getString(R.string.issueTitleEmpty));
return;
}
/*if (newIssueDescriptionForm.equals("")) {
Toasty.error(ctx, getString(R.string.issueDescriptionEmpty));
return;
}*/
if (newIssueDueDateForm.equals("")) {
newIssueDueDateForm = null;
} else {
}
else {
newIssueDueDateForm = (AppUtil.customDateCombine(AppUtil.customDateFormat(newIssueDueDateForm)));
}
List<String> newIssueAssigneesListForm_ = new ArrayList<>(Arrays.asList(newIssueAssigneesListForm.split(",")));
for (int i = 0; i < newIssueAssigneesListForm_.size(); i++) {
newIssueAssigneesListForm_.set(i, newIssueAssigneesListForm_.get(i).trim());
}
int[] integers;
if (!newIssueLabelsIdHolderForm.equals("") && !newIssueLabelsIdHolderForm.equals("[]")) {
String[] items = newIssueLabelsIdHolderForm.replaceAll("\\[", "").replaceAll("]", "").replaceAll("\\s", "").split(",");
integers = new int[items.length];
for (int i = 0; i < integers.length; i++) {
integers[i] = Integer.parseInt(items[i]);
}
}
else {
integers = new int[0];
}
//Log.i("FormData", String.valueOf(newIssueLabelsForm));
disableProcessButton();
createNewIssueFunc(instanceUrl, instanceToken, repoOwner, repoName, loginUid, newIssueDescriptionForm, newIssueDueDateForm, newIssueMilestoneIdForm, newIssueTitleForm, newIssueAssigneesListForm_, integers);
createNewIssueFunc(instanceUrl, instanceToken, repoOwner, repoName, loginUid, newIssueDescriptionForm, newIssueDueDateForm, milestoneId, newIssueTitleForm);
}
public void loadCollaboratorsList() {
private void createNewIssueFunc(final String instanceUrl, final String instanceToken, String repoOwner, String repoName, String loginUid, String newIssueDescriptionForm, String newIssueDueDateForm, int newIssueMilestoneIdForm, String newIssueTitleForm) {
final TinyDB tinyDb = new TinyDB(appCtx);
final String instanceUrl = tinyDb.getString("instanceUrl");
final String loginUid = tinyDb.getString("loginUid");
final String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
String repoFullName = tinyDb.getString("repoFullName");
String[] parts = repoFullName.split("/");
final String repoOwner = parts[0];
final String repoName = parts[1];
Call<List<Collaborators>> call = RetrofitClient
.getInstance(instanceUrl, ctx)
.getApiInterface()
.getCollaborators(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName);
call.enqueue(new Callback<List<Collaborators>>() {
@Override
public void onResponse(@NonNull Call<List<Collaborators>> call, @NonNull Response<List<Collaborators>> response) {
if (response.isSuccessful()) {
assert response.body() != null;
String fullName = "";
for (int i = 0; i < response.body().size(); i++) {
if(!response.body().get(i).getFull_name().equals("")) {
fullName = response.body().get(i).getFull_name();
}
defaultMentionAdapter.add(
new Mention(response.body().get(i).getUsername(), fullName, response.body().get(i).getAvatar_url()));
}
} else {
Log.i("onResponse", String.valueOf(response.code()));
}
}
@Override
public void onFailure(@NonNull Call<List<Collaborators>> call, @NonNull Throwable t) {
Log.i("onFailure", t.toString());
}
});
}
private void createNewIssueFunc(final String instanceUrl, final String instanceToken, String repoOwner, String repoName, String loginUid, String newIssueDescriptionForm, String newIssueDueDateForm, int newIssueMilestoneIdForm, String newIssueTitleForm, List<String> newIssueAssigneesListForm, int[] newIssueLabelsForm) {
CreateIssue createNewIssueJson = new CreateIssue(loginUid, newIssueDescriptionForm, false, newIssueDueDateForm, newIssueMilestoneIdForm, newIssueTitleForm, newIssueAssigneesListForm, newIssueLabelsForm);
CreateIssue createNewIssueJson = new CreateIssue(loginUid, newIssueDescriptionForm, false, newIssueDueDateForm, newIssueMilestoneIdForm, newIssueTitleForm, assigneesListData, labelsIds);
Call<JsonElement> call3;
@ -285,19 +260,14 @@ public class CreateIssueActivity extends BaseActivity implements View.OnClickLis
@Override
public void onResponse(@NonNull Call<JsonElement> call, @NonNull retrofit2.Response<JsonElement> response2) {
if(response2.isSuccessful()) {
if(response2.code() == 201) {
if(response2.code() == 201) {
//Log.i("isSuccessful1", String.valueOf(response2.body()));
TinyDB tinyDb = new TinyDB(appCtx);
tinyDb.putBoolean("resumeIssues", true);
Toasty.success(ctx, getString(R.string.issueCreated));
enableProcessButton();
finish();
}
TinyDB tinyDb = new TinyDB(appCtx);
tinyDb.putBoolean("resumeIssues", true);
Toasty.success(ctx, getString(R.string.issueCreated));
enableProcessButton();
finish();
}
else if(response2.code() == 401) {
@ -306,21 +276,19 @@ public class CreateIssueActivity extends BaseActivity implements View.OnClickLis
getResources().getString(R.string.alertDialogTokenRevokedMessage),
getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton),
getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
}
else {
Toasty.error(ctx, getString(R.string.issueCreatedError));
enableProcessButton();
//Log.i("isSuccessful2", String.valueOf(response2.body()));
}
}
@Override
public void onFailure(@NonNull Call<JsonElement> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
Toasty.error(ctx, getString(R.string.genericServerResponseError));
enableProcessButton();
}
});
@ -368,12 +336,16 @@ public class CreateIssueActivity extends BaseActivity implements View.OnClickLis
}
ArrayAdapter<Milestones> adapter = new ArrayAdapter<>(CreateIssueActivity.this,
R.layout.spinner_item, milestonesList);
R.layout.list_spinner_items, milestonesList);
adapter.setDropDownViewResource(R.layout.spinner_dropdown_item);
newIssueMilestoneSpinner.setAdapter(adapter);
viewBinding.newIssueMilestoneSpinner.setAdapter(adapter);
enableProcessButton();
viewBinding.newIssueMilestoneSpinner.setOnItemClickListener ((parent, view, position, id) ->
milestoneId = milestonesList.get(position).getId()
);
}
}
@ -381,143 +353,8 @@ public class CreateIssueActivity extends BaseActivity implements View.OnClickLis
@Override
public void onFailure(@NonNull Call<List<Milestones>> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
}
});
}
private void getCollaborators(String instanceUrl, String instanceToken, String repoOwner, String repoName, String loginUid, String loginFullName) {
Call<List<Collaborators>> call = RetrofitClient
.getInstance(instanceUrl, ctx)
.getApiInterface()
.getCollaborators(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName);
listOfAssignees.add(new MultiSelectModel(-1, loginFullName));
call.enqueue(new Callback<List<Collaborators>>() {
@Override
public void onResponse(@NonNull Call<List<Collaborators>> call, @NonNull retrofit2.Response<List<Collaborators>> response) {
if(response.isSuccessful()) {
if(response.code() == 200) {
List<Collaborators> assigneesList_ = response.body();
assert assigneesList_ != null;
if(assigneesList_.size() > 0) {
for (int i = 0; i < assigneesList_.size(); i++) {
/*String assigneesCopy;
if(!assigneesList_.get(i).getFull_name().equals("")) {
assigneesCopy = getString(R.string.dialogAssignessText, assigneesList_.get(i).getFull_name(), assigneesList_.get(i).getLogin());
}
else {
assigneesCopy = assigneesList_.get(i).getLogin();
}*/
listOfAssignees.add(new MultiSelectModel(assigneesList_.get(i).getId(), assigneesList_.get(i).getLogin().trim()));
}
assigneesFlag = true;
}
multiSelectDialog = new MultiSelectDialog()
.title(getResources().getString(R.string.newIssueSelectAssigneesListTitle))
.titleSize(25)
.positiveText(getResources().getString(R.string.okButton))
.negativeText(getResources().getString(R.string.cancelButton))
.setMinSelectionLimit(0)
.setMaxSelectionLimit(listOfAssignees.size())
.multiSelectList(listOfAssignees)
.onSubmit(new MultiSelectDialog.SubmitCallbackListener() {
@Override
public void onSelected(List<Integer> selectedIds, List<String> selectedNames, String dataString) {
assigneesList.setText(dataString);
}
@Override
public void onCancel() {
//Log.d("multiSelect","Dialog cancelled");
}
});
}
}
}
@Override
public void onFailure(@NonNull Call<List<Collaborators>> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
}
});
}
private void getLabels(String instanceUrl, String instanceToken, String repoOwner, String repoName, String loginUid) {
Call<List<Labels>> call = RetrofitClient
.getInstance(instanceUrl, ctx)
.getApiInterface()
.getlabels(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName);
call.enqueue(new Callback<List<Labels>>() {
@Override
public void onResponse(@NonNull Call<List<Labels>> call, @NonNull retrofit2.Response<List<Labels>> response) {
if(response.isSuccessful()) {
if(response.code() == 200) {
List<Labels> labelsList_ = response.body();
assert labelsList_ != null;
if(labelsList_.size() > 0) {
for (int i = 0; i < labelsList_.size(); i++) {
listOfLabels.add(new MultiSelectModel(labelsList_.get(i).getId(), labelsList_.get(i).getName().trim()));
}
labelsFlag = true;
}
multiSelectDialogLabels = new MultiSelectDialog()
.title(getResources().getString(R.string.newIssueSelectLabelsListTitle))
.titleSize(25)
.positiveText(getResources().getString(R.string.okButton))
.negativeText(getResources().getString(R.string.cancelButton))
.setMinSelectionLimit(0)
.setMaxSelectionLimit(listOfLabels.size())
.multiSelectList(listOfLabels)
.onSubmit(new MultiSelectDialog.SubmitCallbackListener() {
@Override
public void onSelected(List<Integer> selectedIds, List<String> selectedNames, String dataString) {
newIssueLabels.setText(dataString.trim());
labelsIdHolder.setText(selectedIds.toString());
}
@Override
public void onCancel() {
//Log.d("multiSelect","Dialog cancelled");
}
});
}
}
}
@Override
public void onFailure(@NonNull Call<List<Labels>> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
Toasty.error(ctx, getString(R.string.genericServerResponseError));
}
});
@ -525,23 +362,8 @@ public class CreateIssueActivity extends BaseActivity implements View.OnClickLis
@Override
public void onClick(View v) {
if (v == assigneesList) {
if(assigneesFlag) {
multiSelectDialog.show(getSupportFragmentManager(), "multiSelectDialog");
}
else {
Toasty.warning(ctx, getResources().getString(R.string.noAssigneesFound));
}
}
else if (v == newIssueLabels) {
if(labelsFlag) {
multiSelectDialogLabels.show(getSupportFragmentManager(), "multiSelectDialogLabels");
}
else {
Toasty.warning(ctx, getResources().getString(R.string.noLabelsFound));
}
}
else if (v == newIssueDueDate) {
if (v == viewBinding.newIssueDueDate) {
final Calendar c = Calendar.getInstance();
int mYear = c.get(Calendar.YEAR);
@ -549,19 +371,10 @@ public class CreateIssueActivity extends BaseActivity implements View.OnClickLis
final int mDay = c.get(Calendar.DAY_OF_MONTH);
DatePickerDialog datePickerDialog = new DatePickerDialog(this,
new DatePickerDialog.OnDateSetListener() {
@Override
public void onDateSet(DatePicker view, int year,
int monthOfYear, int dayOfMonth) {
newIssueDueDate.setText(getString(R.string.setDueDate, year, (monthOfYear + 1), dayOfMonth));
}
}, mYear, mMonth, mDay);
(view, year, monthOfYear, dayOfMonth) -> viewBinding.newIssueDueDate.setText(getString(R.string.setDueDate, year, (monthOfYear + 1), dayOfMonth)), mYear, mMonth, mDay);
datePickerDialog.show();
}
else if(v == createNewIssueButton) {
else if(v == viewBinding.createNewIssueButton) {
processNewIssue();
}
@ -569,11 +382,11 @@ public class CreateIssueActivity extends BaseActivity implements View.OnClickLis
private void disableProcessButton() {
createNewIssueButton.setEnabled(false);
viewBinding.createNewIssueButton.setEnabled(false);
}
private void enableProcessButton() {
createNewIssueButton.setEnabled(true);
viewBinding.createNewIssueButton.setEnabled(true);
}
}

View File

@ -12,6 +12,7 @@ import android.widget.ArrayAdapter;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import org.mian.gitnex.R;
import org.mian.gitnex.actions.LabelsActions;
import org.mian.gitnex.adapters.LabelsListAdapter;
import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.databinding.ActivityCreatePrBinding;
@ -48,8 +49,8 @@ public class CreatePullRequestActivity extends BaseActivity implements LabelsLis
private int resultLimit = StaticGlobalVariables.resultLimitOldGiteaInstances;
private Dialog dialogLabels;
private String labelsSetter;
private ArrayList<Integer> labelsIds = new ArrayList<>();
private ArrayList<String> assignees = new ArrayList<>();
private List<Integer> labelsIds = new ArrayList<>();
private List<String> assignees = new ArrayList<>();
private int milestoneId;
private String instanceUrl;
@ -96,7 +97,7 @@ public class CreatePullRequestActivity extends BaseActivity implements LabelsLis
resultLimit = StaticGlobalVariables.resultLimitNewGiteaInstances;
}
labelsAdapter = new LabelsListAdapter(labelsList, CreatePullRequestActivity.this);
labelsAdapter = new LabelsListAdapter(labelsList, CreatePullRequestActivity.this, labelsIds);
ImageView closeActivity = findViewById(R.id.close);
@ -164,7 +165,7 @@ public class CreatePullRequestActivity extends BaseActivity implements LabelsLis
//Log.e("processPullRequest", String.valueOf(milestoneId));
}
private void createPullRequest(String prTitle, String prDescription, String mergeInto, String pullFrom, int milestoneId, String dueDate, ArrayList<String> assignees) {
private void createPullRequest(String prTitle, String prDescription, String mergeInto, String pullFrom, int milestoneId, String dueDate, List<String> assignees) {
CreatePullRequest createPullRequest = new CreatePullRequest(prTitle, prDescription, loginUid, mergeInto, pullFrom, milestoneId, dueDate, assignees, labelsIds);
@ -213,14 +214,14 @@ public class CreatePullRequestActivity extends BaseActivity implements LabelsLis
}
@Override
public void labelsStringData(ArrayList<String> data) {
public void labelsInterface(List<String> data) {
labelsSetter = String.valueOf(data);
viewBinding.prLabels.setText(labelsSetter.replace("]", "").replace("[", ""));
}
@Override
public void labelsIdsData(ArrayList<Integer> data) {
public void labelsIdsInterface(List<Integer> data) {
labelsIds = data;
}
@ -242,57 +243,7 @@ public class CreatePullRequestActivity extends BaseActivity implements LabelsLis
dialogLabels.dismiss()
);
Call<List<Labels>> call = RetrofitClient
.getInstance(instanceUrl, ctx)
.getApiInterface()
.getlabels(instanceToken, repoOwner, repoName);
call.enqueue(new Callback<List<Labels>>() {
@Override
public void onResponse(@NonNull Call<List<Labels>> call, @NonNull retrofit2.Response<List<Labels>> response) {
labelsList.clear();
List<Labels> labelsList_ = response.body();
labelsBinding.progressBar.setVisibility(View.GONE);
labelsBinding.dialogFrame.setVisibility(View.VISIBLE);
if (response.code() == 200) {
assert labelsList_ != null;
if(labelsList_.size() > 0) {
for (int i = 0; i < labelsList_.size(); i++) {
labelsList.add(new Labels(labelsList_.get(i).getId(), labelsList_.get(i).getName()));
}
}
else {
dialogLabels.dismiss();
Toasty.warning(ctx, getString(R.string.noLabelsFound));
}
labelsBinding.labelsRecyclerView.setAdapter(labelsAdapter);
}
else {
Toasty.error(ctx, getString(R.string.genericError));
}
}
@Override
public void onFailure(@NonNull Call<List<Labels>> call, @NonNull Throwable t) {
Toasty.error(ctx, getString(R.string.genericServerResponseError));
}
});
dialogLabels.show();
LabelsActions.getRepositoryLabels(ctx, instanceUrl, instanceToken, repoOwner, repoName, labelsList, dialogLabels, labelsAdapter, labelsBinding);
}
private void getBranches(String instanceUrl, String instanceToken, String repoOwner, String repoName, String loginUid) {

View File

@ -1,43 +1,48 @@
package org.mian.gitnex.activities;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.Html;
import android.text.Spanned;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.Toolbar;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.amulyakhare.textdrawable.TextDrawable;
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
import com.google.gson.JsonElement;
import com.vdurmont.emoji.EmojiParser;
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.IssueCommentsAdapter;
import org.mian.gitnex.adapters.LabelsListAdapter;
import org.mian.gitnex.clients.PicassoService;
import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.databinding.ActivityIssueDetailBinding;
import org.mian.gitnex.databinding.CustomAssigneesSelectionDialogBinding;
import org.mian.gitnex.databinding.CustomLabelsSelectionDialogBinding;
import org.mian.gitnex.fragments.BottomSheetSingleIssueFragment;
import org.mian.gitnex.helpers.AlertDialogs;
import org.mian.gitnex.helpers.AppUtil;
@ -48,15 +53,22 @@ import org.mian.gitnex.helpers.LabelWidthCalculator;
import org.mian.gitnex.helpers.RoundedTransformation;
import org.mian.gitnex.helpers.TimeHelper;
import org.mian.gitnex.helpers.TinyDB;
import org.mian.gitnex.helpers.Toasty;
import org.mian.gitnex.helpers.UserMentions;
import org.mian.gitnex.helpers.Version;
import org.mian.gitnex.models.Collaborators;
import org.mian.gitnex.models.Issues;
import org.mian.gitnex.models.Labels;
import org.mian.gitnex.models.UpdateIssueAssignees;
import org.mian.gitnex.models.WatchInfo;
import org.mian.gitnex.viewmodels.IssueCommentsViewModel;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import io.noties.markwon.AbstractMarkwonPlugin;
@ -82,29 +94,36 @@ import retrofit2.Response;
* Author M M Arif
*/
public class IssueDetailActivity extends BaseActivity {
public class IssueDetailActivity extends BaseActivity implements LabelsListAdapter.LabelsListAdapterListener, AssigneesListAdapter.AssigneesListAdapterListener, BottomSheetSingleIssueFragment.BottomSheetListener {
public ImageView closeActivity;
private IssueCommentsAdapter adapter;
private RecyclerView mRecyclerView;
private ImageView assigneeAvatar;
private TextView issueTitle;
private TextView issueDescription;
private TextView issueMilestone;
private TextView issueDueDate;
private TextView issueCreatedTime;
private HorizontalScrollView labelsScrollView;
private HorizontalScrollView assigneesScrollView;
private ScrollView scrollViewComments;
private TextView issueModified;
private ExtendedFloatingActionButton createNewComment;
final Context ctx = this;
private Context appCtx;
private LinearLayout labelsLayout;
private LinearLayout assigneesLayout;
private View divider;
private ProgressBar progressBar;
private ImageView issuePrState;
private TinyDB tinyDb;
private String instanceUrl;
private String loginUid;
private String instanceToken;
private String repoOwner;
private String repoName;
private int issueIndex;
private LabelsListAdapter labelsAdapter;
private AssigneesListAdapter assigneesAdapter;
private List<Integer> currentLabelsIds = new ArrayList<>();
private List<Integer> labelsIds = new ArrayList<>();
private List<Labels> labelsList = new ArrayList<>();
private List<Collaborators> assigneesList = new ArrayList<>();
private List<String> assigneesListData = new ArrayList<>();
private List<String> currentAssignees = new ArrayList<>();
private Dialog dialogLabels;
private Dialog dialogAssignees;
private CustomLabelsSelectionDialogBinding labelsBinding;
private CustomAssigneesSelectionDialogBinding assigneesBinding;
private ActivityIssueDetailBinding viewBinding;
@Override
protected int getLayoutResourceId() {
@ -117,80 +136,65 @@ public class IssueDetailActivity extends BaseActivity {
super.onCreate(savedInstanceState);
appCtx = getApplicationContext();
tinyDb = new TinyDB(appCtx);
final TinyDB tinyDb = new TinyDB(appCtx);
viewBinding = ActivityIssueDetailBinding.inflate(getLayoutInflater());
View view = viewBinding.getRoot();
setContentView(view);
final String instanceUrl = tinyDb.getString("instanceUrl");
final String loginUid = tinyDb.getString("loginUid");
final String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
instanceUrl = tinyDb.getString("instanceUrl");
loginUid = tinyDb.getString("loginUid");
instanceToken = "token " + tinyDb.getString(loginUid + "-token");
String repoFullName = tinyDb.getString("repoFullName");
String[] parts = repoFullName.split("/");
final String repoOwner = parts[0];
final String repoName = parts[1];
final int issueIndex = Integer.parseInt(tinyDb.getString("issueNumber"));
repoOwner = parts[0];
repoName = parts[1];
issueIndex = Integer.parseInt(tinyDb.getString("issueNumber"));
final SwipeRefreshLayout swipeRefresh = findViewById(R.id.pullToRefresh);
assigneeAvatar = findViewById(R.id.assigneeAvatar);
issueTitle = findViewById(R.id.issueTitle);
issueDescription = findViewById(R.id.issueDescription);
issueMilestone = findViewById(R.id.issueMilestone);
issueDueDate = findViewById(R.id.issueDueDate);
issueCreatedTime = findViewById(R.id.issueCreatedTime);
labelsScrollView = findViewById(R.id.labelsScrollView);
assigneesScrollView = findViewById(R.id.assigneesScrollView);
scrollViewComments = findViewById(R.id.scrollViewComments);
issueModified = findViewById(R.id.issueModified);
createNewComment = findViewById(R.id.addNewComment);
labelsLayout = findViewById(R.id.frameLabels);
assigneesLayout = findViewById(R.id.frameAssignees);
divider = findViewById(R.id.divider);
progressBar = findViewById(R.id.progressBar);
issuePrState = findViewById(R.id.issuePrState);
Toolbar toolbar = findViewById(R.id.toolbar);
TextView toolbarTitle = toolbar.findViewById(R.id.toolbar_title);
setSupportActionBar(toolbar);
setSupportActionBar(viewBinding.toolbar);
Objects.requireNonNull(getSupportActionBar()).setTitle(repoName);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
mRecyclerView = findViewById(R.id.recyclerView);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setNestedScrollingEnabled(false);
mRecyclerView.setLayoutManager(new LinearLayoutManager(ctx));
viewBinding.recyclerView.setHasFixedSize(true);
viewBinding.recyclerView.setNestedScrollingEnabled(false);
viewBinding.recyclerView.setLayoutManager(new LinearLayoutManager(ctx));
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(mRecyclerView.getContext(), DividerItemDecoration.VERTICAL);
mRecyclerView.addItemDecoration(dividerItemDecoration);
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(viewBinding.recyclerView.getContext(), DividerItemDecoration.VERTICAL);
viewBinding.recyclerView.addItemDecoration(dividerItemDecoration);
createNewComment.setOnClickListener(v -> startActivity(new Intent(ctx, ReplyToIssueActivity.class)));
viewBinding.addNewComment.setOnClickListener(v -> startActivity(new Intent(ctx, ReplyToIssueActivity.class)));
labelsAdapter = new LabelsListAdapter(labelsList, IssueDetailActivity.this, currentLabelsIds);
assigneesAdapter = new AssigneesListAdapter(ctx, assigneesList, IssueDetailActivity.this, currentAssignees);
LabelsActions.getCurrentIssueLabels(ctx, instanceUrl, loginUid, instanceToken, repoOwner, repoName, issueIndex, currentLabelsIds);
AssigneesActions.getCurrentIssueAssignees(ctx, instanceUrl, loginUid, instanceToken, repoOwner, repoName, issueIndex, currentAssignees);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
scrollViewComments.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
viewBinding.scrollViewComments.setOnScrollChangeListener((v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
if((scrollY - oldScrollY) > 0 && createNewComment.isShown()) {
createNewComment.setVisibility(View.GONE);
if((scrollY - oldScrollY) > 0 && viewBinding.addNewComment.isShown()) {
viewBinding.addNewComment.setVisibility(View.GONE);
}
else if((scrollY - oldScrollY) < 0) {
createNewComment.setVisibility(View.VISIBLE);
viewBinding.addNewComment.setVisibility(View.VISIBLE);
}
if(!scrollViewComments.canScrollVertically(1)) { // bottom
createNewComment.setVisibility(View.GONE);
if(!viewBinding.scrollViewComments.canScrollVertically(1)) { // bottom
viewBinding.addNewComment.setVisibility(View.GONE);
}
if(!scrollViewComments.canScrollVertically(-1)) { // top
createNewComment.setVisibility(View.VISIBLE);
if(!viewBinding.scrollViewComments.canScrollVertically(-1)) { // top
viewBinding.addNewComment.setVisibility(View.VISIBLE);
}
});
}
swipeRefresh.setOnRefreshListener(() -> new Handler().postDelayed(() -> {
viewBinding.pullToRefresh.setOnRefreshListener(() -> new Handler(Looper.getMainLooper()).postDelayed(() -> {
swipeRefresh.setRefreshing(false);
viewBinding.pullToRefresh.setRefreshing(false);
IssueCommentsViewModel
.loadIssueComments(instanceUrl, Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex,
ctx);
@ -215,14 +219,231 @@ public class IssueDetailActivity extends BaseActivity {
}
toolbarTitle.setTypeface(myTypeface);
toolbarTitle.setText(repoName);
viewBinding.toolbarTitle.setTypeface(myTypeface);
viewBinding.toolbarTitle.setText(repoName);
getSingleIssue(instanceUrl, instanceToken, repoOwner, repoName, issueIndex, loginUid);
fetchDataAsync(instanceUrl, instanceToken, repoOwner, repoName, issueIndex, loginUid);
}
@Override
public void onButtonClicked(String text) {
switch(text) {
case "showLabels":
showLabels();
break;
case "showAssignees":
showAssignees();
break;
}
}
@Override
public void labelsInterface(List<String> data) {
}
@Override
public void labelsIdsInterface(List<Integer> data) {
labelsIds = data;
}
@Override
public void assigneesInterface(List<String> data) {
assigneesListData = data;
}
private void showAssignees() {
assigneesAdapter.updateList(currentAssignees);
dialogAssignees = new Dialog(ctx, R.style.ThemeOverlay_MaterialComponents_Dialog_Alert);
dialogAssignees.setCancelable(false);
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_ -> {
currentAssignees = new ArrayList<>(new LinkedHashSet<>(currentAssignees));
assigneesListData = new ArrayList<>(new LinkedHashSet<>(assigneesListData));
Collections.sort(assigneesListData);
Collections.sort(currentAssignees);
if(!assigneesListData.equals(currentAssignees)) {
updateIssueAssignees();
}
else {
dialogAssignees.dismiss();
}
});
AssigneesActions.getRepositoryAssignees(ctx, instanceUrl, instanceToken, repoOwner, repoName, assigneesList, dialogAssignees, assigneesAdapter, assigneesBinding);
}
public void showLabels() {
labelsAdapter.updateList(currentLabelsIds);
dialogLabels = new Dialog(ctx, R.style.ThemeOverlay_MaterialComponents_Dialog_Alert);
dialogLabels.setCancelable(false);
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_ -> {
currentLabelsIds = new ArrayList<>(new LinkedHashSet<>(currentLabelsIds));
labelsIds = new ArrayList<>(new LinkedHashSet<>(labelsIds));
Collections.sort(labelsIds);
Collections.sort(currentLabelsIds);
if(!labelsIds.equals(currentLabelsIds)) {
updateIssueLabels();
}
else {
dialogLabels.dismiss();
}
});
LabelsActions.getRepositoryLabels(ctx, instanceUrl, instanceToken, repoOwner, repoName, labelsList, dialogLabels, labelsAdapter, labelsBinding);
}
private void updateIssueAssignees() {
UpdateIssueAssignees updateAssigneeJson = new UpdateIssueAssignees(assigneesListData);
Call<JsonElement> call3;
call3 = RetrofitClient
.getInstance(instanceUrl, ctx)
.getApiInterface()
.patchIssueAssignees(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex, updateAssigneeJson);
call3.enqueue(new Callback<JsonElement>() {
@Override
public void onResponse(@NonNull Call<JsonElement> call, @NonNull retrofit2.Response<JsonElement> response2) {
if(response2.code() == 201) {
Toasty.success(ctx, ctx.getString(R.string.assigneesUpdated));
dialogAssignees.dismiss();
viewBinding.frameAssignees.removeAllViews();
viewBinding.frameLabels.removeAllViews();
getSingleIssue(instanceUrl, instanceToken, repoOwner, repoName, issueIndex, loginUid);
currentAssignees.clear();
new Handler(Looper.getMainLooper()).postDelayed(() -> AssigneesActions.getCurrentIssueAssignees(ctx, instanceUrl, loginUid, instanceToken, repoOwner, repoName, issueIndex, currentAssignees), 1000);
}
else if(response2.code() == 401) {
AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle),
getResources().getString(R.string.alertDialogTokenRevokedMessage),
getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton),
getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
}
else if(response2.code() == 403) {
Toasty.error(ctx, ctx.getString(R.string.authorizeError));
}
else if(response2.code() == 404) {
Toasty.warning(ctx, ctx.getString(R.string.apiNotFound));
}
else {
Toasty.error(ctx, getString(R.string.genericError));
}
}
@Override
public void onFailure(@NonNull Call<JsonElement> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
}
});
}
private void updateIssueLabels() {
Labels patchIssueLabels = new Labels(labelsIds);
Call<JsonElement> call = RetrofitClient
.getInstance(instanceUrl, ctx)
.getApiInterface()
.updateIssueLabels(Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex, patchIssueLabels);
call.enqueue(new Callback<JsonElement>() {
@Override
public void onResponse(@NonNull Call<JsonElement> call, @NonNull retrofit2.Response<JsonElement> response) {
if(response.code() == 200) {
Toasty.success(ctx, ctx.getString(R.string.labelsUpdated));
dialogLabels.dismiss();
viewBinding.frameAssignees.removeAllViews();
viewBinding.frameLabels.removeAllViews();
getSingleIssue(instanceUrl, instanceToken, repoOwner, repoName, issueIndex, loginUid);
currentLabelsIds.clear();
new Handler(Looper.getMainLooper()).postDelayed(() -> LabelsActions.getCurrentIssueLabels(ctx, instanceUrl, loginUid, instanceToken, repoOwner, repoName, issueIndex, currentLabelsIds), 1000);
}
else if(response.code() == 401) {
AlertDialogs.authorizationTokenRevokedDialog(ctx, getResources().getString(R.string.alertDialogTokenRevokedTitle),
getResources().getString(R.string.alertDialogTokenRevokedMessage),
getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton),
getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
}
else if(response.code() == 403) {
Toasty.error(ctx, ctx.getString(R.string.authorizeError));
}
else if(response.code() == 404) {
Toasty.warning(ctx, ctx.getString(R.string.apiNotFound));
}
else {
Toasty.error(ctx, getString(R.string.genericError));
}
}
@Override
public void onFailure(@NonNull Call<JsonElement> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
@ -254,24 +475,15 @@ public class IssueDetailActivity extends BaseActivity {
public void onResume() {
super.onResume();
final TinyDB tinyDb = new TinyDB(appCtx);
final String instanceUrl = tinyDb.getString("instanceUrl");
final String loginUid = tinyDb.getString("loginUid");
String repoFullName = tinyDb.getString("repoFullName");
String[] parts = repoFullName.split("/");
final String repoOwner = parts[0];
final String repoName = parts[1];
final String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
final int issueIndex = Integer.parseInt(tinyDb.getString("issueNumber"));
if(tinyDb.getBoolean("commentPosted")) {
scrollViewComments.post(() -> {
viewBinding.scrollViewComments.post(() -> {
IssueCommentsViewModel
.loadIssueComments(instanceUrl, Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex,
ctx);
new Handler().postDelayed(() -> scrollViewComments.fullScroll(ScrollView.FOCUS_DOWN), 1000);
new Handler(Looper.getMainLooper()).postDelayed(() -> viewBinding.scrollViewComments.fullScroll(ScrollView.FOCUS_DOWN), 1000);
tinyDb.putBoolean("commentPosted", false);
@ -279,7 +491,7 @@ public class IssueDetailActivity extends BaseActivity {
}
if(tinyDb.getBoolean("commentEdited")) {
scrollViewComments.post(() -> {
viewBinding.scrollViewComments.post(() -> {
IssueCommentsViewModel
.loadIssueComments(instanceUrl, Authorization.returnAuthentication(ctx, loginUid, instanceToken), repoOwner, repoName, issueIndex,
@ -291,10 +503,10 @@ public class IssueDetailActivity extends BaseActivity {
if(tinyDb.getBoolean("singleIssueUpdate")) {
new Handler().postDelayed(() -> {
new Handler(Looper.getMainLooper()).postDelayed(() -> {
assigneesLayout.removeAllViews();
labelsLayout.removeAllViews();
viewBinding.frameAssignees.removeAllViews();
viewBinding.frameLabels.removeAllViews();
getSingleIssue(instanceUrl, instanceToken, repoOwner, repoName, issueIndex, loginUid);
tinyDb.putBoolean("singleIssueUpdate", false);
@ -304,10 +516,10 @@ public class IssueDetailActivity extends BaseActivity {
if(tinyDb.getBoolean("issueEdited")) {
new Handler().postDelayed(() -> {
new Handler(Looper.getMainLooper()).postDelayed(() -> {
assigneesLayout.removeAllViews();
labelsLayout.removeAllViews();
viewBinding.frameAssignees.removeAllViews();
viewBinding.frameLabels.removeAllViews();
getSingleIssue(instanceUrl, instanceToken, repoOwner, repoName, issueIndex, loginUid);
tinyDb.putBoolean("issueEdited", false);
@ -327,11 +539,11 @@ public class IssueDetailActivity extends BaseActivity {
assert issueCommentsMain != null;
if(issueCommentsMain.size() > 0) {
divider.setVisibility(View.VISIBLE);
viewBinding.divider.setVisibility(View.VISIBLE);
}
adapter = new IssueCommentsAdapter(ctx, issueCommentsMain);
mRecyclerView.setAdapter(adapter);
viewBinding.recyclerView.setAdapter(adapter);
});
@ -353,25 +565,25 @@ public class IssueDetailActivity extends BaseActivity {
Issues singleIssue = response.body();
assert singleIssue != null;
issuePrState.setVisibility(View.VISIBLE);
viewBinding.issuePrState.setVisibility(View.VISIBLE);
if(singleIssue.getPull_request() != null) {
if(singleIssue.getPull_request().isMerged()) { // merged
issuePrState.setImageResource(R.drawable.ic_pull_request_merged);
viewBinding.issuePrState.setImageResource(R.drawable.ic_pull_request_merged);
}
else if(!singleIssue.getPull_request().isMerged() && singleIssue.getState().equals("closed")) { // closed
issuePrState.setImageResource(R.drawable.ic_pull_request_closed);
viewBinding.issuePrState.setImageResource(R.drawable.ic_pull_request_closed);
}
else { // open
issuePrState.setImageResource(R.drawable.ic_pull_request);
viewBinding.issuePrState.setImageResource(R.drawable.ic_pull_request);
}
}
else if(singleIssue.getState().equals("closed")) { // issue closed
issuePrState.setImageResource(R.drawable.ic_issue_closed_red);
viewBinding.issuePrState.setImageResource(R.drawable.ic_issue_closed_red);
}
final Markwon markwon = Markwon.builder(Objects.requireNonNull(ctx)).usePlugin(CorePlugin.create())
@ -424,21 +636,21 @@ public class IssueDetailActivity extends BaseActivity {
tinyDb.putString("singleIssueHtmlUrl", singleIssue.getHtml_url());
PicassoService.getInstance(ctx).get().load(singleIssue.getUser().getAvatar_url()).placeholder(R.drawable.loader_animated)
.transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(assigneeAvatar);
.transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(viewBinding.assigneeAvatar);
String issueNumber_ = "<font color='" + appCtx.getResources().getColor(R.color.lightGray) + "'>" + appCtx.getResources()
.getString(R.string.hash) + singleIssue.getNumber() + "</font>";
issueTitle.setText(Html.fromHtml(issueNumber_ + " " + singleIssue.getTitle()));
viewBinding.issueTitle.setText(Html.fromHtml(issueNumber_ + " " + singleIssue.getTitle()));
String cleanIssueDescription = singleIssue.getBody().trim();
Spanned bodyWithMD = markwon.toMarkdown(EmojiParser.parseToUnicode(cleanIssueDescription));
markwon.setParsedMarkdown(issueDescription, UserMentions.UserMentionsFunc(ctx, bodyWithMD, cleanIssueDescription));
markwon.setParsedMarkdown(viewBinding.issueDescription, UserMentions.UserMentionsFunc(ctx, bodyWithMD, cleanIssueDescription));
RelativeLayout.LayoutParams paramsDesc = (RelativeLayout.LayoutParams) issueDescription.getLayoutParams();
RelativeLayout.LayoutParams paramsDesc = (RelativeLayout.LayoutParams) viewBinding.issueDescription.getLayoutParams();
LinearLayout.LayoutParams params1 = new LinearLayout.LayoutParams(80, 80);
params1.setMargins(15, 0, 0, 0);
if(singleIssue.getAssignees() != null) {
assigneesScrollView.setVisibility(View.VISIBLE);
viewBinding.assigneesScrollView.setVisibility(View.VISIBLE);
for(int i = 0; i < singleIssue.getAssignees().size(); i++) {
ImageView assigneesView = new ImageView(ctx);
@ -447,7 +659,7 @@ public class IssueDetailActivity extends BaseActivity {
.placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(100, 100).centerCrop()
.into(assigneesView);
assigneesLayout.addView(assigneesView);
viewBinding.frameAssignees.addView(assigneesView);
assigneesView.setLayoutParams(params1);
if(!singleIssue.getAssignees().get(i).getFull_name().equals("")) {
assigneesView.setOnClickListener(
@ -461,7 +673,7 @@ public class IssueDetailActivity extends BaseActivity {
}
}
else {
assigneesScrollView.setVisibility(View.GONE);
viewBinding.assigneesScrollView.setVisibility(View.GONE);
}
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
@ -469,7 +681,7 @@ public class IssueDetailActivity extends BaseActivity {
params.setMargins(0, 0, 15, 0);
if(singleIssue.getLabels() != null) {
labelsScrollView.setVisibility(View.VISIBLE);
viewBinding.labelsScrollView.setVisibility(View.VISIBLE);
for(int i = 0; i < singleIssue.getLabels().size(); i++) {
@ -478,8 +690,8 @@ public class IssueDetailActivity extends BaseActivity {
int color = Color.parseColor("#" + labelColor);
ImageView labelsView = new ImageView(ctx);
labelsLayout.setOrientation(LinearLayout.HORIZONTAL);
labelsLayout.setGravity(Gravity.START | Gravity.TOP);
viewBinding.frameLabels.setOrientation(LinearLayout.HORIZONTAL);
viewBinding.frameLabels.setGravity(Gravity.START | Gravity.TOP);
labelsView.setLayoutParams(params);
int height = AppUtil.getPixelsFromDensity(ctx, 25);
@ -491,12 +703,12 @@ public class IssueDetailActivity extends BaseActivity {
.height(height).endConfig().buildRoundRect(labelName, color, AppUtil.getPixelsFromDensity(ctx, 5));
labelsView.setImageDrawable(drawable);
labelsLayout.addView(labelsView);
viewBinding.frameLabels.addView(labelsView);
}
}
else {
labelsScrollView.setVisibility(View.GONE);
viewBinding.labelsScrollView.setVisibility(View.GONE);
}
if(singleIssue.getDue_date() != null) {
@ -504,77 +716,77 @@ public class IssueDetailActivity extends BaseActivity {
if(timeFormat.equals("normal") || timeFormat.equals("pretty")) {
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", new Locale(locale));
String dueDate = formatter.format(singleIssue.getDue_date());
issueDueDate.setText(dueDate);
issueDueDate
viewBinding.issueDueDate.setText(dueDate);
viewBinding.issueDueDate
.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(singleIssue.getDue_date()), ctx));
}
else if(timeFormat.equals("normal1")) {
DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy", new Locale(locale));
String dueDate = formatter.format(singleIssue.getDue_date());
issueDueDate.setText(dueDate);
viewBinding.issueDueDate.setText(dueDate);
}
}
else {
issueDueDate.setVisibility(View.GONE);
viewBinding.issueDueDate.setVisibility(View.GONE);
}
String edited;
if(!singleIssue.getUpdated_at().equals(singleIssue.getCreated_at())) {
edited = getString(R.string.colorfulBulletSpan) + getString(R.string.modifiedText);
issueModified.setVisibility(View.VISIBLE);
issueModified.setText(edited);
issueModified
viewBinding.issueModified.setVisibility(View.VISIBLE);
viewBinding.issueModified.setText(edited);
viewBinding.issueModified
.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(singleIssue.getUpdated_at()), ctx));
}
else {
issueModified.setVisibility(View.INVISIBLE);
viewBinding.issueModified.setVisibility(View.INVISIBLE);
}
if((singleIssue.getDue_date() == null && singleIssue.getMilestone() == null) && singleIssue.getAssignees() != null) {
paramsDesc.setMargins(0, 35, 0, 0);
issueDescription.setLayoutParams(paramsDesc);
viewBinding.issueDescription.setLayoutParams(paramsDesc);
}
else if(singleIssue.getDue_date() == null && singleIssue.getMilestone() == null) {
paramsDesc.setMargins(0, 55, 0, 0);
issueDescription.setLayoutParams(paramsDesc);
viewBinding.issueDescription.setLayoutParams(paramsDesc);
}
else if(singleIssue.getAssignees() == null) {
paramsDesc.setMargins(0, 35, 0, 0);
issueDescription.setLayoutParams(paramsDesc);
viewBinding.issueDescription.setLayoutParams(paramsDesc);
}
else {
paramsDesc.setMargins(0, 15, 0, 0);
issueDescription.setLayoutParams(paramsDesc);
viewBinding.issueDescription.setLayoutParams(paramsDesc);
}
issueCreatedTime.setText(TimeHelper.formatTime(singleIssue.getCreated_at(), new Locale(locale), timeFormat, ctx));
issueCreatedTime.setVisibility(View.VISIBLE);
viewBinding.issueCreatedTime.setText(TimeHelper.formatTime(singleIssue.getCreated_at(), new Locale(locale), timeFormat, ctx));
viewBinding.issueCreatedTime.setVisibility(View.VISIBLE);
if(timeFormat.equals("pretty")) {
issueCreatedTime
viewBinding.issueCreatedTime
.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(singleIssue.getCreated_at()), ctx));
}
if(singleIssue.getMilestone() != null) {
issueMilestone.setText(getString(R.string.issueMilestone, singleIssue.getMilestone().getTitle()));
viewBinding.issueMilestone.setText(getString(R.string.issueMilestone, singleIssue.getMilestone().getTitle()));
}
else {
issueMilestone.setVisibility(View.GONE);
viewBinding.issueMilestone.setVisibility(View.GONE);
}
if(!singleIssue.getUser().getFull_name().equals("")) {
assigneeAvatar.setOnClickListener(
viewBinding.assigneeAvatar.setOnClickListener(
new ClickListener(ctx.getResources().getString(R.string.issueCreator) + singleIssue.getUser().getFull_name(), ctx));
}
else {
assigneeAvatar.setOnClickListener(
viewBinding.assigneeAvatar.setOnClickListener(
new ClickListener(ctx.getResources().getString(R.string.issueCreator) + singleIssue.getUser().getLogin(), ctx));
}
progressBar.setVisibility(View.GONE);
viewBinding.progressBar.setVisibility(View.GONE);
}

View File

@ -4,19 +4,16 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import com.tooltip.Tooltip;
import org.mian.gitnex.R;
import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.database.api.UserAccountsApi;
@ -82,7 +79,6 @@ public class LoginActivity extends BaseActivity {
loginPassword = findViewById(R.id.login_passwd);
otpCode = findViewById(R.id.otpCode);
otpInfo = findViewById(R.id.otpInfo);
ImageView info_button = findViewById(R.id.info);
protocolSpinner = findViewById(R.id.httpsSpinner);
loginMethod = findViewById(R.id.loginMethod);
loginTokenCode = findViewById(R.id.loginTokenCode);
@ -108,11 +104,6 @@ public class LoginActivity extends BaseActivity {
});
info_button.setOnClickListener(
view -> new Tooltip.Builder(view).setText(R.string.urlInfoTooltip).setTextColor(getResources().getColor(R.color.colorWhite))
.setBackgroundColor(getResources().getColor(R.color.tooltipBackground)).setCancelable(true).setDismissOnClick(true).setPadding(30)
.setCornerRadius(R.dimen.tooltipCornor).setGravity(Gravity.BOTTOM).show());
loginMethod.setOnCheckedChangeListener((group, checkedId) -> {
if(checkedId == R.id.loginToken) {

View File

@ -0,0 +1,130 @@
package org.mian.gitnex.adapters;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import org.mian.gitnex.R;
import org.mian.gitnex.clients.PicassoService;
import org.mian.gitnex.helpers.RoundedTransformation;
import org.mian.gitnex.models.Collaborators;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
/**
* Author M M Arif
*/
public class AssigneesListAdapter extends RecyclerView.Adapter<AssigneesListAdapter.AssigneesViewHolder> {
private Context mCtx;
private List<Collaborators> assigneesList;
private List<String> assigneesStrings = new ArrayList<>();
private List<String> currentAssignees;
private AssigneesListAdapterListener assigneesListener;
public interface AssigneesListAdapterListener {
void assigneesInterface(List<String> data);
}
public AssigneesListAdapter(Context mCtx, List<Collaborators> dataMain, AssigneesListAdapterListener assigneesListener, List<String> currentAssignees) {
this.mCtx = mCtx;
this.assigneesList = dataMain;
this.assigneesListener = assigneesListener;
this.currentAssignees = currentAssignees;
}
static class AssigneesViewHolder extends RecyclerView.ViewHolder {
private CheckBox assigneesSelection;
private TextView assigneesName;
private ImageView assigneesAvatar;
private AssigneesViewHolder(View itemView) {
super(itemView);
assigneesSelection = itemView.findViewById(R.id.assigneesSelection);
assigneesName = itemView.findViewById(R.id.assigneesName);
assigneesAvatar = itemView.findViewById(R.id.assigneesAvatar);
}
}
@NonNull
@Override
public AssigneesListAdapter.AssigneesViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.custom_assignees_list, parent, false);
return new AssigneesListAdapter.AssigneesViewHolder(v);
}
@Override
public void onBindViewHolder(@NonNull AssigneesListAdapter.AssigneesViewHolder holder, int position) {
Collaborators currentItem = assigneesList.get(position);
if(currentItem.getFull_name().equals("")) {
holder.assigneesName.setText(currentItem.getLogin());
}
else {
holder.assigneesName.setText(currentItem.getFull_name());
}
PicassoService
.getInstance(mCtx).get().load(currentItem.getAvatar_url()).placeholder(R.drawable.loader_animated).transform(new RoundedTransformation(8, 0)).resize(180, 180).centerCrop().into(holder.assigneesAvatar);
for(int i = 0; i < assigneesList.size(); i++) {
if(assigneesStrings.contains(currentItem.getLogin())) {
holder.assigneesSelection.setChecked(true);
}
}
currentAssignees = new ArrayList<>(new LinkedHashSet<>(currentAssignees));
for(int i = 0; i < currentAssignees.size(); i++) {
if(currentAssignees.contains(currentItem.getLogin())) {
holder.assigneesSelection.setChecked(true);
assigneesStrings.add(currentAssignees.get(i));
}
}
assigneesListener.assigneesInterface(assigneesStrings);
holder.assigneesSelection.setOnCheckedChangeListener((buttonView, isChecked) -> {
if(isChecked) {
assigneesStrings.add(currentItem.getLogin());
}
else {
assigneesStrings.remove(currentItem.getLogin());
}
assigneesListener.assigneesInterface(assigneesStrings);
});
assigneesStrings = new ArrayList<>(new LinkedHashSet<>(assigneesStrings));
}
@Override
public int getItemCount() {
return assigneesList.size();
}
public void updateList(List<String> list) {
currentAssignees = list;
notifyDataSetChanged();
}
}

View File

@ -1,14 +1,18 @@
package org.mian.gitnex.adapters;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import org.mian.gitnex.R;
import org.mian.gitnex.models.Labels;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
/**
@ -17,32 +21,38 @@ import java.util.List;
public class LabelsListAdapter extends RecyclerView.Adapter<LabelsListAdapter.LabelsViewHolder> {
private List<Integer> currentLabelsIds;
private List<Labels> labels;
private ArrayList<String> labelsStrings = new ArrayList<>();
private ArrayList<Integer> labelsIds = new ArrayList<>();
private List<String> labelsStrings = new ArrayList<>();
private List<Integer> labelsIds = new ArrayList<>();
private LabelsListAdapterListener labelsListener;
public interface LabelsListAdapterListener {
void labelsStringData(ArrayList<String> data);
void labelsIdsData(ArrayList<Integer> data);
void labelsInterface(List<String> data);
void labelsIdsInterface(List<Integer> data);
}
public LabelsListAdapter(List<Labels> labelsMain, LabelsListAdapterListener labelsListener) {
public LabelsListAdapter(List<Labels> labelsMain, LabelsListAdapterListener labelsListener, List<Integer> currentLabelsIds) {
this.labels = labelsMain;
this.labelsListener = labelsListener;
this.currentLabelsIds = currentLabelsIds;
}
static class LabelsViewHolder extends RecyclerView.ViewHolder {
private CheckBox labelSelection;
private TextView labelText;
private ImageView labelColor;
private LabelsViewHolder(View itemView) {
super(itemView);
labelSelection = itemView.findViewById(R.id.labelSelection);
labelText = itemView.findViewById(R.id.labelText);
labelColor = itemView.findViewById(R.id.labelColor);
}
}
@ -60,7 +70,11 @@ public class LabelsListAdapter extends RecyclerView.Adapter<LabelsListAdapter.La
Labels currentItem = labels.get(position);
holder.labelSelection.setText(currentItem.getName());
String labelColor = currentItem.getColor();
int color = Color.parseColor("#" + labelColor);
holder.labelText.setText(currentItem.getName());
holder.labelColor.setBackgroundColor(color);
for(int i = 0; i < labelsIds.size(); i++) {
@ -70,6 +84,17 @@ public class LabelsListAdapter extends RecyclerView.Adapter<LabelsListAdapter.La
}
}
currentLabelsIds = new ArrayList<>(new LinkedHashSet<>(currentLabelsIds));
for(int i = 0; i < currentLabelsIds.size(); i++) {
if(currentLabelsIds.contains(currentItem.getId())) {
holder.labelSelection.setChecked(true);
labelsIds.add(currentLabelsIds.get(i));
}
}
labelsListener.labelsIdsInterface(labelsIds);
holder.labelSelection.setOnCheckedChangeListener((buttonView, isChecked) -> {
if(isChecked) {
@ -83,13 +108,21 @@ public class LabelsListAdapter extends RecyclerView.Adapter<LabelsListAdapter.La
labelsIds.remove(Integer.valueOf(currentItem.getId()));
}
labelsListener.labelsStringData(labelsStrings);
labelsListener.labelsIdsData(labelsIds);
labelsListener.labelsInterface(labelsStrings);
labelsListener.labelsIdsInterface(labelsIds);
});
labelsIds = new ArrayList<>(new LinkedHashSet<>(labelsIds));
}
@Override
public int getItemCount() {
return labels.size();
}
public void updateList(List<Integer> list) {
currentLabelsIds = list;
notifyDataSetChanged();
}
}

View File

@ -1,190 +0,0 @@
package org.mian.gitnex.adapters;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Typeface;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.style.TextAppearanceSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.widget.AppCompatCheckBox;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.RecyclerView;
import org.mian.gitnex.R;
import org.mian.gitnex.helpers.MultiSelectDialog;
import org.mian.gitnex.models.MultiSelectModel;
import java.util.List;
/**
* Author com.github.abumoallim, modified by M M Arif
*/
public class MutliSelectAdapter extends RecyclerView.Adapter<MutliSelectAdapter.MultiSelectDialogViewHolder> {
private List<MultiSelectModel> mDataSet;
private String mSearchQuery = "";
private Context mContext;
public MutliSelectAdapter(List<MultiSelectModel> dataSet, Context context) {
this.mDataSet = dataSet;
this.mContext = context;
}
@NonNull
@Override
public MultiSelectDialogViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.multi_select_item, parent, false);
return new MultiSelectDialogViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull final MultiSelectDialogViewHolder holder, int position) {
if (!mSearchQuery.equals("") && mSearchQuery.length() > 1) {
setHighlightedText(position, holder.dialog_name_item);
} else {
holder.dialog_name_item.setText(mDataSet.get(position).getName());
}
if (mDataSet.get(position).getSelected()) {
if (!MultiSelectDialog.selectedIdsForCallback.contains(mDataSet.get(position).getId())) {
MultiSelectDialog.selectedIdsForCallback.add(mDataSet.get(position).getId());
}
}
if (checkForSelection(mDataSet.get(position).getId())) {
holder.dialog_item_checkbox.setChecked(true);
} else {
holder.dialog_item_checkbox.setChecked(false);
}
/*holder.dialog_item_checkbox.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (holder.dialog_item_checkbox.isChecked()) {
MultiSelectDialog.selectedIdsForCallback.add(mDataSet.get(holder.getAdapterPosition()).getId());
holder.dialog_item_checkbox.setChecked(true);
} else {
removeFromSelection(mDataSet.get(holder.getAdapterPosition()).getId());
holder.dialog_item_checkbox.setChecked(false);
}
}
});*/
holder.main_container.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (!holder.dialog_item_checkbox.isChecked()) {
MultiSelectDialog.selectedIdsForCallback.add(mDataSet.get(holder.getAdapterPosition()).getId());
holder.dialog_item_checkbox.setChecked(true);
mDataSet.get(holder.getAdapterPosition()).setSelected(true);
notifyItemChanged(holder.getAdapterPosition());
} else {
removeFromSelection(mDataSet.get(holder.getAdapterPosition()).getId());
holder.dialog_item_checkbox.setChecked(false);
mDataSet.get(holder.getAdapterPosition()).setSelected(false);
notifyItemChanged(holder.getAdapterPosition());
}
}
});
}
private void setHighlightedText(int position, TextView textview) {
String name = mDataSet.get(position).getName();
SpannableString str = new SpannableString(name);
int endLength = name.toLowerCase().indexOf(mSearchQuery) + mSearchQuery.length();
ColorStateList highlightedColor = new ColorStateList(new int[][]{new int[]{}}, new int[]{ContextCompat.getColor(mContext, R.color.colorAccent)});
TextAppearanceSpan textAppearanceSpan = new TextAppearanceSpan(null, Typeface.NORMAL, -1, highlightedColor, null);
str.setSpan(textAppearanceSpan, name.toLowerCase().indexOf(mSearchQuery), endLength, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textview.setText(str);
}
private void removeFromSelection(Integer id) {
for (int i = 0; i < MultiSelectDialog.selectedIdsForCallback.size(); i++) {
if (id.equals(MultiSelectDialog.selectedIdsForCallback.get(i))) {
MultiSelectDialog.selectedIdsForCallback.remove(i);
}
}
}
private boolean checkForSelection(Integer id) {
for (int i = 0; i < MultiSelectDialog.selectedIdsForCallback.size(); i++) {
if (id.equals(MultiSelectDialog.selectedIdsForCallback.get(i))) {
return true;
}
}
return false;
}
/*//get selected name string separated by coma
public String getDataString() {
String data = "";
for (int i = 0; i < mDataSet.size(); i++) {
if (checkForSelection(mDataSet.get(i).getId())) {
data = data + ", " + mDataSet.get(i).getName();
}
}
if (data.length() > 0) {
return data.substring(1);
} else {
return "";
}
}
//get selected name ararylist
public ArrayList<String> getSelectedNameList() {
ArrayList<String> names = new ArrayList<>();
for (int i = 0; i < mDataSet.size(); i++) {
if (checkForSelection(mDataSet.get(i).getId())) {
names.add(mDataSet.get(i).getName());
}
}
// return names.toArray(new String[names.size()]);
return names;
}*/
@Override
public int getItemCount() {
return mDataSet.size();
}
public void setData(List<MultiSelectModel> data, String query, MutliSelectAdapter mutliSelectAdapter) {
this.mDataSet = data;
this.mSearchQuery = query;
mutliSelectAdapter.notifyDataSetChanged();
}
class MultiSelectDialogViewHolder extends RecyclerView.ViewHolder {
private TextView dialog_name_item;
private AppCompatCheckBox dialog_item_checkbox;
private LinearLayout main_container;
MultiSelectDialogViewHolder(View view) {
super(view);
dialog_name_item = view.findViewById(R.id.dialog_item_name);
dialog_item_checkbox = view.findViewById(R.id.dialog_item_checkbox);
main_container = view.findViewById(R.id.main_container);
}
}
}

View File

@ -14,8 +14,6 @@ import androidx.annotation.Nullable;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import org.mian.gitnex.R;
import org.mian.gitnex.actions.IssueActions;
import org.mian.gitnex.activities.AddRemoveAssigneesActivity;
import org.mian.gitnex.activities.AddRemoveLabelsActivity;
import org.mian.gitnex.activities.EditIssueActivity;
import org.mian.gitnex.activities.FileDiffActivity;
import org.mian.gitnex.activities.MergePullRequestActivity;
@ -30,6 +28,8 @@ import java.util.Objects;
public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment {
private BottomSheetListener bmListener;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
@ -78,42 +78,36 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment {
else {
mergePullRequest.setVisibility(View.GONE);
}
mergePullRequest.setOnClickListener(v13 -> {
startActivity(new Intent(ctx, MergePullRequestActivity.class));
dismiss();
});
openFilesDiff.setOnClickListener(v14 -> {
startActivity(new Intent(ctx, FileDiffActivity.class));
dismiss();
});
editIssue.setOnClickListener(v15 -> {
startActivity(new Intent(ctx, EditIssueActivity.class));
dismiss();
});
editLabels.setOnClickListener(v16 -> {
startActivity(new Intent(ctx, AddRemoveLabelsActivity.class));
bmListener.onButtonClicked("showLabels");
dismiss();
});
addRemoveAssignees.setOnClickListener(v17 -> {
startActivity(new Intent(ctx, AddRemoveAssigneesActivity.class));
bmListener.onButtonClicked("showAssignees");
dismiss();
});
shareIssue.setOnClickListener(v1 -> {
@ -125,7 +119,6 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment {
startActivity(Intent.createChooser(sharingIntent, getResources().getString(R.string.hash) + tinyDB.getString("issueNumber") + " " + tinyDB.getString("issueTitle")));
dismiss();
});
copyIssueUrl.setOnClickListener(v12 -> {
@ -139,7 +132,6 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment {
Toasty.info(ctx, ctx.getString(R.string.copyIssueUrlToastMsg));
dismiss();
});
if(tinyDB.getString("issueType").equalsIgnoreCase("Issue")) {
@ -183,14 +175,12 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment {
IssueActions.subscribe(ctx);
dismiss();
});
unsubscribeIssue.setOnClickListener(unsubscribeToIssue -> {
IssueActions.unsubscribe(ctx);
dismiss();
});
if(new Version(tinyDB.getString("giteaVersion")).less("1.12.0")) {
@ -209,4 +199,23 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment {
return v;
}
public interface BottomSheetListener {
void onButtonClicked(String text);
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
try {
bmListener = (BottomSheetListener) context;
}
catch(ClassCastException e) {
throw new ClassCastException(context.toString() + " must implement BottomSheetListener");
}
}
}

View File

@ -1,318 +0,0 @@
package org.mian.gitnex.helpers;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatDialogFragment;
import androidx.appcompat.widget.SearchView;
import androidx.recyclerview.widget.LinearLayoutManager;
import org.mian.gitnex.R;
import org.mian.gitnex.adapters.MutliSelectAdapter;
import org.mian.gitnex.models.MultiSelectModel;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Author com.github.abumoallim, modified by M M Arif
*/
public class MultiSelectDialog extends AppCompatDialogFragment implements SearchView.OnQueryTextListener, View.OnClickListener {
public static List<Integer> selectedIdsForCallback = new ArrayList<>();
public List<MultiSelectModel> mainListOfAdapter = new ArrayList<>();
private MutliSelectAdapter mutliSelectAdapter;
//Default Values
private String title;
private float titleSize = 25;
private String positiveText = "DONE";
private String negativeText = "CANCEL";
private TextView dialogTitle, dialogSubmit, dialogCancel;
private List<Integer> previouslySelectedIdsList = new ArrayList<>();
private List<Integer> tempPreviouslySelectedIdsList = new ArrayList<>();
private List<MultiSelectModel> tempMainListOfAdapter = new ArrayList<>();
private SubmitCallbackListener submitCallbackListener;
private int minSelectionLimit = 1;
private String minSelectionMessage = null;
private int maxSelectionLimit = 0;
private String maxSelectionMessage = null;
@NonNull
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Dialog dialog = new Dialog(Objects.requireNonNull(getActivity()));
Objects.requireNonNull(dialog.getWindow()).requestFeature(Window.FEATURE_NO_TITLE);
dialog.getWindow().setFlags(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
dialog.setContentView(R.layout.custom_multi_select);
dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
RecyclerViewEmptySupport mrecyclerView = dialog.findViewById(R.id.recycler_view);
SearchView searchView = dialog.findViewById(R.id.search_view);
dialogTitle = dialog.findViewById(R.id.title);
dialogSubmit = dialog.findViewById(R.id.done);
dialogCancel = dialog.findViewById(R.id.cancel);
mrecyclerView.setEmptyView(dialog.findViewById(R.id.list_empty1));
@SuppressLint("WrongConstant") LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL, false);
mrecyclerView.setLayoutManager(layoutManager);
dialogSubmit.setOnClickListener(this);
dialogCancel.setOnClickListener(this);
settingValues();
mainListOfAdapter = setCheckedIDS(mainListOfAdapter, previouslySelectedIdsList);
mutliSelectAdapter = new MutliSelectAdapter(mainListOfAdapter, getContext());
mrecyclerView.setAdapter(mutliSelectAdapter);
searchView.setOnQueryTextListener(this);
searchView.onActionViewExpanded();
searchView.clearFocus();
return dialog;
}
public MultiSelectDialog title(String title) {
this.title = title;
return this;
}
public MultiSelectDialog titleSize(float titleSize) {
this.titleSize = titleSize;
return this;
}
public MultiSelectDialog positiveText(@NonNull String message) {
this.positiveText = message;
return this;
}
public MultiSelectDialog negativeText(@NonNull String message) {
this.negativeText = message;
return this;
}
public MultiSelectDialog preSelectIDsList(List<Integer> list) {
this.previouslySelectedIdsList = list;
this.tempPreviouslySelectedIdsList = new ArrayList<>(previouslySelectedIdsList);
return this;
}
public MultiSelectDialog multiSelectList(List<MultiSelectModel> list) {
this.mainListOfAdapter = list;
this.tempMainListOfAdapter = new ArrayList<>(mainListOfAdapter);
if(maxSelectionLimit == 0)
maxSelectionLimit = list.size();
return this;
}
public MultiSelectDialog setMaxSelectionLimit(int limit){
this.maxSelectionLimit = limit;
return this;
}
public MultiSelectDialog setMaxSelectionMessage(String message) {
this.maxSelectionMessage = message;
return this;
}
public MultiSelectDialog setMinSelectionLimit(int limit){
this.minSelectionLimit = limit;
return this;
}
public MultiSelectDialog setMinSelectionMessage(String message) {
this.minSelectionMessage = message;
return this;
}
public MultiSelectDialog onSubmit(@NonNull SubmitCallbackListener callback) {
this.submitCallbackListener = callback;
return this;
}
private void settingValues() {
dialogTitle.setText(title);
dialogTitle.setTextSize(TypedValue.COMPLEX_UNIT_SP, titleSize);
dialogSubmit.setText(positiveText.toUpperCase());
dialogCancel.setText(negativeText.toUpperCase());
}
private List<MultiSelectModel> setCheckedIDS(List<MultiSelectModel> multiselectdata, List<Integer> listOfIdsSelected) {
for (int i = 0; i < multiselectdata.size(); i++) {
multiselectdata.get(i).setSelected(false);
for (int j = 0; j < listOfIdsSelected.size(); j++) {
if (listOfIdsSelected.get(j) == (multiselectdata.get(i).getId())) {
multiselectdata.get(i).setSelected(true);
}
}
}
return multiselectdata;
}
private List<MultiSelectModel> filter(List<MultiSelectModel> models, String query) {
query = query.toLowerCase();
final List<MultiSelectModel> filteredModelList = new ArrayList<>();
if (query.equals("") | query.isEmpty()) {
filteredModelList.addAll(models);
return filteredModelList;
}
for (MultiSelectModel model : models) {
final String name = model.getName().toLowerCase();
if (name.contains(query)) {
filteredModelList.add(model);
}
}
return filteredModelList;
}
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
selectedIdsForCallback = previouslySelectedIdsList;
mainListOfAdapter = setCheckedIDS(mainListOfAdapter, selectedIdsForCallback);
List<MultiSelectModel> filteredlist = filter(mainListOfAdapter, newText);
mutliSelectAdapter.setData(filteredlist, newText.toLowerCase(), mutliSelectAdapter);
return false;
}
@Override
public void onClick(View view) {
if (view.getId() == R.id.done) {
List<Integer> callBackListOfIds = selectedIdsForCallback;
if (callBackListOfIds.size() >= minSelectionLimit) {
if (callBackListOfIds.size() <= maxSelectionLimit) {
//to remember last selected ids which were successfully done
tempPreviouslySelectedIdsList = new ArrayList<>(callBackListOfIds);
if(submitCallbackListener !=null) {
submitCallbackListener.onSelected(callBackListOfIds, getSelectNameList(), getSelectedDataString());
}
dismiss();
} else {
String youCan = getResources().getString(R.string.you_can_only_select_upto);
String options = getResources().getString(R.string.options);
String option = getResources().getString(R.string.option);
String message = "";
if(this.maxSelectionMessage != null) {
message = maxSelectionMessage;
}
else {
if (maxSelectionLimit > 1)
message = youCan + " " + maxSelectionLimit + " " + options;
else
message = youCan + " " + maxSelectionLimit + " " + option;
}
Toast.makeText(getActivity(), message, Toast.LENGTH_LONG).show();
}
} else {
String pleaseSelect = getResources().getString(R.string.please_select_atleast);
String options = getResources().getString(R.string.options);
String option = getResources().getString(R.string.option);
String message = "";
if(this.minSelectionMessage != null) {
message = minSelectionMessage;
}
else {
if (minSelectionLimit > 1)
message = pleaseSelect + " " + minSelectionLimit + " " + options;
else
message = pleaseSelect + " " + minSelectionLimit + " " + option;
}
Toast.makeText(getActivity(), message, Toast.LENGTH_LONG).show();
}
}
if (view.getId() == R.id.cancel) {
if(submitCallbackListener!=null){
selectedIdsForCallback.clear();
selectedIdsForCallback.addAll(tempPreviouslySelectedIdsList);
submitCallbackListener.onCancel();
}
dismiss();
}
}
private String getSelectedDataString() {
String data = "";
for (int i = 0; i < tempMainListOfAdapter.size(); i++) {
if (checkForSelection(tempMainListOfAdapter.get(i).getId())) {
data = data + ", " + tempMainListOfAdapter.get(i).getName();
}
}
if (data.length() > 0) {
return data.substring(1);
} else {
return "";
}
}
private List<String> getSelectNameList() {
List<String> names = new ArrayList<>();
for(int i=0;i<tempMainListOfAdapter.size();i++){
if(checkForSelection(tempMainListOfAdapter.get(i).getId())){
names.add(tempMainListOfAdapter.get(i).getName());
}
}
return names;
}
private boolean checkForSelection(Integer id) {
for (int i = 0; i < MultiSelectDialog.selectedIdsForCallback.size(); i++) {
if (id.equals(MultiSelectDialog.selectedIdsForCallback.get(i))) {
return true;
}
}
return false;
}
/* public void setCallbackListener(SubmitCallbackListener submitCallbackListener) {
this.submitCallbackListener = submitCallbackListener;
}*/
public interface SubmitCallbackListener {
void onSelected(List<Integer> selectedIds, List<String> selectedNames, String commonSeperatedData);
void onCancel();
}
}

View File

@ -163,6 +163,9 @@ public interface ApiInterface {
@GET("repos/{owner}/{repo}/collaborators") // get collaborators list
Call<List<Collaborators>> getCollaborators(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName);
@GET("orgs/{org}/members") // get organization members
Call<List<Collaborators>> getOrgMembers(@Header("Authorization") String token, @Path("org") String ownerName);
@POST("repos/{owner}/{repo}/milestones") // create new milestone
Call<Milestones> createMilestone(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName, @Body Milestones jsonStr);

View File

@ -1,5 +1,7 @@
package org.mian.gitnex.models;
import java.util.Objects;
/**
* Author M M Arif
*/
@ -14,6 +16,13 @@ public class Collaborators {
private String language;
private String username;
public Collaborators(String full_name, String login, String avatar_url) {
this.full_name = full_name;
this.login = login;
this.avatar_url = avatar_url;
}
public int getId() {
return id;
}
@ -41,4 +50,24 @@ public class Collaborators {
public String getUsername() {
return username;
}
@Override
public boolean equals(Object o) {
if(this == o) {
return true;
}
if(o == null || getClass() != o.getClass()) {
return false;
}
Collaborators that = (Collaborators) o;
return Objects.equals(login, that.login);
}
@Override
public int hashCode() {
return Objects.hash(login);
}
}

View File

@ -15,9 +15,9 @@ public class CreateIssue {
private String title;
private List<String> assignees;
private int[] labels;
private List<Integer> labels;
public CreateIssue(String assignee, String body, boolean closed, String due_date, int milestone, String title, List<String> assignees, int[] labels) {
public CreateIssue(String assignee, String body, boolean closed, String due_date, int milestone, String title, List<String> assignees, List<Integer> labels) {
this.body = body;
this.closed = closed;
this.due_date = due_date;

View File

@ -1,6 +1,6 @@
package org.mian.gitnex.models;
import java.util.ArrayList;
import java.util.List;
/**
* Author M M Arif
@ -17,10 +17,10 @@ public class CreatePullRequest {
private String due_date;
private String message;
private ArrayList<String> assignees;
private ArrayList<Integer> labels;
private List<String> assignees;
private List<Integer> labels;
public CreatePullRequest(String title, String body, String assignee, String base, String head, int milestone, String due_date, ArrayList<String> assignees, ArrayList<Integer> labels) {
public CreatePullRequest(String title, String body, String assignee, String base, String head, int milestone, String due_date, List<String> assignees, List<Integer> labels) {
this.title = title;
this.body = body;

View File

@ -1,5 +1,7 @@
package org.mian.gitnex.models;
import java.util.List;
/**
* Author M M Arif
*/
@ -10,20 +12,21 @@ public class Labels {
private String name;
private String color;
private String url;
private int[] labels;
private List<Integer> labels;
public Labels(String name, String color) {
this.name = name;
this.color = color;
}
public Labels(int[] labels) {
public Labels(List<Integer> labels) {
this.labels = labels;
}
public Labels(int id, String name) {
public Labels(int id, String name, String color) {
this.id = id;
this.name = name;
this.color = color;
}
public int getId() {

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/primaryBackgroundColor"
tools:context=".activities.AddRemoveAssigneesActivity" />

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/primaryBackgroundColor"
tools:context=".activities.AddRemoveLabelsActivity" />

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:layout_width="match_parent"
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="?attr/primaryBackgroundColor">
@ -55,155 +55,155 @@
android:padding="16dp"
android:orientation="vertical">
<TextView
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/newIssueTitleLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/newIssueTitle"
android:textColor="?attr/primaryTextColor"
android:textSize="16sp" />
<EditText
android:id="@+id/newIssueTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:padding="10dp"
android:textSize="14sp"
tools:ignore="Autofill"
android:inputType="textCapSentences|text"
android:labelFor="@+id/newIssueTitle"
android:background="@drawable/shape_inputs"
android:textColor="?attr/inputTextColor"
app:boxBackgroundColor="?attr/inputBackgroundColor"
android:textColorHint="?attr/hintColor"
android:textColorHighlight="?attr/primaryTextColor" />
app:hintTextColor="?attr/hintColor"
app:boxStrokeErrorColor="@color/darkRed"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:endIconMode="clear_text"
app:endIconTint="?attr/iconsColor"
app:counterEnabled="true"
app:counterMaxLength="255"
app:counterTextColor="?attr/inputTextColor"
android:hint="@string/newIssueTitle">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/newIssueDescriptionTitle"
android:textColor="?attr/primaryTextColor"
android:textSize="16sp"
android:layout_marginTop="10dp"/>
<com.hendraanggrian.appcompat.widget.SocialAutoCompleteTextView
android:id="@+id/newIssueDescription"
android:layout_width="match_parent"
android:layout_height="240dp"
android:layout_marginTop="10dp"
android:padding="10dp"
android:completionThreshold="1"
android:background="@drawable/shape_inputs"
android:maxLines="10"
android:minLines="8"
tools:ignore="Autofill"
android:labelFor="@+id/newReplyToIssue"
android:scrollbars="vertical"
android:gravity="top|start"
android:textSize="14sp"
android:inputType="textCapSentences|textMultiLine"
android:textColor="?attr/inputTextColor"
android:textColorHint="?attr/hintColor"
android:textColorHighlight="?attr/primaryTextColor" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/newIssueAssigneesListTitle"
android:textColor="?attr/primaryTextColor"
android:textSize="16sp"
android:layout_marginTop="10dp"/>
<TextView
android:id="@+id/newIssueAssigneesList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:padding="10dp"
android:textSize="14sp"
tools:ignore="Autofill"
android:background="@drawable/shape_inputs"
android:textColor="?attr/inputTextColor"
android:textColorHint="?attr/hintColor"
android:inputType="none"
android:textColorHighlight="?attr/primaryTextColor"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/labelsIdHolder"
android:visibility="gone" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/newIssueMilestoneTitle"
android:textColor="?attr/primaryTextColor"
android:textSize="16sp"
android:layout_marginTop="10dp"/>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/shape_dropdown"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp" >
<Spinner
android:id="@+id/newIssueMilestoneSpinner"
android:layout_width="wrap_content"
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/newIssueTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:spinnerMode="dropdown"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:paddingStart="5dp" />
android:textColor="?attr/inputTextColor"
android:textColorHighlight="?attr/hintColor"
android:textColorHint="?attr/hintColor"
android:textSize="16sp" />
</RelativeLayout>
</com.google.android.material.textfield.TextInputLayout>
<TextView
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/newIssueDescriptionLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/newIssueLabelsTitle"
android:textColor="?attr/primaryTextColor"
android:textSize="16sp"
android:layout_marginTop="10dp"/>
<TextView
android:id="@+id/newIssueLabels"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:padding="10dp"
android:textSize="14sp"
tools:ignore="Autofill"
android:background="@drawable/shape_inputs"
android:textColor="?attr/inputTextColor"
app:boxBackgroundColor="?attr/inputBackgroundColor"
android:textColorHint="?attr/hintColor"
android:inputType="none"
android:textColorHighlight="?attr/primaryTextColor"/>
app:hintTextColor="?attr/hintColor"
app:boxStrokeErrorColor="@color/darkRed"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:endIconMode="clear_text"
app:endIconTint="?attr/iconsColor"
android:hint="@string/newIssueDescriptionTitle">
<TextView
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/newIssueDescription"
android:layout_width="match_parent"
android:layout_height="140dp"
android:textColor="?attr/inputTextColor"
android:textColorHighlight="?attr/hintColor"
android:textColorHint="?attr/hintColor"
android:textSize="16sp" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/newIssueAssigneesListLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/newIssueDueDateTitle"
android:textColor="?attr/primaryTextColor"
android:textSize="16sp"
android:layout_marginTop="10dp" />
<TextView
android:id="@+id/newIssueDueDate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:focusable="false"
android:layout_marginTop="10dp"
android:padding="10dp"
android:textSize="14sp"
tools:ignore="Autofill"
android:background="@drawable/shape_inputs"
android:textColor="?attr/inputTextColor"
app:boxBackgroundColor="?attr/inputBackgroundColor"
android:textColorHint="?attr/hintColor"
android:textColorHighlight="?attr/primaryTextColor"/>
app:hintTextColor="?attr/hintColor"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:hint="@string/newIssueAssigneesListTitle">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/newIssueAssigneesList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?attr/inputTextColor"
android:textColorHighlight="?attr/hintColor"
android:textColorHint="?attr/hintColor"
android:focusable="false"
android:textSize="16sp" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/newIssueMilestoneSpinnerLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:boxBackgroundColor="?attr/inputBackgroundColor"
android:textColorHint="?attr/hintColor"
app:hintTextColor="?attr/hintColor"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:hint="@string/newIssueMilestoneTitle"
app:endIconTint="?attr/iconsColor"
style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox.ExposedDropdownMenu">
<AutoCompleteTextView
android:id="@+id/newIssueMilestoneSpinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="none"
android:textColor="?attr/inputTextColor"
android:labelFor="@+id/newIssueMilestoneSpinner"
android:textSize="16sp" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/newIssueLabelsLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:boxBackgroundColor="?attr/inputBackgroundColor"
android:textColorHint="?attr/hintColor"
app:hintTextColor="?attr/hintColor"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:hint="@string/newIssueLabelsTitle">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/newIssueLabels"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?attr/inputTextColor"
android:textColorHighlight="?attr/hintColor"
android:textColorHint="?attr/hintColor"
android:focusable="false"
android:textSize="16sp" />
</com.google.android.material.textfield.TextInputLayout>
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/newIssueDueDateLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:boxBackgroundColor="?attr/inputBackgroundColor"
android:textColorHint="?attr/hintColor"
app:hintTextColor="?attr/hintColor"
app:boxStrokeErrorColor="@color/darkRed"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:endIconMode="clear_text"
app:endIconTint="?attr/iconsColor"
android:hint="@string/newIssueDueDateTitle">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/newIssueDueDate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?attr/inputTextColor"
android:textColorHighlight="?attr/hintColor"
android:textColorHint="?attr/hintColor"
android:maxLines="1"
android:focusable="false"
android:textSize="16sp" />
</com.google.android.material.textfield.TextInputLayout>
<Button
android:id="@+id/createNewIssueButton"

View File

@ -81,22 +81,6 @@
</RelativeLayout>
<LinearLayout
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/info"
android:layout_width="28dp"
android:layout_height="28dp"
android:src="@drawable/ic_info"
android:layout_marginStart="120dp"
android:layout_marginTop="10dp"
android:contentDescription="@string/urlInfoTooltip" />
</LinearLayout>
<EditText
android:id="@+id/instance_url"
android:layout_width="match_parent"

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/layoutFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<CheckBox
android:id="@+id/assigneesSelection"
android:layout_width="match_parent"
android:layout_height="36dp"
android:checked="false"
android:textColor="?attr/primaryTextColor"
android:textSize="16sp" />
<androidx.cardview.widget.CardView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginTop="8dp"
android:layout_marginStart="36dp"
android:layout_marginEnd="4dp"
app:cardElevation="0dp"
app:cardCornerRadius="4dp">
<ImageView
android:id="@+id/assigneesAvatar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="@string/generalImgContentText" />
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/assigneesName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingStart="64dp"
android:paddingEnd="4dp"
android:textColor="?attr/primaryTextColor"
android:textSize="16sp" />
</RelativeLayout>

View File

@ -0,0 +1,101 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="@drawable/shape_custom_dialog"
android:orientation="vertical">
<com.google.android.material.progressindicator.ProgressIndicator
android:id="@+id/progressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/Widget.MaterialComponents.ProgressIndicator.Linear.Indeterminate"
app:indicatorColor="?attr/progressIndicatorColor" />
<LinearLayout
android:id="@+id/dialogFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:id="@+id/TitleFrame"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?attr/primaryTextColor"
android:textSize="20sp"
android:textStyle="bold"
android:drawableStart="@drawable/ic_person_add"
android:drawablePadding="16dp"
android:text="@string/newIssueSelectAssigneesListTitle" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:id="@+id/dividerTitle"
android:background="?attr/dividerColor" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="100"
android:layout_margin="12dp"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/assigneesRecyclerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:id="@+id/divider"
android:background="?attr/dividerColor" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:orientation="vertical" >
<Button
android:id="@+id/cancel"
android:layout_width="wrap_content"
android:layout_height="60dp"
style="?android:attr/button"
android:layout_alignParentStart="true"
android:text="@string/close"
android:textColor="@color/colorWhite"
android:textSize="16sp" />
</RelativeLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/layoutFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -10,10 +10,37 @@
<CheckBox
android:id="@+id/labelSelection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="36dp"
android:checked="false"
android:textColor="?attr/primaryTextColor"
android:textSize="16sp" />
<androidx.cardview.widget.CardView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_marginTop="8dp"
android:layout_marginStart="36dp"
android:layout_marginEnd="4dp"
app:cardElevation="0dp"
app:cardCornerRadius="4dp">
<ImageView
android:id="@+id/labelColor"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/generalImgContentText" />
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/labelText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingStart="64dp"
android:paddingEnd="4dp"
android:textColor="?attr/primaryTextColor"
android:textSize="16sp" />
</RelativeLayout>

View File

@ -1,116 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:background="?attr/primaryBackgroundColor"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/select_entry"
android:textColor="?attr/primaryTextColor"
android:textSize="24sp" />
<androidx.appcompat.widget.SearchView
android:id="@+id/search_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:layout_marginTop="5dp"
android:iconifiedByDefault="true"
android:queryHint="Search" />
<LinearLayout
android:id="@+id/select_all_container"
android:visibility="gone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="@color/colorPrimary"
android:orientation="horizontal">
<androidx.appcompat.widget.AppCompatCheckBox
android:id="@+id/select_all_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textColor="?attr/primaryTextColor"
android:clickable="false" />
<TextView
android:textAllCaps="true"
android:id="@+id/select_all_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:textSize="18sp"
android:textColor="?attr/primaryTextColor"
android:text="@string/select_all"/>
</LinearLayout>
<View
android:layout_marginTop="10dp"
android:layout_marginBottom="5dp"
android:layout_marginStart="40dp"
android:layout_marginEnd="40dp"
android:layout_width="match_parent"
android:layout_height="0.1dp"
android:visibility="gone"
android:background="?attr/primaryBackgroundColor"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<view
android:id="@+id/recycler_view"
class="org.mian.gitnex.helpers.RecyclerViewEmptySupport"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<TextView
android:id="@+id/list_empty1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/noDataFound"
android:textColor="?attr/primaryTextColor"
android:textSize="16sp" />
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/cancel"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:textColor="@color/darkGreen"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cancelButton" />
<Space
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<Button
android:id="@+id/done"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:textColor="@color/darkRed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/doneButton" />
</LinearLayout>
</LinearLayout>

View File

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="?attr/primaryBackgroundColor"
android:orientation="horizontal">
<androidx.appcompat.widget.AppCompatCheckBox
android:id="@+id/dialog_item_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textColor="?attr/primaryTextColor"
android:clickable="false" />
<TextView
android:id="@+id/dialog_item_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="10dp"
android:layout_marginStart="10dp"
android:textColor="?attr/primaryTextColor"
android:textSize="18sp" />
</LinearLayout>

View File

@ -219,7 +219,7 @@
<string name="issueCreated">New issue created successfully</string>
<string name="issueCreatedError">Something went wrong, please try again</string>
<string name="issueCreatedNoMilestone">No milestone</string>
<string name="noAssigneesFound">No collaborators found</string>
<string name="noAssigneesFound">No assignees found</string>
<string name="noLabelsFound">No labels found</string>
<!-- settings -->

View File

@ -7,7 +7,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.1'
classpath 'com.android.tools.build:gradle:4.0.2'
}
}