Repository PR List #198

Merged
mmarif merged 7 commits from 8-repo-pr-list into master 2019-11-24 12:43:01 +00:00
39 changed files with 2345 additions and 84 deletions

View File

@ -23,7 +23,7 @@ android {
}
dependencies {
def lifecycle_version = "2.2.0-rc01"
def lifecycle_version = "2.2.0-rc02"
final def markwon_version = "4.1.1"
implementation fileTree(include: ['*.jar'], dir: 'libs')

View File

@ -66,6 +66,7 @@
<activity android:name=".activities.NewRepoActivity" />
<activity android:name=".activities.NewOrganizationActivity" />
<activity android:name=".activities.OpenRepoInBrowserActivity" />
<activity android:name=".activities.FileDiffActivity" />
</application>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

View File

@ -100,7 +100,13 @@ public class EditIssueActivity extends AppCompatActivity implements View.OnClick
editIssueButton.setOnClickListener(this);
if(!tinyDb.getString("issueNumber").isEmpty()) {
toolbar_title.setText(getString(R.string.editIssueNavHeader, String.valueOf(issueIndex)));
if(tinyDb.getString("issueType").equals("pr")) {
toolbar_title.setText(getString(R.string.editPrNavHeader, String.valueOf(issueIndex)));
}
else {
toolbar_title.setText(getString(R.string.editIssueNavHeader, String.valueOf(issueIndex)));
}
}
disableProcessButton();
@ -240,7 +246,13 @@ public class EditIssueActivity extends AppCompatActivity implements View.OnClick
if(response.code() == 201) {
Toasty.info(getApplicationContext(), getString(R.string.editIssueSuccessMessage));
if(tinyDb.getString("issueType").equals("pr")) {
Toasty.info(getApplicationContext(), getString(R.string.editPrSuccessMessage));
}
else {
Toasty.info(getApplicationContext(), getString(R.string.editIssueSuccessMessage));
}
tinyDb.putBoolean("issueEdited", true);
tinyDb.putBoolean("resumeIssues", true);
finish();

View File

@ -0,0 +1,212 @@
package org.mian.gitnex.activities;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import org.apache.commons.io.FilenameUtils;
import org.mian.gitnex.R;
import org.mian.gitnex.adapters.FilesDiffAdapter;
import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.helpers.AlertDialogs;
import org.mian.gitnex.helpers.Toasty;
import org.mian.gitnex.models.FileDiffView;
import org.mian.gitnex.util.AppUtil;
import org.mian.gitnex.util.TinyDB;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.Callback;
/**
* Author M M Arif
*/
public class FileDiffActivity extends AppCompatActivity {
private View.OnClickListener onClickListener;
private TextView toolbar_title;
private RecyclerView mRecyclerView;
private ProgressBar mProgressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_file_diff);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
final TinyDB tinyDb = new TinyDB(getApplicationContext());
String repoFullName = tinyDb.getString("repoFullName");
String[] parts = repoFullName.split("/");
final String repoOwner = parts[0];
final String repoName = parts[1];
final String instanceUrl = tinyDb.getString("instanceUrl");
final String loginUid = tinyDb.getString("loginUid");
final String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
ImageView closeActivity = findViewById(R.id.close);
toolbar_title = findViewById(R.id.toolbar_title);
mRecyclerView = findViewById(R.id.recyclerView);
mProgressBar = findViewById(R.id.progress_bar);
mRecyclerView.setHasFixedSize(true);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
toolbar_title.setText(R.string.processingText);
initCloseListener();
closeActivity.setOnClickListener(onClickListener);
mProgressBar.setVisibility(View.VISIBLE);
String fileDiffName = tinyDb.getString("issueNumber")+".diff";
getFileContents(tinyDb.getString("instanceUrlWithProtocol"), repoOwner, repoName, fileDiffName);
}
private void getFileContents(String instanceUrl, String owner, String repo, String filename) {
Call<ResponseBody> call = RetrofitClient
.getInstance(instanceUrl, getApplicationContext())
.getApiInterface()
.getFileDiffContents(owner, repo, filename);
call.enqueue(new Callback<ResponseBody>() {
@Override
public void onResponse(@NonNull Call<ResponseBody> call, @NonNull retrofit2.Response<ResponseBody> response) {
if (response.code() == 200) {
try {
assert response.body() != null;
AppUtil appUtil = new AppUtil();
List<FileDiffView> fileContentsArray = new ArrayList<>();
String[] lines = response.body().string().split("diff");
if(lines.length > 0) {
for (int i = 1; i < lines.length; i++) {
if(lines[i].contains("@@ -")) {
String[] level2nd = lines[i].split("@@ -"); // main content part of single diff view
String[] fileName_ = level2nd[0].split("\\+\\+\\+ b/"); // filename part
String fileNameFinal = fileName_[1];
String[] fileContents_ = level2nd[1].split("@@"); // file info / content part
String fileInfoFinal = fileContents_[0];
String fileContentsFinal = (fileContents_[1]);
if(level2nd.length > 2) {
for (int j = 2; j < level2nd.length; j++) {
fileContentsFinal += (level2nd[j]);
}
}
String fileExtension = FilenameUtils.getExtension(fileNameFinal);
String fileContentsFinalWithBlankLines = fileContentsFinal.replaceAll( ".*@@.*", "" );
String fileContentsFinalWithoutBlankLines = fileContentsFinal.replaceAll( ".*@@.*(\r?\n|\r)?", "" );
fileContentsFinalWithoutBlankLines = fileContentsFinalWithoutBlankLines.replaceAll( ".*\\ No newline at end of file.*(\r?\n|\r)?", "" );
fileContentsArray.add(new FileDiffView(fileNameFinal, appUtil.imageExtension(fileExtension), fileInfoFinal, fileContentsFinalWithoutBlankLines));
}
else {
String[] getFileName = lines[i].split("--git a/");
String[] getFileName_ = getFileName[1].split("b/");
String getFileNameFinal = getFileName_[0].trim();
String[] binaryFile = getFileName_[1].split("GIT binary patch");
String binaryFileRaw = binaryFile[1].substring(binaryFile[1].indexOf('\n')+1);
String binaryFileFinal = binaryFile[1].substring(binaryFileRaw.indexOf('\n')+1);
String fileExtension = FilenameUtils.getExtension(getFileNameFinal);
fileContentsArray.add(new FileDiffView(getFileNameFinal, appUtil.imageExtension(fileExtension),"", binaryFileFinal));
}
}
}
int filesCount = fileContentsArray.size();
if(filesCount > 1) {
toolbar_title.setText(getResources().getString(R.string.fileDiffViewHeader, Integer.toString(filesCount)));
}
else {
toolbar_title.setText(getResources().getString(R.string.fileDiffViewHeaderSingle, Integer.toString(filesCount)));
}
FilesDiffAdapter adapter = new FilesDiffAdapter(fileContentsArray, getApplicationContext());
mRecyclerView.setAdapter(adapter);
mProgressBar.setVisibility(View.GONE);
} catch (IOException e) {
e.printStackTrace();
}
}
else if(response.code() == 401) {
AlertDialogs.authorizationTokenRevokedDialog(getApplicationContext(), getResources().getString(R.string.alertDialogTokenRevokedTitle),
getResources().getString(R.string.alertDialogTokenRevokedMessage),
getResources().getString(R.string.alertDialogTokenRevokedCopyNegativeButton),
getResources().getString(R.string.alertDialogTokenRevokedCopyPositiveButton));
}
else if(response.code() == 403) {
Toasty.info(getApplicationContext(), getApplicationContext().getString(R.string.authorizeError));
}
else if(response.code() == 404) {
Toasty.info(getApplicationContext(), getApplicationContext().getString(R.string.apiNotFound));
}
else {
Toasty.info(getApplicationContext(), getString(R.string.labelGeneralError));
}
}
@Override
public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {
Log.e("onFailure", t.toString());
}
});
}
private void initCloseListener() {
onClickListener = new View.OnClickListener() {
@Override
public void onClick(View view) {
getIntent().removeExtra("singleFileName");
finish();
}
};
}
}

View File

@ -226,6 +226,7 @@ public class ReplyToIssueActivity extends AppCompatActivity {
Toasty.info(getApplicationContext(), getString(R.string.commentSuccess));
tinyDb.putBoolean("commentPosted", true);
tinyDb.putBoolean("resumeIssues", true);
tinyDb.putBoolean("resumePullRequests", true);
finish();
}

View File

