diff --git a/README.md b/README.md index e7f76f8d..a71f4ef4 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ GitNex is a free/paid, open-source Android client for Git repository management GitNex is licensed under GPLv3 License. See the LICENSE file for the full license text. **No trackers are used** and source code is available here for anyone to audit. ## Downloads -[Get it on F-droid](https://f-droid.org/en/packages/org.mian.gitnex/) +[Get it on F-droid](https://f-droid.org/en/packages/org.mian.gitnex/) [Get it on Google Play](https://play.google.com/store/apps/details?id=org.mian.gitnex.pro) [Download builds and releases](https://cloud.swatian.com/s/DN7E5xxtaw4fRbE) diff --git a/app/build.gradle b/app/build.gradle index 3738d3cb..86c3f050 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -56,7 +56,7 @@ configurations { dependencies { def lifecycle_version = '2.3.1' def markwon_version = '4.6.2' - def work_version = "2.5.0" + def work_version = "2.7.0-alpha03" def acra = "5.7.0" implementation fileTree(include: ['*.jar'], dir: 'libs') @@ -103,12 +103,12 @@ dependencies { implementation "ch.acra:acra-mail:$acra" implementation "ch.acra:acra-limiter:$acra" implementation "ch.acra:acra-notification:$acra" - implementation 'androidx.room:room-runtime:2.2.6' - annotationProcessor 'androidx.room:room-compiler:2.2.6' + implementation 'androidx.room:room-runtime:2.3.0' + annotationProcessor 'androidx.room:room-compiler:2.3.0' implementation "androidx.work:work-runtime:$work_version" implementation "io.mikael:urlbuilder:2.0.9" implementation "org.codeberg.gitnex-garage:emoji-java:v5.1.2" - implementation "org.codeberg.gitnex:tea4j:1.0.5" + implementation "org.codeberg.gitnex:tea4j:1.0.10" coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5" implementation 'androidx.biometric:biometric:1.1.0' implementation 'com.github.chrisvest:stormpot:2.4.1' diff --git a/app/src/main/java/org/mian/gitnex/adapters/PublicOrganizationsAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/PublicOrganizationsAdapter.java new file mode 100644 index 00000000..5a8e7528 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/adapters/PublicOrganizationsAdapter.java @@ -0,0 +1,148 @@ +package org.mian.gitnex.adapters; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.Intent; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import org.gitnex.tea4j.models.Organization; +import org.mian.gitnex.R; +import org.mian.gitnex.activities.OrganizationDetailActivity; +import org.mian.gitnex.clients.PicassoService; +import org.mian.gitnex.helpers.AppUtil; +import org.mian.gitnex.helpers.RoundedTransformation; +import org.mian.gitnex.helpers.TinyDB; +import java.util.List; + +/** + * Author M M Arif + */ + +public class PublicOrganizationsAdapter extends RecyclerView.Adapter { + + private final Context context; + private final int TYPE_LOAD = 0; + private List organizationsList; + private OnLoadMoreListener loadMoreListener; + private boolean isLoading = false, isMoreDataAvailable = true; + + public PublicOrganizationsAdapter(Context ctx, List organizationsListMain) { + this.context = ctx; + this.organizationsList = organizationsListMain; + } + + @NonNull + @Override + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + LayoutInflater inflater = LayoutInflater.from(context); + if(viewType == TYPE_LOAD) { + return new PublicOrganizationsAdapter.OrganizationsHolder(inflater.inflate(R.layout.list_organizations, parent, false)); + } + else { + return new PublicOrganizationsAdapter.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) { + ((PublicOrganizationsAdapter.OrganizationsHolder) holder).bindData(organizationsList.get(position)); + } + } + + @Override + public int getItemViewType(int position) { + if(organizationsList.get(position).getFull_name() != null) { + return TYPE_LOAD; + } + else { + return 1; + } + } + + @Override + public int getItemCount() { + return organizationsList.size(); + } + + class OrganizationsHolder extends RecyclerView.ViewHolder { + private Organization organization; + private final ImageView image; + private final TextView orgName; + private final TextView orgDescription; + + OrganizationsHolder(View itemView) { + super(itemView); + image = itemView.findViewById(R.id.imageAvatar); + orgName = itemView.findViewById(R.id.orgName); + orgDescription = itemView.findViewById(R.id.orgDescription); + + itemView.setOnClickListener(v -> { + Context context = v.getContext(); + Intent intent = new Intent(context, OrganizationDetailActivity.class); + intent.putExtra("orgName", organization.getUsername()); + + TinyDB tinyDb = TinyDB.getInstance(context); + tinyDb.putString("orgName", organization.getUsername()); + tinyDb.putString("organizationId", String.valueOf(organization.getId())); + tinyDb.putBoolean("organizationAction", true); + context.startActivity(intent); + }); + } + + @SuppressLint("SetTextI18n") + void bindData(Organization organization) { + this.organization = organization; + int imgRadius = AppUtil.getPixelsFromDensity(context, 3); + orgName.setText(organization.getUsername()); + PicassoService.getInstance(context).get() + .load(organization.getAvatar_url()) + .placeholder(R.drawable.loader_animated) + .transform(new RoundedTransformation(imgRadius, 0)) + .resize(120, 120) + .centerCrop() + .into(image); + if (!organization.getDescription().equals("")) { + orgDescription.setText(organization.getDescription()); + } + } + } + + 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(OnLoadMoreListener loadMoreListener) { + this.loadMoreListener = loadMoreListener; + } + + public void updateList(List list) { + organizationsList = list; + notifyDataSetChanged(); + } +} diff --git a/app/src/main/java/org/mian/gitnex/fragments/ExploreFragment.java b/app/src/main/java/org/mian/gitnex/fragments/ExploreFragment.java index 58544f8f..5ea4b5cf 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/ExploreFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/ExploreFragment.java @@ -24,9 +24,6 @@ import org.mian.gitnex.helpers.TinyDB; public class ExploreFragment extends Fragment { - private Context ctx; - private TinyDB tinyDB; - private int tabsCount; public ViewPager mViewPager; @@ -36,8 +33,8 @@ public class ExploreFragment extends Fragment { View view = inflater.inflate(R.layout.fragment_explore, container, false); - ctx = getContext(); - tinyDB = TinyDB.getInstance(ctx); + Context ctx = getContext(); + TinyDB tinyDB = TinyDB.getInstance(ctx); ((MainActivity) requireActivity()).setActionBarTitle(getResources().getString(R.string.navExplore)); @@ -111,7 +108,11 @@ public class ExploreFragment extends Fragment { break; case 1: // Issues - fragment = new SearchIssuesFragment(); + fragment = new ExploreIssuesFragment(); + break; + + case 2: // Organizations + fragment = new ExplorePublicOrganizationsFragment(); break; } diff --git a/app/src/main/java/org/mian/gitnex/fragments/SearchIssuesFragment.java b/app/src/main/java/org/mian/gitnex/fragments/ExploreIssuesFragment.java similarity index 94% rename from app/src/main/java/org/mian/gitnex/fragments/SearchIssuesFragment.java rename to app/src/main/java/org/mian/gitnex/fragments/ExploreIssuesFragment.java index 374dc47c..180a8a90 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/SearchIssuesFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/ExploreIssuesFragment.java @@ -19,7 +19,6 @@ import org.mian.gitnex.databinding.FragmentSearchIssuesBinding; import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.Authorization; import org.mian.gitnex.helpers.InfiniteScrollListener; -import org.mian.gitnex.helpers.TinyDB; import java.util.ArrayList; import java.util.List; import java.util.Objects; @@ -31,27 +30,22 @@ import retrofit2.Response; * Author M M Arif */ -public class SearchIssuesFragment extends Fragment { +public class ExploreIssuesFragment extends Fragment { - private Context ctx; - private TinyDB tinyDb; private FragmentSearchIssuesBinding viewBinding; private SearchIssuesAdapter adapter; private List dataList; + Context ctx; private int apiCallCurrentValue = 10; private int pageCurrentIndex = 1; - private String type = "issues"; - private String state = "open"; @Override public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { viewBinding = FragmentSearchIssuesBinding.inflate(inflater, container, false); setHasOptionsMenu(true); - ctx = getContext(); - tinyDb = TinyDB.getInstance(getContext()); dataList = new ArrayList<>(); adapter = new SearchIssuesAdapter(dataList, ctx); @@ -65,11 +59,8 @@ public class SearchIssuesFragment extends Fragment { viewBinding.recyclerViewSearchIssues.setAdapter(adapter); viewBinding.searchKeyword.setOnEditorActionListener((v1, actionId, event) -> { - if(actionId == EditorInfo.IME_ACTION_SEND) { - if(!Objects.requireNonNull(viewBinding.searchKeyword.getText()).toString().equals("")) { - InputMethodManager imm = (InputMethodManager) requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE); imm.hideSoftInputFromWindow(viewBinding.searchKeyword.getWindowToken(), 0); @@ -79,27 +70,21 @@ public class SearchIssuesFragment extends Fragment { loadData(false, viewBinding.searchKeyword.getText().toString()); } } - return false; }); viewBinding.recyclerViewSearchIssues.addOnScrollListener(new InfiniteScrollListener(pageCurrentIndex, linearLayoutManager) { - @Override public void onScrolledToEnd(int firstVisibleItemPosition) { - pageCurrentIndex++; loadData(true, Objects.requireNonNull(viewBinding.searchKeyword.getText()).toString()); - } }); viewBinding.pullToRefresh.setOnRefreshListener(() -> { - pageCurrentIndex = 1; apiCallCurrentValue = 10; loadData(false, Objects.requireNonNull(viewBinding.searchKeyword.getText()).toString()); - }); loadData(false, ""); @@ -117,69 +102,50 @@ public class SearchIssuesFragment extends Fragment { } if(pageCurrentIndex == 1 || !append) { - dataList.clear(); adapter.notifyDataSetChanged(); viewBinding.pullToRefresh.setRefreshing(false); viewBinding.progressBar.setVisibility(View.VISIBLE); } else { - viewBinding.loadingMoreView.setVisibility(View.VISIBLE); } Call> call = RetrofitClient.getApiInterface(getContext()) - .queryIssues(Authorization.get(getContext()), searchKeyword, type, state, pageCurrentIndex); + .queryIssues(Authorization.get(getContext()), searchKeyword, "issues", "open", pageCurrentIndex); call.enqueue(new Callback>() { - @Override public void onResponse(@NonNull Call> call, @NonNull Response> response) { - if(response.code() == 200) { - assert response.body() != null; apiCallCurrentValue = response.body().size(); - if(!append) { - dataList.clear(); } - dataList.addAll(response.body()); adapter.notifyDataSetChanged(); - } else { - dataList.clear(); adapter.notifyDataChanged(); viewBinding.noData.setVisibility(View.VISIBLE); - } - onCleanup(); - } @Override public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - Log.e("onFailure", Objects.requireNonNull(t.getMessage())); onCleanup(); - } private void onCleanup() { - AppUtil.setMultiVisibility(View.GONE, viewBinding.loadingMoreView, viewBinding.progressBar); - if(dataList.isEmpty()) { - viewBinding.noData.setVisibility(View.VISIBLE); } } }); } - } diff --git a/app/src/main/java/org/mian/gitnex/fragments/ExplorePublicOrganizationsFragment.java b/app/src/main/java/org/mian/gitnex/fragments/ExplorePublicOrganizationsFragment.java new file mode 100644 index 00000000..d49de8b1 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/fragments/ExplorePublicOrganizationsFragment.java @@ -0,0 +1,160 @@ +package org.mian.gitnex.fragments; + +import android.content.Context; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.LinearLayoutManager; +import org.gitnex.tea4j.models.Organization; +import org.mian.gitnex.R; +import org.mian.gitnex.adapters.PublicOrganizationsAdapter; +import org.mian.gitnex.clients.RetrofitClient; +import org.mian.gitnex.databinding.FragmentOrganizationsBinding; +import org.mian.gitnex.helpers.Authorization; +import org.mian.gitnex.helpers.Constants; +import org.mian.gitnex.helpers.SnackBar; +import org.mian.gitnex.helpers.TinyDB; +import org.mian.gitnex.helpers.Version; +import java.util.ArrayList; +import java.util.List; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +/** + * Author M M Arif + */ + +public class ExplorePublicOrganizationsFragment extends Fragment { + + private FragmentOrganizationsBinding fragmentPublicOrgBinding; + private List organizationsList; + private PublicOrganizationsAdapter adapter; + private Context context; + private int pageSize; + private final String TAG = Constants.publicOrganizations; + private int resultLimit = Constants.resultLimitOldGiteaInstances; + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + + fragmentPublicOrgBinding = FragmentOrganizationsBinding.inflate(inflater, container, false); + context = getContext(); + + TinyDB tinyDb = TinyDB.getInstance(getContext()); + final String loginUid = tinyDb.getString("loginUid"); + final String instanceToken = "token " + tinyDb.getString(loginUid + "-token"); + + // if gitea is 1.12 or higher use the new limit + if(new Version(tinyDb.getString("giteaVersion")).higherOrEqual("1.12.0")) { + resultLimit = Constants.resultLimitNewGiteaInstances; + } + + fragmentPublicOrgBinding.addNewOrganization.setVisibility(View.GONE); + organizationsList = new ArrayList<>(); + + fragmentPublicOrgBinding.pullToRefresh.setOnRefreshListener(() -> new Handler(Looper.getMainLooper()).postDelayed(() -> { + fragmentPublicOrgBinding.pullToRefresh.setRefreshing(false); + loadInitial(instanceToken, resultLimit); + adapter.notifyDataChanged(); + }, 200)); + + adapter = new PublicOrganizationsAdapter(getContext(), organizationsList); + adapter.setLoadMoreListener(() -> fragmentPublicOrgBinding.recyclerView.post(() -> { + if(organizationsList.size() == resultLimit || pageSize == resultLimit) { + int page = (organizationsList.size() + resultLimit) / resultLimit; + loadMore(Authorization.get(getContext()), page, resultLimit); + } + })); + + DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(fragmentPublicOrgBinding.recyclerView.getContext(), DividerItemDecoration.VERTICAL); + fragmentPublicOrgBinding.recyclerView.setHasFixedSize(true); + fragmentPublicOrgBinding.recyclerView.addItemDecoration(dividerItemDecoration); + fragmentPublicOrgBinding.recyclerView.setLayoutManager(new LinearLayoutManager(context)); + fragmentPublicOrgBinding.recyclerView.setAdapter(adapter); + + loadInitial(Authorization.get(getContext()), resultLimit); + + return fragmentPublicOrgBinding.getRoot(); + } + + private void loadInitial(String token, int resultLimit) { + + Call> call = RetrofitClient + .getApiInterface(context).getAllOrgs(token, Constants.publicOrganizationsPageInit, resultLimit); + call.enqueue(new Callback>() { + @Override + public void onResponse(@NonNull Call> call, @NonNull Response> response) { + if(response.isSuccessful()) { + if(response.body() != null && response.body().size() > 0) { + organizationsList.clear(); + organizationsList.addAll(response.body()); + adapter.notifyDataChanged(); + fragmentPublicOrgBinding.noDataOrg.setVisibility(View.GONE); + } + else { + organizationsList.clear(); + adapter.notifyDataChanged(); + fragmentPublicOrgBinding.noDataOrg.setVisibility(View.VISIBLE); + } + fragmentPublicOrgBinding.progressBar.setVisibility(View.GONE); + } + else if(response.code() == 404) { + fragmentPublicOrgBinding.noDataOrg.setVisibility(View.VISIBLE); + fragmentPublicOrgBinding.progressBar.setVisibility(View.GONE); + } + else { + Log.e(TAG, String.valueOf(response.code())); + } + } + + @Override + public void onFailure(@NonNull Call> call, @NonNull Throwable t) { + Log.e(TAG, t.toString()); + } + }); + } + + private void loadMore(String token, int page, int resultLimit) { + + fragmentPublicOrgBinding.progressLoadMore.setVisibility(View.VISIBLE); + Call> call = RetrofitClient.getApiInterface(context).getAllOrgs(token, page, resultLimit); + call.enqueue(new Callback>() { + @Override + public void onResponse(@NonNull Call> call, @NonNull Response> response) { + if(response.isSuccessful()) { + List result = response.body(); + if(result != null) { + if(result.size() > 0) { + pageSize = result.size(); + organizationsList.addAll(result); + } + else { + SnackBar.info(context, fragmentPublicOrgBinding.getRoot(), getString(R.string.noMoreData)); + adapter.setMoreDataAvailable(false); + } + } + adapter.notifyDataChanged(); + fragmentPublicOrgBinding.progressLoadMore.setVisibility(View.GONE); + } + else { + Log.e(TAG, String.valueOf(response.code())); + } + } + + @Override + public void onFailure(@NonNull Call> call, @NonNull Throwable t) { + Log.e(TAG, t.toString()); + } + }); + } +} diff --git a/app/src/main/java/org/mian/gitnex/fragments/IssuesFragment.java b/app/src/main/java/org/mian/gitnex/fragments/IssuesFragment.java index 88095d88..eda404ac 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/IssuesFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/IssuesFragment.java @@ -29,8 +29,8 @@ import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.databinding.FragmentIssuesBinding; import org.mian.gitnex.helpers.Authorization; import org.mian.gitnex.helpers.Constants; +import org.mian.gitnex.helpers.SnackBar; import org.mian.gitnex.helpers.TinyDB; -import org.mian.gitnex.helpers.Toasty; import org.mian.gitnex.helpers.Version; import java.util.ArrayList; import java.util.List; @@ -44,6 +44,7 @@ import retrofit2.Response; public class IssuesFragment extends Fragment { + private FragmentIssuesBinding fragmentIssuesBinding; private Menu menu; private RecyclerView recyclerView; private List issuesList; @@ -51,18 +52,17 @@ public class IssuesFragment extends Fragment { private Context context; private int pageSize = Constants.issuesPageInit; private ProgressBar mProgressBar; - private String TAG = Constants.tagIssuesList; + private final String TAG = Constants.tagIssuesList; private TextView noDataIssues; private int resultLimit = Constants.resultLimitOldGiteaInstances; - private String requestType = Constants.issuesRequestType; + private final String requestType = Constants.issuesRequestType; private ProgressBar progressLoadMore; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - FragmentIssuesBinding fragmentIssuesBinding = FragmentIssuesBinding.inflate(inflater, container, false); - + fragmentIssuesBinding = FragmentIssuesBinding.inflate(inflater, container, false); setHasOptionsMenu(true); context = getContext(); @@ -89,23 +89,17 @@ public class IssuesFragment extends Fragment { noDataIssues = fragmentIssuesBinding.noDataIssues; swipeRefresh.setOnRefreshListener(() -> new Handler(Looper.getMainLooper()).postDelayed(() -> { - swipeRefresh.setRefreshing(false); loadInitial(instanceToken, repoOwner, repoName, resultLimit, requestType, tinyDb.getString("repoIssuesState")); adapter.notifyDataChanged(); - }, 200)); adapter = new IssuesAdapter(getContext(), issuesList); adapter.setLoadMoreListener(() -> recyclerView.post(() -> { - if(issuesList.size() == resultLimit || pageSize == resultLimit) { - int page = (issuesList.size() + resultLimit) / resultLimit; loadMore(Authorization.get(getContext()), repoOwner, repoName, page, resultLimit, requestType, tinyDb.getString("repoIssuesState")); - } - })); DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL); @@ -129,12 +123,9 @@ public class IssuesFragment extends Fragment { adapter.setLoadMoreListener(() -> recyclerView.post(() -> { if(issuesList.size() == resultLimit || pageSize == resultLimit) { - int page = (issuesList.size() + resultLimit) / resultLimit; loadMore(Authorization.get(getContext()), repoOwner, repoName, page, resultLimit, requestType, tinyDb.getString("repoIssuesState")); - } - })); tinyDb.putString("repoIssuesState", issueState); @@ -144,13 +135,11 @@ public class IssuesFragment extends Fragment { loadInitial(Authorization.get(getContext()), repoOwner, repoName, resultLimit, requestType, issueState); recyclerView.setAdapter(adapter); - }); loadInitial(Authorization.get(getContext()), repoOwner, repoName, resultLimit, requestType, tinyDb.getString("repoIssuesState")); return fragmentIssuesBinding.getRoot(); - } @Override @@ -165,11 +154,9 @@ public class IssuesFragment extends Fragment { final String repoName = parts[1]; if(tinyDb.getBoolean("resumeIssues")) { - loadInitial(Authorization.get(getContext()), repoOwner, repoName, resultLimit, requestType, tinyDb.getString("repoIssuesState")); tinyDb.putBoolean("resumeIssues", false); } - } private void loadInitial(String token, String repoOwner, String repoName, int resultLimit, String requestType, String issueState) { @@ -177,49 +164,38 @@ public class IssuesFragment extends Fragment { Call> call = RetrofitClient.getApiInterface(context).getIssues(token, repoOwner, repoName, 1, resultLimit, requestType, issueState); call.enqueue(new Callback>() { - @Override public void onResponse(@NonNull Call> call, @NonNull Response> response) { if(response.code() == 200) { - assert response.body() != null; if(response.body().size() > 0) { - issuesList.clear(); issuesList.addAll(response.body()); adapter.notifyDataChanged(); noDataIssues.setVisibility(View.GONE); } else { - issuesList.clear(); adapter.notifyDataChanged(); noDataIssues.setVisibility(View.VISIBLE); } - mProgressBar.setVisibility(View.GONE); - } else if(response.code() == 404) { - noDataIssues.setVisibility(View.VISIBLE); mProgressBar.setVisibility(View.GONE); } else { Log.e(TAG, String.valueOf(response.code())); } - } @Override public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - Log.e(TAG, t.toString()); } - }); - } private void loadMore(String token, String repoOwner, String repoName, int page, int resultLimit, String requestType, String issueState) { @@ -232,44 +208,29 @@ public class IssuesFragment extends Fragment { @Override public void onResponse(@NonNull Call> call, @NonNull Response> response) { - if(response.code() == 200) { - List result = response.body(); - assert result != null; if(result.size() > 0) { - pageSize = result.size(); issuesList.addAll(result); - } else { - - Toasty.warning(context, getString(R.string.noMoreData)); + SnackBar.info(context, fragmentIssuesBinding.getRoot(), getString(R.string.noMoreData)); adapter.setMoreDataAvailable(false); - } - adapter.notifyDataChanged(); progressLoadMore.setVisibility(View.GONE); - } else { - Log.e(TAG, String.valueOf(response.code())); - } - } @Override public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - Log.e(TAG, t.toString()); - } - }); } @@ -298,19 +259,15 @@ public class IssuesFragment extends Fragment { @Override public boolean onQueryTextSubmit(String query) { - return false; } @Override public boolean onQueryTextChange(String newText) { - filter(newText); return false; } - }); - } private void filter(String text) { @@ -328,5 +285,4 @@ public class IssuesFragment extends Fragment { adapter.updateList(arr); } - } diff --git a/app/src/main/java/org/mian/gitnex/fragments/NotificationsFragment.java b/app/src/main/java/org/mian/gitnex/fragments/NotificationsFragment.java index a62be27d..61a42132 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/NotificationsFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/NotificationsFragment.java @@ -16,6 +16,7 @@ import android.widget.TextView; 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; @@ -95,12 +96,15 @@ public class NotificationsFragment extends Fragment implements NotificationsAdap notificationsActions = new NotificationsActions(context); notificationsAdapter = new NotificationsAdapter(context, notificationThreads, this, this); + RecyclerView recyclerView = fragmentNotificationsBinding.notifications; LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context); - RecyclerView recyclerView = fragmentNotificationsBinding.notifications; + DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL); + recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(linearLayoutManager); recyclerView.setAdapter(notificationsAdapter); + recyclerView.addItemDecoration(dividerItemDecoration); recyclerView.addOnScrollListener(new InfiniteScrollListener(pageResultLimit, linearLayoutManager) { @Override diff --git a/app/src/main/java/org/mian/gitnex/helpers/Constants.java b/app/src/main/java/org/mian/gitnex/helpers/Constants.java index c9683935..3c06b35d 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/Constants.java +++ b/app/src/main/java/org/mian/gitnex/helpers/Constants.java @@ -30,6 +30,7 @@ public class Constants { public static final String replyToIssueActivity = "ReplyToIssueActivity"; public static final String tagDraftsBottomSheet = "BottomSheetDraftsFragment"; public static final String userAccountsApi = "UserAccountsApi"; + public static final String publicOrganizations = "PublicOrganizations"; // issues variables public static final int issuesPageInit = 1; @@ -51,6 +52,9 @@ public class Constants { public static final int defaultPollingDelay = 15; public static final int maximumPollingDelay = 720; + // public organizations + public static final int publicOrganizationsPageInit = 1; + public static final int maximumFileViewerSize = 3 * 1024 * 1024; public static final String mainNotificationChannelId = "main_channel"; diff --git a/app/src/main/java/org/mian/gitnex/helpers/SnackBar.java b/app/src/main/java/org/mian/gitnex/helpers/SnackBar.java new file mode 100644 index 00000000..c2febdac --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/helpers/SnackBar.java @@ -0,0 +1,46 @@ +package org.mian.gitnex.helpers; + +import android.content.Context; +import android.view.View; +import android.widget.TextView; +import com.google.android.material.snackbar.Snackbar; +import org.mian.gitnex.R; + +/** + * Author M M Arif + */ + +public class SnackBar { + + public static void info(Context context, View view, String message) { + Snackbar snackBar = Snackbar.make(view, message, Snackbar.LENGTH_LONG); + View sbView = snackBar.getView(); + TextView textView = sbView.findViewById(R.id.snackbar_text); + textView.setTextColor(context.getResources().getColor(R.color.colorWhite)); + snackBar.show(); + } + + public static void success(Context context, View view, String message) { + Snackbar snackBar = Snackbar.make(view, message, Snackbar.LENGTH_LONG); + View sbView = snackBar.getView(); + TextView textView = sbView.findViewById(R.id.snackbar_text); + textView.setTextColor(context.getResources().getColor(R.color.colorLightGreen)); + snackBar.show(); + } + + public static void warning(Context context, View view, String message) { + Snackbar snackBar = Snackbar.make(view, message, Snackbar.LENGTH_LONG); + View sbView = snackBar.getView(); + TextView textView = sbView.findViewById(R.id.snackbar_text); + textView.setTextColor(context.getResources().getColor(R.color.lightYellow)); + snackBar.show(); + } + + public static void error(Context context, View view, String message) { + Snackbar snackBar = Snackbar.make(view, message, Snackbar.LENGTH_LONG); + View sbView = snackBar.getView(); + TextView textView = sbView.findViewById(R.id.snackbar_text); + textView.setTextColor(context.getResources().getColor(R.color.darkRed)); + snackBar.show(); + } +} diff --git a/app/src/main/res/layout/activity_add_collaborator_to_repository.xml b/app/src/main/res/layout/activity_add_collaborator_to_repository.xml index 218adf7d..34114444 100644 --- a/app/src/main/res/layout/activity_add_collaborator_to_repository.xml +++ b/app/src/main/res/layout/activity_add_collaborator_to_repository.xml @@ -10,6 +10,7 @@ android:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="wrap_content" + app:elevation="0dp" android:theme="@style/Widget.AppCompat.SearchView"> - @@ -11,6 +11,7 @@ android:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="wrap_content" + app:elevation="0dp" android:theme="@style/Widget.AppCompat.SearchView"> - @@ -11,6 +11,7 @@ android:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="wrap_content" + app:elevation="0dp" android:theme="@style/Widget.AppCompat.SearchView"> + android:src="@drawable/ic_close" /> + android:elevation="0dp"> + android:src="@drawable/ic_close" /> - @@ -11,6 +11,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" + app:elevation="0dp" android:theme="@style/Widget.AppCompat.SearchView"> @@ -10,6 +11,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" + app:elevation="0dp" android:theme="@style/Widget.AppCompat.SearchView"> @@ -10,6 +11,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" + app:elevation="0dp" android:theme="@style/Widget.AppCompat.SearchView"> @@ -10,6 +11,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" + app:elevation="0dp" android:theme="@style/Widget.AppCompat.SearchView"> @@ -10,6 +11,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" + app:elevation="0dp" android:theme="@style/Widget.AppCompat.SearchView"> - @@ -11,6 +11,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" + app:elevation="0dp" android:theme="@style/Widget.AppCompat.SearchView"> + + diff --git a/app/src/main/res/layout/fragment_organizations.xml b/app/src/main/res/layout/fragment_organizations.xml index e7a0ae12..7af6fb1d 100644 --- a/app/src/main/res/layout/fragment_organizations.xml +++ b/app/src/main/res/layout/fragment_organizations.xml @@ -27,6 +27,15 @@ style="@style/Widget.MaterialComponents.LinearProgressIndicator" app:indicatorColor="?attr/progressIndicatorColor" /> + + - + android:gravity="center_vertical" + tools:ignore="UseCompoundDrawables"> diff --git a/app/src/main/res/layout/list_notifications.xml b/app/src/main/res/layout/list_notifications.xml index 588bccce..33b8a273 100644 --- a/app/src/main/res/layout/list_notifications.xml +++ b/app/src/main/res/layout/list_notifications.xml @@ -6,7 +6,7 @@ android:layout_height="wrap_content" android:background="?attr/primaryBackgroundColor" android:padding="16dp" - android:orientation="vertical"> + android:orientation="horizontal">