mirror of
https://github.com/gitnex-org/gitnex.git
synced 2026-05-21 15:21:41 -05:00
Dependencies for issues and pr (view/add/remove) (#1420)
Closes #878 Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/1420 Co-authored-by: M M Arif <mmarif@swatian.com> Co-committed-by: M M Arif <mmarif@swatian.com>
This commit is contained in:
@@ -115,7 +115,7 @@ dependencies {
|
||||
implementation 'com.github.chrisvest:stormpot:2.4.2'
|
||||
implementation 'androidx.browser:browser:1.8.0'
|
||||
implementation 'com.google.android.flexbox:flexbox:3.0.0'
|
||||
implementation('org.codeberg.gitnex:tea4j-autodeploy:a91978be6a') {
|
||||
implementation('org.codeberg.gitnex:tea4j-autodeploy:268d5b9c96') {
|
||||
exclude module: 'org.apache.oltu.oauth2.common'
|
||||
}
|
||||
implementation 'io.github.amrdeveloper:codeview:1.3.9'
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package org.mian.gitnex.adapters;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import java.util.List;
|
||||
import org.gitnex.tea4j.v2.models.Issue;
|
||||
import org.mian.gitnex.databinding.ListIssueDependencyBinding;
|
||||
|
||||
/**
|
||||
* @author mmarif
|
||||
*/
|
||||
public class DependencyAdapter
|
||||
extends RecyclerView.Adapter<DependencyAdapter.DependencyViewHolder> {
|
||||
|
||||
private final List<Issue> dependenciesList;
|
||||
private final boolean showDeleteIcon;
|
||||
private OnItemClickListener itemClickListener;
|
||||
|
||||
public DependencyAdapter(List<Issue> dependenciesList, boolean showDeleteIcon) {
|
||||
this.dependenciesList = dependenciesList;
|
||||
this.showDeleteIcon = showDeleteIcon;
|
||||
}
|
||||
|
||||
public void setOnItemClickListener(OnItemClickListener listener) {
|
||||
this.itemClickListener = listener;
|
||||
}
|
||||
|
||||
@NonNull @Override
|
||||
public DependencyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
ListIssueDependencyBinding binding =
|
||||
ListIssueDependencyBinding.inflate(
|
||||
LayoutInflater.from(parent.getContext()), parent, false);
|
||||
return new DependencyViewHolder(binding);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull DependencyViewHolder holder, int position) {
|
||||
|
||||
Issue issue = dependenciesList.get(position);
|
||||
holder.binding.dependencyTitle.setText(issue.getTitle());
|
||||
holder.binding.deleteDependency.setVisibility(showDeleteIcon ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (showDeleteIcon) {
|
||||
holder.binding.deleteDependency.setOnClickListener(
|
||||
v -> {
|
||||
if (itemClickListener != null) {
|
||||
itemClickListener.onItemClick(issue, position);
|
||||
}
|
||||
});
|
||||
holder.binding.cardView.setOnClickListener(null);
|
||||
} else {
|
||||
holder.binding.cardView.setOnClickListener(
|
||||
v -> {
|
||||
if (itemClickListener != null) {
|
||||
itemClickListener.onItemClick(issue, position);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return dependenciesList.size();
|
||||
}
|
||||
|
||||
public static class DependencyViewHolder extends RecyclerView.ViewHolder {
|
||||
private final ListIssueDependencyBinding binding;
|
||||
|
||||
DependencyViewHolder(ListIssueDependencyBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnItemClickListener {
|
||||
void onItemClick(Issue issue, int position);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,339 @@
|
||||
package org.mian.gitnex.fragments;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import org.gitnex.tea4j.v2.models.Issue;
|
||||
import org.gitnex.tea4j.v2.models.IssueMeta;
|
||||
import org.mian.gitnex.R;
|
||||
import org.mian.gitnex.adapters.DependencyAdapter;
|
||||
import org.mian.gitnex.clients.RetrofitClient;
|
||||
import org.mian.gitnex.databinding.BottomSheetIssueDependenciesBinding;
|
||||
import org.mian.gitnex.helpers.Toasty;
|
||||
import org.mian.gitnex.helpers.contexts.IssueContext;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
/**
|
||||
* @author mmarif
|
||||
*/
|
||||
public class BottomSheetIssueDependenciesFragment extends BottomSheetDialogFragment {
|
||||
|
||||
private BottomSheetIssueDependenciesBinding binding;
|
||||
private IssueContext issue;
|
||||
private DependencyAdapter dependenciesAdapter;
|
||||
private DependencyAdapter searchResultsAdapter;
|
||||
private List<Issue> dependenciesList;
|
||||
private List<Issue> searchResultsList;
|
||||
|
||||
public static BottomSheetIssueDependenciesFragment newInstance(IssueContext issue) {
|
||||
|
||||
BottomSheetIssueDependenciesFragment fragment = new BottomSheetIssueDependenciesFragment();
|
||||
Bundle args = new Bundle();
|
||||
args.putSerializable(IssueContext.INTENT_EXTRA, issue);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Nullable @Override
|
||||
public View onCreateView(
|
||||
@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
|
||||
binding = BottomSheetIssueDependenciesBinding.inflate(inflater, container, false);
|
||||
|
||||
if (getArguments() != null) {
|
||||
issue = (IssueContext) getArguments().getSerializable(IssueContext.INTENT_EXTRA);
|
||||
}
|
||||
if (issue == null) {
|
||||
throw new IllegalStateException("IssueContext is required");
|
||||
}
|
||||
|
||||
dependenciesList = new ArrayList<>();
|
||||
dependenciesAdapter = new DependencyAdapter(dependenciesList, true);
|
||||
binding.dependenciesRecyclerView.setLayoutManager(
|
||||
new LinearLayoutManager(requireContext()));
|
||||
binding.dependenciesRecyclerView.setAdapter(dependenciesAdapter);
|
||||
dependenciesAdapter.setOnItemClickListener(this::deleteDependency);
|
||||
|
||||
searchResultsList = new ArrayList<>();
|
||||
searchResultsAdapter = new DependencyAdapter(searchResultsList, false);
|
||||
binding.searchResultsRecyclerView.setLayoutManager(
|
||||
new LinearLayoutManager(requireContext()));
|
||||
binding.searchResultsRecyclerView.setAdapter(searchResultsAdapter);
|
||||
searchResultsAdapter.setOnItemClickListener(this::addDependency);
|
||||
|
||||
binding.searchInputLayout.setEndIconOnClickListener(
|
||||
v -> {
|
||||
String query =
|
||||
Objects.requireNonNull(binding.searchInput.getText()).toString().trim();
|
||||
if (!query.isEmpty()) {
|
||||
searchIssues(query);
|
||||
} else {
|
||||
clearSearchResults();
|
||||
}
|
||||
});
|
||||
|
||||
loadDependencies();
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
private void loadDependencies() {
|
||||
Call<List<Issue>> call =
|
||||
RetrofitClient.getApiInterface(requireContext())
|
||||
.issueListIssueDependencies(
|
||||
issue.getRepository().getOwner(),
|
||||
issue.getRepository().getName(),
|
||||
String.valueOf(issue.getIssueIndex()),
|
||||
1,
|
||||
10);
|
||||
|
||||
call.enqueue(
|
||||
new Callback<>() {
|
||||
@Override
|
||||
public void onResponse(
|
||||
@NonNull Call<List<Issue>> call,
|
||||
@NonNull Response<List<Issue>> response) {
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
List<Issue> newDependencies = response.body();
|
||||
int oldSize = dependenciesList.size();
|
||||
|
||||
dependenciesList.clear();
|
||||
if (oldSize > 0) {
|
||||
dependenciesAdapter.notifyItemRangeRemoved(0, oldSize);
|
||||
}
|
||||
|
||||
dependenciesList.addAll(newDependencies);
|
||||
if (!newDependencies.isEmpty()) {
|
||||
dependenciesAdapter.notifyItemRangeInserted(
|
||||
0, newDependencies.size());
|
||||
}
|
||||
|
||||
if (dependenciesList.isEmpty()) {
|
||||
binding.dependenciesRecyclerView.setVisibility(View.GONE);
|
||||
binding.noDependenciesText.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.dependenciesRecyclerView.setVisibility(View.VISIBLE);
|
||||
binding.noDependenciesText.setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
int oldSize = dependenciesList.size();
|
||||
dependenciesList.clear();
|
||||
if (oldSize > 0) {
|
||||
dependenciesAdapter.notifyItemRangeRemoved(0, oldSize);
|
||||
}
|
||||
binding.dependenciesRecyclerView.setVisibility(View.GONE);
|
||||
binding.noDependenciesText.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<List<Issue>> call, @NonNull Throwable t) {
|
||||
int oldSize = dependenciesList.size();
|
||||
dependenciesList.clear();
|
||||
if (oldSize > 0) {
|
||||
dependenciesAdapter.notifyItemRangeRemoved(0, oldSize);
|
||||
}
|
||||
binding.dependenciesRecyclerView.setVisibility(View.GONE);
|
||||
binding.noDependenciesText.setVisibility(View.VISIBLE);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void deleteDependency(Issue dependency, int position) {
|
||||
IssueMeta meta = new IssueMeta();
|
||||
meta.setOwner(issue.getRepository().getOwner());
|
||||
meta.setRepo(issue.getRepository().getName());
|
||||
meta.setIndex(dependency.getId());
|
||||
|
||||
Call<Void> call =
|
||||
RetrofitClient.getApiInterface(requireContext())
|
||||
.customIssueRemoveIssueDependencies(
|
||||
issue.getRepository().getOwner(),
|
||||
issue.getRepository().getName(),
|
||||
String.valueOf(issue.getIssue().getId()),
|
||||
meta);
|
||||
|
||||
call.enqueue(
|
||||
new Callback<>() {
|
||||
@Override
|
||||
public void onResponse(
|
||||
@NonNull Call<Void> call, @NonNull Response<Void> response) {
|
||||
if (response.isSuccessful()) {
|
||||
dependenciesList.remove(position);
|
||||
dependenciesAdapter.notifyItemRemoved(position);
|
||||
dependenciesAdapter.notifyItemRangeChanged(
|
||||
position, dependenciesList.size());
|
||||
|
||||
if (dependenciesList.isEmpty()) {
|
||||
binding.dependenciesRecyclerView.setVisibility(View.GONE);
|
||||
binding.noDependenciesText.setVisibility(View.VISIBLE);
|
||||
}
|
||||
Toasty.success(
|
||||
requireContext(), getString(R.string.dependency_removed));
|
||||
} else {
|
||||
Toasty.error(
|
||||
requireContext(),
|
||||
getString(R.string.dependency_removal_failed));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<Void> call, @NonNull Throwable t) {
|
||||
Toasty.error(
|
||||
requireContext(), getString(R.string.genericServerResponseError));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void searchIssues(String query) {
|
||||
Call<List<Issue>> call =
|
||||
RetrofitClient.getApiInterface(requireContext())
|
||||
.issueListIssues(
|
||||
issue.getRepository().getOwner(),
|
||||
issue.getRepository().getName(),
|
||||
"open",
|
||||
null,
|
||||
query,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
1,
|
||||
3);
|
||||
|
||||
call.enqueue(
|
||||
new Callback<>() {
|
||||
@Override
|
||||
public void onResponse(
|
||||
@NonNull Call<List<Issue>> call,
|
||||
@NonNull Response<List<Issue>> response) {
|
||||
if (response.isSuccessful() && response.body() != null) {
|
||||
int oldSize = searchResultsList.size();
|
||||
searchResultsList.clear();
|
||||
if (oldSize > 0) {
|
||||
searchResultsAdapter.notifyItemRangeRemoved(0, oldSize);
|
||||
}
|
||||
|
||||
List<Issue> results = response.body();
|
||||
long currentIssueId = issue.getIssue().getId();
|
||||
List<Long> dependencyIds = new ArrayList<>();
|
||||
for (Issue dep : dependenciesList) {
|
||||
dependencyIds.add(dep.getId());
|
||||
}
|
||||
|
||||
for (Issue result : results) {
|
||||
if (result.getId() != currentIssueId
|
||||
&& !dependencyIds.contains(result.getId())) {
|
||||
searchResultsList.add(result);
|
||||
}
|
||||
}
|
||||
|
||||
if (searchResultsList.isEmpty()) {
|
||||
binding.searchResultsRecyclerView.setVisibility(View.GONE);
|
||||
Toasty.info(
|
||||
requireContext(),
|
||||
getString(R.string.no_dependency_search_results));
|
||||
} else {
|
||||
searchResultsAdapter.notifyItemRangeInserted(
|
||||
0, searchResultsList.size());
|
||||
binding.searchResultsRecyclerView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
} else {
|
||||
int oldSize = searchResultsList.size();
|
||||
if (oldSize > 0) {
|
||||
searchResultsList.clear();
|
||||
searchResultsAdapter.notifyItemRangeRemoved(0, oldSize);
|
||||
}
|
||||
binding.searchResultsRecyclerView.setVisibility(View.GONE);
|
||||
Toasty.error(requireContext(), getString(R.string.search_failed));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<List<Issue>> call, @NonNull Throwable t) {
|
||||
int oldSize = searchResultsList.size();
|
||||
if (oldSize > 0) {
|
||||
searchResultsList.clear();
|
||||
searchResultsAdapter.notifyItemRangeRemoved(0, oldSize);
|
||||
}
|
||||
binding.searchResultsRecyclerView.setVisibility(View.GONE);
|
||||
Toasty.error(
|
||||
requireContext(), getString(R.string.genericServerResponseError));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void addDependency(Issue dependency, int position) {
|
||||
IssueMeta meta = new IssueMeta();
|
||||
meta.setOwner(issue.getRepository().getOwner());
|
||||
meta.setRepo(issue.getRepository().getName());
|
||||
meta.setIndex(dependency.getId());
|
||||
|
||||
Call<Issue> call =
|
||||
RetrofitClient.getApiInterface(requireContext())
|
||||
.issueCreateIssueDependencies(
|
||||
issue.getRepository().getOwner(),
|
||||
issue.getRepository().getName(),
|
||||
String.valueOf(issue.getIssue().getId()),
|
||||
meta);
|
||||
|
||||
call.enqueue(
|
||||
new Callback<>() {
|
||||
@Override
|
||||
public void onResponse(
|
||||
@NonNull Call<Issue> call, @NonNull Response<Issue> response) {
|
||||
if (response.isSuccessful()) {
|
||||
binding.searchInput.setText("");
|
||||
int oldSize = searchResultsList.size();
|
||||
if (oldSize > 0) {
|
||||
searchResultsList.clear();
|
||||
searchResultsAdapter.notifyItemRangeRemoved(0, oldSize);
|
||||
}
|
||||
binding.searchResultsRecyclerView.setVisibility(View.GONE);
|
||||
|
||||
loadDependencies();
|
||||
Toasty.success(requireContext(), getString(R.string.dependency_added));
|
||||
} else {
|
||||
Toasty.error(
|
||||
requireContext(), getString(R.string.dependency_add_failed));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(@NonNull Call<Issue> call, @NonNull Throwable t) {
|
||||
Toasty.error(
|
||||
requireContext(), getString(R.string.genericServerResponseError));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void clearSearchResults() {
|
||||
int oldSize = searchResultsList.size();
|
||||
if (oldSize > 0) {
|
||||
searchResultsList.clear();
|
||||
searchResultsAdapter.notifyItemRangeRemoved(0, oldSize);
|
||||
}
|
||||
binding.searchResultsRecyclerView.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,6 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment {
|
||||
private final IssueContext issue;
|
||||
private final String issueCreator;
|
||||
private BottomSheetListener bmListener;
|
||||
private boolean issuePinStatus = false;
|
||||
|
||||
public BottomSheetSingleIssueFragment(IssueContext issue, String username) {
|
||||
this.issue = issue;
|
||||
@@ -244,6 +243,15 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment {
|
||||
});
|
||||
}
|
||||
|
||||
binding.manageDependencies.setOnClickListener(
|
||||
v -> {
|
||||
BottomSheetIssueDependenciesFragment dependenciesSheet =
|
||||
BottomSheetIssueDependenciesFragment.newInstance(issue);
|
||||
dependenciesSheet.show(
|
||||
getParentFragmentManager(), "issueDependenciesBottomSheet");
|
||||
dismiss();
|
||||
});
|
||||
|
||||
binding.subscribeIssue.setOnClickListener(
|
||||
subscribeToIssue -> {
|
||||
IssueActions.subscribe(ctx, issue);
|
||||
@@ -277,6 +285,7 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment {
|
||||
binding.bottomSheetHeaderFrame.setVisibility(View.GONE);
|
||||
binding.mergePullRequest.setVisibility(View.GONE);
|
||||
binding.updatePullRequest.setVisibility(View.GONE);
|
||||
binding.manageDependencies.setVisibility(View.GONE);
|
||||
if (issue.getIssueType().equalsIgnoreCase("issue")) {
|
||||
binding.issuePrDivider.setVisibility(View.GONE);
|
||||
}
|
||||
@@ -287,6 +296,7 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment {
|
||||
} else {
|
||||
binding.addRemoveAssignees.setVisibility(View.GONE);
|
||||
binding.editLabels.setVisibility(View.GONE);
|
||||
binding.manageDependencies.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
binding.pinIssue.setOnClickListener(
|
||||
|
||||
97
app/src/main/res/drawable/ic_dependencies.xml
Normal file
97
app/src/main/res/drawable/ic_dependencies.xml
Normal file
@@ -0,0 +1,97 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M10,8v8h2a2,2 0,0 0,2 -2v-4a2,2 0,0 0,-2 -2z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/iconsColor"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M7.5,4.21v0.01"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/iconsColor"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M4.21,7.5v0.01"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/iconsColor"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M3,12v0.01"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/iconsColor"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M4.21,16.5v0.01"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/iconsColor"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M7.5,19.79v0.01"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/iconsColor"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M12,21v0.01"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/iconsColor"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M16.5,19.79v0.01"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/iconsColor"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M19.79,16.5v0.01"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/iconsColor"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M21,12v0.01"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/iconsColor"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M19.79,7.5v0.01"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/iconsColor"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M16.5,4.21v0.01"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/iconsColor"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M12,3v0.01"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="2"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="?attr/iconsColor"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
@@ -471,6 +471,7 @@
|
||||
android:id="@+id/divider_info"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/materialCardBackgroundColor"
|
||||
android:text="@string/timeline"
|
||||
android:paddingTop="@dimen/dimen4dp"
|
||||
android:paddingBottom="@dimen/dimen4dp"
|
||||
|
||||
76
app/src/main/res/layout/bottom_sheet_issue_dependencies.xml
Normal file
76
app/src/main/res/layout/bottom_sheet_issue_dependencies.xml
Normal file
@@ -0,0 +1,76 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:orientation="vertical"
|
||||
android:padding="@dimen/dimen16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/bottomSheetHeader"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/dependencies"
|
||||
android:textAppearance="?attr/textAppearanceTitleLarge"
|
||||
android:textColor="?attr/colorOnSurface"
|
||||
android:paddingBottom="@dimen/dimen16dp" />
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/searchInputLayout"
|
||||
style="@style/Widget.Material3.TextInputLayout.OutlinedBox"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/search"
|
||||
app:endIconMode="custom"
|
||||
app:endIconDrawable="@drawable/ic_search"
|
||||
app:endIconContentDescription="@string/search">
|
||||
|
||||
<com.google.android.material.textfield.TextInputEditText
|
||||
android:id="@+id/searchInput"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="text"
|
||||
android:maxLines="1"
|
||||
android:textSize="@dimen/dimen16sp"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium" />
|
||||
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/searchResultsRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="@dimen/dimen16dp"
|
||||
android:overScrollMode="never"
|
||||
tools:listitem="@layout/list_issue_dependency"
|
||||
tools:itemCount="3" />
|
||||
|
||||
<com.google.android.material.divider.MaterialDivider
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginVertical="@dimen/dimen16dp"
|
||||
app:dividerColor="?attr/colorOutlineVariant" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/dependenciesRecyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/dimen8dp"
|
||||
android:overScrollMode="never"
|
||||
tools:listitem="@layout/list_issue_dependency"
|
||||
tools:itemCount="0" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/noDependenciesText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/no_dependencies_set"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||
android:textColor="?attr/colorOnSurfaceVariant"
|
||||
android:gravity="center"
|
||||
android:padding="@dimen/dimen16dp"
|
||||
android:visibility="visible" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -163,6 +163,19 @@
|
||||
app:drawableTopCompat="@drawable/ic_tag"
|
||||
app:layout_alignSelf="flex_start" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/manageDependencies"
|
||||
android:layout_width="@dimen/dimen132dp"
|
||||
android:layout_height="@dimen/dimen100dp"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||
android:gravity="center"
|
||||
android:padding="@dimen/dimen4dp"
|
||||
android:text="@string/dependencies"
|
||||
android:textColor="?attr/primaryTextColor"
|
||||
android:textSize="@dimen/dimen14sp"
|
||||
app:drawableTopCompat="@drawable/ic_dependencies"
|
||||
app:layout_alignSelf="flex_start" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pin_issue"
|
||||
android:layout_width="@dimen/dimen132dp"
|
||||
|
||||
49
app/src/main/res/layout/list_issue_dependency.xml
Normal file
49
app/src/main/res/layout/list_issue_dependency.xml
Normal file
@@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingBottom="@dimen/dimen4dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<com.google.android.material.card.MaterialCardView
|
||||
android:id="@+id/card_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="?attr/materialCardViewElevatedStyle"
|
||||
app:cardElevation="@dimen/dimen0dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:background="?attr/materialCardBackgroundColor"
|
||||
android:padding="@dimen/dimen12dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/dependencyTitle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:textAppearance="?attr/textAppearanceBodyMedium"
|
||||
android:textColor="?attr/colorOnSurface"
|
||||
android:gravity="center_vertical"
|
||||
android:ellipsize="end" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/deleteDependency"
|
||||
android:layout_width="@dimen/dimen22dp"
|
||||
android:layout_height="@dimen/dimen22dp"
|
||||
android:src="@drawable/ic_delete"
|
||||
android:layout_marginStart="@dimen/dimen12dp"
|
||||
android:contentDescription="@string/menuDeleteText"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:visibility="gone"
|
||||
app:tint="?attr/colorOnSurfaceVariant" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</com.google.android.material.card.MaterialCardView>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -947,4 +947,13 @@
|
||||
<string name="search_no_matches">No matches found</string>
|
||||
<string name="heatmap_contribution">%1$d contributions on %2$s</string>
|
||||
<string name="im_mentioned">I\'m mentioned</string>
|
||||
|
||||
<string name="dependencies">Dependencies</string>
|
||||
<string name="no_dependencies_set">No dependencies set</string>
|
||||
<string name="dependency_removed">Dependency removed</string>
|
||||
<string name="dependency_removal_failed">Failed to remove dependency</string>
|
||||
<string name="dependency_added">Dependency added</string>
|
||||
<string name="dependency_add_failed">Failed to add dependency</string>
|
||||
<string name="search_failed">Search failed</string>
|
||||
<string name="no_dependency_search_results">No matching results found</string>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user