@ -31,6 +31,7 @@ import org.mian.gitnex.fragments.FilesFragment;
import org.mian.gitnex.fragments.IssuesFragment;
import org.mian.gitnex.fragments.LabelsFragment;
import org.mian.gitnex.fragments.MilestonesFragment;
import org.mian.gitnex.fragments.PullRequestsFragment;
import org.mian.gitnex.fragments.ReleasesFragment;
import org.mian.gitnex.fragments.RepoBottomSheetFragment;
import org.mian.gitnex.fragments.RepoInfoFragment;
@ -213,15 +214,18 @@ public class RepoDetailActivity extends AppCompatActivity implements RepoBottomS
case 3: // closed issues
fragment = new ClosedIssuesFragment();
break;
case 4: // milestones
case 4: // pull requests
fragment = new PullRequestsFragment();
break;
case 5: // milestones
return MilestonesFragment.newInstance(repoOwner, repoName);
case 5: // labels
case 6: // labels
return LabelsFragment.newInstance(repoOwner, repoName);
case 6: // branches
case 7: // branches
return BranchesFragment.newInstance(repoOwner, repoName);
case 7: // releases
case 8: // releases
return ReleasesFragment.newInstance(repoOwner, repoName);
case 8: // collaborators
case 9: // collaborators
return CollaboratorsFragment.newInstance(repoOwner, repoName);
}
return fragment;
@ -229,7 +233,7 @@ public class RepoDetailActivity extends AppCompatActivity implements RepoBottomS
@Override
public int getCount() {
return 9;
return 10;
}
}

View File

@ -11,6 +11,7 @@ import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.squareup.picasso.Picasso;
import org.mian.gitnex.R;
@ -109,7 +110,7 @@ public class ClosedIssuesAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
private TextView issueTitle;
private TextView issueCreatedTime;
private TextView issueCommentsCount;
private ImageView issueType;
private RelativeLayout relativeLayoutFrame;
IssuesHolder(View itemView) {
@ -121,7 +122,7 @@ public class ClosedIssuesAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
issueCommentsCount = itemView.findViewById(R.id.issueCommentsCount);
LinearLayout frameCommentsCount = itemView.findViewById(R.id.frameCommentsCount);
issueCreatedTime = itemView.findViewById(R.id.issueCreatedTime);
issueType = itemView.findViewById(R.id.issueType);
relativeLayoutFrame = itemView.findViewById(R.id.relativeLayoutFrame);
issueTitle.setOnClickListener(new View.OnClickListener() {
@Override
@ -135,6 +136,7 @@ public class ClosedIssuesAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
TinyDB tinyDb = new TinyDB(context);
tinyDb.putString("issueNumber", issueNumber.getText().toString());
tinyDb.putString("issueType", "issue");
context.startActivity(intent);
}
@ -151,6 +153,7 @@ public class ClosedIssuesAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
TinyDB tinyDb = new TinyDB(context);
tinyDb.putString("issueNumber", issueNumber.getText().toString());
tinyDb.putString("issueType", "issue");
context.startActivity(intent);
}
@ -165,6 +168,13 @@ public class ClosedIssuesAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
final String locale = tinyDb.getString("locale");
final String timeFormat = tinyDb.getString("dateFormat");
if(issuesModel.getPull_request() != null) {
if (issuesModel.getPull_request().isMerged()) {
relativeLayoutFrame.setVisibility(View.GONE);
relativeLayoutFrame.setLayoutParams(new RecyclerView.LayoutParams(0, 0));
}
}
if (!issuesModel.getUser().getFull_name().equals("")) {
issueAssigneeAvatar.setOnClickListener(new ClickListener(context.getResources().getString(R.string.issueCreator) + issuesModel.getUser().getFull_name(), context));
} else {
@ -177,14 +187,6 @@ public class ClosedIssuesAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
Picasso.get().load(issuesModel.getUser().getAvatar_url()).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(issueAssigneeAvatar);
}
if (issuesModel.getPull_request() == null) {
issueType.setImageResource(R.drawable.ic_issues);
issueType.setOnClickListener(new ClickListener(context.getResources().getString(R.string.issueTypeIssue), context));
} else {
issueType.setImageResource(R.drawable.ic_merge);
issueType.setOnClickListener(new ClickListener(context.getResources().getString(R.string.issueTypePullRequest), context));
}
String issueNumber_ = "<font color='" + context.getResources().getColor(R.color.lightGray) + "'>" + context.getResources().getString(R.string.hash) + issuesModel.getNumber() + "</font>";
issueTitle.setText(Html.fromHtml(issueNumber_ + " " + issuesModel.getTitle()));

View File

@ -0,0 +1,138 @@
package org.mian.gitnex.adapters;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import org.mian.gitnex.R;
import org.mian.gitnex.models.FileDiffView;
import java.util.List;
/**
* Author M M Arif
*/
public class FilesDiffAdapter extends RecyclerView.Adapter<FilesDiffAdapter.FilesDiffViewHolder> {
private List<FileDiffView> dataList;
private Context ctx;
static class FilesDiffViewHolder extends RecyclerView.ViewHolder {
private TextView fileContents;
private TextView fileName;
private TextView fileInfo;
private ImageView fileImage;
private HorizontalScrollView fileContentsView;
private LinearLayout allLines;
private FilesDiffViewHolder(View itemView) {
super(itemView);
fileContents = itemView.findViewById(R.id.fileContents);
fileName = itemView.findViewById(R.id.fileName);
fileInfo = itemView.findViewById(R.id.fileInfo);
fileImage = itemView.findViewById(R.id.fileImage);
fileContentsView = itemView.findViewById(R.id.fileContentsView);
allLines = itemView.findViewById(R.id.allLinesLayout);
}
}
public FilesDiffAdapter(List<FileDiffView> dataListMain, Context ctx) {
this.dataList = dataListMain;
this.ctx = ctx;
}
@NonNull
@Override
public FilesDiffAdapter.FilesDiffViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.files_diffs_list, parent, false);
return new FilesDiffAdapter.FilesDiffViewHolder(v);
}
@Override
public void onBindViewHolder(@NonNull FilesDiffViewHolder holder, int position) {
FileDiffView data = dataList.get(position);
if(data.isFileType()) {
holder.fileName.setText(data.getFileName());
holder.fileInfo.setVisibility(View.GONE);
//byte[] imageData = Base64.decode(data.getFileContents(), Base64.DEFAULT);
//Drawable imageDrawable = new BitmapDrawable(ctx.getResources(), BitmapFactory.decodeByteArray(imageData, 0, imageData.length));
//holder.fileImage.setImageDrawable(imageDrawable);
holder.fileContentsView.setVisibility(View.GONE);
}
else {
String[] splitData = data.getFileContents().split("\\R");
for (String eachSplit : splitData) {
TextView textLine = new TextView(ctx);
textLine.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
if (eachSplit.startsWith("+")) {
textLine.setText(eachSplit);
holder.allLines.addView(textLine);
textLine.setTextColor(ctx.getResources().getColor(R.color.colorPrimary));
textLine.setPadding(5, 5, 5, 5);
textLine.setBackgroundColor(ctx.getResources().getColor(R.color.diffAddedColor));
}
else if (eachSplit.startsWith("-")) {
textLine.setText(eachSplit);
holder.allLines.addView(textLine);
textLine.setTextColor(ctx.getResources().getColor(R.color.colorPrimary));
textLine.setPadding(5, 5, 5, 5);
textLine.setBackgroundColor(ctx.getResources().getColor(R.color.diffRemovedColor));
}
else {
if(eachSplit.length() > 0) {
textLine.setText(eachSplit);
holder.allLines.addView(textLine);
textLine.setTextColor(ctx.getResources().getColor(R.color.colorPrimary));
textLine.setPadding(5, 5, 5, 5);
textLine.setBackgroundColor(ctx.getResources().getColor(R.color.white));
}
}
}
holder.fileName.setText(data.getFileName());
if(!data.getFileInfo().equals("")) {
holder.fileInfo.setText(ctx.getResources().getString(R.string.fileDiffInfoChanges, data.getFileInfo()));
}
else {
holder.fileInfo.setVisibility(View.GONE);
}
}
}
@Override
public int getItemCount() {
return dataList.size();
}
}

View File

