Dashboard view (#1279)

Closes #1231

Closes #1280

Closes #1295

Closes #1298

Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/1279
Co-authored-by: M M Arif <mmarif@swatian.com>
Co-committed-by: M M Arif <mmarif@swatian.com>
This commit is contained in:
M M Arif 2023-09-19 05:23:43 +00:00 committed by M M Arif
parent ea954503bf
commit d2ff9b8a8a
33 changed files with 1696 additions and 102 deletions

View File

@ -1,4 +1,4 @@
pipeline:
steps:
build:
image: alvrme/alpine-android:android-32-jdk17
commands:

View File

@ -1,4 +1,4 @@
pipeline:
steps:
author-header:
image: qwerty287/woodpecker-regex-check
group: check

View File

@ -6,7 +6,7 @@ depends_on:
run_on: [ success, failure ]
skip_clone: true
pipeline:
steps:
discord:
image: appleboy/drone-discord
settings:

View File

@ -1,4 +1,4 @@
pipeline:
steps:
prepare:
image: alpine
commands:

View File

@ -4,29 +4,27 @@
# GitNex - Android client for Forgejo and Gitea
GitNex is a free/paid, open-source Android client for Git repository management tool Forgejo and Gitea.
GitNex is a free/paid, open-source Android client for the Git repository management tools Forgejo and Gitea.
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.
GitNex is licensed under the GPLv3 License. Please refer to the LICENSE file for the full text of the license. **No trackers are used**, and the source code is available here for anyone to audit.
## Downloads
[<img alt='Get it on F-Droid' src='https://codeberg.org/gitnex/GitNex/raw/branch/main/assets/fdroid.png' height="80"/>](https://f-droid.org/en/packages/org.mian.gitnex/)
[<img alt='Get it on Google Play' src='https://codeberg.org/gitnex/GitNex/raw/branch/main/assets/google-play.png' height="80"/>](https://play.google.com/store/apps/details?id=org.mian.gitnex.pro)
[<img alt='Download builds and releases' src='https://codeberg.org/gitnex/GitNex/raw/branch/main/assets/apk-badge.png' height="82"/>](https://cloud.swatian.com/s/DN7E5xxtaw4fRbE)
[<img alt='Download builds and releases' src='https://codeberg.org/gitnex/GitNex/raw/branch/main/assets/apk-badge.png' height="82"/>](https://cloud.swatian.com/s/WS4k3seXnmfQppo)
## Note about Forgejo and Gitea version
Please make sure that you are on latest stable release or later for better app experience.
Please make sure that you are on the latest stable release or later for a better app experience.
Check the versions [compatibility page](https://codeberg.org/gitnex/GitNex/wiki/Compatibility) which lists all the supported versions with
compatibility ratio.
Check the version [compatibility page](https://codeberg.org/gitnex/GitNex/wiki/Compatibility), which lists all the supported versions along with their compatibility ratios.
## Build from source
Option 1 - Download the source code, open it in Android Studio and build it there.
Option 1 - Download the source code, open it in Android Studio, and build it there.
Option 2 - Open terminal(Linux) and cd to the project dir. Run `./gradlew assembleFree`.
Option 2 - Open the terminal (Linux) and navigate to the project directory. Then, run `./gradlew assembleFree`.
## Features
@ -48,10 +46,9 @@ Option 2 - Open terminal(Linux) and cd to the project dir. Run `./gradlew assemb
## Translation
Help us translate GitNex to your native language.
Help us translate GitNex into your native language.
We use [Crowdin](https://crowdin.com/project/gitnex) for translation. If your language is not listed, please
request [here](https://codeberg.org/gitnex/GitNex/issues) to add it to the project.
We use [Crowdin](https://crowdin.com/project/gitnex) for translations. If your language is not listed, please request to add it to the project [here](https://codeberg.org/gitnex/GitNex/issues).
**Link: https://crowdin.com/project/GitNex**
@ -75,7 +72,7 @@ request [here](https://codeberg.org/gitnex/GitNex/issues) to add it to the proje
## Thanks
Thanks to all the open source libraries, contributors and donators.
Thanks to all the open source libraries, contributors, and donors.
#### Open source libraries

View File

@ -54,16 +54,16 @@ configurations {
}
dependencies {
def lifecycle_version = '2.6.1'
def lifecycle_version = '2.6.2'
def markwon_version = '4.6.2'
def work_version = '2.8.1'
def acra = '5.9.7'
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.11.0-alpha01'
implementation 'androidx.compose.material3:material3:1.2.0-alpha03'
implementation 'androidx.compose.material3:material3-window-size-class:1.2.0-alpha03'
implementation 'com.google.android.material:material:1.11.0-alpha02'
implementation 'androidx.compose.material3:material3:1.2.0-alpha07'
implementation 'androidx.compose.material3:material3-window-size-class:1.2.0-alpha07'
implementation 'androidx.viewpager2:viewpager2:1.1.0-beta02'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation "androidx.legacy:legacy-support-v4:1.0.0"
@ -111,7 +111,7 @@ dependencies {
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.5"
implementation 'androidx.biometric:biometric:1.1.0'
implementation 'com.github.chrisvest:stormpot:2.4.2'
implementation 'androidx.browser:browser:1.5.0'
implementation 'androidx.browser:browser:1.6.0'
implementation 'com.google.android.flexbox:flexbox:3.0.0'
implementation('org.codeberg.gitnex:tea4j-autodeploy:65f700d036') {
exclude module: 'org.apache.oltu.oauth2.common'

View File

@ -101,7 +101,7 @@ public class DeepLinksActivity extends BaseActivity {
accountFound = true;
AppUtil.switchToAccount(ctx, userAccount, true);
AppUtil.switchToAccount(ctx, userAccount, false);
break;
}
}

View File

@ -700,6 +700,26 @@ public class IssueDetailActivity extends BaseActivity
viewBinding.issuePrState,
ColorStateList.valueOf(
ctx.getResources().getColor(R.color.iconIssuePrClosedColor, null)));
} else if (issue.getIssue().getTitle().contains("[WIP]")
|| issue.getIssue().getTitle().contains("[wip]")) { // draft
viewBinding.issuePrState.setImageResource(R.drawable.ic_draft);
ImageViewCompat.setImageTintList(
viewBinding.issuePrState,
ColorStateList.valueOf(
ctx.getResources().getColor(R.color.colorWhite, null)));
viewBinding.issuePrState.setBackgroundResource(R.drawable.shape_draft_release);
viewBinding.issuePrState.setPadding(
(int) ctx.getResources().getDimension(R.dimen.dimen4dp),
(int) ctx.getResources().getDimension(R.dimen.dimen2dp),
(int) ctx.getResources().getDimension(R.dimen.dimen4dp),
(int) ctx.getResources().getDimension(R.dimen.dimen2dp));
viewBinding.toolbarTitle.setPadding(
(int) ctx.getResources().getDimension(R.dimen.dimen12dp),
(int) ctx.getResources().getDimension(R.dimen.dimen0dp),
(int) ctx.getResources().getDimension(R.dimen.dimen0dp),
(int) ctx.getResources().getDimension(R.dimen.dimen0dp));
} else { // open
viewBinding.issuePrState.setImageResource(R.drawable.ic_pull_request);
@ -1034,7 +1054,7 @@ public class IssueDetailActivity extends BaseActivity
RetrofitClient.getApiInterface(this)
.repoGetPullRequest(repoOwner, repoName, (long) issueIndex)
.enqueue(
new Callback<PullRequest>() {
new Callback<>() {
@Override
public void onResponse(

View File

@ -639,7 +639,9 @@ public class LoginActivity extends BaseActivity {
final String credential = Credentials.basic(loginUid, loginPass, StandardCharsets.UTF_8);
CreateAccessTokenOption createUserToken = new CreateAccessTokenOption().name(tokenName);
if (giteaVersion.higherOrEqual("1.19.0")) {
if (giteaVersion.higherOrEqual("1.20.0")) {
createUserToken.addScopesItem("all");
} else if (giteaVersion.less("1.20.0") && (giteaVersion.higherOrEqual("1.19.0"))) {
createUserToken.addScopesItem("all");
createUserToken.addScopesItem("sudo");
}

View File

@ -39,6 +39,7 @@ import org.mian.gitnex.databinding.ActivityMainBinding;
import org.mian.gitnex.fragments.AdministrationFragment;
import org.mian.gitnex.fragments.BottomSheetDraftsFragment;
import org.mian.gitnex.fragments.BottomSheetMyIssuesFilterFragment;
import org.mian.gitnex.fragments.DashboardFragment;
import org.mian.gitnex.fragments.DraftsFragment;
import org.mian.gitnex.fragments.ExploreFragment;
import org.mian.gitnex.fragments.MostVisitedReposFragment;
@ -150,6 +151,8 @@ public class MainActivity extends BaseActivity
toolbarTitle.setText(getResources().getString(R.string.pageTitleAdministration));
} else if (fragmentById instanceof MyIssuesFragment) {
toolbarTitle.setText(getResources().getString(R.string.navMyIssues));
} else if (fragmentById instanceof DashboardFragment) {
toolbarTitle.setText(getResources().getString(R.string.dashboard));
}
getNotificationsCount();
@ -161,6 +164,11 @@ public class MainActivity extends BaseActivity
Menu menu = navigationView.getMenu();
navNotifications = menu.findItem(R.id.nav_notifications);
MenuItem navDashboard = menu.findItem(R.id.nav_dashboard);
navDashboard.getActionView().findViewById(R.id.betaBadge).setVisibility(View.VISIBLE);
TextView dashboardBetaView = navDashboard.getActionView().findViewById(R.id.betaBadge);
dashboardBetaView.setText(R.string.beta);
navigationView
.getViewTreeObserver()
@ -296,6 +304,10 @@ public class MainActivity extends BaseActivity
if (getAccount().requiresVersion("1.14.0")) {
navigationView.getMenu().findItem(R.id.nav_my_issues).setVisible(true);
}
if (getAccount().requiresVersion("1.20.0")) {
navigationView.getMenu().findItem(R.id.nav_dashboard).setVisible(true);
}
}
@Override
@ -489,7 +501,14 @@ public class MainActivity extends BaseActivity
.commit();
navigationView.setCheckedItem(R.id.nav_notes);
break;
case 11:
toolbarTitle.setText(getResources().getString(R.string.dashboard));
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, new DashboardFragment())
.commit();
navigationView.setCheckedItem(R.id.nav_dashboard);
break;
default:
toolbarTitle.setText(getResources().getString(R.string.navMyRepos));
getSupportFragmentManager()
@ -710,6 +729,13 @@ public class MainActivity extends BaseActivity
.beginTransaction()
.replace(R.id.fragment_container, new NotesFragment())
.commit();
} else if (id == R.id.nav_dashboard) {
toolbarTitle.setText(getResources().getString(R.string.dashboard));
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, new DashboardFragment())
.commit();
}
drawer.closeDrawer(GravityCompat.START);

View File

@ -1,11 +1,12 @@
package org.mian.gitnex.activities;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.view.inputmethod.EditorInfo;
import androidx.appcompat.widget.SearchView;
import androidx.lifecycle.ViewModelProvider;
import org.mian.gitnex.R;
import org.mian.gitnex.adapters.UserGridAdapter;
@ -18,37 +19,29 @@ import org.mian.gitnex.viewmodels.RepoStargazersViewModel;
*/
public class RepoStargazersActivity extends BaseActivity {
private TextView noDataStargazers;
private View.OnClickListener onClickListener;
private UserGridAdapter adapter;
private GridView mGridView;
private ProgressBar mProgressBar;
private RepositoryContext repository;
private ActivityRepoStargazersBinding activityRepoStargazersBinding;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityRepoStargazersBinding activityRepoStargazersBinding =
ActivityRepoStargazersBinding.inflate(getLayoutInflater());
activityRepoStargazersBinding = ActivityRepoStargazersBinding.inflate(getLayoutInflater());
setContentView(activityRepoStargazersBinding.getRoot());
ImageView closeActivity = activityRepoStargazersBinding.close;
TextView toolbarTitle = activityRepoStargazersBinding.toolbarTitle;
noDataStargazers = activityRepoStargazersBinding.noDataStargazers;
mGridView = activityRepoStargazersBinding.gridView;
mProgressBar = activityRepoStargazersBinding.progressBar;
setSupportActionBar(activityRepoStargazersBinding.toolbar);
repository = RepositoryContext.fromIntent(getIntent());
final String repoOwner = repository.getOwner();
final String repoName = repository.getName();
initCloseListener();
closeActivity.setOnClickListener(onClickListener);
activityRepoStargazersBinding.close.setOnClickListener(onClickListener);
toolbarTitle.setText(R.string.repoStargazersInMenu);
activityRepoStargazersBinding.toolbarTitle.setText(R.string.repoStargazersInMenu);
fetchDataAsync(repoOwner, repoName);
}
@ -67,16 +60,18 @@ public class RepoStargazersActivity extends BaseActivity {
if (adapter.getCount() > 0) {
mGridView.setAdapter(adapter);
noDataStargazers.setVisibility(View.GONE);
activityRepoStargazersBinding.gridView.setAdapter(adapter);
activityRepoStargazersBinding.noDataStargazers.setVisibility(
View.GONE);
} else {
adapter.notifyDataSetChanged();
mGridView.setAdapter(adapter);
noDataStargazers.setVisibility(View.VISIBLE);
activityRepoStargazersBinding.gridView.setAdapter(adapter);
activityRepoStargazersBinding.noDataStargazers.setVisibility(
View.VISIBLE);
}
mProgressBar.setVisibility(View.GONE);
activityRepoStargazersBinding.progressBar.setVisibility(View.GONE);
});
}
@ -90,4 +85,36 @@ public class RepoStargazersActivity extends BaseActivity {
super.onResume();
repository.checkAccountSwitch(this);
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
final MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.search_menu, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) searchItem.getActionView();
searchView.setImeOptions(EditorInfo.IME_ACTION_DONE);
searchView.setOnQueryTextListener(
new androidx.appcompat.widget.SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return true;
}
@Override
public boolean onQueryTextChange(String newText) {
if (activityRepoStargazersBinding.gridView.getAdapter() != null) {
adapter.getFilter().filter(newText);
}
return false;
}
});
return true;
}
}

View File

@ -1,11 +1,12 @@
package org.mian.gitnex.activities;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.view.inputmethod.EditorInfo;
import androidx.appcompat.widget.SearchView;
import androidx.lifecycle.ViewModelProvider;
import org.mian.gitnex.R;
import org.mian.gitnex.adapters.UserGridAdapter;
@ -18,12 +19,9 @@ import org.mian.gitnex.viewmodels.RepoWatchersViewModel;
*/
public class RepoWatchersActivity extends BaseActivity {
private TextView noDataWatchers;
private View.OnClickListener onClickListener;
private UserGridAdapter adapter;
private GridView mGridView;
private ProgressBar mProgressBar;
private ActivityRepoWatchersBinding activityRepoWatchersBinding;
private RepositoryContext repository;
@Override
@ -31,24 +29,19 @@ public class RepoWatchersActivity extends BaseActivity {
super.onCreate(savedInstanceState);
ActivityRepoWatchersBinding activityRepoWatchersBinding =
ActivityRepoWatchersBinding.inflate(getLayoutInflater());
activityRepoWatchersBinding = ActivityRepoWatchersBinding.inflate(getLayoutInflater());
setContentView(activityRepoWatchersBinding.getRoot());
ImageView closeActivity = activityRepoWatchersBinding.close;
TextView toolbarTitle = activityRepoWatchersBinding.toolbarTitle;
noDataWatchers = activityRepoWatchersBinding.noDataWatchers;
mGridView = activityRepoWatchersBinding.gridView;
mProgressBar = activityRepoWatchersBinding.progressBar;
setSupportActionBar(activityRepoWatchersBinding.toolbar);
repository = RepositoryContext.fromIntent(getIntent());
final String repoOwner = repository.getOwner();
final String repoName = repository.getName();
initCloseListener();
closeActivity.setOnClickListener(onClickListener);
activityRepoWatchersBinding.close.setOnClickListener(onClickListener);
toolbarTitle.setText(R.string.repoWatchersInMenu);
activityRepoWatchersBinding.toolbarTitle.setText(R.string.repoWatchersInMenu);
fetchDataAsync(repoOwner, repoName);
}
@ -67,16 +60,17 @@ public class RepoWatchersActivity extends BaseActivity {
if (adapter.getCount() > 0) {
mGridView.setAdapter(adapter);
noDataWatchers.setVisibility(View.GONE);
activityRepoWatchersBinding.gridView.setAdapter(adapter);
activityRepoWatchersBinding.noDataWatchers.setVisibility(View.GONE);
} else {
adapter.notifyDataSetChanged();
mGridView.setAdapter(adapter);
noDataWatchers.setVisibility(View.VISIBLE);
activityRepoWatchersBinding.gridView.setAdapter(adapter);
activityRepoWatchersBinding.noDataWatchers.setVisibility(
View.VISIBLE);
}
mProgressBar.setVisibility(View.GONE);
activityRepoWatchersBinding.progressBar.setVisibility(View.GONE);
});
}
@ -90,4 +84,35 @@ public class RepoWatchersActivity extends BaseActivity {
super.onResume();
repository.checkAccountSwitch(this);
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
final MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.search_menu, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
SearchView searchView = (SearchView) searchItem.getActionView();
searchView.setImeOptions(EditorInfo.IME_ACTION_DONE);
searchView.setOnQueryTextListener(
new androidx.appcompat.widget.SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
return true;
}
@Override
public boolean onQueryTextChange(String newText) {
if (activityRepoWatchersBinding.gridView.getAdapter() != null) {
adapter.getFilter().filter(newText);
}
return false;
}
});
return true;
}
}

View File

@ -93,6 +93,9 @@ public class SettingsGeneralActivity extends BaseActivity {
} else if (homeScreenSelectedChoice == 10) {
viewBinding.homeScreenSelected.setText(getResources().getString(R.string.navNotes));
} else if (homeScreenSelectedChoice == 11) {
viewBinding.homeScreenSelected.setText(getResources().getString(R.string.dashboard));
}
viewBinding.homeScreenFrame.setOnClickListener(

View File

@ -0,0 +1,892 @@
package org.mian.gitnex.adapters;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.res.ResourcesCompat;
import androidx.core.text.HtmlCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.vdurmont.emoji.EmojiParser;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.gitnex.tea4j.v2.models.Activity;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mian.gitnex.R;
import org.mian.gitnex.activities.IssueDetailActivity;
import org.mian.gitnex.activities.ProfileActivity;
import org.mian.gitnex.activities.RepoDetailActivity;
import org.mian.gitnex.clients.PicassoService;
import org.mian.gitnex.helpers.AppUtil;
import org.mian.gitnex.helpers.ClickListener;
import org.mian.gitnex.helpers.RoundedTransformation;
import org.mian.gitnex.helpers.TimeHelper;
import org.mian.gitnex.helpers.TinyDB;
import org.mian.gitnex.helpers.contexts.IssueContext;
import org.mian.gitnex.helpers.contexts.RepositoryContext;
/**
* @author M M Arif
*/
public class DashboardAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final Context context;
TinyDB tinyDb;
private List<Activity> activityList;
private OnLoadMoreListener loadMoreListener;
private boolean isLoading = false, isMoreDataAvailable = true;
private Intent intent;
public boolean isUserOrg = false;
public DashboardAdapter(List<Activity> dataList, Context ctx) {
this.context = ctx;
this.activityList = dataList;
this.tinyDb = TinyDB.getInstance(ctx);
}
@NonNull @Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
return new DashboardAdapter.DashboardHolder(
inflater.inflate(R.layout.list_dashboard_activity, parent, false));
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if (position >= getItemCount() - 1
&& isMoreDataAvailable
&& !isLoading
&& loadMoreListener != null) {
isLoading = true;
loadMoreListener.onLoadMore();
}
((DashboardAdapter.DashboardHolder) holder).bindData(activityList.get(position));
}
@Override
public int getItemViewType(int position) {
return position;
}
@Override
public int getItemCount() {
return activityList.size();
}
public void setMoreDataAvailable(boolean moreDataAvailable) {
isMoreDataAvailable = moreDataAvailable;
if (!isMoreDataAvailable) {
loadMoreListener.onLoadFinished();
}
}
@SuppressLint("NotifyDataSetChanged")
public void notifyDataChanged() {
notifyDataSetChanged();
isLoading = false;
loadMoreListener.onLoadFinished();
}
public void setLoadMoreListener(OnLoadMoreListener loadMoreListener) {
this.loadMoreListener = loadMoreListener;
}
public void updateList(List<Activity> list) {
activityList = list;
notifyDataChanged();
}
public interface OnLoadMoreListener {
void onLoadMore();
void onLoadFinished();
}
class DashboardHolder extends RecyclerView.ViewHolder {
private final ImageView userAvatar;
private final TextView typeDetails;
private final TextView createdTime;
private final ImageView typeIcon;
private final TextView dashText;
private final LinearLayout dashTextFrame;
private Activity activityObject;
DashboardHolder(View itemView) {
super(itemView);
userAvatar = itemView.findViewById(R.id.user_avatar);
typeDetails = itemView.findViewById(R.id.type_details);
typeIcon = itemView.findViewById(R.id.type_icon);
createdTime = itemView.findViewById(R.id.created_time);
dashText = itemView.findViewById(R.id.text);
dashTextFrame = itemView.findViewById(R.id.dash_text_frame);
new Handler()
.postDelayed(
() -> {
if (!AppUtil.checkGhostUsers(
activityObject.getActUser().getLogin())) {
userAvatar.setOnLongClickListener(
loginId -> {
AppUtil.copyToClipboard(
context,
activityObject.getActUser().getLogin(),
context.getString(
R.string.copyLoginIdToClipBoard,
activityObject
.getActUser()
.getLogin()));
return true;
});
userAvatar.setOnClickListener(
v -> {
intent = new Intent(context, ProfileActivity.class);
intent.putExtra(
"username",
activityObject.getActUser().getLogin());
context.startActivity(intent);
});
}
if (activityObject.getOpType().equalsIgnoreCase("create_repo")
|| activityObject
.getOpType()
.equalsIgnoreCase("rename_repo")
|| activityObject.getOpType().equalsIgnoreCase("star_repo")
|| activityObject
.getOpType()
.equalsIgnoreCase("transfer_repo")) {
itemView.setOnClickListener(
v -> {
Context context = v.getContext();
RepositoryContext repo =
new RepositoryContext(
activityObject.getRepo(), context);
repo.saveToDB(context);
Intent intent =
repo.getIntent(
context, RepoDetailActivity.class);
if (isUserOrg) {
intent.putExtra("openedFromUserOrg", true);
}
context.startActivity(intent);
});
}
if (activityObject.getOpType().equalsIgnoreCase("create_issue")
|| activityObject
.getOpType()
.equalsIgnoreCase("comment_issue")
|| activityObject
.getOpType()
.equalsIgnoreCase("close_issue")
|| activityObject
.getOpType()
.equalsIgnoreCase("reopen_issue")) {
String[] parts =
activityObject.getRepo().getFullName().split("/");
final String repoOwner = parts[0];
final String repoName = parts[1];
RepositoryContext repo =
new RepositoryContext(repoOwner, repoName, context);
String[] contentParts =
activityObject.getContent().split("\\|");
String id = contentParts[0];
Intent intentIssueDetail =
new IssueContext(repo, Integer.parseInt(id), "open")
.getIntent(context, IssueDetailActivity.class);
intentIssueDetail.putExtra("openedFromLink", "true");
itemView.setOnClickListener(
v -> {
repo.saveToDB(context);
context.startActivity(intentIssueDetail);
});
}
if (activityObject
.getOpType()
.equalsIgnoreCase("create_pull_request")
|| activityObject
.getOpType()
.equalsIgnoreCase("close_pull_request")
|| activityObject
.getOpType()
.equalsIgnoreCase("reopen_pull_request")
|| activityObject
.getOpType()
.equalsIgnoreCase("approve_pull_request")
|| activityObject
.getOpType()
.equalsIgnoreCase("reject_pull_request")
|| activityObject
.getOpType()
.equalsIgnoreCase("comment_pull")
|| activityObject
.getOpType()
.equalsIgnoreCase("auto_merge_pull_request")
|| activityObject
.getOpType()
.equalsIgnoreCase("merge_pull_request")) {
String[] parts =
activityObject.getRepo().getFullName().split("/");
final String repoOwner = parts[0];
final String repoName = parts[1];
RepositoryContext repo =
new RepositoryContext(repoOwner, repoName, context);
String[] contentParts =
activityObject.getContent().split("\\|");
String id = contentParts[0];
Intent intentIssueDetail =
new IssueContext(repo, Integer.parseInt(id), "open")
.getIntent(context, IssueDetailActivity.class);
intentIssueDetail.putExtra("openedFromLink", "true");
itemView.setOnClickListener(
v -> {
repo.saveToDB(context);
context.startActivity(intentIssueDetail);
});
}
if (activityObject.getOpType().equalsIgnoreCase("commit_repo")) {
if (activityObject.getContent().isEmpty()) {
itemView.setOnClickListener(
v -> {
RepositoryContext repo =
new RepositoryContext(
activityObject.getRepo(),
context);
Intent repoIntent =
new Intent(
context,
RepoDetailActivity.class);
repoIntent.putExtra("goToSection", "yes");
repoIntent.putExtra(
"goToSectionType", "commitsList");
repoIntent.putExtra(
"branchName",
activityObject
.getRefName()
.substring(
activityObject
.getRefName()
.lastIndexOf(
"/")
+ 1)
.trim());
repo.saveToDB(context);
repoIntent.putExtra(
RepositoryContext.INTENT_EXTRA, repo);
context.startActivity(repoIntent);
});
}
}
if (activityObject.getOpType().equalsIgnoreCase("publish_release")
|| activityObject
.getOpType()
.equalsIgnoreCase("push_tag")) {
itemView.setOnClickListener(
v -> {
RepositoryContext repo =
new RepositoryContext(
activityObject.getRepo(), context);
Intent repoIntent =
new Intent(
context, RepoDetailActivity.class);
repoIntent.putExtra("goToSection", "yes");
repoIntent.putExtra("goToSectionType", "releases");
repoIntent.putExtra(
"releaseTagName",
activityObject
.getRefName()
.substring(
activityObject
.getRefName()
.lastIndexOf(
"/")
+ 1)
.trim());
repo.saveToDB(context);
repoIntent.putExtra(
RepositoryContext.INTENT_EXTRA, repo);
context.startActivity(repoIntent);
});
}
},
200);
}
void bindData(Activity activity) {
this.activityObject = activity;
Locale locale = context.getResources().getConfiguration().locale;
int imgRadius = AppUtil.getPixelsFromDensity(context, 3);
PicassoService.getInstance(context)
.get()
.load(activity.getActUser().getAvatarUrl())
.placeholder(R.drawable.loader_animated)
.transform(new RoundedTransformation(imgRadius, 0))
.resize(120, 120)
.centerCrop()
.into(userAvatar);
String username =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getActUser().getLogin()
+ "</font>";
String headerString = "";
String typeString = "";
if (activity.getOpType().contains("repo")) {
if (activity.getOpType().equalsIgnoreCase("create_repo")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ "</font>";
typeString = "created repository";
typeIcon.setImageResource(R.drawable.ic_repo);
} else if (activity.getOpType().equalsIgnoreCase("rename_repo")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ "</font>";
typeString = "renamed repository from " + activity.getContent() + " to";
typeIcon.setImageResource(R.drawable.ic_repo);
} else if (activity.getOpType().equalsIgnoreCase("star_repo")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ "</font>";
typeString = "starred";
typeIcon.setImageResource(R.drawable.ic_star);
} else if (activity.getOpType().equalsIgnoreCase("transfer_repo")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ "</font>";
typeString = "transferred repository " + activity.getContent() + " to";
typeIcon.setImageResource(R.drawable.ic_arrow_up);
} else if (activity.getOpType().equalsIgnoreCase("commit_repo")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ "</font>";
if (activity.getContent().isEmpty()) {
String branch =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRefName()
.substring(
activity.getRefName().lastIndexOf("/") + 1)
.trim()
+ "</font>";
typeString = "created branch " + branch + " in";
} else {
String branch =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRefName()
.substring(
activity.getRefName().lastIndexOf("/") + 1)
.trim()
+ "</font>";
typeString = "pushed to " + branch + " at";
JSONObject commitsObj = null;
try {
commitsObj = new JSONObject(activity.getContent());
} catch (JSONException ignored) {
}
JSONArray commitsShaArray = null;
try {
commitsShaArray =
Objects.requireNonNull(commitsObj).getJSONArray("Commits");
} catch (JSONException ignored) {
}
dashTextFrame.setVisibility(View.VISIBLE);
dashTextFrame.setOrientation(LinearLayout.VERTICAL);
dashTextFrame.removeAllViews();
for (int i = 0; i < Objects.requireNonNull(commitsShaArray).length(); i++) {
try {
String timelineCommits =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(),
R.color.lightBlue,
null)
+ "'>"
+ StringUtils.substring(
String.valueOf(commitsShaArray.get(i)),
9,
19)
+ "</font>";
TextView dynamicCommitTv = new TextView(context);
dynamicCommitTv.setId(View.generateViewId());
dynamicCommitTv.setText(
HtmlCompat.fromHtml(
timelineCommits, HtmlCompat.FROM_HTML_MODE_LEGACY));
JSONObject sha1Obj = null;
try {
sha1Obj = (JSONObject) commitsShaArray.get(i);
} catch (JSONException ignored) {
}
JSONObject finalSha1Obj = sha1Obj;
dynamicCommitTv.setOnClickListener(
v14 -> {
RepositoryContext repo =
new RepositoryContext(
activity.getRepo(), context);
Intent repoIntent =
new Intent(context, RepoDetailActivity.class);
repoIntent.putExtra("goToSection", "yes");
repoIntent.putExtra("goToSectionType", "commit");
try {
assert finalSha1Obj != null;
repoIntent.putExtra(
"sha", (String) finalSha1Obj.get("Sha1"));
} catch (JSONException ignored) {
}
repo.saveToDB(context);
repoIntent.putExtra(
RepositoryContext.INTENT_EXTRA, repo);
context.startActivity(repoIntent);
});
dashTextFrame.setOrientation(LinearLayout.VERTICAL);
dashTextFrame.addView(dynamicCommitTv);
} catch (JSONException ignored) {
}
}
}
typeIcon.setImageResource(R.drawable.ic_commit);
}
} else if (activity.getOpType().contains("issue")) {
String id;
String content;
String[] contentParts = activity.getContent().split("\\|");
if (contentParts.length > 1) {
id = contentParts[0];
content = contentParts[1];
dashTextFrame.setVisibility(View.VISIBLE);
dashText.setText(EmojiParser.parseToUnicode(content));
} else {
id = contentParts[0];
}
if (activity.getOpType().equalsIgnoreCase("create_issue")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ context.getResources().getString(R.string.hash)
+ id
+ "</font>";
typeString = "opened issue";
typeIcon.setImageResource(R.drawable.ic_issue);
} else if (activity.getOpType().equalsIgnoreCase("comment_issue")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ context.getResources().getString(R.string.hash)
+ id
+ "</font>";
typeString = "commented on issue";
typeIcon.setImageResource(R.drawable.ic_comment);
} else if (activity.getOpType().equalsIgnoreCase("close_issue")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ context.getResources().getString(R.string.hash)
+ id
+ "</font>";
typeString = "closed issue";
typeIcon.setImageResource(R.drawable.ic_issue_closed);
} else if (activity.getOpType().equalsIgnoreCase("reopen_issue")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ context.getResources().getString(R.string.hash)
+ id
+ "</font>";
typeString = "reopened issue";
typeIcon.setImageResource(R.drawable.ic_reopen);
}
} else if (activity.getOpType().contains("pull")) {
String id;
String content;
String[] contentParts = activity.getContent().split("\\|");
if (contentParts.length > 1) {
id = contentParts[0];
content = contentParts[1];
dashTextFrame.setVisibility(View.VISIBLE);
dashText.setText(EmojiParser.parseToUnicode(content));
} else {
id = contentParts[0];
}
if (activity.getOpType().equalsIgnoreCase("create_pull_request")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ context.getResources().getString(R.string.hash)
+ id
+ "</font>";
typeString = "created pull request";
typeIcon.setImageResource(R.drawable.ic_pull_request);
} else if (activity.getOpType().equalsIgnoreCase("close_pull_request")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ context.getResources().getString(R.string.hash)
+ id
+ "</font>";
typeString = "closed pull request";
typeIcon.setImageResource(R.drawable.ic_issue_closed);
} else if (activity.getOpType().equalsIgnoreCase("reopen_pull_request")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ context.getResources().getString(R.string.hash)
+ id
+ "</font>";
typeString = "reopened pull request";
typeIcon.setImageResource(R.drawable.ic_reopen);
} else if (activity.getOpType().equalsIgnoreCase("merge_pull_request")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ context.getResources().getString(R.string.hash)
+ id
+ "</font>";
typeString = "merged pull request";
typeIcon.setImageResource(R.drawable.ic_pull_request);
} else if (activity.getOpType().equalsIgnoreCase("approve_pull_request")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ context.getResources().getString(R.string.hash)
+ id
+ "</font>";
typeString = "approved";
typeIcon.setImageResource(R.drawable.ic_done);
} else if (activity.getOpType().equalsIgnoreCase("reject_pull_request")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ context.getResources().getString(R.string.hash)
+ id
+ "</font>";
typeString = "suggested changes for";
typeIcon.setImageResource(R.drawable.ic_diff);
} else if (activity.getOpType().equalsIgnoreCase("comment_pull")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ context.getResources().getString(R.string.hash)
+ id
+ "</font>";
typeString = "commented on pull request";
typeIcon.setImageResource(R.drawable.ic_comment);
} else if (activity.getOpType().equalsIgnoreCase("auto_merge_pull_request")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ context.getResources().getString(R.string.hash)
+ id
+ "</font>";
typeString = "automatically merged pull request";
typeIcon.setImageResource(R.drawable.ic_issue_closed);
}
} else if (activity.getOpType().contains("branch")) {
String content;
String[] contentParts = activity.getContent().split("\\|");
if (contentParts.length > 1) {
content = contentParts[1];
dashTextFrame.setVisibility(View.VISIBLE);
dashText.setText(EmojiParser.parseToUnicode(content));
}
if (activity.getOpType().equalsIgnoreCase("delete_branch")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ "</font>";
String branch =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRefName()
.substring(activity.getRefName().lastIndexOf("/") + 1)
.trim()
+ "</font>";
typeString = "deleted branch " + branch + " at";
typeIcon.setImageResource(R.drawable.ic_commit);
}
} else if (activity.getOpType().contains("tag")) {
if (activity.getOpType().equalsIgnoreCase("push_tag")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ "</font>";
String branch =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRefName()
.substring(activity.getRefName().lastIndexOf("/") + 1)
.trim()
+ "</font>";
typeString = "pushed tag " + branch + " to";
typeIcon.setImageResource(R.drawable.ic_commit);
} else if (activity.getOpType().equalsIgnoreCase("delete_tag")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ "</font>";
String branch =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRefName()
.substring(activity.getRefName().lastIndexOf("/") + 1)
.trim()
+ "</font>";
typeString = "deleted tag " + branch + " from";
typeIcon.setImageResource(R.drawable.ic_commit);
}
} else if (activity.getOpType().contains("release")) {
if (activity.getOpType().equalsIgnoreCase("publish_release")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ "</font>";
String branch =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRefName()
.substring(activity.getRefName().lastIndexOf("/") + 1)
.trim()
+ "</font>";
typeString = "released " + branch + " at";
typeIcon.setImageResource(R.drawable.ic_tag);
}
} else if (activity.getOpType().contains("mirror")) {
if (activity.getOpType().equalsIgnoreCase("mirror_sync_push")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ "</font>";
typeString = "synced commits to " + headerString + " at";
typeIcon.setImageResource(R.drawable.ic_tag);
} else if (activity.getOpType().equalsIgnoreCase("mirror_sync_create")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ "</font>";
typeString = "synced new reference " + headerString + " to";
typeIcon.setImageResource(R.drawable.ic_tag);
} else if (activity.getOpType().equalsIgnoreCase("mirror_sync_delete")) {
headerString =
"<font color='"
+ ResourcesCompat.getColor(
context.getResources(), R.color.lightGray, null)
+ "'>"
+ activity.getRepo().getFullName()
+ "</font>";
typeString = "synced and deleted reference " + headerString + " at";
typeIcon.setImageResource(R.drawable.ic_tag);
}
} else {
dashTextFrame.setVisibility(View.GONE);
dashText.setVisibility(View.GONE);
}
typeDetails.setText(
HtmlCompat.fromHtml(
username + " " + typeString + " " + headerString,
HtmlCompat.FROM_HTML_MODE_LEGACY));
this.createdTime.setText(TimeHelper.formatTime(activity.getCreated(), locale));
this.createdTime.setOnClickListener(
new ClickListener(
TimeHelper.customDateFormatForToastDateFormat(activity.getCreated()),
context));
}
}
}

View File

@ -170,26 +170,31 @@ public class ExploreIssuesAdapter extends RecyclerView.Adapter<RecyclerView.View
repo.saveToDB(context);
context.startActivity(intentIssueDetail);
});
if (!AppUtil.checkGhostUsers(issue.getUser().getLogin())) {
issueAssigneeAvatar.setOnClickListener(
v -> {
Intent intent =
new Intent(context, ProfileActivity.class);
intent.putExtra(
"username", issue.getUser().getLogin());
context.startActivity(intent);
});
issueAssigneeAvatar.setOnLongClickListener(
loginId -> {
AppUtil.copyToClipboard(
context,
issue.getUser().getLogin(),
context.getString(
R.string.copyLoginIdToClipBoard,
issue.getUser().getLogin()));
return true;
});
}
},
200);
issueAssigneeAvatar.setOnClickListener(
v -> {
Intent intent = new Intent(context, ProfileActivity.class);
intent.putExtra("username", issue.getUser().getLogin());
context.startActivity(intent);
});
issueAssigneeAvatar.setOnLongClickListener(
loginId -> {
AppUtil.copyToClipboard(
context,
issue.getUser().getLogin(),
context.getString(
R.string.copyLoginIdToClipBoard,
issue.getUser().getLogin()));
return true;
});
}
void bindData(Issue issue) {

View File

@ -3,6 +3,7 @@ package org.mian.gitnex.adapters;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.content.res.ColorStateList;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Handler;
@ -18,6 +19,7 @@ import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.core.content.res.ResourcesCompat;
import androidx.core.text.HtmlCompat;
import androidx.core.widget.ImageViewCompat;
import androidx.recyclerview.widget.RecyclerView;
import com.amulyakhare.textdrawable.TextDrawable;
import com.vdurmont.emoji.EmojiParser;
@ -106,6 +108,7 @@ public class PullRequestsAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
private final ImageView assigneeAvatar;
private final TextView prTitle;
private final ImageView issuePrState;
private final TextView prCreatedTime;
private final TextView prCommentsCount;
private final HorizontalScrollView labelsScrollViewWithText;
@ -120,6 +123,7 @@ public class PullRequestsAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
super(itemView);
assigneeAvatar = itemView.findViewById(R.id.assigneeAvatar);
prTitle = itemView.findViewById(R.id.prTitle);
issuePrState = itemView.findViewById(R.id.issuePrState);
prCommentsCount = itemView.findViewById(R.id.prCommentsCount);
prCreatedTime = itemView.findViewById(R.id.prCreatedTime);
labelsScrollViewWithText = itemView.findViewById(R.id.labelsScrollViewWithText);
@ -284,6 +288,29 @@ public class PullRequestsAdapter extends RecyclerView.Adapter<RecyclerView.ViewH
+ pullRequest.getNumber()
+ "</font>";
if (pullRequest.getTitle().contains("[WIP]")
|| pullRequest.getTitle().contains("[wip]")) {
this.issuePrState.setVisibility(View.VISIBLE);
this.issuePrState.setImageResource(R.drawable.ic_draft);
ImageViewCompat.setImageTintList(
this.issuePrState,
ColorStateList.valueOf(
context.getResources().getColor(R.color.colorWhite, null)));
this.issuePrState.setBackgroundResource(R.drawable.shape_draft_release);
this.issuePrState.setPadding(
(int) context.getResources().getDimension(R.dimen.dimen4dp),
(int) context.getResources().getDimension(R.dimen.dimen0dp),
(int) context.getResources().getDimension(R.dimen.dimen4dp),
(int) context.getResources().getDimension(R.dimen.dimen0dp));
this.prTitle.setPadding(
(int) context.getResources().getDimension(R.dimen.dimen12dp),
(int) context.getResources().getDimension(R.dimen.dimen0dp),
(int) context.getResources().getDimension(R.dimen.dimen0dp),
(int) context.getResources().getDimension(R.dimen.dimen0dp));
} else {
this.issuePrState.setVisibility(View.GONE);
}
this.prTitle.setText(
HtmlCompat.fromHtml(
prNumber_ + " " + EmojiParser.parseToUnicode(pullRequest.getTitle()),

View File

@ -314,7 +314,7 @@ public class BottomSheetReplyFragment extends BottomSheetDialogFragment {
}
draftsHint.setVisibility(View.VISIBLE);
valueAnimator.start();
// valueAnimator.start();
}
}

View File

@ -0,0 +1,123 @@
package org.mian.gitnex.fragments;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager;
import java.util.ArrayList;
import java.util.List;
import org.gitnex.tea4j.v2.models.Activity;
import org.mian.gitnex.R;
import org.mian.gitnex.activities.BaseActivity;
import org.mian.gitnex.activities.MainActivity;
import org.mian.gitnex.adapters.DashboardAdapter;
import org.mian.gitnex.databinding.FragmentDashboardBinding;
import org.mian.gitnex.helpers.TinyDB;
import org.mian.gitnex.viewmodels.DashboardViewModel;
/**
* @author M M Arif
*/
public class DashboardFragment extends Fragment {
protected TinyDB tinyDB;
private DashboardViewModel dashboardViewModel;
private FragmentDashboardBinding binding;
private DashboardAdapter adapter;
private List<Activity> activityList;
private int page = 1;
private String username;
@Override
public View onCreateView(
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
binding = FragmentDashboardBinding.inflate(inflater, container, false);
Context ctx = getContext();
setHasOptionsMenu(true);
((MainActivity) requireActivity())
.setActionBarTitle(getResources().getString(R.string.dashboard));
activityList = new ArrayList<>();
dashboardViewModel = new ViewModelProvider(this).get(DashboardViewModel.class);
username = ((BaseActivity) requireActivity()).getAccount().getAccount().getUserName();
binding.recyclerView.setHasFixedSize(true);
binding.recyclerView.setLayoutManager(new LinearLayoutManager(ctx));
adapter = new DashboardAdapter(activityList, ctx);
binding.pullToRefresh.setOnRefreshListener(
() ->
new Handler(Looper.getMainLooper())
.postDelayed(
() -> {
activityList.clear();
binding.pullToRefresh.setRefreshing(false);
binding.progressBar.setVisibility(View.VISIBLE);
fetchDataAsync(username);
},
250));
fetchDataAsync(username);
return binding.getRoot();
}
@Override
public void onResume() {
super.onResume();
// fetchDataAsync(username);
}
private void fetchDataAsync(String username) {
dashboardViewModel
.getActivitiesList(username, getContext(), binding)
.observe(
getViewLifecycleOwner(),
activityListMain -> {
adapter = new DashboardAdapter(activityListMain, getContext());
adapter.setLoadMoreListener(
new DashboardAdapter.OnLoadMoreListener() {
@Override
public void onLoadMore() {
page += 1;
dashboardViewModel.loadMoreActivities(
username, page, getContext(), adapter, binding);
binding.progressBar.setVisibility(View.VISIBLE);
}
@Override
public void onLoadFinished() {
binding.progressBar.setVisibility(View.GONE);
}
});
if (adapter.getItemCount() > 0) {
binding.recyclerView.setAdapter(adapter);
binding.noData.setVisibility(View.GONE);
} else {
adapter.notifyDataChanged();
binding.recyclerView.setAdapter(adapter);
binding.noData.setVisibility(View.VISIBLE);
}
binding.progressBar.setVisibility(View.GONE);
});
}
}

View File

@ -0,0 +1,114 @@
package org.mian.gitnex.viewmodels;
import android.content.Context;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import java.util.List;
import org.gitnex.tea4j.v2.models.Activity;
import org.mian.gitnex.R;
import org.mian.gitnex.adapters.DashboardAdapter;
import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.databinding.FragmentDashboardBinding;
import org.mian.gitnex.helpers.Constants;
import org.mian.gitnex.helpers.Toasty;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
/**
* @author M M Arif
*/
public class DashboardViewModel extends ViewModel {
private MutableLiveData<List<Activity>> activityList;
private int resultLimit;
public LiveData<List<Activity>> getActivitiesList(
String username, Context ctx, FragmentDashboardBinding binding) {
activityList = new MutableLiveData<>();
resultLimit = Constants.getCurrentResultLimit(ctx);
loadActivityList(username, ctx, binding);
return activityList;
}
public void loadActivityList(String username, Context ctx, FragmentDashboardBinding binding) {
Call<List<Activity>> call =
RetrofitClient.getApiInterface(ctx)
.userListActivityFeeds(username, false, null, 1, resultLimit);
call.enqueue(
new Callback<>() {
@Override
public void onResponse(
@NonNull Call<List<Activity>> call,
@NonNull Response<List<Activity>> response) {
if (response.isSuccessful()) {
activityList.postValue(response.body());
} else {
binding.progressBar.setVisibility(View.GONE);
Toasty.error(ctx, ctx.getString(R.string.genericError));
}
}
@Override
public void onFailure(
@NonNull Call<List<Activity>> call, @NonNull Throwable t) {
Toasty.error(ctx, ctx.getString(R.string.genericServerResponseError));
}
});
}
public void loadMoreActivities(
String username,
int page,
Context ctx,
DashboardAdapter adapter,
FragmentDashboardBinding binding) {
Call<List<Activity>> call =
RetrofitClient.getApiInterface(ctx)
.userListActivityFeeds(username, false, null, page, resultLimit);
call.enqueue(
new Callback<>() {
@Override
public void onResponse(
@NonNull Call<List<Activity>> call,
@NonNull Response<List<Activity>> response) {
if (response.isSuccessful()) {
List<Activity> list = activityList.getValue();
assert list != null;
assert response.body() != null;
if (response.body().size() != 0) {
list.addAll(response.body());
adapter.updateList(list);
} else {
adapter.setMoreDataAvailable(false);
}
} else {
binding.progressBar.setVisibility(View.GONE);
Toasty.error(ctx, ctx.getString(R.string.genericError));
}
}
@Override
public void onFailure(
@NonNull Call<List<Activity>> call, @NonNull Throwable t) {
Toasty.error(ctx, ctx.getString(R.string.genericServerResponseError));
}
});
}
}

View File

@ -0,0 +1,34 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M4,3L9,3A1,1 0,0 1,10 4L10,11A1,1 0,0 1,9 12L4,12A1,1 0,0 1,3 11L3,4A1,1 0,0 1,4 3z"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="?attr/iconsColor"
android:strokeLineCap="round"/>
<path
android:pathData="M15,3L20,3A1,1 0,0 1,21 4L21,7A1,1 0,0 1,20 8L15,8A1,1 0,0 1,14 7L14,4A1,1 0,0 1,15 3z"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="?attr/iconsColor"
android:strokeLineCap="round"/>
<path
android:pathData="M15,12L20,12A1,1 0,0 1,21 13L21,20A1,1 0,0 1,20 21L15,21A1,1 0,0 1,14 20L14,13A1,1 0,0 1,15 12z"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="?attr/iconsColor"
android:strokeLineCap="round"/>
<path
android:pathData="M4,16L9,16A1,1 0,0 1,10 17L10,20A1,1 0,0 1,9 21L4,21A1,1 0,0 1,3 20L3,17A1,1 0,0 1,4 16z"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="?attr/iconsColor"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,48 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M6,18m-2,0a2,2 0,1 0,4 0a2,2 0,1 0,-4 0"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="?attr/iconsColor"
android:strokeLineCap="round"/>
<path
android:pathData="M6,6m-2,0a2,2 0,1 0,4 0a2,2 0,1 0,-4 0"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="?attr/iconsColor"
android:strokeLineCap="round"/>
<path
android:pathData="M18,18m-2,0a2,2 0,1 0,4 0a2,2 0,1 0,-4 0"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="?attr/iconsColor"
android:strokeLineCap="round"/>
<path
android:pathData="M6,8v8"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="?attr/iconsColor"
android:strokeLineCap="round"/>
<path
android:pathData="M18,11h0.01"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="?attr/iconsColor"
android:strokeLineCap="round"/>
<path
android:pathData="M18,6h0.01"
android:strokeLineJoin="round"
android:strokeWidth="2"
android:fillColor="#00000000"
android:strokeColor="?attr/iconsColor"
android:strokeLineCap="round"/>
</vector>

View File

@ -0,0 +1,14 @@
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/dimen12dp"/>
<solid
android:color="?attr/primaryTextColor"/>
<padding
android:bottom="@dimen/dimen2dp"
android:left="@dimen/dimen4dp"
android:right="@dimen/dimen4dp"
android:top="@dimen/dimen2dp"/>
</shape>

View File

@ -8,7 +8,7 @@
</solid>
<corners
android:radius="18dp">
android:radius="@dimen/dimen6dp">
</corners>
</shape>

View File

@ -8,7 +8,7 @@
</solid>
<corners
android:radius="18dp">
android:radius="@dimen/dimen6dp">
</corners>
</shape>

View File

@ -8,7 +8,7 @@
</solid>
<corners
android:radius="18dp">
android:radius="@dimen/dimen6dp">
</corners>
</shape>

View File

@ -0,0 +1,21 @@
<?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="match_parent"
android:gravity="center_vertical">
<TextView
android:id="@+id/betaBadge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/shape_beta_badge"
android:ellipsize="marquee"
android:paddingStart="@dimen/dimen8dp"
android:paddingEnd="@dimen/dimen8dp"
android:singleLine="true"
android:textColor="?attr/primaryBackgroundColor"
android:textSize="@dimen/dimen12sp"
android:visibility="gone" />
</LinearLayout>

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".activities.RepoDetailActivity">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/primaryBackgroundColor"
android:padding="@dimen/dimen8dp">
<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" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</FrameLayout>
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
style="@style/Widget.MaterialComponents.LinearProgressIndicator"
app:indicatorColor="?attr/progressIndicatorColor" />
<TextView
android:id="@+id/noData"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/dimen16dp"
android:text="@string/noDataFound"
android:textColor="?attr/primaryTextColor"
android:gravity="center"
android:textSize="@dimen/dimen18sp"
android:visibility="gone" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/relativeLayoutFrameIssuesList"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/dimen4dp"
android:paddingBottom="@dimen/dimen4dp"
android:orientation="vertical">
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?attr/materialCardViewElevatedStyle"
app:cardElevation="@dimen/dimen0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?android:attr/selectableItemBackground"
android:background="?attr/materialCardBackgroundColor"
android:padding="@dimen/dimen12dp"
android:orientation="vertical">
<LinearLayout
android:id="@+id/info_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<com.google.android.material.card.MaterialCardView
android:layout_width="@dimen/dimen24dp"
android:layout_height="@dimen/dimen24dp"
style="?attr/materialCardViewElevatedStyle"
app:cardElevation="@dimen/dimen0dp"
android:layout_marginEnd="@dimen/dimen12dp"
app:cardCornerRadius="@dimen/dimen12dp">
<ImageView
android:id="@+id/user_avatar"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:contentDescription="@string/generalImgContentText"
tools:src="@tools:sample/avatars"/>
</com.google.android.material.card.MaterialCardView>
<LinearLayout
android:id="@+id/header_detail_section"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/type_details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="top|center_vertical"
android:text="@string/newIssueTitle"
android:textAlignment="gravity"
android:textColor="?attr/primaryTextColor"
android:textSize="@dimen/dimen16sp"
tools:text="Id illum odio repellat omnis fuga deserunt aut. Ut est aut similique qui incidunt quia et." />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/dash_text_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:visibility="gone"
android:layout_marginTop="@dimen/dimen16dp">
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start"
android:textColor="?attr/primaryTextColor"
android:textSize="@dimen/dimen12sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/dash_info_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal"
android:layout_marginTop="@dimen/dimen16dp">
<TextView
android:id="@+id/created_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="start"
android:textColor="?attr/hintColor"
android:textSize="@dimen/dimen12sp"
tools:text="25.08.2023" />
<LinearLayout
android:id="@+id/type"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginStart="@dimen/dimen10dp"
android:layout_marginEnd="@dimen/dimen0dp"
android:layout_weight="1"
android:gravity="end"
android:orientation="horizontal"
android:paddingStart="@dimen/dimen4dp"
android:paddingEnd="@dimen/dimen0dp"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/type_icon"
android:layout_width="@dimen/dimen16dp"
android:layout_height="@dimen/dimen16dp"
android:layout_marginStart="@dimen/dimen2dp"
android:layout_marginEnd="@dimen/dimen4dp"
android:contentDescription="@string/generalImgContentText"
app:srcCompat="@drawable/ic_comment"
app:tint="?attr/iconsColor" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</LinearLayout>

View File

@ -54,15 +54,31 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/prTitle"
android:layout_width="wrap_content"
<LinearLayout
android:id="@+id/titleStateSection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top|center_vertical"
android:textAlignment="gravity"
android:text="@string/newIssueTitle"
android:textColor="?attr/primaryTextColor"
android:textSize="@dimen/dimen16sp" />
android:orientation="horizontal">
<ImageView
android:id="@+id/issuePrState"
android:layout_width="@dimen/dimen26dp"
android:layout_height="@dimen/dimen26dp"
android:contentDescription="@string/generalImgContentText"
android:src="@drawable/ic_draft"
android:visibility="gone" />
<TextView
android:id="@+id/prTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="top|center_vertical"
android:textAlignment="gravity"
android:text="@string/newIssueTitle"
android:textColor="?attr/primaryTextColor"
android:textSize="@dimen/dimen16sp" />
</LinearLayout>
<HorizontalScrollView
android:id="@+id/labelsScrollViewDots"

View File

@ -112,8 +112,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/shape_stable_release"
android:paddingStart="@dimen/dimen8dp"
android:paddingEnd="@dimen/dimen8dp"
android:paddingStart="@dimen/dimen6dp"
android:paddingEnd="@dimen/dimen6dp"
android:paddingTop="@dimen/dimen2dp"
android:paddingBottom="@dimen/dimen2dp"
android:textColor="@color/colorWhite"
android:textSize="@dimen/dimen14sp" />

View File

@ -7,6 +7,12 @@
<group android:id="@+id/nav_main"
android:checkableBehavior="single">
<item android:id="@+id/nav_dashboard"
android:icon="@drawable/ic_dashboard"
android:title="@string/dashboard"
app:actionLayout="@layout/badge_beta"
android:visible="false"/>
<item android:id="@+id/nav_home"
android:icon="@drawable/ic_repo"
android:title="@string/navMyRepos"/>

View File

@ -74,6 +74,7 @@
<item>@string/navMyIssues</item>
<item>@string/navMostVisited</item>
<item>@string/navNotes</item>
<item>@string/dashboard</item>
</string-array>
<string-array name="linkHandlerDefaultScreen">

View File

@ -515,6 +515,7 @@
<string name="openInBrowser">Open in Browser</string>
<string name="deleteGenericTitle">Delete %s</string>
<string name="reset">Reset</string>
<string name="beta">BETA</string>
<!-- generic copy -->
<string name="exploreUsers">Explore users</string>
@ -863,4 +864,6 @@
<string name="lang_statistics">Language Statistics</string>
<string name="lang_percentage" translatable="false">%s%%</string>
<string name="dashboard">Dashboard</string>
</resources>