Add support for tags (#1002) All checks were successful GitLab CI GitLab continuous integration tool
All checks were successful
GitLab CI GitLab continuous integration tool
Closes #983 TODO - [x] create tags - [x] view tags - [x] apply pagination - [x] delete tags Co-authored-by: qwerty287 <ndev@web.de> Co-authored-by: M M Arif <mmarif@noreply.codeberg.org> Co-authored-by: M M Arif <mmarif@swatian.com> Reviewed-on: #1002 Reviewed-by: M M Arif <mmarif@noreply.codeberg.org> Co-authored-by: qwerty287 <qwerty287@noreply.codeberg.org> Co-committed-by: qwerty287 <qwerty287@noreply.codeberg.org>
This commit is contained in:
parent 8cfd4b6a31
commit f79d227bff
15 changed files with 908 additions and 30 deletions
| @ -109,7 +109,7 @@ dependencies { | |||
implementation "androidx.work:work-runtime:$work_version" | ||||
implementation "io.mikael:urlbuilder:2.0.9" | ||||
implementation "org.codeberg.gitnex-garage:emoji-java:v5.1.2" | ||||
implementation "org.codeberg.gitnex:tea4j:1.0.29" | ||||
implementation "org.codeberg.gitnex:tea4j:1.0.30" | ||||
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5" | ||||
implementation 'androidx.biometric:biometric:1.1.0' | ||||
implementation 'com.github.chrisvest:stormpot:2.4.2' | ||||
| |
| @ -15,6 +15,8 @@ import android.widget.EditText; | |||
import android.widget.ImageView; | ||||
import androidx.annotation.NonNull; | ||||
import org.gitnex.tea4j.models.Branches; | ||||
import org.gitnex.tea4j.models.CreateTagOptions; | ||||
import org.gitnex.tea4j.models.GitTag; | ||||
import org.gitnex.tea4j.models.Releases; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.clients.RetrofitClient; | ||||
| @ -44,6 +46,7 @@ public class CreateReleaseActivity extends BaseActivity { | |||
private CheckBox releaseDraft; | ||||
private Button createNewRelease; | ||||
private String selectedBranch; | ||||
private Button createNewTag; | ||||
| ||||
private String repoOwner; | ||||
private String repoName; | ||||
| @ -97,6 +100,7 @@ public class CreateReleaseActivity extends BaseActivity { | |||
getBranches(Authorization.get(ctx), repoOwner, repoName); | ||||
| ||||
createNewRelease = activityCreateReleaseBinding.createNewRelease; | ||||
createNewTag = activityCreateReleaseBinding.createNewTag; | ||||
disableProcessButton(); | ||||
| ||||
if(!connToInternet) { | ||||
| @ -108,6 +112,79 @@ public class CreateReleaseActivity extends BaseActivity { | |||
createNewRelease.setOnClickListener(createReleaseListener); | ||||
} | ||||
| ||||
createNewTag.setOnClickListener(v -> createNewTag()); | ||||
| ||||
} | ||||
| ||||
private void createNewTag() { | ||||
boolean connToInternet = AppUtil.hasNetworkConnection(appCtx); | ||||
| ||||
String tagName = releaseTagName.getText().toString(); | ||||
String message = releaseTitle.getText().toString() + "\n\n" + releaseContent.getText().toString(); | ||||
| ||||
if(!connToInternet) { | ||||
Toasty.error(ctx, getResources().getString(R.string.checkNetConnection)); | ||||
return; | ||||
} | ||||
| ||||
if(tagName.equals("")) { | ||||
Toasty.error(ctx, getString(R.string.tagNameErrorEmpty)); | ||||
return; | ||||
} | ||||
| ||||
if(selectedBranch == null) { | ||||
Toasty.error(ctx, getString(R.string.selectBranchError)); | ||||
return; | ||||
} | ||||
| ||||
disableProcessButton(); | ||||
| ||||
CreateTagOptions createReleaseJson = new CreateTagOptions(message, tagName, selectedBranch); | ||||
| ||||
Call<GitTag> call = RetrofitClient | ||||
.getApiInterface(ctx) | ||||
.createTag(Authorization.get(ctx), repoOwner, repoName, createReleaseJson); | ||||
| ||||
call.enqueue(new Callback<GitTag>() { | ||||
| ||||
@Override | ||||
public void onResponse(@NonNull Call<GitTag> call, @NonNull retrofit2.Response<GitTag> response) { | ||||
| ||||
if (response.code() == 201) { | ||||
tinyDB.putBoolean("updateReleases", true); | ||||
Toasty.success(ctx, getString(R.string.tagCreated)); | ||||
finish(); | ||||
} | ||||
else if(response.code() == 401) { | ||||
enableProcessButton(); | ||||
AlertDialogs.authorizationTokenRevokedDialog(ctx, ctx.getResources().getString(R.string.alertDialogTokenRevokedTitle), | ||||
ctx.getResources().getString(R.string.alertDialogTokenRevokedMessage), | ||||
ctx.getResources().getString(R.string.cancelButton), | ||||
ctx.getResources().getString(R.string.navLogout)); | ||||
} | ||||
else if(response.code() == 403) { | ||||
enableProcessButton(); | ||||
Toasty.error(ctx, ctx.getString(R.string.authorizeError)); | ||||
} | ||||
else if(response.code() == 404) { | ||||
enableProcessButton(); | ||||
Toasty.warning(ctx, ctx.getString(R.string.apiNotFound)); | ||||
} | ||||
else { | ||||
enableProcessButton(); | ||||
Toasty.error(ctx, ctx.getString(R.string.genericError)); | ||||
} | ||||
} | ||||
| ||||
@Override | ||||
public void onFailure(@NonNull Call<GitTag> call, @NonNull Throwable t) { | ||||
Log.e("onFailure", t.toString()); | ||||
enableProcessButton(); | ||||
} | ||||
}); | ||||
| ||||
| ||||
| ||||
} | ||||
| ||||
private final View.OnClickListener createReleaseListener = v -> processNewRelease(); | ||||
| @ -268,12 +345,12 @@ public class CreateReleaseActivity extends BaseActivity { | |||
} | ||||
| ||||
private void disableProcessButton() { | ||||
| ||||
createNewTag.setEnabled(false); | ||||
createNewRelease.setEnabled(false); | ||||
} | ||||
| ||||
private void enableProcessButton() { | ||||
| ||||
createNewTag.setEnabled(true); | ||||
createNewRelease.setEnabled(true); | ||||
} | ||||
| ||||
| |
| @ -39,6 +39,7 @@ import org.mian.gitnex.database.models.UserAccount; | |||
import org.mian.gitnex.fragments.BottomSheetIssuesFilterFragment; | ||||
import org.mian.gitnex.fragments.BottomSheetMilestonesFilterFragment; | ||||
import org.mian.gitnex.fragments.BottomSheetPullRequestFilterFragment; | ||||
import org.mian.gitnex.fragments.BottomSheetReleasesTagsFragment; | ||||
import org.mian.gitnex.fragments.BottomSheetRepoFragment; | ||||
import org.mian.gitnex.fragments.CollaboratorsFragment; | ||||
import org.mian.gitnex.fragments.FilesFragment; | ||||
| @ -76,6 +77,7 @@ public class RepoDetailActivity extends BaseActivity implements BottomSheetListe | |||
private FragmentRefreshListener fragmentRefreshListenerMilestone; | ||||
private FragmentRefreshListener fragmentRefreshListenerFiles; | ||||
private FragmentRefreshListener fragmentRefreshListenerFilterIssuesByMilestone; | ||||
private FragmentRefreshListener fragmentRefreshListenerReleases; | ||||
| ||||
private String repositoryOwner; | ||||
private String repositoryName; | ||||
| @ -388,6 +390,11 @@ public class RepoDetailActivity extends BaseActivity implements BottomSheetListe | |||
ctx.startActivity(intent); | ||||
return true; | ||||
} | ||||
else if(id == R.id.filterReleases && new Version(tinyDB.getString("giteaVersion")).higherOrEqual("1.15.0")) { | ||||
BottomSheetReleasesTagsFragment bottomSheetReleasesTagsFragment = new BottomSheetReleasesTagsFragment(); | ||||
bottomSheetReleasesTagsFragment.show(getSupportFragmentManager(), "repoFilterReleasesMenuBottomSheet"); | ||||
return true; | ||||
} | ||||
| ||||
return super.onOptionsItemSelected(item); | ||||
| ||||
| @ -498,6 +505,16 @@ public class RepoDetailActivity extends BaseActivity implements BottomSheetListe | |||
| ||||
startActivity(new Intent(RepoDetailActivity.this, CreatePullRequestActivity.class)); | ||||
break; | ||||
case "tags": | ||||
if(getFragmentRefreshListenerReleases() != null) { | ||||
getFragmentRefreshListenerReleases().onRefresh("tags"); | ||||
} | ||||
break; | ||||
case "releases": | ||||
if(getFragmentRefreshListenerReleases() != null) { | ||||
getFragmentRefreshListenerReleases().onRefresh("releases"); | ||||
} | ||||
break; | ||||
} | ||||
} | ||||
| ||||
| @ -819,4 +836,9 @@ public class RepoDetailActivity extends BaseActivity implements BottomSheetListe | |||
| ||||
public void setFragmentRefreshListenerFiles(FragmentRefreshListener fragmentRefreshListenerFiles) { this.fragmentRefreshListenerFiles = fragmentRefreshListenerFiles; } | ||||
| ||||
//Releases interface | ||||
public FragmentRefreshListener getFragmentRefreshListenerReleases() { return fragmentRefreshListenerReleases; } | ||||
| ||||
public void setFragmentRefreshListenerReleases(FragmentRefreshListener fragmentRefreshListener) { this.fragmentRefreshListenerReleases = fragmentRefreshListener; } | ||||
| ||||
} | ||||
| |
| @ -32,9 +32,12 @@ import java.util.Locale; | |||
| ||||
public class ReleasesAdapter extends RecyclerView.Adapter<ReleasesAdapter.ReleasesViewHolder> { | ||||
| ||||
private final List<Releases> releasesList; | ||||
private List<Releases> releasesList; | ||||
private final Context context; | ||||
| ||||
private OnLoadMoreListener loadMoreListener; | ||||
private boolean isLoading = false, isMoreDataAvailable = true; | ||||
| ||||
static class ReleasesViewHolder extends RecyclerView.ViewHolder { | ||||
| ||||
private Releases releases; | ||||
| @ -172,6 +175,11 @@ public class ReleasesAdapter extends RecyclerView.Adapter<ReleasesAdapter.Releas | |||
| ||||
ReleasesDownloadsAdapter adapter = new ReleasesDownloadsAdapter(currentItem.getAssets()); | ||||
holder.downloadList.setAdapter(adapter); | ||||
| ||||
if(position >= getItemCount() - 1 && isMoreDataAvailable && !isLoading && loadMoreListener != null) { | ||||
isLoading = true; | ||||
loadMoreListener.onLoadMore(); | ||||
} | ||||
} | ||||
| ||||
@Override | ||||
| @ -179,4 +187,32 @@ public class ReleasesAdapter extends RecyclerView.Adapter<ReleasesAdapter.Releas | |||
return releasesList.size(); | ||||
} | ||||
| ||||
public void setMoreDataAvailable(boolean moreDataAvailable) { | ||||
isMoreDataAvailable = moreDataAvailable; | ||||
if(!isMoreDataAvailable) { | ||||
loadMoreListener.onLoadFinished(); | ||||
} | ||||
} | ||||
| ||||
public void notifyDataChanged() { | ||||
notifyDataSetChanged(); | ||||
isLoading = false; | ||||
loadMoreListener.onLoadFinished(); | ||||
} | ||||
| ||||
public interface OnLoadMoreListener { | ||||
void onLoadMore(); | ||||
| ||||
void onLoadFinished(); | ||||
} | ||||
| ||||
public void setLoadMoreListener(OnLoadMoreListener loadMoreListener) { | ||||
this.loadMoreListener = loadMoreListener; | ||||
} | ||||
| ||||
public void updateList(List<Releases> list) { | ||||
releasesList = list; | ||||
notifyDataChanged(); | ||||
} | ||||
| ||||
} | ||||
| |
174 app/src/main/java/org/mian/gitnex/adapters/TagsAdapter.java Normal file
174
app/src/main/java/org/mian/gitnex/adapters/TagsAdapter.java Normal file | @ -0,0 +1,174 @@ | |||
package org.mian.gitnex.adapters; | ||||
| ||||
import android.annotation.SuppressLint; | ||||
import android.content.Context; | ||||
import android.text.method.LinkMovementMethod; | ||||
import android.view.LayoutInflater; | ||||
import android.view.View; | ||||
import android.view.ViewGroup; | ||||
import android.widget.ImageView; | ||||
import android.widget.LinearLayout; | ||||
import android.widget.TextView; | ||||
import androidx.annotation.NonNull; | ||||
import androidx.core.text.HtmlCompat; | ||||
import androidx.recyclerview.widget.RecyclerView; | ||||
import com.google.android.material.bottomsheet.BottomSheetDialog; | ||||
import org.gitnex.tea4j.models.GitTag; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.helpers.AlertDialogs; | ||||
import org.mian.gitnex.helpers.Markdown; | ||||
import org.mian.gitnex.helpers.TinyDB; | ||||
import java.util.List; | ||||
| ||||
/** | ||||
* Author qwerty287 | ||||
*/ | ||||
| ||||
public class TagsAdapter extends RecyclerView.Adapter<TagsAdapter.TagsViewHolder> { | ||||
| ||||
private List<GitTag> tags; | ||||
private final Context context; | ||||
private final String repo; | ||||
private final String owner; | ||||
private Context ctx; | ||||
| ||||
private OnLoadMoreListener loadMoreListener; | ||||
private boolean isLoading = false, isMoreDataAvailable = true; | ||||
| ||||
static class TagsViewHolder extends RecyclerView.ViewHolder { | ||||
| ||||
private final TextView tagName; | ||||
private final TextView tagBody; | ||||
private final LinearLayout downloadFrame; | ||||
private final LinearLayout downloads; | ||||
private final TextView releaseZipDownload; | ||||
private final TextView releaseTarDownload; | ||||
private final ImageView downloadDropdownIcon; | ||||
private final ImageView options; | ||||
| ||||
private TagsViewHolder(View itemView) { | ||||
| ||||
super(itemView); | ||||
| ||||
tagName = itemView.findViewById(R.id.tagName); | ||||
tagBody = itemView.findViewById(R.id.tagBodyContent); | ||||
downloadFrame = itemView.findViewById(R.id.downloadFrame); | ||||
downloads = itemView.findViewById(R.id.downloads); | ||||
releaseZipDownload = itemView.findViewById(R.id.releaseZipDownload); | ||||
releaseTarDownload = itemView.findViewById(R.id.releaseTarDownload); | ||||
downloadDropdownIcon = itemView.findViewById(R.id.downloadDropdownIcon); | ||||
options = itemView.findViewById(R.id.tagsOptionsMenu); | ||||
} | ||||
} | ||||
| ||||
public TagsAdapter(Context ctx, List<GitTag> releasesMain, String repoOwner, String repoName) { | ||||
this.context = ctx; | ||||
this.tags = releasesMain; | ||||
owner = repoOwner; | ||||
repo = repoName; | ||||
} | ||||
| ||||
@NonNull | ||||
@Override | ||||
public TagsViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | ||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_tags, parent, false); | ||||
return new TagsViewHolder(v); | ||||
} | ||||
| ||||
@Override | ||||
public void onBindViewHolder(@NonNull TagsViewHolder holder, int position) { | ||||
| ||||
GitTag currentItem = tags.get(position); | ||||
| ||||
holder.tagName.setText(currentItem.getName()); | ||||
| ||||
if(!currentItem.getMessage().equals("")) { | ||||
Markdown.render(context, currentItem.getMessage(), holder.tagBody); | ||||
} | ||||
else { | ||||
holder.tagBody.setVisibility(View.GONE); | ||||
} | ||||
| ||||
holder.downloadFrame.setOnClickListener(v -> { | ||||
| ||||
if(holder.downloads.getVisibility() == View.GONE) { | ||||
| ||||
holder.downloadDropdownIcon.setImageResource(R.drawable.ic_chevron_down); | ||||
holder.downloads.setVisibility(View.VISIBLE); | ||||
} | ||||
else { | ||||
| ||||
holder.downloadDropdownIcon.setImageResource(R.drawable.ic_chevron_right); | ||||
holder.downloads.setVisibility(View.GONE); | ||||
} | ||||
}); | ||||
| ||||
if(!TinyDB.getInstance(ctx).getBoolean("isRepoAdmin")) { | ||||
holder.options.setVisibility(View.GONE); | ||||
} | ||||
| ||||
holder.options.setOnClickListener(v -> { | ||||
final Context context = v.getContext(); | ||||
| ||||
@SuppressLint("InflateParams") | ||||
View view = LayoutInflater.from(context).inflate(R.layout.bottom_sheet_tag_in_list, null); | ||||
| ||||
TextView delete = view.findViewById(R.id.tagMenuDelete); | ||||
| ||||
BottomSheetDialog dialog = new BottomSheetDialog(context); | ||||
dialog.setContentView(view); | ||||
dialog.show(); | ||||
| ||||
delete.setOnClickListener(v1 -> { | ||||
AlertDialogs.tagDeleteDialog(context, currentItem.getName(), owner, repo); | ||||
dialog.dismiss(); | ||||
}); | ||||
}); | ||||
| ||||
holder.releaseZipDownload.setText( | ||||
HtmlCompat.fromHtml("<a href='" + currentItem.getZipballUrl() + "'>" + context.getResources().getString(R.string.zipArchiveDownloadReleasesTab) + "</a> ", HtmlCompat.FROM_HTML_MODE_LEGACY)); | ||||
holder.releaseZipDownload.setMovementMethod(LinkMovementMethod.getInstance()); | ||||
| ||||
holder.releaseTarDownload.setText( | ||||
HtmlCompat.fromHtml("<a href='" + currentItem.getTarballUrl() + "'>" + context.getResources().getString(R.string.tarArchiveDownloadReleasesTab) + "</a> ", HtmlCompat.FROM_HTML_MODE_LEGACY)); | ||||
holder.releaseTarDownload.setMovementMethod(LinkMovementMethod.getInstance()); | ||||
| ||||
if(position >= getItemCount() - 1 && isMoreDataAvailable && !isLoading && loadMoreListener != null) { | ||||
isLoading = true; | ||||
loadMoreListener.onLoadMore(); | ||||
} | ||||
} | ||||
| ||||
@Override | ||||
public int getItemCount() { | ||||
return tags.size(); | ||||
} | ||||
| ||||
public void setMoreDataAvailable(boolean moreDataAvailable) { | ||||
isMoreDataAvailable = moreDataAvailable; | ||||
if(!isMoreDataAvailable) { | ||||
loadMoreListener.onLoadFinished(); | ||||
} | ||||
} | ||||
| ||||
public void notifyDataChanged() { | ||||
notifyDataSetChanged(); | ||||
isLoading = false; | ||||
loadMoreListener.onLoadFinished(); | ||||
} | ||||
| ||||
public interface OnLoadMoreListener { | ||||
void onLoadMore(); | ||||
void onLoadFinished(); | ||||
} | ||||
| ||||
public void setLoadMoreListener(OnLoadMoreListener loadMoreListener) { | ||||
this.loadMoreListener = loadMoreListener; | ||||
} | ||||
| ||||
public void updateList(List<GitTag> list) { | ||||
tags = list; | ||||
notifyDataChanged(); | ||||
} | ||||
| ||||
} |
| @ -0,0 +1,55 @@ | |||
package org.mian.gitnex.fragments; | ||||
| ||||
import android.content.Context; | ||||
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 com.google.android.material.bottomsheet.BottomSheetDialogFragment; | ||||
import org.mian.gitnex.databinding.BottomSheetReleasesTagsBinding; | ||||
import org.mian.gitnex.structs.BottomSheetListener; | ||||
| ||||
/** | ||||
* Author opyale | ||||
*/ | ||||
| ||||
public class BottomSheetReleasesTagsFragment extends BottomSheetDialogFragment { | ||||
| ||||
private BottomSheetListener bmListener; | ||||
| ||||
@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"); | ||||
} | ||||
} | ||||
| ||||
@Nullable | ||||
@Override | ||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { | ||||
| ||||
BottomSheetReleasesTagsBinding binding = BottomSheetReleasesTagsBinding.inflate(inflater, container, false); | ||||
| ||||
binding.tags.setOnClickListener(v1 -> { | ||||
| ||||
bmListener.onButtonClicked("tags"); | ||||
dismiss(); | ||||
}); | ||||
| ||||
binding.releases.setOnClickListener(v12 -> { | ||||
| ||||
bmListener.onButtonClicked("releases"); | ||||
dismiss(); | ||||
}); | ||||
| ||||
return binding.getRoot(); | ||||
} | ||||
} |
| @ -5,6 +5,8 @@ import android.os.Bundle; | |||
import android.os.Handler; | ||||
import android.os.Looper; | ||||
import android.view.LayoutInflater; | ||||
import android.view.Menu; | ||||
import android.view.MenuInflater; | ||||
import android.view.View; | ||||
import android.view.ViewGroup; | ||||
import android.widget.ProgressBar; | ||||
| @ -19,10 +21,14 @@ import androidx.recyclerview.widget.LinearLayoutManager; | |||
import androidx.recyclerview.widget.RecyclerView; | ||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; | ||||
import org.gitnex.tea4j.models.Releases; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.activities.RepoDetailActivity; | ||||
import org.mian.gitnex.adapters.ReleasesAdapter; | ||||
import org.mian.gitnex.adapters.TagsAdapter; | ||||
import org.mian.gitnex.databinding.FragmentReleasesBinding; | ||||
import org.mian.gitnex.helpers.Authorization; | ||||
import org.mian.gitnex.helpers.TinyDB; | ||||
import org.mian.gitnex.helpers.Version; | ||||
import org.mian.gitnex.viewmodels.ReleasesViewModel; | ||||
import java.util.List; | ||||
| ||||
| @ -34,6 +40,7 @@ public class ReleasesFragment extends Fragment { | |||
| ||||
private ProgressBar mProgressBar; | ||||
private ReleasesAdapter adapter; | ||||
private TagsAdapter tagsAdapter; | ||||
private RecyclerView mRecyclerView; | ||||
private TextView noDataReleases; | ||||
private static String repoNameF = "param2"; | ||||
| @ -42,6 +49,9 @@ public class ReleasesFragment extends Fragment { | |||
private String repoName; | ||||
private String repoOwner; | ||||
private String releaseTag; | ||||
private boolean viewTypeIsTags = false; | ||||
private int page = 1; | ||||
private int pageReleases = 1; | ||||
| ||||
private OnFragmentInteractionListener mListener; | ||||
| ||||
| @ -90,12 +100,30 @@ public class ReleasesFragment extends Fragment { | |||
swipeRefresh.setOnRefreshListener(() -> new Handler(Looper.getMainLooper()).postDelayed(() -> { | ||||
| ||||
swipeRefresh.setRefreshing(false); | ||||
ReleasesViewModel.loadReleasesList(Authorization.get(getContext()), repoOwner, repoName, getContext()); | ||||
if(viewTypeIsTags) { | ||||
ReleasesViewModel.loadTagsList(Authorization.get(getContext()), repoOwner, repoName, getContext()); | ||||
} else { | ||||
ReleasesViewModel.loadReleasesList(Authorization.get(getContext()), repoOwner, repoName, getContext()); | ||||
} | ||||
mProgressBar.setVisibility(View.VISIBLE); | ||||
| ||||
}, 50)); | ||||
| ||||
fetchDataAsync(Authorization.get(getContext()), repoOwner, repoName); | ||||
| ||||
setHasOptionsMenu(true); | ||||
((RepoDetailActivity) requireActivity()).setFragmentRefreshListenerReleases(type -> { | ||||
viewTypeIsTags = type.equals("tags"); | ||||
page = 1; | ||||
pageReleases = 1; | ||||
if(viewTypeIsTags) { | ||||
ReleasesViewModel.loadTagsList(Authorization.get(getContext()), repoOwner, repoName, getContext()); | ||||
} else { | ||||
ReleasesViewModel.loadReleasesList(Authorization.get(getContext()), repoOwner, repoName, getContext()); | ||||
} | ||||
mProgressBar.setVisibility(View.VISIBLE); | ||||
}); | ||||
| ||||
return fragmentReleasesBinding.getRoot(); | ||||
| ||||
} | ||||
| @ -107,10 +135,14 @@ public class ReleasesFragment extends Fragment { | |||
TinyDB tinyDb = TinyDB.getInstance(getContext()); | ||||
| ||||
if(tinyDb.getBoolean("updateReleases")) { | ||||
ReleasesViewModel.loadReleasesList(Authorization.get(getContext()), repoOwner, repoName, getContext()); | ||||
tinyDb.putBoolean("updateReleases", false); | ||||
if(viewTypeIsTags) { | ||||
ReleasesViewModel.loadTagsList(Authorization.get(getContext()), repoOwner, repoName, getContext()); | ||||
} else { | ||||
ReleasesViewModel.loadReleasesList(Authorization.get(getContext()), repoOwner, repoName, getContext()); | ||||
} | ||||
mProgressBar.setVisibility(View.VISIBLE); | ||||
tinyDb.putBoolean("updateReleases", false); | ||||
} | ||||
| ||||
} | ||||
| ||||
public void onButtonPressed(Uri uri) { | ||||
| @ -136,27 +168,73 @@ public class ReleasesFragment extends Fragment { | |||
releasesModel.getReleasesList(instanceToken, owner, repo, getContext()).observe(getViewLifecycleOwner(), new Observer<List<Releases>>() { | ||||
@Override | ||||
public void onChanged(@Nullable List<Releases> releasesListMain) { | ||||
adapter = new ReleasesAdapter(getContext(), releasesListMain); | ||||
if(adapter.getItemCount() > 0) { | ||||
mRecyclerView.setAdapter(adapter); | ||||
if(releasesListMain != null && releaseTag != null) { | ||||
int index = getReleaseIndex(releaseTag, releasesListMain); | ||||
releaseTag = null; | ||||
if(index != -1) { | ||||
mRecyclerView.scrollToPosition(index); | ||||
} | ||||
} | ||||
noDataReleases.setVisibility(View.GONE); | ||||
} | ||||
else { | ||||
adapter.notifyDataSetChanged(); | ||||
mRecyclerView.setAdapter(adapter); | ||||
noDataReleases.setVisibility(View.VISIBLE); | ||||
} | ||||
mProgressBar.setVisibility(View.GONE); | ||||
if(!viewTypeIsTags) { | ||||
adapter = new ReleasesAdapter(getContext(), releasesListMain); | ||||
adapter.setLoadMoreListener(new ReleasesAdapter.OnLoadMoreListener() { | ||||
| ||||
@Override | ||||
public void onLoadMore() { | ||||
pageReleases += 1; | ||||
ReleasesViewModel.loadMoreReleases(instanceToken, owner, repo , pageReleases, getContext(), adapter); | ||||
mProgressBar.setVisibility(View.VISIBLE); | ||||
} | ||||
| ||||
@Override | ||||
public void onLoadFinished() { | ||||
mProgressBar.setVisibility(View.GONE); | ||||
} | ||||
}); | ||||
if(adapter.getItemCount() > 0) { | ||||
mRecyclerView.setAdapter(adapter); | ||||
if(releasesListMain != null && releaseTag != null) { | ||||
int index = getReleaseIndex(releaseTag, releasesListMain); | ||||
releaseTag = null; | ||||
if(index != -1) { | ||||
mRecyclerView.scrollToPosition(index); | ||||
} | ||||
} | ||||
noDataReleases.setVisibility(View.GONE); | ||||
} | ||||
else { | ||||
adapter.notifyDataSetChanged(); | ||||
mRecyclerView.setAdapter(adapter); | ||||
noDataReleases.setVisibility(View.VISIBLE); | ||||
} | ||||
mProgressBar.setVisibility(View.GONE); | ||||
} | ||||
} | ||||
}); | ||||
| ||||
releasesModel.getTagsList(instanceToken, owner, repo, getContext()).observe(getViewLifecycleOwner(), tagList -> { | ||||
if(viewTypeIsTags) { | ||||
tagsAdapter = new TagsAdapter(getContext(), tagList, owner, repo); | ||||
tagsAdapter.setLoadMoreListener(new TagsAdapter.OnLoadMoreListener() { | ||||
| ||||
@Override | ||||
public void onLoadMore() { | ||||
page += 1; | ||||
ReleasesViewModel.loadMoreTags(instanceToken, owner, repo , page, getContext(), tagsAdapter); | ||||
mProgressBar.setVisibility(View.VISIBLE); | ||||
} | ||||
| ||||
@Override | ||||
public void onLoadFinished() { | ||||
mProgressBar.setVisibility(View.GONE); | ||||
} | ||||
}); | ||||
if(tagsAdapter.getItemCount() > 0) { | ||||
mRecyclerView.setAdapter(tagsAdapter); | ||||
noDataReleases.setVisibility(View.GONE); | ||||
} | ||||
else { | ||||
tagsAdapter.notifyDataSetChanged(); | ||||
mRecyclerView.setAdapter(tagsAdapter); | ||||
noDataReleases.setVisibility(View.VISIBLE); | ||||
} | ||||
mProgressBar.setVisibility(View.GONE); | ||||
} | ||||
}); | ||||
| ||||
} | ||||
| ||||
private static int getReleaseIndex(String tag, List<Releases> releases) { | ||||
| @ -168,4 +246,11 @@ public class ReleasesFragment extends Fragment { | |||
return -1; | ||||
} | ||||
| ||||
@Override | ||||
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { | ||||
if(new Version(TinyDB.getInstance(requireContext()).getString("giteaVersion")).less("1.15.0")) | ||||
return; | ||||
inflater.inflate(R.menu.filter_menu_releases, menu); | ||||
super.onCreateOptionsMenu(menu, inflater); | ||||
} | ||||
} | ||||
| |
| @ -6,6 +6,7 @@ import android.content.Intent; | |||
import android.graphics.Color; | ||||
import android.graphics.drawable.ColorDrawable; | ||||
import android.widget.Button; | ||||
import androidx.annotation.NonNull; | ||||
import androidx.appcompat.app.AlertDialog; | ||||
import org.mian.gitnex.R; | ||||
import org.mian.gitnex.actions.CollaboratorActions; | ||||
| @ -13,6 +14,10 @@ import org.mian.gitnex.actions.PullRequestActions; | |||
import org.mian.gitnex.actions.TeamActions; | ||||
import org.mian.gitnex.activities.CreateLabelActivity; | ||||
import org.mian.gitnex.activities.LoginActivity; | ||||
import org.mian.gitnex.clients.RetrofitClient; | ||||
import retrofit2.Call; | ||||
import retrofit2.Callback; | ||||
import retrofit2.Response; | ||||
| ||||
/** | ||||
* Author M M Arif | ||||
| @ -87,6 +92,34 @@ public class AlertDialogs { | |||
| ||||
} | ||||
| ||||
public static void tagDeleteDialog(final Context context, final String tagName, final String owner, final String repo) { | ||||
new AlertDialog.Builder(context) | ||||
.setTitle(String.format(context.getString(R.string.deleteTagTitle), tagName)) | ||||
.setMessage(R.string.deleteTagConfirmation) | ||||
.setIcon(R.drawable.ic_delete) | ||||
.setPositiveButton(R.string.menuDeleteText, (dialog, whichButton) -> RetrofitClient.getApiInterface(context).deleteTag(Authorization.get(context), owner, repo, tagName).enqueue(new Callback<Void>() { | ||||
| ||||
@Override | ||||
public void onResponse(@NonNull Call<Void> call, @NonNull Response<Void> response) { | ||||
if(response.isSuccessful()) { | ||||
Toasty.success(context, context.getString(R.string.tagDeleted)); | ||||
} | ||||
else if(response.code() == 403) { | ||||
Toasty.error(context, context.getString(R.string.authorizeError)); | ||||
} | ||||
else { | ||||
Toasty.error(context, context.getString(R.string.genericError)); | ||||
} | ||||
} | ||||
| ||||
@Override | ||||
public void onFailure(@NonNull Call<Void> call, @NonNull Throwable t) { | ||||
Toasty.error(context, context.getString(R.string.genericError)); | ||||
} | ||||
})) | ||||
.setNeutralButton(R.string.cancelButton, null).show(); | ||||
} | ||||
| ||||
public static void collaboratorRemoveDialog(final Context context, final String userNameMain, String title, String message, String positiveButton, String negativeButton, final String searchKeyword) { | ||||
| ||||
new AlertDialog.Builder(context) | ||||
| |
| @ -6,8 +6,14 @@ import androidx.annotation.NonNull; | |||
import androidx.lifecycle.LiveData; | ||||
import androidx.lifecycle.MutableLiveData; | ||||
import androidx.lifecycle.ViewModel; | ||||
import org.gitnex.tea4j.models.GitTag; | ||||
import org.gitnex.tea4j.models.Releases; | ||||
import org.mian.gitnex.adapters.ReleasesAdapter; | ||||
import org.mian.gitnex.adapters.TagsAdapter; | ||||
import org.mian.gitnex.clients.RetrofitClient; | ||||
import org.mian.gitnex.helpers.Constants; | ||||
import org.mian.gitnex.helpers.TinyDB; | ||||
import org.mian.gitnex.helpers.Version; | ||||
import java.util.List; | ||||
import retrofit2.Call; | ||||
import retrofit2.Callback; | ||||
| @ -20,10 +26,17 @@ import retrofit2.Response; | |||
public class ReleasesViewModel extends ViewModel { | ||||
| ||||
private static MutableLiveData<List<Releases>> releasesList; | ||||
private static int resultLimit = Constants.resultLimitOldGiteaInstances; | ||||
| ||||
public LiveData<List<Releases>> getReleasesList(String token, String owner, String repo, Context ctx) { | ||||
| ||||
releasesList = new MutableLiveData<>(); | ||||
| ||||
// if gitea is 1.12 or higher use the new limit | ||||
if(new Version(TinyDB.getInstance(ctx).getString("giteaVersion")).higherOrEqual("1.12.0")) { | ||||
resultLimit = Constants.resultLimitNewGiteaInstances; | ||||
} | ||||
| ||||
loadReleasesList(token, owner, repo, ctx); | ||||
| ||||
return releasesList; | ||||
| @ -33,7 +46,7 @@ public class ReleasesViewModel extends ViewModel { | |||
| ||||
Call<List<Releases>> call = RetrofitClient | ||||
.getApiInterface(ctx) | ||||
.getReleases(token, owner, repo); | ||||
.getReleases(token, owner, repo, 1, resultLimit); | ||||
| ||||
call.enqueue(new Callback<List<Releases>>() { | ||||
| ||||
| @ -42,18 +55,130 @@ public class ReleasesViewModel extends ViewModel { | |||
| ||||
if (response.isSuccessful()) { | ||||
releasesList.postValue(response.body()); | ||||
} else { | ||||
} | ||||
else { | ||||
Log.i("onResponse", String.valueOf(response.code())); | ||||
} | ||||
| ||||
} | ||||
| ||||
@Override | ||||
public void onFailure(@NonNull Call<List<Releases>> call, Throwable t) { | ||||
Log.i("onFailure", t.toString()); | ||||
} | ||||
| ||||
}); | ||||
} | ||||
| ||||
public static void loadMoreReleases(String token, String owner, String repo, int page, Context ctx, ReleasesAdapter adapter) { | ||||
| ||||
Call<List<Releases>> call = RetrofitClient | ||||
.getApiInterface(ctx) | ||||
.getReleases(token, owner, repo, page, resultLimit); | ||||
| ||||
call.enqueue(new Callback<List<Releases>>() { | ||||
| ||||
@Override | ||||
public void onResponse(@NonNull Call<List<Releases>> call, @NonNull Response<List<Releases>> response) { | ||||
| ||||
if (response.isSuccessful()) { | ||||
List<Releases> list = releasesList.getValue(); | ||||
assert list != null; | ||||
assert response.body() != null; | ||||
| ||||
if(response.body().size() != 0) { | ||||
list.addAll(response.body()); | ||||
adapter.updateList(list); | ||||
} | ||||
else { | ||||
adapter.setMoreDataAvailable(false); | ||||
} | ||||
} | ||||
else { | ||||
Log.i("onResponse", String.valueOf(response.code())); | ||||
} | ||||
} | ||||
| ||||
@Override | ||||
public void onFailure(@NonNull Call<List<Releases>> call, @NonNull Throwable t) { | ||||
Log.i("onFailure", t.toString()); | ||||
} | ||||
}); | ||||
} | ||||
| ||||
private static MutableLiveData<List<GitTag>> tagsList; | ||||
| ||||
public LiveData<List<GitTag>> getTagsList(String token, String owner, String repo, Context ctx) { | ||||
| ||||
tagsList = new MutableLiveData<>(); | ||||
| ||||
// if gitea is 1.12 or higher use the new limit | ||||
if(new Version(TinyDB.getInstance(ctx).getString("giteaVersion")).higherOrEqual("1.12.0")) { | ||||
resultLimit = Constants.resultLimitNewGiteaInstances; | ||||
} | ||||
| ||||
loadTagsList(token, owner, repo, ctx); | ||||
| ||||
return tagsList; | ||||
} | ||||
| ||||
public static void loadTagsList(String token, String owner, String repo, Context ctx) { | ||||
| ||||
Call<List<GitTag>> call = RetrofitClient | ||||
.getApiInterface(ctx) | ||||
.getTags(token, owner, repo, 1, resultLimit); | ||||
| ||||
call.enqueue(new Callback<List<GitTag>>() { | ||||
| ||||
@Override | ||||
public void onResponse(@NonNull Call<List<GitTag>> call, @NonNull Response<List<GitTag>> response) { | ||||
| ||||
if (response.isSuccessful()) { | ||||
tagsList.postValue(response.body()); | ||||
} | ||||
else { | ||||
Log.i("onResponse", String.valueOf(response.code())); | ||||
} | ||||
} | ||||
| ||||
@Override | ||||
public void onFailure(@NonNull Call<List<GitTag>> call, @NonNull Throwable t) { | ||||
Log.i("onFailure", t.toString()); | ||||
} | ||||
}); | ||||
} | ||||
public static void loadMoreTags(String token, String owner, String repo, int page, Context ctx, TagsAdapter adapter) { | ||||
| ||||
Call<List<GitTag>> call = RetrofitClient | ||||
.getApiInterface(ctx) | ||||
.getTags(token, owner, repo, page, resultLimit); | ||||
| ||||
call.enqueue(new Callback<List<GitTag>>() { | ||||
| ||||
@Override | ||||
public void onResponse(@NonNull Call<List<GitTag>> call, @NonNull Response<List<GitTag>> response) { | ||||
| ||||
if (response.isSuccessful()) { | ||||
| ||||
List<GitTag> list = tagsList.getValue(); | ||||
assert list != null; | ||||
assert response.body() != null; | ||||
| ||||
if(response.body().size() != 0) { | ||||
list.addAll(response.body()); | ||||
adapter.updateList(list); | ||||
} | ||||
else { | ||||
adapter.setMoreDataAvailable(false); | ||||
} | ||||
} | ||||
else { | ||||
Log.i("onResponse", String.valueOf(response.code())); | ||||
} | ||||
} | ||||
| ||||
@Override | ||||
public void onFailure(@NonNull Call<List<GitTag>> call, @NonNull Throwable t) { | ||||
Log.i("onFailure", t.toString()); | ||||
} | ||||
}); | ||||
} | ||||
} | ||||
| |
| @ -193,6 +193,14 @@ | |||
android:text="@string/newCreateButtonCopy" | ||||
android:textColor="@color/btnTextColor" /> | ||||
| ||||
<Button | ||||
android:id="@+id/createNewTag" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="60dp" | ||||
android:layout_marginTop="8dp" | ||||
android:text="@string/create_tag" | ||||
android:textColor="@color/btnTextColor" /> | ||||
| ||||
</LinearLayout> | ||||
| ||||
</ScrollView> | ||||
| |
53 app/src/main/res/layout/bottom_sheet_releases_tags.xml Normal file
53
app/src/main/res/layout/bottom_sheet_releases_tags.xml Normal file | @ -0,0 +1,53 @@ | |||
<?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:orientation="vertical" | ||||
android:paddingTop="6dp" | ||||
android:paddingBottom="12dp" | ||||
android:background="?attr/primaryBackgroundColor"> | ||||
| ||||
<androidx.core.widget.NestedScrollView | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content"> | ||||
| ||||
<LinearLayout | ||||
android:layout_width="match_parent" | ||||
android:orientation="vertical" | ||||
android:layout_height="wrap_content"> | ||||
| ||||
<TextView | ||||
android:id="@+id/releases" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content" | ||||
android:layout_gravity="center_vertical" | ||||
android:background="?android:attr/selectableItemBackground" | ||||
android:focusable="true" | ||||
android:clickable="true" | ||||
android:drawablePadding="24dp" | ||||
android:padding="12dp" | ||||
android:text="@string/tabTextReleases" | ||||
android:textColor="?attr/primaryTextColor" | ||||
android:textSize="16sp" | ||||
app:drawableStartCompat="@drawable/ic_release" /> | ||||
| ||||
<TextView | ||||
android:id="@+id/tags" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content" | ||||
android:layout_gravity="center_vertical" | ||||
android:background="?android:attr/selectableItemBackground" | ||||
android:focusable="true" | ||||
android:clickable="true" | ||||
android:drawablePadding="24dp" | ||||
android:padding="12dp" | ||||
android:text="@string/tags" | ||||
android:textColor="?attr/primaryTextColor" | ||||
android:textSize="16sp" | ||||
app:drawableStartCompat="@drawable/ic_label" /> | ||||
| ||||
</LinearLayout> | ||||
</androidx.core.widget.NestedScrollView> | ||||
</LinearLayout> |
40 app/src/main/res/layout/bottom_sheet_tag_in_list.xml Normal file
40
app/src/main/res/layout/bottom_sheet_tag_in_list.xml Normal file | @ -0,0 +1,40 @@ | |||
<?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:orientation="vertical" | ||||
android:paddingTop="6dp" | ||||
android:paddingBottom="12dp" | ||||
android:background="?attr/primaryBackgroundColor"> | ||||
| ||||
<androidx.core.widget.NestedScrollView | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content"> | ||||
| ||||
<LinearLayout | ||||
android:layout_width="match_parent" | ||||
android:orientation="vertical" | ||||
android:layout_height="wrap_content"> | ||||
| ||||
<TextView | ||||
android:id="@+id/tagMenuDelete" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content" | ||||
android:layout_gravity="center_vertical" | ||||
android:background="?android:attr/selectableItemBackground" | ||||
android:focusable="true" | ||||
android:clickable="true" | ||||
android:drawablePadding="24dp" | ||||
android:padding="12dp" | ||||
android:text="@string/menuDeleteText" | ||||
android:textColor="?attr/primaryTextColor" | ||||
android:textSize="16sp" | ||||
app:drawableStartCompat="@drawable/ic_delete" /> | ||||
| ||||
</LinearLayout> | ||||
| ||||
</androidx.core.widget.NestedScrollView> | ||||
| ||||
</LinearLayout> |
150 app/src/main/res/layout/list_tags.xml Normal file
150
app/src/main/res/layout/list_tags.xml Normal file | @ -0,0 +1,150 @@ | |||
<?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" | ||||
xmlns:tools="http://schemas.android.com/tools" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content" | ||||
android:orientation="vertical" | ||||
android:padding="16dp"> | ||||
| ||||
<LinearLayout | ||||
android:id="@+id/headerFrame" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="match_parent" | ||||
android:orientation="horizontal"> | ||||
| ||||
<TextView | ||||
android:id="@+id/tagName" | ||||
android:layout_width="0dp" | ||||
android:layout_height="wrap_content" | ||||
android:layout_weight="0.9" | ||||
android:singleLine="true" | ||||
android:textColor="?attr/primaryTextColor" | ||||
android:textSize="16sp" | ||||
android:layout_marginBottom="10dp" | ||||
android:textStyle="bold" /> | ||||
| ||||
<ImageView | ||||
android:id="@+id/tagsOptionsMenu" | ||||
android:layout_width="wrap_content" | ||||
android:layout_height="wrap_content" | ||||
android:layout_gravity="center_vertical|end" | ||||
android:contentDescription="@string/labelMenuContentDesc" | ||||
android:background="?android:attr/selectableItemBackgroundBorderless" | ||||
android:src="@drawable/ic_dotted_menu_horizontal" /> | ||||
| ||||
</LinearLayout> | ||||
| ||||
<LinearLayout | ||||
android:id="@+id/bodyFrame" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="match_parent" | ||||
android:layout_marginTop="10dp" | ||||
android:orientation="vertical"> | ||||
| ||||
<TextView | ||||
android:id="@+id/tagBodyContent" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content" | ||||
android:autoLink="web|email" | ||||
android:textColorLink="@color/lightBlue" | ||||
android:textColor="?attr/primaryTextColor" | ||||
android:textSize="14sp" /> | ||||
| ||||
</LinearLayout> | ||||
| ||||
<LinearLayout | ||||
android:id="@+id/downloadFrame" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content" | ||||
android:layout_marginTop="10dp" | ||||
android:orientation="vertical"> | ||||
| ||||
<LinearLayout | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content" | ||||
android:gravity="center_vertical" | ||||
android:orientation="horizontal" | ||||
tools:ignore="UseCompoundDrawables"> | ||||
| ||||
<ImageView | ||||
android:id="@+id/downloadDropdownIcon" | ||||
android:layout_width="15dp" | ||||
android:layout_height="wrap_content" | ||||
android:contentDescription="@string/generalImgContentText" | ||||
app:tint="?attr/primaryTextColor" | ||||
android:src="@drawable/ic_chevron_right" | ||||
app:srcCompat="@drawable/ic_chevron_right" /> | ||||
| ||||
<TextView | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content" | ||||
android:layout_marginStart="3dp" | ||||
android:textColor="?attr/primaryTextColor" | ||||
android:textSize="14sp" | ||||
android:text="@string/releaseDownloadText" /> | ||||
| ||||
</LinearLayout> | ||||
| ||||
<LinearLayout | ||||
android:id="@+id/downloads" | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content" | ||||
android:gravity="center_vertical" | ||||
android:orientation="vertical" | ||||
android:visibility="gone" | ||||
android:paddingTop="8dp"> | ||||
| ||||
<LinearLayout | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content" | ||||
android:gravity="center_vertical" | ||||
android:orientation="horizontal" | ||||
android:layout_marginStart="8dp" | ||||
tools:ignore="UseCompoundDrawables"> | ||||
| ||||
<ImageView | ||||
android:layout_width="18dp" | ||||
android:layout_height="18dp" | ||||
app:srcCompat="@drawable/ic_download" | ||||
android:contentDescription="@string/generalImgContentText" /> | ||||
| ||||
<TextView | ||||
android:id="@+id/releaseZipDownload" | ||||
android:layout_width="wrap_content" | ||||
android:layout_height="wrap_content" | ||||
android:layout_marginStart="8dp" | ||||
android:textColor="?attr/primaryTextColor" | ||||
android:textSize="12sp" /> | ||||
| ||||
</LinearLayout> | ||||
| ||||
<LinearLayout | ||||
android:layout_width="match_parent" | ||||
android:layout_height="wrap_content" | ||||
android:gravity="center_vertical" | ||||
android:orientation="horizontal" | ||||
android:layout_marginStart="8dp" | ||||
tools:ignore="UseCompoundDrawables"> | ||||
| ||||
<ImageView | ||||
android:layout_width="18dp" | ||||
android:layout_height="18dp" | ||||
app:srcCompat="@drawable/ic_download" | ||||
android:contentDescription="@string/generalImgContentText" /> | ||||
| ||||
<TextView | ||||
android:id="@+id/releaseTarDownload" | ||||
android:layout_width="wrap_content" | ||||
android:layout_height="wrap_content" | ||||
android:layout_marginStart="8dp" | ||||
android:textColor="?attr/primaryTextColor" | ||||
android:textSize="12sp" /> | ||||
| ||||
</LinearLayout> | ||||
| ||||
</LinearLayout> | ||||
| ||||
</LinearLayout> | ||||
| ||||
</LinearLayout> |
12 app/src/main/res/menu/filter_menu_releases.xml Normal file
12
app/src/main/res/menu/filter_menu_releases.xml Normal file | @ -0,0 +1,12 @@ | |||
<?xml version="1.0" encoding="utf-8"?> | ||||
<menu xmlns:android="http://schemas.android.com/apk/res/android" | ||||
xmlns:app="http://schemas.android.com/apk/res-auto"> | ||||
| ||||
<item | ||||
android:id="@+id/filterReleases" | ||||
android:icon="@drawable/ic_filter" | ||||
android:title="@string/releasesTags" | ||||
android:orderInCategory="0" | ||||
app:showAsAction="ifRoom" /> | ||||
| ||||
</menu> |
| @ -753,6 +753,14 @@ | |||
<string name="closePr">Close Pull Request</string> | ||||
<string name="reopenPr">Reopen Pull Request</string> | ||||
<string name="userAvatar">Avatar</string> | ||||
<string name="tags">Tags</string> | ||||
<string name="releasesTags">Releases/Tags</string> | ||||
<string name="create_tag">Create Tag Only</string> | ||||
<string name="tagCreated">Tag created</string> | ||||
<string name="asRef">Use as reference</string> | ||||
<string name="deleteTagConfirmation">Do you really want to delete this tag?</string> | ||||
<string name="deleteTagTitle">Delete tag %s</string> | ||||
<string name="tagDeleted">Tag deleted</string> | ||||
<string name="useCustomTabs">Use Custom Tabs</string> | ||||
<string name="browserOpenFailed">No application found to open this link. SSH URLs and URLs with another prefix the http:// or https:// are not supported by most browser</string> | ||||
</resources> | ||||
| |
Loading…
Add table
Add a link
Reference in a new issue