@ -11,6 +11,7 @@ import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.squareup.picasso.Picasso;
import org.mian.gitnex.R;
@ -109,7 +110,7 @@ public class IssuesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
private TextView issueTitle;
private TextView issueCreatedTime;
private TextView issueCommentsCount;
private ImageView issueType;
private RelativeLayout relativeLayoutFrame;
IssuesHolder(View itemView) {
@ -121,7 +122,7 @@ public class IssuesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
issueCommentsCount = itemView.findViewById(R.id.issueCommentsCount);
LinearLayout frameCommentsCount = itemView.findViewById(R.id.frameCommentsCount);
issueCreatedTime = itemView.findViewById(R.id.issueCreatedTime);
issueType = itemView.findViewById(R.id.issueType);
relativeLayoutFrame = itemView.findViewById(R.id.relativeLayoutFrame);
issueTitle.setOnClickListener(new View.OnClickListener() {
@Override
@ -135,6 +136,7 @@ public class IssuesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
TinyDB tinyDb = new TinyDB(context);
tinyDb.putString("issueNumber", issueNumber.getText().toString());
tinyDb.putString("issueType", "issue");
context.startActivity(intent);
}
@ -151,6 +153,7 @@ public class IssuesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
TinyDB tinyDb = new TinyDB(context);
tinyDb.putString("issueNumber", issueNumber.getText().toString());
tinyDb.putString("issueType", "issue");
context.startActivity(intent);
}
@ -165,6 +168,13 @@ public class IssuesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
final String locale = tinyDb.getString("locale");
final String timeFormat = tinyDb.getString("dateFormat");
if(issuesModel.getPull_request() != null) {
if (!issuesModel.getPull_request().isMerged()) {
relativeLayoutFrame.setVisibility(View.GONE);
relativeLayoutFrame.setLayoutParams(new RecyclerView.LayoutParams(0, 0));
}
}
if (!issuesModel.getUser().getFull_name().equals("")) {
issueAssigneeAvatar.setOnClickListener(new ClickListener(context.getResources().getString(R.string.issueCreator) + issuesModel.getUser().getFull_name(), context));
} else {
@ -177,14 +187,6 @@ public class IssuesAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
Picasso.get().load(issuesModel.getUser().getAvatar_url()).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(issueAssigneeAvatar);
}
if (issuesModel.getPull_request() == null) {
issueType.setImageResource(R.drawable.ic_issues);
issueType.setOnClickListener(new ClickListener(context.getResources().getString(R.string.issueTypeIssue), context));
} else {
issueType.setImageResource(R.drawable.ic_merge);
issueType.setOnClickListener(new ClickListener(context.getResources().getString(R.string.issueTypePullRequest), context));
}
String issueNumber_ = "<font color='" + context.getResources().getColor(R.color.lightGray) + "'>" + context.getResources().getString(R.string.hash) + issuesModel.getNumber() + "</font>";
issueTitle.setText(Html.fromHtml(issueNumber_ + " " + issuesModel.getTitle()));

View File

@ -50,6 +50,7 @@ public class MyReposListAdapter extends RecyclerView.Adapter<MyReposListAdapter.
private TextView repoStarsMy;
private TextView repoForksMy;
private TextView repoOpenIssuesCountMy;
private TextView repoType;
private MyReposViewHolder(View itemView) {
super(itemView);
@ -62,6 +63,7 @@ public class MyReposListAdapter extends RecyclerView.Adapter<MyReposListAdapter.
repoForksMy = itemView.findViewById(R.id.repoForksMy);
repoOpenIssuesCountMy = itemView.findViewById(R.id.repoOpenIssuesCountMy);
ImageView reposDropdownMenu = itemView.findViewById(R.id.reposDropdownMenu);
repoType = itemView.findViewById(R.id.repoType);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
@ -74,6 +76,7 @@ public class MyReposListAdapter extends RecyclerView.Adapter<MyReposListAdapter.
TinyDB tinyDb = new TinyDB(context);
tinyDb.putString("repoFullName", fullNameMy.getText().toString());
tinyDb.putString("repoType", repoType.getText().toString());
tinyDb.putBoolean("resumeIssues", true);
context.startActivity(intent);
@ -198,9 +201,11 @@ public class MyReposListAdapter extends RecyclerView.Adapter<MyReposListAdapter.
holder.fullNameMy.setText(currentItem.getFullname());
if(currentItem.getPrivateFlag()) {
holder.repoPrivatePublicMy.setImageResource(R.drawable.ic_lock_bold);
holder.repoType.setText(R.string.strPrivate);
}
else {
holder.repoPrivatePublicMy.setImageResource(R.drawable.ic_public);
holder.repoType.setText(R.string.strPublic);
}
holder.repoStarsMy.setText(currentItem.getStars_count());
holder.repoForksMy.setText(currentItem.getForks_count());

View File

@ -0,0 +1,279 @@
package org.mian.gitnex.adapters;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.squareup.picasso.Picasso;
import org.mian.gitnex.R;
import org.mian.gitnex.activities.IssueDetailActivity;
import org.mian.gitnex.helpers.ClickListener;
import org.mian.gitnex.helpers.RoundedTransformation;
import org.mian.gitnex.helpers.TimeHelper;
import org.mian.gitnex.models.PullRequests;
import org.mian.gitnex.util.TinyDB;
import org.ocpsoft.prettytime.PrettyTime;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
/**
* Author M M Arif
*/
public class PullRequestsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements Filterable {
private Context context;
private final int TYPE_LOAD = 0;
private List<PullRequests> prList;
private List<PullRequests> prListFull;
private PullRequestsAdapter.OnLoadMoreListener loadMoreListener;
private boolean isLoading = false, isMoreDataAvailable = true;
public PullRequestsAdapter(Context context, List<PullRequests> prListMain) {
this.context = context;
this.prList = prListMain;
prListFull = new ArrayList<>(prList);
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
if(viewType == TYPE_LOAD){
return new PullRequestsAdapter.PullRequestsHolder(inflater.inflate(R.layout.repo_pr_list, parent,false));
}
else {
return new PullRequestsAdapter.LoadHolder(inflater.inflate(R.layout.row_load,parent,false));
}
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if(position >= getItemCount()-1 && isMoreDataAvailable && !isLoading && loadMoreListener!=null) {
isLoading = true;
loadMoreListener.onLoadMore();
}
if(getItemViewType(position) == TYPE_LOAD) {
((PullRequestsAdapter.PullRequestsHolder)holder).bindData(prList.get(position));
}
}
@Override
public int getItemViewType(int position) {
if(prList.get(position).getTitle() != null) {
return TYPE_LOAD;
}
else {
return 1;
}
}
@Override
public int getItemCount() {
return prList.size();
}
class PullRequestsHolder extends RecyclerView.ViewHolder {
private TextView prNumber;
private ImageView assigneeAvatar;
private TextView prTitle;
private TextView prCreatedTime;
private TextView prCommentsCount;
PullRequestsHolder(View itemView) {
super(itemView);
prNumber = itemView.findViewById(R.id.prNumber);
assigneeAvatar = itemView.findViewById(R.id.assigneeAvatar);
prTitle = itemView.findViewById(R.id.prTitle);
prCommentsCount = itemView.findViewById(R.id.prCommentsCount);
LinearLayout frameCommentsCount = itemView.findViewById(R.id.frameCommentsCount);
prCreatedTime = itemView.findViewById(R.id.prCreatedTime);
prTitle.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(context, IssueDetailActivity.class);
intent.putExtra("issueNumber", prNumber.getText());
TinyDB tinyDb = new TinyDB(context);
tinyDb.putString("issueNumber", prNumber.getText().toString());
tinyDb.putString("issueType", "pr");
context.startActivity(intent);
}
});
frameCommentsCount.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Context context = v.getContext();
Intent intent = new Intent(context, IssueDetailActivity.class);
intent.putExtra("issueNumber", prNumber.getText());
TinyDB tinyDb = new TinyDB(context);
tinyDb.putString("issueNumber", prNumber.getText().toString());
tinyDb.putString("issueType", "pr");
context.startActivity(intent);
}
});
}
@SuppressLint("SetTextI18n")
void bindData(PullRequests prModel){
final TinyDB tinyDb = new TinyDB(context);
final String locale = tinyDb.getString("locale");
final String timeFormat = tinyDb.getString("dateFormat");
if (!prModel.getUser().getFull_name().equals("")) {
assigneeAvatar.setOnClickListener(new ClickListener(context.getResources().getString(R.string.prCreator) + prModel.getUser().getFull_name(), context));
} else {
assigneeAvatar.setOnClickListener(new ClickListener(context.getResources().getString(R.string.prCreator) + prModel.getUser().getLogin(), context));
}
if (prModel.getUser().getAvatar_url() != null) {
Picasso.get().load(prModel.getUser().getAvatar_url()).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(assigneeAvatar);
} else {
Picasso.get().load(prModel.getUser().getAvatar_url()).transform(new RoundedTransformation(8, 0)).resize(120, 120).centerCrop().into(assigneeAvatar);
}
String prNumber_ = "<font color='" + context.getResources().getColor(R.color.lightGray) + "'>" + context.getResources().getString(R.string.hash) + prModel.getNumber() + "</font>";
prTitle.setText(Html.fromHtml(prNumber_ + " " + prModel.getTitle()));
prNumber.setText(String.valueOf(prModel.getNumber()));
prCommentsCount.setText(String.valueOf(prModel.getComments()));
switch (timeFormat) {
case "pretty": {
PrettyTime prettyTime = new PrettyTime(new Locale(locale));
String createdTime = prettyTime.format(prModel.getCreated_at());
prCreatedTime.setText(createdTime);
prCreatedTime.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(prModel.getCreated_at()), context));
break;
}
case "normal": {
DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd '" + context.getResources().getString(R.string.timeAtText) + "' HH:mm", new Locale(locale));
String createdTime = formatter.format(prModel.getCreated_at());
prCreatedTime.setText(createdTime);
break;
}
case "normal1": {
DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy '" + context.getResources().getString(R.string.timeAtText) + "' HH:mm", new Locale(locale));
String createdTime = formatter.format(prModel.getCreated_at());
prCreatedTime.setText(createdTime);
break;
}
}
}
}
static class LoadHolder extends RecyclerView.ViewHolder {
LoadHolder(View itemView) {
super(itemView);
}
}
public void setMoreDataAvailable(boolean moreDataAvailable) {
isMoreDataAvailable = moreDataAvailable;
}
public void notifyDataChanged() {
notifyDataSetChanged();
isLoading = false;
}
public interface OnLoadMoreListener {
void onLoadMore();
}
public void setLoadMoreListener(PullRequestsAdapter.OnLoadMoreListener loadMoreListener) {
this.loadMoreListener = loadMoreListener;
}
@Override
public Filter getFilter() {
return prFilter;
}
private Filter prFilter = new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
List<PullRequests> filteredList = new ArrayList<>();
if (constraint == null || constraint.length() == 0) {
filteredList.addAll(prList);
} else {
String filterPattern = constraint.toString().toLowerCase().trim();
for (PullRequests item : prList) {
if (item.getTitle().toLowerCase().contains(filterPattern) || item.getBody().toLowerCase().contains(filterPattern)) {
filteredList.add(item);
}
}
}
FilterResults results = new FilterResults();
results.values = filteredList;
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
prList.clear();
prList.addAll((List) results.values);
notifyDataSetChanged();
}
};
}

