460 lines
14 KiB
Java
460 lines
14 KiB
Java
package org.mian.gitnex.fragments;
|
|
|
|
import android.content.ClipData;
|
|
import android.content.ClipboardManager;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
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.core.text.HtmlCompat;
|
|
import androidx.fragment.app.Fragment;
|
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
|
import com.vdurmont.emoji.EmojiParser;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Objects;
|
|
import org.gitnex.tea4j.v2.models.Commit;
|
|
import org.gitnex.tea4j.v2.models.CommitStatus;
|
|
import org.mian.gitnex.R;
|
|
import org.mian.gitnex.activities.BaseActivity;
|
|
import org.mian.gitnex.activities.ProfileActivity;
|
|
import org.mian.gitnex.adapters.CommitStatusesAdapter;
|
|
import org.mian.gitnex.adapters.DiffFilesAdapter;
|
|
import org.mian.gitnex.clients.PicassoService;
|
|
import org.mian.gitnex.clients.RetrofitClient;
|
|
import org.mian.gitnex.databinding.FragmentCommitDetailsBinding;
|
|
import org.mian.gitnex.helpers.AlertDialogs;
|
|
import org.mian.gitnex.helpers.AppUtil;
|
|
import org.mian.gitnex.helpers.FileDiffView;
|
|
import org.mian.gitnex.helpers.ParseDiff;
|
|
import org.mian.gitnex.helpers.RoundedTransformation;
|
|
import org.mian.gitnex.helpers.TimeHelper;
|
|
import org.mian.gitnex.helpers.Toasty;
|
|
import org.mian.gitnex.helpers.contexts.IssueContext;
|
|
import org.mian.gitnex.helpers.contexts.RepositoryContext;
|
|
import retrofit2.Call;
|
|
import retrofit2.Callback;
|
|
import retrofit2.Response;
|
|
|
|
/**
|
|
* @author qwerty287
|
|
*/
|
|
public class CommitDetailFragment extends Fragment {
|
|
|
|
private FragmentCommitDetailsBinding binding;
|
|
private String repoOwner;
|
|
private String repoName;
|
|
private String sha;
|
|
private List<FileDiffView> fileDiffViews = new ArrayList<>();
|
|
private DiffFilesAdapter adapter;
|
|
private int loadingFinished = 0;
|
|
|
|
public static CommitDetailFragment newInstance() {
|
|
return new CommitDetailFragment();
|
|
}
|
|
|
|
@Nullable @Override
|
|
public View onCreateView(
|
|
@NonNull LayoutInflater inflater,
|
|
@Nullable ViewGroup container,
|
|
@Nullable Bundle savedInstanceState) {
|
|
|
|
super.onCreateView(inflater, container, savedInstanceState);
|
|
|
|
if (binding != null) {
|
|
return binding.getRoot();
|
|
}
|
|
|
|
binding = FragmentCommitDetailsBinding.inflate(getLayoutInflater(), container, false);
|
|
|
|
IssueContext issue = IssueContext.fromIntent(requireActivity().getIntent());
|
|
RepositoryContext repository = RepositoryContext.fromIntent(requireActivity().getIntent());
|
|
repoOwner = repository.getOwner();
|
|
repoName = repository.getName();
|
|
sha = requireActivity().getIntent().getStringExtra("sha");
|
|
binding.toolbarTitle.setText(sha.substring(0, Math.min(sha.length(), 10)));
|
|
|
|
adapter = new DiffFilesAdapter(requireContext(), fileDiffViews, issue, "commit");
|
|
|
|
binding.diffFiles.setHasFixedSize(true);
|
|
binding.diffFiles.setLayoutManager(new LinearLayoutManager(requireContext()));
|
|
binding.diffFiles.setAdapter(adapter);
|
|
|
|
getCommit();
|
|
getDiff();
|
|
getStatuses();
|
|
|
|
binding.statuses.setOnClickListener(
|
|
view -> {
|
|
if (binding.statusesLv.getVisibility() == View.GONE) {
|
|
binding.statusesExpandCollapse.setImageResource(R.drawable.ic_chevron_up);
|
|
binding.statusesLv.setVisibility(View.VISIBLE);
|
|
} else {
|
|
binding.statusesExpandCollapse.setImageResource(R.drawable.ic_chevron_down);
|
|
binding.statusesLv.setVisibility(View.GONE);
|
|
}
|
|
});
|
|
|
|
binding.close.setOnClickListener((v) -> requireActivity().finish());
|
|
|
|
return binding.getRoot();
|
|
}
|
|
|
|
private void getDiff() {
|
|
Call<String> call =
|
|
((BaseActivity) requireActivity()).getAccount().requiresVersion("1.16.0")
|
|
? RetrofitClient.getApiInterface(requireContext())
|
|
.repoDownloadCommitDiffOrPatch(repoOwner, repoName, sha, "diff")
|
|
: RetrofitClient.getWebInterface(requireContext())
|
|
.repoDownloadCommitDiffOrPatch(repoOwner, repoName, sha, "diff");
|
|
|
|
call.enqueue(
|
|
new Callback<>() {
|
|
|
|
@Override
|
|
public void onResponse(
|
|
@NonNull Call<String> call, @NonNull Response<String> response) {
|
|
|
|
checkLoading();
|
|
switch (response.code()) {
|
|
case 200:
|
|
assert response.body() != null;
|
|
fileDiffViews = ParseDiff.getFileDiffViewArray(response.body());
|
|
|
|
requireActivity()
|
|
.runOnUiThread(
|
|
() -> {
|
|
adapter.updateList(fileDiffViews);
|
|
adapter.notifyDataChanged();
|
|
});
|
|
break;
|
|
|
|
case 401:
|
|
requireActivity()
|
|
.runOnUiThread(
|
|
() ->
|
|
AlertDialogs
|
|
.authorizationTokenRevokedDialog(
|
|
requireContext()));
|
|
break;
|
|
|
|
case 403:
|
|
requireActivity()
|
|
.runOnUiThread(
|
|
() ->
|
|
Toasty.error(
|
|
requireContext(),
|
|
getString(
|
|
R.string.authorizeError)));
|
|
break;
|
|
|
|
case 404:
|
|
requireActivity()
|
|
.runOnUiThread(
|
|
() ->
|
|
Toasty.warning(
|
|
requireContext(),
|
|
getString(R.string.apiNotFound)));
|
|
break;
|
|
|
|
default:
|
|
requireActivity()
|
|
.runOnUiThread(
|
|
() ->
|
|
Toasty.error(
|
|
requireContext(),
|
|
getString(R.string.genericError)));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(@NonNull Call<String> call, @NonNull Throwable t) {
|
|
|
|
checkLoading();
|
|
if (getContext() != null) {
|
|
Toasty.error(requireContext(), getString(R.string.genericError));
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private void getCommit() {
|
|
|
|
RetrofitClient.getApiInterface(requireContext())
|
|
.repoGetSingleCommit(repoOwner, repoName, sha, true, false, true)
|
|
.enqueue(
|
|
new Callback<>() {
|
|
|
|
@Override
|
|
public void onResponse(
|
|
@NonNull Call<Commit> call,
|
|
@NonNull Response<Commit> response) {
|
|
|
|
checkLoading();
|
|
Commit commitsModel = response.body();
|
|
if (commitsModel == null) {
|
|
onFailure(call, new Throwable());
|
|
return;
|
|
}
|
|
String[] commitMessageParts =
|
|
commitsModel.getCommit().getMessage().split("(\r\n|\n)", 2);
|
|
|
|
if (commitMessageParts.length > 1
|
|
&& !commitMessageParts[1].trim().isEmpty()) {
|
|
binding.commitBody.setVisibility(View.VISIBLE);
|
|
binding.commitSubject.setText(
|
|
EmojiParser.parseToUnicode(
|
|
commitMessageParts[0].trim()));
|
|
binding.commitBody.setText(
|
|
EmojiParser.parseToUnicode(
|
|
commitMessageParts[1].trim()));
|
|
} else {
|
|
binding.commitSubject.setText(
|
|
EmojiParser.parseToUnicode(
|
|
commitMessageParts[0].trim()));
|
|
binding.commitBody.setVisibility(View.GONE);
|
|
}
|
|
|
|
if (!Objects.equals(
|
|
commitsModel.getCommit().getCommitter().getEmail(),
|
|
commitsModel.getCommit().getCommitter().getEmail())) {
|
|
binding.commitAuthorAndCommitter.setText(
|
|
HtmlCompat.fromHtml(
|
|
CommitDetailFragment.this.getString(
|
|
R.string
|
|
.commitAuthoredByAndCommittedByWhen,
|
|
commitsModel
|
|
.getCommit()
|
|
.getAuthor()
|
|
.getName(),
|
|
commitsModel
|
|
.getCommit()
|
|
.getCommitter()
|
|
.getName(),
|
|
TimeHelper.formatTime(
|
|
TimeHelper.parseIso8601(
|
|
commitsModel
|
|
.getCommit()
|
|
.getCommitter()
|
|
.getDate()),
|
|
getResources()
|
|
.getConfiguration()
|
|
.locale)),
|
|
HtmlCompat.FROM_HTML_MODE_COMPACT));
|
|
} else {
|
|
binding.commitAuthorAndCommitter.setText(
|
|
HtmlCompat.fromHtml(
|
|
CommitDetailFragment.this.getString(
|
|
R.string.commitCommittedByWhen,
|
|
commitsModel
|
|
.getCommit()
|
|
.getCommitter()
|
|
.getName(),
|
|
TimeHelper.formatTime(
|
|
TimeHelper.parseIso8601(
|
|
commitsModel
|
|
.getCommit()
|
|
.getCommitter()
|
|
.getDate()),
|
|
getResources()
|
|
.getConfiguration()
|
|
.locale)),
|
|
HtmlCompat.FROM_HTML_MODE_COMPACT));
|
|
}
|
|
|
|
if (commitsModel.getAuthor() != null
|
|
&& commitsModel.getAuthor().getAvatarUrl() != null
|
|
&& !commitsModel.getAuthor().getAvatarUrl().isEmpty()) {
|
|
|
|
binding.commitAuthorAvatarFrame.setVisibility(View.VISIBLE);
|
|
|
|
int imgRadius =
|
|
AppUtil.getPixelsFromDensity(requireContext(), 3);
|
|
|
|
PicassoService.getInstance(requireContext())
|
|
.get()
|
|
.load(commitsModel.getAuthor().getAvatarUrl())
|
|
.placeholder(R.drawable.loader_animated)
|
|
.transform(new RoundedTransformation(imgRadius, 0))
|
|
.resize(120, 120)
|
|
.centerCrop()
|
|
.into(binding.commitAuthorAvatar);
|
|
|
|
binding.commitAuthorAvatar.setOnClickListener(
|
|
(v) -> {
|
|
Intent intent =
|
|
new Intent(
|
|
requireContext(),
|
|
ProfileActivity.class);
|
|
intent.putExtra(
|
|
"username",
|
|
commitsModel.getAuthor().getLogin());
|
|
startActivity(intent);
|
|
});
|
|
|
|
} else {
|
|
binding.commitAuthorAvatar.setImageDrawable(null);
|
|
binding.commitAuthorAvatarFrame.setVisibility(View.GONE);
|
|
}
|
|
|
|
if (commitsModel.getCommitter() != null
|
|
&& (commitsModel.getAuthor() == null
|
|
|| !commitsModel
|
|
.getAuthor()
|
|
.getLogin()
|
|
.equals(
|
|
commitsModel
|
|
.getCommitter()
|
|
.getLogin()))
|
|
&& commitsModel.getCommitter().getAvatarUrl() != null
|
|
&& !commitsModel.getCommitter().getAvatarUrl().isEmpty()) {
|
|
|
|
binding.commitCommitterAvatarFrame.setVisibility(View.VISIBLE);
|
|
|
|
int imgRadius =
|
|
AppUtil.getPixelsFromDensity(requireContext(), 3);
|
|
|
|
PicassoService.getInstance(requireContext())
|
|
.get()
|
|
.load(commitsModel.getCommitter().getAvatarUrl())
|
|
.placeholder(R.drawable.loader_animated)
|
|
.transform(new RoundedTransformation(imgRadius, 0))
|
|
.resize(120, 120)
|
|
.centerCrop()
|
|
.into(binding.commitCommitterAvatar);
|
|
|
|
binding.commitCommitterAvatar.setOnClickListener(
|
|
(v) -> {
|
|
Intent intent =
|
|
new Intent(
|
|
requireContext(),
|
|
ProfileActivity.class);
|
|
intent.putExtra(
|
|
"username",
|
|
commitsModel.getCommitter().getLogin());
|
|
startActivity(intent);
|
|
});
|
|
|
|
} else {
|
|
binding.commitCommitterAvatar.setImageDrawable(null);
|
|
binding.commitCommitterAvatarFrame.setVisibility(View.GONE);
|
|
}
|
|
|
|
binding.commitSha.setText(
|
|
commitsModel
|
|
.getSha()
|
|
.substring(
|
|
0,
|
|
Math.min(
|
|
commitsModel.getSha().length(),
|
|
10)));
|
|
binding.commitSha.setOnClickListener(
|
|
(v) -> {
|
|
ClipboardManager clipboard =
|
|
(ClipboardManager)
|
|
requireContext()
|
|
.getSystemService(
|
|
Context
|
|
.CLIPBOARD_SERVICE);
|
|
ClipData clip =
|
|
ClipData.newPlainText(
|
|
"commitSha", commitsModel.getSha());
|
|
assert clipboard != null;
|
|
clipboard.setPrimaryClip(clip);
|
|
Toasty.success(
|
|
requireContext(),
|
|
getString(R.string.copyShaToastMsg));
|
|
});
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(
|
|
@NonNull Call<Commit> call, @NonNull Throwable t) {
|
|
|
|
checkLoading();
|
|
if (getContext() != null) {
|
|
Toasty.error(
|
|
requireContext(), getString(R.string.genericError));
|
|
requireActivity().finish();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private void getStatuses() {
|
|
RetrofitClient.getApiInterface(requireContext())
|
|
.repoListStatuses(repoOwner, repoName, sha, null, null, null, null)
|
|
.enqueue(
|
|
new Callback<>() {
|
|
|
|
@Override
|
|
public void onResponse(
|
|
@NonNull Call<List<CommitStatus>> call,
|
|
@NonNull Response<List<CommitStatus>> response) {
|
|
|
|
checkLoading();
|
|
|
|
if (!response.isSuccessful() || response.body() == null) {
|
|
onFailure(call, new Throwable());
|
|
return;
|
|
}
|
|
|
|
if (response.body().size() < 1) {
|
|
binding.statusesLvMain.setVisibility(View.GONE);
|
|
return;
|
|
}
|
|
|
|
// merge statuses: a status can be added multiple times with the
|
|
// same context, so we only use the newest one
|
|
ArrayList<CommitStatus> result = new ArrayList<>();
|
|
for (CommitStatus c : response.body()) {
|
|
CommitStatus statusInList = null;
|
|
for (CommitStatus s : result) {
|
|
if (Objects.equals(s.getContext(), c.getContext())) {
|
|
statusInList = s;
|
|
break;
|
|
}
|
|
}
|
|
if (statusInList != null) {
|
|
// if the status that's already in the list was created
|
|
// before this one, replace it
|
|
if (statusInList.getCreatedAt().before(c.getCreatedAt())) {
|
|
result.remove(statusInList);
|
|
result.add(c);
|
|
}
|
|
} else {
|
|
result.add(c);
|
|
}
|
|
}
|
|
|
|
binding.statusesList.setLayoutManager(
|
|
new LinearLayoutManager(requireContext()));
|
|
binding.statusesList.setAdapter(new CommitStatusesAdapter(result));
|
|
}
|
|
|
|
@Override
|
|
public void onFailure(
|
|
@NonNull Call<List<CommitStatus>> call, @NonNull Throwable t) {
|
|
|
|
checkLoading();
|
|
if (getContext() != null) {
|
|
Toasty.error(
|
|
requireContext(), getString(R.string.genericError));
|
|
requireActivity().finish();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
private void checkLoading() {
|
|
loadingFinished += 1;
|
|
if (loadingFinished >= 3) {
|
|
binding.progressBar.setVisibility(View.GONE);
|
|
}
|
|
}
|
|
}
|