GitNex-Android-App/app/src/main/java/org/mian/gitnex/fragments/CommitDetailFragment.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);
}
}
}