View File

@ -50,6 +50,7 @@ public class ReposListAdapter extends RecyclerView.Adapter<ReposListAdapter.Repo
private TextView repoStars;
private TextView repoForks;
private TextView repoOpenIssuesCount;
private TextView repoType;
private ReposViewHolder(View itemView) {
@ -63,6 +64,7 @@ public class ReposListAdapter extends RecyclerView.Adapter<ReposListAdapter.Repo
repoForks = itemView.findViewById(R.id.repoForks);
repoOpenIssuesCount = itemView.findViewById(R.id.repoOpenIssuesCount);
ImageView reposDropdownMenu = itemView.findViewById(R.id.reposDropdownMenu);
repoType = itemView.findViewById(R.id.repoType);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
@ -70,12 +72,14 @@ public class ReposListAdapter extends RecyclerView.Adapter<ReposListAdapter.Repo
Context context = v.getContext();
TextView repoFullName = v.findViewById(R.id.repoFullName);
TextView repoType_ = v.findViewById(R.id.repoType);
Intent intent = new Intent(context, RepoDetailActivity.class);
intent.putExtra("repoFullName", repoFullName.getText().toString());
TinyDB tinyDb = new TinyDB(context);
tinyDb.putString("repoFullName", repoFullName.getText().toString());
tinyDb.putString("repoType", repoType_.getText().toString());
tinyDb.putBoolean("resumeIssues", true);
context.startActivity(intent);
@ -200,9 +204,11 @@ public class ReposListAdapter extends RecyclerView.Adapter<ReposListAdapter.Repo
holder.fullName.setText(currentItem.getFullname());
if(currentItem.getPrivateFlag()) {
holder.repoPrivatePublic.setImageResource(R.drawable.ic_lock_bold);
holder.repoType.setText(R.string.strPrivate);
}
else {
holder.repoPrivatePublic.setImageResource(R.drawable.ic_public);
holder.repoType.setText(R.string.strPublic);
}
holder.repoStars.setText(currentItem.getStars_count());
holder.repoForks.setText(currentItem.getForks_count());

View File

@ -50,6 +50,7 @@ public class RepositoriesByOrgAdapter extends RecyclerView.Adapter<RepositoriesB
private TextView repoStars;
private TextView repoForks;
private TextView repoOpenIssuesCount;
private TextView repoType;
private OrgReposViewHolder(View itemView) {
super(itemView);
@ -62,6 +63,7 @@ public class RepositoriesByOrgAdapter extends RecyclerView.Adapter<RepositoriesB
repoForks = itemView.findViewById(R.id.repoForks);
repoOpenIssuesCount = itemView.findViewById(R.id.repoOpenIssuesCount);
ImageView reposDropdownMenu = itemView.findViewById(R.id.reposDropdownMenu);
repoType = itemView.findViewById(R.id.repoType);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
@ -74,6 +76,7 @@ public class RepositoriesByOrgAdapter extends RecyclerView.Adapter<RepositoriesB
TinyDB tinyDb = new TinyDB(context);
tinyDb.putString("repoFullName", fullName.getText().toString());
tinyDb.putString("repoType", repoType.getText().toString());
tinyDb.putBoolean("resumeIssues", true);
context.startActivity(intent);
@ -199,9 +202,11 @@ public class RepositoriesByOrgAdapter extends RecyclerView.Adapter<RepositoriesB
holder.fullName.setText(currentItem.getFullname());
if(currentItem.getPrivateFlag()) {
holder.repoPrivatePublic.setImageResource(R.drawable.ic_lock_bold);
holder.repoType.setText(R.string.strPrivate);
}
else {
holder.repoPrivatePublic.setImageResource(R.drawable.ic_public);
holder.repoType.setText(R.string.strPublic);
}
holder.repoStars.setText(currentItem.getStars_count());
holder.repoForks.setText(currentItem.getForks_count());

View File

@ -50,6 +50,7 @@ public class StarredReposListAdapter extends RecyclerView.Adapter<StarredReposLi
private TextView repoStars;
private TextView repoForks;
private TextView repoOpenIssuesCount;
private TextView repoType;
private StarredReposViewHolder(View itemView) {
super(itemView);
@ -62,6 +63,7 @@ public class StarredReposListAdapter extends RecyclerView.Adapter<StarredReposLi
repoForks = itemView.findViewById(R.id.repoForks);
repoOpenIssuesCount = itemView.findViewById(R.id.repoOpenIssuesCount);
ImageView reposDropdownMenu = itemView.findViewById(R.id.reposDropdownMenu);
repoType = itemView.findViewById(R.id.repoType);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
@ -74,6 +76,7 @@ public class StarredReposListAdapter extends RecyclerView.Adapter<StarredReposLi
TinyDB tinyDb = new TinyDB(context);
tinyDb.putString("repoFullName", fullName.getText().toString());
tinyDb.putString("repoType", repoType.getText().toString());
tinyDb.putBoolean("resumeIssues", true);
context.startActivity(intent);
@ -199,9 +202,11 @@ public class StarredReposListAdapter extends RecyclerView.Adapter<StarredReposLi
holder.fullName.setText(currentItem.getFullname());
if(currentItem.getPrivateFlag()) {
holder.repoPrivatePublic.setImageResource(R.drawable.ic_lock_bold);
holder.repoType.setText(R.string.strPrivate);
}
else {
holder.repoPrivatePublic.setImageResource(R.drawable.ic_public);
holder.repoType.setText(R.string.strPublic);
}
holder.repoStars.setText(currentItem.getStars_count());
holder.repoForks.setText(currentItem.getForks_count());

View File

@ -24,7 +24,7 @@ public class IssuesService {
final boolean connToInternet = AppUtil.haveNetworkConnection(ctx);
File httpCacheDirectory = new File(ctx.getCacheDir(), "responses");
int cacheSize = 20 * 1024 * 1024;
int cacheSize = 50 * 1024 * 1024; // 50MB
Cache cache = new Cache(httpCacheDirectory, cacheSize);
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();

View File

@ -0,0 +1,61 @@
package org.mian.gitnex.clients;
import android.content.Context;
import androidx.annotation.NonNull;
import org.mian.gitnex.util.AppUtil;
import java.io.File;
import java.io.IOException;
import okhttp3.Cache;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* Author M M Arif
*/
public class PullRequestsService {
public static <S> S createService(Class<S> serviceClass, String instanceURL, Context ctx) {
final boolean connToInternet = AppUtil.haveNetworkConnection(ctx);
File httpCacheDirectory = new File(ctx.getCacheDir(), "responses");
int cacheSize = 50 * 1024 * 1024; // 50MB
Cache cache = new Cache(httpCacheDirectory, cacheSize);
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.cache(cache)
//.addInterceptor(logging)
.addInterceptor(new Interceptor() {
@NonNull
@Override public Response intercept(@NonNull Chain chain) throws IOException {
Request request = chain.request();
if (connToInternet) {
request = request.newBuilder().header("Cache-Control", "public, max-age=" + 60).build();
} else {
request = request.newBuilder().header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 30).build();
}
return chain.proceed(request);
}
})
.build();
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(instanceURL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = builder.build();
return retrofit.create(serviceClass);
}
}

View File

@ -27,7 +27,7 @@ public class RetrofitClient {
private RetrofitClient(String instanceUrl, Context ctx) {
final boolean connToInternet = AppUtil.haveNetworkConnection(ctx);
int cacheSize = 20 * 1024 * 1024;
int cacheSize = 50 * 1024 * 1024; // 50MB
File httpCacheDirectory = new File(ctx.getCacheDir(), "responses");
Cache cache = new Cache(httpCacheDirectory, cacheSize);

View File

@ -28,7 +28,6 @@ import java.util.Objects;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
@ -120,10 +119,7 @@ public class ClosedIssuesFragment extends Fragment {
}
});
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerViewClosed.getContext(),
DividerItemDecoration.VERTICAL);
recyclerViewClosed.setHasFixedSize(true);
recyclerViewClosed.addItemDecoration(dividerItemDecoration);
recyclerViewClosed.setLayoutManager(new LinearLayoutManager(context));
recyclerViewClosed.setAdapter(adapterClosed);

View File

@ -5,7 +5,6 @@ import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
@ -119,10 +118,7 @@ public class IssuesFragment extends Fragment {
}
});
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(),
DividerItemDecoration.VERTICAL);
recyclerView.setHasFixedSize(true);
recyclerView.addItemDecoration(dividerItemDecoration);
recyclerView.setLayoutManager(new LinearLayoutManager(context));
recyclerView.setAdapter(adapter);

View File

@ -0,0 +1,293 @@
package org.mian.gitnex.fragments;
import android.content.Context;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.DividerItemDecoration;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.ProgressBar;
import android.widget.TextView;
import org.mian.gitnex.R;
import org.mian.gitnex.adapters.PullRequestsAdapter;
import org.mian.gitnex.clients.PullRequestsService;
import org.mian.gitnex.helpers.Authorization;
import org.mian.gitnex.helpers.Toasty;
import org.mian.gitnex.interfaces.ApiInterface;
import org.mian.gitnex.models.PullRequests;
import org.mian.gitnex.util.AppUtil;
import org.mian.gitnex.util.TinyDB;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
/**
* Author M M Arif
*/
public class PullRequestsFragment extends Fragment {
private ProgressBar mProgressBar;
private RecyclerView recyclerView;
private List<PullRequests> prList;
private PullRequestsAdapter adapter;
private ApiInterface apiPR;
private String TAG = "PullRequestsListFragment - ";
private Context context;
private int pageSize = 1;
private TextView noData;
private String prState = "open";
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
final View v = inflater.inflate(R.layout.fragment_pull_requests, container, false);
setHasOptionsMenu(true);
TinyDB tinyDb = new TinyDB(getContext());
String repoFullName = tinyDb.getString("repoFullName");
//Log.i("repoFullName", tinyDb.getString("repoFullName"));
String[] parts = repoFullName.split("/");
final String repoOwner = parts[0];
final String repoName = parts[1];
final String instanceUrl = tinyDb.getString("instanceUrl");
final String loginUid = tinyDb.getString("loginUid");
final String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
final SwipeRefreshLayout swipeRefresh = v.findViewById(R.id.pullToRefresh);
context = getContext();
recyclerView = v.findViewById(R.id.recyclerView);
prList = new ArrayList<>();
mProgressBar = v.findViewById(R.id.progress_bar);
noData = v.findViewById(R.id.noData);
swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
swipeRefresh.setRefreshing(false);
loadInitial(instanceToken, repoOwner, repoName, pageSize, prState);
adapter.notifyDataChanged();
}
}, 200);
}
});
adapter = new PullRequestsAdapter(getContext(), prList);
adapter.setLoadMoreListener(new PullRequestsAdapter.OnLoadMoreListener() {
@Override
public void onLoadMore() {
recyclerView.post(new Runnable() {
@Override
public void run() {
if(prList.size() == 10 || pageSize == 10) {
int page = (prList.size() + 10) / 10;
loadMore(Authorization.returnAuthentication(getContext(), loginUid, instanceToken), repoOwner, repoName, page, prState);
}
/*else {
Toasty.info(context, getString(R.string.noMoreData));
}*/
}
});
}
});
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(),
DividerItemDecoration.VERTICAL);
recyclerView.setHasFixedSize(true);
recyclerView.addItemDecoration(dividerItemDecoration);
recyclerView.setLayoutManager(new LinearLayoutManager(context));
recyclerView.setAdapter(adapter);
apiPR = PullRequestsService.createService(ApiInterface.class, instanceUrl, getContext());
loadInitial(Authorization.returnAuthentication(getContext(), loginUid, instanceToken), repoOwner, repoName, pageSize, prState);
return v;
}
@Override
public void onResume() {
super.onResume();
TinyDB tinyDb = new TinyDB(getContext());
final String loginUid = tinyDb.getString("loginUid");
String repoFullName = tinyDb.getString("repoFullName");
String[] parts = repoFullName.split("/");
final String repoOwner = parts[0];
final String repoName = parts[1];
final String instanceToken = "token " + tinyDb.getString(loginUid + "-token");
if(tinyDb.getBoolean("resumePullRequests")) {
loadInitial(Authorization.returnAuthentication(getContext(), loginUid, instanceToken), repoOwner, repoName, pageSize, prState);
tinyDb.putBoolean("resumePullRequests", false);
}
}
private void loadInitial(String token, String repoOwner, String repoName, int page, String prState) {
Call<List<PullRequests>> call = apiPR.getPullRequests(token, repoOwner, repoName, page, prState);
call.enqueue(new Callback<List<PullRequests>>() {
@Override
public void onResponse(@NonNull Call<List<PullRequests>> call, @NonNull Response<List<PullRequests>> response) {
if(response.isSuccessful()) {
assert response.body() != null;
if(response.body().size() > 0) {
prList.clear();
prList.addAll(response.body());
adapter.notifyDataChanged();
noData.setVisibility(View.GONE);
}
else {
prList.clear();
adapter.notifyDataChanged();
noData.setVisibility(View.VISIBLE);
}
mProgressBar.setVisibility(View.GONE);
}
else {
Log.i(TAG, String.valueOf(response.code()));
}
Log.i("http", String.valueOf(response.code()));
}
@Override
public void onFailure(@NonNull Call<List<PullRequests>> call, @NonNull Throwable t) {
Log.e(TAG, t.toString());
}
});
}
private void loadMore(String token, String repoOwner, String repoName, int page, String prState){
//add loading progress view
prList.add(new PullRequests("load"));
adapter.notifyItemInserted((prList.size() - 1));
Call<List<PullRequests>> call = apiPR.getPullRequests(token, repoOwner, repoName, page, prState);
call.enqueue(new Callback<List<PullRequests>>() {
@Override
public void onResponse(@NonNull Call<List<PullRequests>> call, @NonNull Response<List<PullRequests>> response) {
if(response.isSuccessful()){
//remove loading view
prList.remove(prList.size()-1);
List<PullRequests> result = response.body();
assert result != null;
if(result.size() > 0) {
pageSize = result.size();
prList.addAll(result);
}
else {
Toasty.info(context, getString(R.string.noMoreData));
adapter.setMoreDataAvailable(false);
}
adapter.notifyDataChanged();
}
else {
Log.e(TAG, String.valueOf(response.code()));
}
}
@Override
public void onFailure(@NonNull Call<List<PullRequests>> call, @NonNull Throwable t) {
Log.e(TAG, t.toString());
}
});
}
@Override
public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
boolean connToInternet = AppUtil.haveNetworkConnection(Objects.requireNonNull(getContext()));
inflater.inflate(R.menu.search_menu, menu);
super.onCreateOptionsMenu(menu, inflater);
MenuItem searchItem = menu.findItem(R.id.action_search);
androidx.appcompat.widget.SearchView searchView = (androidx.appcompat.widget.SearchView) searchItem.getActionView();
searchView.setImeOptions(EditorInfo.IME_ACTION_DONE);
//searchView.setQueryHint(getContext().getString(R.string.strFilter));
/*if(!connToInternet) {
return;
}*/
searchView.setOnQueryTextListener(new androidx.appcompat.widget.SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return false;
}
@Override
public boolean onQueryTextChange(String newText) {
adapter.getFilter().filter(newText);
return false;
}
});
}
}

View File

@ -12,6 +12,7 @@ import org.mian.gitnex.actions.IssueActions;
import org.mian.gitnex.activities.AddRemoveAssigneesActivity;
import org.mian.gitnex.activities.AddRemoveLabelsActivity;
import org.mian.gitnex.activities.EditIssueActivity;
import org.mian.gitnex.activities.FileDiffActivity;
import org.mian.gitnex.activities.ReplyToIssueActivity;
import org.mian.gitnex.helpers.Toasty;
import org.mian.gitnex.util.TinyDB;
@ -42,6 +43,7 @@ public class SingleIssueBottomSheetFragment extends BottomSheetDialogFragment {
TextView reOpenIssue = v.findViewById(R.id.reOpenIssue);
TextView addRemoveAssignees = v.findViewById(R.id.addRemoveAssignees);
TextView copyIssueUrl = v.findViewById(R.id.copyIssueUrl);
TextView openFilesDiff = v.findViewById(R.id.openFilesDiff);
replyToIssue.setOnClickListener(new View.OnClickListener() {
@Override
@ -53,6 +55,26 @@ public class SingleIssueBottomSheetFragment extends BottomSheetDialogFragment {
}
});
if(tinyDB.getString("issueType").equals("pr")) {
editIssue.setText(R.string.editPrText);
copyIssueUrl.setText(R.string.copyPrUrlText);
if(tinyDB.getString("repoType").equals("public")) {
openFilesDiff.setVisibility(View.VISIBLE);
}
}
openFilesDiff.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(getContext(), FileDiffActivity.class));
dismiss();
}
});
editIssue.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
@ -90,7 +112,7 @@ public class SingleIssueBottomSheetFragment extends BottomSheetDialogFragment {
// get url of repo
String repoFullName = tinyDB.getString("repoFullName");
String instanceUrlWithProtocol = "https://" + tinyDB.getString("instanceUrlRaw");
if(!tinyDB.getString("instanceUrlWithProtocol").isEmpty()) {
if (!tinyDB.getString("instanceUrlWithProtocol").isEmpty()) {
instanceUrlWithProtocol = tinyDB.getString("instanceUrlWithProtocol");
}
@ -100,6 +122,7 @@ public class SingleIssueBottomSheetFragment extends BottomSheetDialogFragment {
// copy to clipboard
ClipboardManager clipboard = (ClipboardManager) Objects.requireNonNull(getContext()).getSystemService(android.content.Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("issueUrl", issueUrl);
assert clipboard != null;
clipboard.setPrimaryClip(clip);
dismiss();
@ -109,35 +132,44 @@ public class SingleIssueBottomSheetFragment extends BottomSheetDialogFragment {
}
});
if(tinyDB.getString("issueState").equals("open")) { // close issue
if(tinyDB.getString("issueType").equals("issue")) {
reOpenIssue.setVisibility(View.GONE);
if (tinyDB.getString("issueState").equals("open")) { // close issue
closeIssue.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
reOpenIssue.setVisibility(View.GONE);
IssueActions.closeReopenIssue(getContext(), Integer.valueOf(tinyDB.getString("issueNumber")), "closed");
dismiss();
closeIssue.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
IssueActions.closeReopenIssue(getContext(), Integer.valueOf(tinyDB.getString("issueNumber")), "closed");
dismiss();
}
});
} else if (tinyDB.getString("issueState").equals("closed")) {
closeIssue.setVisibility(View.GONE);
reOpenIssue.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
IssueActions.closeReopenIssue(getContext(), Integer.valueOf(tinyDB.getString("issueNumber")), "open");
dismiss();
}
});
}
}
else if(tinyDB.getString("issueState").equals("closed")) {
else {
reOpenIssue.setVisibility(View.GONE);
closeIssue.setVisibility(View.GONE);
reOpenIssue.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
IssueActions.closeReopenIssue(getContext(), Integer.valueOf(tinyDB.getString("issueNumber")), "open");
dismiss();
}
});
}
return v;

View File

@ -6,6 +6,7 @@ import org.mian.gitnex.models.Branches;
import org.mian.gitnex.models.ExploreRepositories;
import org.mian.gitnex.models.Files;
import org.mian.gitnex.models.NewFile;
import org.mian.gitnex.models.PullRequests;
import org.mian.gitnex.models.UpdateIssueAssignee;
import org.mian.gitnex.models.UpdateIssueState;
import org.mian.gitnex.models.Collaborators;
@ -30,6 +31,8 @@ import org.mian.gitnex.models.UserSearch;
import org.mian.gitnex.models.UserTokens;
import org.mian.gitnex.models.WatchRepository;
import java.util.List;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.DELETE;
@ -247,4 +250,11 @@ public interface ApiInterface {
@DELETE("repos/{owner}/{repo}/subscription") // un watch a repository
Call<JsonElement> unWatchRepository(@Header("Authorization") String token, @Path("owner") String ownerName, @Path("repo") String repoName);
@GET("repos/{owner}/{repo}/pulls") // get repository pull requests
Call<List<PullRequests>> getPullRequests(@Header("Authorization") String token, @Path("owner") String owner, @Path("repo") String repo, @Query("page") int page, @Query("state") String state);
@GET("{owner}/{repo}/pulls/{filename}") // get pull diff file contents
Call<ResponseBody> getFileDiffContents(@Path("owner") String owner, @Path("repo") String repo, @Path("filename") String fileName);
}

View File

@ -0,0 +1,39 @@
package org.mian.gitnex.models;
/**
* Author M M Arif
*/
public class FileDiffView {
private String fileName;
private boolean fileType;
private String fileInfo;
private String fileContents;
public FileDiffView(String fileName, boolean fileType, String fileInfo, String fileContents)
{
this.fileName = fileName;
this.fileType = fileType;
this.fileInfo = fileInfo;
this.fileContents = fileContents;
}
public String getFileName() {
return fileName;
}
public boolean isFileType() {
return fileType;
}
public String getFileInfo() {
return fileInfo;
}
public String getFileContents() {
return fileContents;
}
}

View File

@ -98,10 +98,10 @@ public class Issues {
public class pullRequestObject {
private String merged;
private boolean merged;
private String merged_at;
public String getMerged() {
public boolean isMerged() {
return merged;
}

View File

@ -0,0 +1,823 @@
package org.mian.gitnex.models;
import com.google.gson.annotations.SerializedName;
import java.util.Date;
import java.util.List;
/**
* Author M M Arif
*/
public class PullRequests {
private int id;
private String body;
private int comments;
private String diff_url;
private String html_url;
private String merge_base;
private String merge_commit_sha;
private boolean mergeable;
private boolean merged;
private int number;
private String patch_url;
private String state;
private String title;
private String url;
private Date closed_at;
private Date created_at;
private Date due_date;
private Date merged_at;
private Date updated_at;
private userObject user;
private List<labelsObject> labels;
private List<assigneesObject> assignees;
Review

can drop L35 ?

can drop L35 ?
Review

Yes, I will drop it.

Yes, I will drop it.
private mergedByObject merged_by;
private milestoneObject milestone;
private baseObject base;
private headObject head;
public PullRequests(String body) {
this.body = body;
}
public class headObject {
private int repo_id;
private String label;
private String ref;
private String sha;
private repoObject repo;
public class repoObject {
private int repo_id;
private boolean allow_merge_commits;
private boolean allow_rebase;
private boolean allow_rebase_explicit;
private boolean allow_squash_merge;
private boolean archived;
private boolean empty;
private boolean fork;
private boolean has_issues;
private boolean has_pull_requests;
private boolean has_wiki;
private boolean ignore_whitespace_conflicts;
@SerializedName("private")
private boolean privateFlag;
private boolean mirror;
private String avatar_url;
private String clone_url;
private String default_branch;
private String description;
private String full_name;
private String html_url;
private String name;
private String ssh_url;
private String website;
private int forks_count;
private int id;
private int open_issues_count;
private int size;
private int stars_count;
private int watchers_count;
private Date created_at;
private Date updated_at;
private ownerObject owner;
private permissionsObject permissions;
public class ownerObject {
private int repo_id;
private boolean is_admin;
private String avatar_url;
private String email;
private String full_name;
private String language;
private String login;
private Date created;
public int getRepo_id() {
return repo_id;
}
public boolean isIs_admin() {
return is_admin;
}
public String getAvatar_url() {
return avatar_url;
}
public String getEmail() {
return email;
}
public String getFull_name() {
return full_name;
}
public String getLanguage() {
return language;
}
public String getLogin() {
return login;
}
public Date getCreated() {
return created;
}
}
public class permissionsObject {
private boolean admin;
private boolean pull;
private boolean push;
public boolean isAdmin() {
return admin;
}
public boolean isPull() {
return pull;
}
public boolean isPush() {
return push;
}
}
public int getRepo_id() {
return repo_id;
}
public boolean isAllow_merge_commits() {
return allow_merge_commits;
}
public boolean isAllow_rebase() {
return allow_rebase;
}
public boolean isAllow_rebase_explicit() {
return allow_rebase_explicit;
}
public boolean isAllow_squash_merge() {
return allow_squash_merge;
}
public boolean isArchived() {
return archived;
}
public boolean isEmpty() {
return empty;
}
public boolean isFork() {
return fork;
}
public boolean isHas_issues() {
return has_issues;
}
public boolean isHas_pull_requests() {
return has_pull_requests;
}
public boolean isHas_wiki() {
return has_wiki;
}
public boolean isIgnore_whitespace_conflicts() {
return ignore_whitespace_conflicts;
}
public boolean isPrivateFlag() {
return privateFlag;
}
public boolean isMirror() {
return mirror;
}
public String getAvatar_url() {
return avatar_url;
}
public String getClone_url() {
return clone_url;
}
public String getDefault_branch() {
return default_branch;
}
public String getDescription() {
return description;
}
public String getFull_name() {
return full_name;
}
public String getHtml_url() {
return html_url;
}
public String getName() {
return name;
}
public String getSsh_url() {
return ssh_url;
}
public String getWebsite() {
return website;
}
public int getForks_count() {
return forks_count;
}
public int getId() {
return id;
}
public int getOpen_issues_count() {
return open_issues_count;
}
public int getSize() {
return size;
}
public int getStars_count() {
return stars_count;
}
public int getWatchers_count() {
return watchers_count;
}
public Date getCreated_at() {
return created_at;
}
public Date getUpdated_at() {
return updated_at;
}
public ownerObject getOwner() {
return owner;
}
public permissionsObject getPermissions() {
return permissions;
}
}
}
public class baseObject {
private int repo_id;
private String label;
private String ref;
private String sha;
private repoObject repo;
public class repoObject {
private int repo_id;
private boolean allow_merge_commits;
private boolean allow_rebase;
private boolean allow_rebase_explicit;
private boolean allow_squash_merge;
private boolean archived;
private boolean empty;
private boolean fork;
private boolean has_issues;
private boolean has_pull_requests;
private boolean has_wiki;
private boolean ignore_whitespace_conflicts;
@SerializedName("private")
private boolean privateFlag;
private boolean mirror;
private String avatar_url;
private String clone_url;
private String default_branch;
private String description;
private String full_name;
private String html_url;
private String name;
private String ssh_url;
private String website;
private int forks_count;
private int id;
private int open_issues_count;
private int size;
private int stars_count;
private int watchers_count;
private Date created_at;
private Date updated_at;
private ownerObject owner;
private permissionsObject permissions;
public class ownerObject {
private int repo_id;
private boolean is_admin;
private String avatar_url;
private String email;
private String full_name;
private String language;
private String login;
private Date created;
public int getRepo_id() {
return repo_id;
}
public boolean isIs_admin() {
return is_admin;
}
public String getAvatar_url() {
return avatar_url;
}
public String getEmail() {
return email;
}
public String getFull_name() {
return full_name;
}
public String getLanguage() {
return language;
}
public String getLogin() {
return login;
}
public Date getCreated() {
return created;
}
}
public class permissionsObject {
private boolean admin;
private boolean pull;
private boolean push;
public boolean isAdmin() {
return admin;
}
public boolean isPull() {
return pull;
}
public boolean isPush() {
return push;
}
}
public int getRepo_id() {
return repo_id;
}
public boolean isAllow_merge_commits() {
return allow_merge_commits;
}
public boolean isAllow_rebase() {
return allow_rebase;
}
public boolean isAllow_rebase_explicit() {
return allow_rebase_explicit;
}
public boolean isAllow_squash_merge() {
return allow_squash_merge;
}
public boolean isArchived() {
return archived;
}
public boolean isEmpty() {
return empty;
}
public boolean isFork() {
return fork;
}
public boolean isHas_issues() {
return has_issues;
}
public boolean isHas_pull_requests() {
return has_pull_requests;
}
public boolean isHas_wiki() {
return has_wiki;
}
public boolean isIgnore_whitespace_conflicts() {
return ignore_whitespace_conflicts;
}
public boolean isPrivateFlag() {
return privateFlag;
}
public boolean isMirror() {
return mirror;
}
public String getAvatar_url() {
return avatar_url;
}
public String getClone_url() {
return clone_url;
}
public String getDefault_branch() {
return default_branch;
}
public String getDescription() {
return description;
}
public String getFull_name() {
return full_name;
}
public String getHtml_url() {
return html_url;
}
public String getName() {
return name;
}
public String getSsh_url() {
return ssh_url;
}
public String getWebsite() {
return website;
}
public int getForks_count() {
return forks_count;
}
public int getId() {
return id;
}
public int getOpen_issues_count() {
return open_issues_count;
}
public int getSize() {
return size;
}
public int getStars_count() {
return stars_count;
}
public int getWatchers_count() {
return watchers_count;
}
public Date getCreated_at() {
return created_at;
}
public Date getUpdated_at() {
return updated_at;
}
public ownerObject getOwner() {
return owner;
}
public permissionsObject getPermissions() {
return permissions;
}
}
}
public class userObject {
private int id;
private String login;
private String full_name;
private String email;
private String avatar_url;
private String language;
private boolean is_admin;
public int getId() {
return id;
}
public String getLogin() {
return login;
}
public String getFull_name() {
return full_name;
}
public String getEmail() {
return email;
}
public String getAvatar_url() {
return avatar_url;
}
public String getLanguage() {
return language;
}
public boolean isIs_admin() {
return is_admin;
}
}
public class labelsObject {
private int id;
private String name;
private String color;
private String url;
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getColor() {
return color;
}
public String getUrl() {
return url;
}
Review

drop this class

drop this class
Review

See my comment

See my comment
}
public class assigneesObject {
private int id;
private String login;
private String full_name;
private String email;
private String avatar_url;
private String language;
private boolean is_admin;
public int getId() {
return id;
}
public String getLogin() {
return login;
}
public String getFull_name() {
return full_name;
}
public String getEmail() {
return email;
}
public String getAvatar_url() {
return avatar_url;
}
public String getLanguage() {
return language;
}
public boolean isIs_admin() {
return is_admin;
}
}
public class mergedByObject {
private int id;
private String login;
private String full_name;
private String email;
private String avatar_url;
private String language;
private boolean is_admin;
public int getId() {
return id;
}
public String getLogin() {
return login;
}
public String getFull_name() {
return full_name;
}
public String getEmail() {
return email;
}
public String getAvatar_url() {
return avatar_url;
}
public String getLanguage() {
return language;
}
public boolean isIs_admin() {
return is_admin;
}
}
public class milestoneObject {
private int id;
private String title;
private String description;
private String state;
private String open_issues;
private String closed_issues;
private String closed_at;
private String due_on;
public int getId() {
return id;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public String getState() {
return state;
}
public String getOpen_issues() {
return open_issues;
}
public String getClosed_issues() {
return closed_issues;
}
public String getClosed_at() {
return closed_at;
}
public String getDue_on() {
return due_on;
}
}
public int getId() {
return id;
}
public String getBody() {
return body;
}
public int getComments() {
return comments;
}
public String getDiff_url() {
return diff_url;
}
public String getHtml_url() {
return html_url;
}
public String getMerge_base() {
return merge_base;
}
public String getMerge_commit_sha() {
return merge_commit_sha;
}
public boolean isMergeable() {
return mergeable;
}
public boolean isMerged() {
return merged;
}
public int getNumber() {
return number;
}
public String getPatch_url() {
return patch_url;
}
public String getState() {
return state;
}
public String getTitle() {
return title;
}
public String getUrl() {
return url;
}
public Date getClosed_at() {
return closed_at;
}
public Date getCreated_at() {
return created_at;
}
public Date getDue_date() {
return due_date;
}
public Date getMerged_at() {
return merged_at;
}
public Date getUpdated_at() {
return updated_at;
}
public userObject getUser() {
return user;
}
public List<labelsObject> getLabels() {
return labels;
}
public List<assigneesObject> getAssignees() {
return assignees;
}
public mergedByObject getMerged_by() {
return merged_by;
}
public milestoneObject getMilestone() {
return milestone;
}
public baseObject getBase() {
return base;
}
public headObject getHead() {
return head;
}
}

View File

@ -217,7 +217,8 @@ public class AppUtil {
"cs", "bash", "sh", "bsh", "cv", "python", "perl", "pm", "rb", "ruby", "javascript",
"coffee", "rc", "rs", "rust", "basic", "clj", "css", "dart", "lisp", "erl", "hs", "lsp", "rkt",
"ss", "llvm", "ll", "lua", "matlab", "pascal", "r", "scala", "sql", "latex", "tex", "vb", "vbs",
"vhd", "tcl", "wiki.meta", "yaml", "yml", "markdown", "xml", "proto", "regex", "py", "pl", "js"};
"vhd", "tcl", "wiki.meta", "yaml", "yml", "markdown", "xml", "proto", "regex", "py", "pl", "js",
"html", "htm", "volt", "ini", "htaccess", "conf", "gitignore"};
return Arrays.asList(extValues).contains(ext);

View File

@ -0,0 +1,72 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/colorPrimary">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
tools:ignore="UnusedAttribute">
<ImageView
android:id="@+id/close"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginRight="15dp"
android:layout_marginLeft="15dp"
android:gravity="center_vertical"
android:contentDescription="@string/close"
android:src="@drawable/ic_close" />
<TextView
android:id="@+id/toolbar_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/fileDiffViewHeader"
android:textColor="@color/white"
android:ellipsize="none"
android:scrollbars="horizontal"
android:singleLine="true"
android:layout_marginEnd="20dp"
android:textSize="18sp" />
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<LinearLayout
android:id="@+id/filesDiffFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary"
android:padding="4dp"
android:scrollbars="vertical" />
</LinearLayout>
<ProgressBar
android:id="@+id/progress_bar"
style="@style/Base.Widget.AppCompat.ProgressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
android:visibility="gone" />
</LinearLayout>

View File

@ -57,6 +57,12 @@
android:layout_height="wrap_content"
android:text="@string/tabItemCloseIssues" />
<com.google.android.material.tabs.TabItem
android:id="@+id/tabItemPullRequests"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/tabPullRequests" />
<com.google.android.material.tabs.TabItem
android:id="@+id/tabItem3_ml"
android:layout_width="wrap_content"

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/singleDiffView"
android:paddingStart="2dp"
android:paddingEnd="2dp"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:background="@color/colorPrimary"
android:orientation="vertical">
<TextView
android:id="@+id/fileName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/colorWhite"
android:textSize="14sp"
android:padding="5dp"
android:background="@color/lightBlue" />
<TextView
android:id="@+id/fileInfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/colorWhite"
android:textSize="14sp"
android:paddingBottom="5dp"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:background="@color/lightBlue" />
<HorizontalScrollView
android:id="@+id/fileContentsView"
android:layout_width="match_parent"
android:fillViewport="true"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/allLinesLayout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="vertical"
>
</LinearLayout>
</HorizontalScrollView>
<TextView
android:id="@+id/fileContents"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/colorPrimary"
android:textSize="14sp"
android:paddingTop="5dp"
android:paddingBottom="15dp"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:visibility="gone"
android:background="@color/white" />
<ImageView
android:id="@+id/fileImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="15dp"
android:paddingStart="5dp"
android:paddingEnd="5dp"
android:contentDescription="@string/generalImgContentText"
android:visibility="gone" />
</LinearLayout>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".activities.RepoDetailActivity">
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/pullToRefresh"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorPrimary"
android:padding="4dp"
android:scrollbars="vertical"
/>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
<ProgressBar
android:id="@+id/progress_bar"
style="@style/Base.Widget.AppCompat.ProgressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:indeterminate="true"
android:visibility="visible" />
<TextView
android:id="@+id/noData"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="15dp"
android:text="@string/noDataPullRequests"
android:textColor="@color/white"
android:gravity="center"
android:textSize="20sp"
android:visibility="gone" />
</RelativeLayout>

View File

@ -4,6 +4,12 @@
android:layout_height="wrap_content"
android:background="@color/backgroundColor" >
<TextView
android:id="@+id/repoType"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible" />
<RelativeLayout
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/linearLayoutFrame"

View File

@ -5,7 +5,9 @@
android:id="@+id/relativeLayoutFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:layout_marginTop="15dp"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
android:theme="@style/AppTheme"
tools:context=".activities.RepoDetailActivity">
@ -39,28 +41,14 @@
<TextView
android:id="@+id/issueTitle"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight=".65"
android:gravity="top|center_vertical"
android:textAlignment="gravity"
android:text="@string/strFilter"
android:textColor="@color/white"
android:textSize="18sp" />
<TextView
android:layout_width="0dp"
android:layout_weight=".08"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/issueType"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center_horizontal"
android:contentDescription="@string/generalImgContentText"
android:src="@drawable/ic_issues" />
</LinearLayout>
<LinearLayout
@ -103,4 +91,12 @@
</LinearLayout>
<View
android:layout_below="@id/infoSection"
android:layout_width="match_parent"
android:layout_height="1dp"
android:id="@+id/divider"
android:layout_marginTop="15dp"
android:background="@color/divider" />
</RelativeLayout>

View File

@ -0,0 +1,91 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/relativeLayoutFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="15dp"
android:theme="@style/AppTheme"
tools:context=".activities.RepoDetailActivity">
<TextView
android:id="@+id/prNumber"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible"/>
<ImageView
android:id="@+id/assigneeAvatar"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginEnd="15dp"
android:contentDescription="@string/generalImgContentText"
android:src="@drawable/ic_android" />
<LinearLayout
android:id="@+id/infoSection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/assigneeAvatar"
android:orientation="vertical">
<LinearLayout
android:id="@+id/framePrNameStatus"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:orientation="horizontal">
<TextView
android:id="@+id/prTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="top|center_vertical"
android:textAlignment="gravity"
android:textColor="@color/white"
android:textSize="18sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/frameCreatedDate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/frameCommentsCount"
android:layout_width="0dp"
android:layout_weight=".25"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/prCommentsCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start"
android:drawablePadding="5dp"
android:drawableStart="@drawable/ic_comment_20"
android:layout_gravity="center_horizontal"
android:textColor="@color/colorWhite"
android:textSize="14sp" />
</LinearLayout>
<TextView
android:id="@+id/prCreatedTime"
android:layout_width="0dp"
android:layout_weight=".25"
android:layout_height="wrap_content"
android:gravity="end"
android:textColor="@color/colorWhite"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
</RelativeLayout>

View File

@ -4,6 +4,12 @@
android:layout_height="wrap_content"
android:background="@color/backgroundColor" >
<TextView
android:id="@+id/repoType"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible" />
<RelativeLayout
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/linearLayoutFrame"

View File

@ -14,6 +14,12 @@
android:background="@color/backgroundColor"
tools:context=".activities.MainActivity">
<TextView
android:id="@+id/repoType"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible" />
<ImageView
android:id="@+id/imageAvatar"
android:layout_width="40dp"

View File

@ -17,6 +17,19 @@
android:orientation="vertical"
android:layout_height="wrap_content">
<TextView
android:id="@+id/openFilesDiff"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="@string/openFileDiffText"
android:drawableStart="@drawable/ic_file"
android:drawablePadding="24dp"
android:textColor="@color/white"
android:textSize="16sp"
android:visibility="gone"
android:padding="16dp" />
<TextView
android:id="@+id/replyToIssue"
android:layout_width="match_parent"

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
@ -15,6 +14,12 @@
android:background="@color/backgroundColor"
tools:context=".activities.MainActivity">
<TextView
android:id="@+id/repoType"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="invisible" />
<ImageView
android:id="@+id/imageAvatar"
android:layout_width="40dp"

View File

@ -24,5 +24,9 @@
<color name="darkRed">#e74c3c</color>
<color name="lightBlue">#3faef7</color>
<color name="lightGray">#b6bbbf</color>
<color name="darkGreen">#368f73</color>
<color name="lightDimGreen">#63fdd9</color>
<color name="diffRemovedColor">#ffe0e0</color>
<color name="diffAddedColor">#d6fcd6</color>
</resources>

View File

@ -13,7 +13,7 @@
<string name="supportLink" translatable="false">https://liberapay.com/mmarif/donate</string>
<string name="supportLinkPatreon" translatable="false">https://www.patreon.com/mmarif</string>
<string name="appVerBuild" translatable="false">%1$s / build %2$d</string>
<string name="appDesc" translatable="false">GitNex is a free, open-source Android client for Git repository management tool Gitea. GitNex is Licensed under GPLv3.\n\nThanks to all the contributors and sponsors for your generous work and donations.</string>
<string name="appDesc" translatable="false">GitNex is a free, open-source Android client for Git repository management tool Gitea. GitNex is Licensed under GPLv3.\n\nThanks to all the contributors and donators for your generous work and donations.</string>
<string name="crowdInLink" translatable="false">https://crowdin.com/project/gitnex</string>
<string name="appRepoIssuesText" translatable="false">Report issues at Gitea</string>
@ -52,7 +52,7 @@
<string name="pageTitleCreateLabel">New Label</string>
<string name="pageTitleCredits">Credits</string>
<string name="pageTitleLabelUpdate">Update Label</string>
<string name="pageTitleSponsors">Sponsors</string>
<string name="pageTitleSponsors">Donators</string>
<string name="pageTitleStarredRepos">Starred Repositories</string>
<string name="pageTitleCreateTeam">New Team</string>
<string name="pageTitleAddEmail">Add Email Address</string>
@ -143,6 +143,7 @@
<string name="tab_text_labels">Labels</string>
<string name="action_settings">Settings</string>
<string name="tab_text_collaborators">Collaborators</string>
<string name="tabPullRequests">Pull Requests</string>
<string name="noDataIssueTab">No issues found</string>
@ -406,7 +407,7 @@
<!-- edit issue -->
<string name="editIssueNavHeader">Edit Issue #%1$s</string>
<string name="editIssueSuccessMessage">Issue updated.</string>
<string name="editIssueSuccessMessage">Issue updated</string>
<!-- edit issue -->
<!-- release -->
@ -459,7 +460,7 @@
<string name="strFilter">Filter</string>
<string name="copyIssueUrl">Copy Issue URL</string>
<string name="copyIssueUrlToastMsg">Issue URL copied to clipboard</string>
<string name="copyIssueUrlToastMsg">URL copied to clipboard</string>
<string name="milestoneCompletion">%1$d\uFF05 completed</string>
@ -495,6 +496,9 @@
<string name="emptyFields">All fields are required</string>
<string name="textContinue">Continue</string>
<string name="copyToken">Token</string>
<string name="dashCharacter" translatable="false">-</string>
<string name="strPrivate" translatable="false">private</string>
<string name="strPublic" translatable="false">public</string>
<!-- generic copy -->
<string name="translateText">Translate GitNex with Crowdin</string>
@ -523,4 +527,15 @@
<string name="unauthorizedApiError">Instance has returned an error - Unauthorized. Check your credentials and try again</string>
<string name="loginTokenError">Please enter the correct token</string>
<string name="noDataPullRequests">No pull requests found</string>
<string name="prCreator">Creator :\u0020</string>
<string name="editPrText">Edit Pull Request</string>
<string name="copyPrUrlText">Copy Pull Request URL</string>
<string name="editPrNavHeader">Edit Pull Request #%1$s</string>
<string name="editPrSuccessMessage">Pull Request updated</string>
<string name="fileDiffViewHeader">%1$s Files Changed</string>
<string name="fileDiffViewHeaderSingle">%1$s File Changed</string>
<string name="fileDiffInfoChanges" translatable="false">-%1$s</string>
<string name="openFileDiffText">Files Changed</string>
</resources>