diff --git a/.gitignore b/.gitignore index 7f5d55f5..a12bd4eb 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ # Release dir app/release/* +app/free/* # Pro dir app/pro/* diff --git a/README.md b/README.md index 79146187..f664148c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![License: GPL v3](https://codeberg.org/gitnex/GitNex/raw/branch/main/assets/license.svg)](https://www.gnu.org/licenses/gpl-3.0) [![Pipeline status](https://ci.codeberg.org/api/badges/gitnex/GitNex/status.svg)](https://ci.codeberg.org/gitnex/GitNex) [![Release](https://img.shields.io/badge/dynamic/json.svg?label=release&url=https://codeberg.org/api/v1/repos/gitnex/GitNex/releases&query=$[0].tag_name)](https://codeberg.org/gitnex/GitNex/releases) [![Crowdin](https://badges.crowdin.net/gitnex/localized.svg)](https://crowdin.com/project/gitnex) [![Join the Discord chat at https://discord.gg/FbSS4rf](https://img.shields.io/discord/632219664587685908.svg)](https://discord.gg/FbSS4rf) -[Become a Patreon](https://www.patreon.com/mmarif) [Buy me a coffee](https://www.buymeacoffee.com/mmarif) +[Become a Patreon](https://www.patreon.com/mmarif) # GitNex - Android client for Forgejo and Gitea diff --git a/app/build.gradle b/app/build.gradle index 405f8ae2..2c12496a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,5 @@ plugins { - id "com.diffplug.spotless" version "6.11.0" + id "com.diffplug.spotless" version "6.25.0" } apply plugin: 'com.android.application' @@ -8,8 +8,8 @@ android { applicationId "org.mian.gitnex" minSdkVersion 23 targetSdkVersion 34 - versionCode 550 - versionName "5.5.0" + versionCode 595 + versionName "6.0.0-dev" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" compileSdk 34 @@ -56,25 +56,25 @@ configurations { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'com.google.android.material:material:1.12.0' - implementation 'androidx.compose.material3:material3:1.2.1' - implementation 'androidx.compose.material3:material3-window-size-class:1.2.1' + implementation 'androidx.compose.material3:material3:1.3.0' + implementation 'androidx.compose.material3:material3-window-size-class:1.3.0' implementation 'androidx.viewpager2:viewpager2:1.1.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.lifecycle:lifecycle-viewmodel:2.7.0" testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.5' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' - implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.12' - implementation 'com.google.code.gson:gson:2.10.1' + androidTestImplementation 'androidx.test.ext:junit:1.2.1' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' + implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.14' + implementation 'com.google.code.gson:gson:2.11.0' implementation "com.squareup.picasso:picasso:2.71828" implementation 'com.github.ramseth001:TextDrawable:1.1.3' implementation 'com.squareup.retrofit2:retrofit:2.11.0' implementation 'com.squareup.retrofit2:converter-gson:2.11.0' implementation 'com.squareup.retrofit2:converter-scalars:2.11.0' - implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.12' + implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.14' implementation 'org.ocpsoft.prettytime:prettytime:5.0.7.Final' implementation "com.github.skydoves:colorpickerview:2.3.0" implementation "io.noties.markwon:core:4.6.2" @@ -89,8 +89,7 @@ dependencies { implementation "io.noties.markwon:recycler:4.6.2" implementation "io.noties.markwon:recycler-table:4.6.2" implementation "io.noties.markwon:simple-ext:4.6.2" - implementation 'com.google.guava:guava:32.1.2-jre' - implementation "io.noties.markwon:image-picasso:4.6.2" + implementation 'com.google.guava:guava:32.1.3-jre' implementation "com.github.HamidrezaAmz:BreadcrumbsView:0.2.9" //noinspection GradleDependency implementation 'commons-io:commons-io:2.5' @@ -101,7 +100,7 @@ dependencies { implementation 'ch.acra:acra-notification:5.11.3' implementation 'androidx.room:room-runtime:2.6.1' annotationProcessor 'androidx.room:room-compiler:2.6.1' - implementation "androidx.work:work-runtime:2.9.0" + implementation "androidx.work:work-runtime:2.9.1" implementation "io.mikael:urlbuilder:2.0.9" implementation "org.codeberg.gitnex-garage:emoji-java:v5.1.2" //noinspection GradleDependency diff --git a/app/src/main/java/org/mian/gitnex/activities/BaseActivity.java b/app/src/main/java/org/mian/gitnex/activities/BaseActivity.java index 517315f0..93a201a3 100644 --- a/app/src/main/java/org/mian/gitnex/activities/BaseActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/BaseActivity.java @@ -4,6 +4,7 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; +import androidx.biometric.BiometricManager; import java.util.Locale; import org.mian.gitnex.R; import org.mian.gitnex.core.MainApplication; @@ -118,15 +119,20 @@ public abstract class BaseActivity extends AppCompatActivity { public void onResume() { super.onResume(); - if (Boolean.parseBoolean( - AppDatabaseSettings.getSettingsValue( - ctx, AppDatabaseSettings.APP_BIOMETRIC_KEY)) - && !Boolean.parseBoolean( - AppDatabaseSettings.getSettingsValue( - ctx, AppDatabaseSettings.APP_BIOMETRIC_LIFE_CYCLE_KEY))) { + if (BiometricManager.from(ctx) + .canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) + == BiometricManager.BIOMETRIC_SUCCESS) { - Intent unlockIntent = new Intent(ctx, BiometricUnlock.class); - ctx.startActivity(unlockIntent); + if (Boolean.parseBoolean( + AppDatabaseSettings.getSettingsValue( + ctx, AppDatabaseSettings.APP_BIOMETRIC_KEY)) + && !Boolean.parseBoolean( + AppDatabaseSettings.getSettingsValue( + ctx, AppDatabaseSettings.APP_BIOMETRIC_LIFE_CYCLE_KEY))) { + + Intent unlockIntent = new Intent(ctx, BiometricUnlock.class); + ctx.startActivity(unlockIntent); + } } } diff --git a/app/src/main/java/org/mian/gitnex/activities/BiometricUnlock.java b/app/src/main/java/org/mian/gitnex/activities/BiometricUnlock.java index e9ad0873..a268581f 100644 --- a/app/src/main/java/org/mian/gitnex/activities/BiometricUnlock.java +++ b/app/src/main/java/org/mian/gitnex/activities/BiometricUnlock.java @@ -45,7 +45,7 @@ public class BiometricUnlock extends AppCompatActivity { super.onAuthenticationError(errorCode, errString); - // Authentication error, close the app + MainActivity.closeActivity = true; finish(); } diff --git a/app/src/main/java/org/mian/gitnex/activities/CreateIssueActivity.java b/app/src/main/java/org/mian/gitnex/activities/CreateIssueActivity.java index 6cebaf0c..56780b9d 100644 --- a/app/src/main/java/org/mian/gitnex/activities/CreateIssueActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/CreateIssueActivity.java @@ -507,6 +507,8 @@ public class CreateIssueActivity extends BaseActivity String newIssueTitleForm, String newIssueDueDateForm) { + viewBinding.topAppBar.getMenu().getItem(2).setVisible(false); + ArrayList labelIds = new ArrayList<>(); for (Integer i : labelsIds) { labelIds.add((long) i); @@ -562,9 +564,11 @@ public class CreateIssueActivity extends BaseActivity } else if (response2.code() == 401) { + viewBinding.topAppBar.getMenu().getItem(2).setVisible(true); AlertDialogs.authorizationTokenRevokedDialog(ctx); } else { + viewBinding.topAppBar.getMenu().getItem(2).setVisible(true); SnackBar.error( ctx, findViewById(android.R.id.content), @@ -575,6 +579,7 @@ public class CreateIssueActivity extends BaseActivity @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { + viewBinding.topAppBar.getMenu().getItem(2).setVisible(true); SnackBar.error( ctx, findViewById(android.R.id.content), diff --git a/app/src/main/java/org/mian/gitnex/activities/CreatePullRequestActivity.java b/app/src/main/java/org/mian/gitnex/activities/CreatePullRequestActivity.java index 05ad2a65..edfa0dd5 100644 --- a/app/src/main/java/org/mian/gitnex/activities/CreatePullRequestActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/CreatePullRequestActivity.java @@ -414,6 +414,8 @@ public class CreatePullRequestActivity extends BaseActivity List assignees, String prDueDate) { + viewBinding.topAppBar.getMenu().getItem(2).setVisible(false); + ArrayList labelIds = new ArrayList<>(); for (Integer i : labelsIds) { labelIds.add((long) i); @@ -470,18 +472,21 @@ public class CreatePullRequestActivity extends BaseActivity } else if (response.code() == 409 || response.message().equals("Conflict")) { + viewBinding.topAppBar.getMenu().getItem(2).setVisible(false); SnackBar.error( ctx, findViewById(android.R.id.content), getString(R.string.prAlreadyExists)); } else if (response.code() == 404) { + viewBinding.topAppBar.getMenu().getItem(2).setVisible(false); SnackBar.error( ctx, findViewById(android.R.id.content), getString(R.string.apiNotFound)); } else { + viewBinding.topAppBar.getMenu().getItem(2).setVisible(false); SnackBar.error( ctx, findViewById(android.R.id.content), @@ -492,6 +497,7 @@ public class CreatePullRequestActivity extends BaseActivity @Override public void onFailure(@NonNull Call call, @NonNull Throwable t) { + viewBinding.topAppBar.getMenu().getItem(2).setVisible(false); SnackBar.error( ctx, findViewById(android.R.id.content), diff --git a/app/src/main/java/org/mian/gitnex/activities/DeepLinksActivity.java b/app/src/main/java/org/mian/gitnex/activities/DeepLinksActivity.java index 5d0122ad..3eeee5d9 100644 --- a/app/src/main/java/org/mian/gitnex/activities/DeepLinksActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/DeepLinksActivity.java @@ -119,10 +119,9 @@ public class DeepLinksActivity extends BaseActivity { finish(); } else if (Objects.equals( data.getLastPathSegment(), - getAccount().getAccount().getUserName())) { // your user profile - mainIntent.putExtra("launchFragmentByLinkHandler", "profile"); - ctx.startActivity(mainIntent); - finish(); + getAccount().getAccount().getUserName())) { // user profile + new Handler(Looper.getMainLooper()) + .postDelayed(() -> getUserOrOrg(data.getLastPathSegment()), 500); } else if (Objects.equals(data.getLastPathSegment(), "admin")) { mainIntent.putExtra("launchFragmentByLinkHandler", "admin"); ctx.startActivity(mainIntent); @@ -577,7 +576,9 @@ public class DeepLinksActivity extends BaseActivity { public void onResponse( @NonNull Call call, @NonNull Response response) { - if (response.code() == 404) { // org doesn't exist or it's a user user + if (response.code() == 404 + || response.code() + == 504) { // org doesn't exist or it's a user user Log.d("getUserOrOrg-404", String.valueOf(response.code())); getUser(userOrgName); } else if (response.code() == 200) { // org diff --git a/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java b/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java index 60070604..f9685865 100644 --- a/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java @@ -373,8 +373,6 @@ public class IssueDetailActivity extends BaseActivity .setSoftInputMode( WindowManager.LayoutParams .SOFT_INPUT_STATE_ALWAYS_VISIBLE); - viewBinding.commentReply.setSelection( - viewBinding.commentReply.length()); viewBinding.send.setAlpha(buttonAlphaStatEnabled); viewBinding.send.setEnabled(true); } else { diff --git a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java index cf41006e..e326d51b 100644 --- a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java @@ -60,7 +60,6 @@ import org.mian.gitnex.helpers.AlertDialogs; import org.mian.gitnex.helpers.AppDatabaseSettings; import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.ChangeLog; -import org.mian.gitnex.helpers.RoundedTransformation; import org.mian.gitnex.helpers.Toasty; import org.mian.gitnex.structs.BottomSheetListener; import org.mian.gitnex.structs.FragmentRefreshListener; @@ -87,6 +86,7 @@ public class MainActivity extends BaseActivity private BottomSheetListener profileInitListener; private FragmentRefreshListener fragmentRefreshListenerMyIssues; private String username; + public static boolean closeActivity = false; @Override public void onCreate(Bundle savedInstanceState) { @@ -168,11 +168,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); + /*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); + dashboardBetaView.setText(R.string.beta);*/ navigationView .getViewTreeObserver() @@ -250,8 +250,16 @@ public class MainActivity extends BaseActivity String userFullNameNav = getAccount().getFullName(); String userAvatarNav = getAccount().getUserInfo().getAvatarUrl(); - if (!userEmailNav.isEmpty()) { - userEmail.setText(userEmailNav); + if (Boolean.parseBoolean( + AppDatabaseSettings.getSettingsValue( + ctx, + AppDatabaseSettings.APP_USER_HIDE_EMAIL_IN_NAV_KEY))) { + userEmail.setVisibility(View.GONE); + } else { + userEmail.setVisibility(View.VISIBLE); + if (!userEmailNav.isEmpty()) { + userEmail.setText(userEmailNav); + } } if (!userFullNameNav.isEmpty()) { @@ -260,13 +268,10 @@ public class MainActivity extends BaseActivity if (!userAvatarNav.isEmpty()) { - int avatarRadius = AppUtil.getPixelsFromDensity(ctx, 60); - PicassoService.getInstance(ctx) .get() .load(userAvatarNav) .placeholder(R.drawable.loader_animated) - .transform(new RoundedTransformation(avatarRadius, 0)) .resize(160, 160) .centerCrop() .into(userAvatar); @@ -582,6 +587,11 @@ public class MainActivity extends BaseActivity public void onResume() { super.onResume(); + if (closeActivity) { + finishAndRemoveTask(); + closeActivity = false; + } + if (refActivity) { this.recreate(); this.overridePendingTransition(0, 0); diff --git a/app/src/main/java/org/mian/gitnex/activities/ProfileActivity.java b/app/src/main/java/org/mian/gitnex/activities/ProfileActivity.java index 8e69354c..f0ef2cb4 100644 --- a/app/src/main/java/org/mian/gitnex/activities/ProfileActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/ProfileActivity.java @@ -126,7 +126,7 @@ public class ProfileActivity extends BaseActivity implements BottomSheetListener RetrofitClient.getApiInterface(this) .userCurrentCheckFollowing(username) .enqueue( - new Callback() { + new Callback<>() { @Override public void onResponse( diff --git a/app/src/main/java/org/mian/gitnex/activities/SettingsAppearanceActivity.java b/app/src/main/java/org/mian/gitnex/activities/SettingsAppearanceActivity.java index f8f9a619..d038141d 100644 --- a/app/src/main/java/org/mian/gitnex/activities/SettingsAppearanceActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/SettingsAppearanceActivity.java @@ -141,6 +141,53 @@ public class SettingsAppearanceActivity extends BaseActivity { activitySettingsAppearanceBinding.switchCounterBadge.setChecked( !activitySettingsAppearanceBinding.switchCounterBadge.isChecked())); + // hide email and language in user profile screen switcher + activitySettingsAppearanceBinding.switchHideEmailLangInProfile.setChecked( + Boolean.parseBoolean( + AppDatabaseSettings.getSettingsValue( + ctx, + AppDatabaseSettings.APP_USER_PROFILE_HIDE_EMAIL_LANGUAGE_KEY))); + + activitySettingsAppearanceBinding.switchHideEmailLangInProfile.setOnCheckedChangeListener( + (buttonView, isChecked) -> { + AppDatabaseSettings.updateSettingsValue( + ctx, + String.valueOf(isChecked), + AppDatabaseSettings.APP_USER_PROFILE_HIDE_EMAIL_LANGUAGE_KEY); + SnackBar.success( + ctx, + findViewById(android.R.id.content), + getString(R.string.settingsSave)); + }); + activitySettingsAppearanceBinding.hideEmailLangInProfileFrame.setOnClickListener( + v -> + activitySettingsAppearanceBinding.switchHideEmailLangInProfile.setChecked( + !activitySettingsAppearanceBinding.switchHideEmailLangInProfile + .isChecked())); + + // hide email in app navigation drawer switcher + activitySettingsAppearanceBinding.switchHideEmailNavDrawer.setChecked( + Boolean.parseBoolean( + AppDatabaseSettings.getSettingsValue( + ctx, AppDatabaseSettings.APP_USER_HIDE_EMAIL_IN_NAV_KEY))); + + activitySettingsAppearanceBinding.switchHideEmailNavDrawer.setOnCheckedChangeListener( + (buttonView, isChecked) -> { + AppDatabaseSettings.updateSettingsValue( + ctx, + String.valueOf(isChecked), + AppDatabaseSettings.APP_USER_HIDE_EMAIL_IN_NAV_KEY); + SnackBar.success( + ctx, + findViewById(android.R.id.content), + getString(R.string.settingsSave)); + }); + activitySettingsAppearanceBinding.hideEmailNavDrawerFrame.setOnClickListener( + v -> + activitySettingsAppearanceBinding.switchHideEmailNavDrawer.setChecked( + !activitySettingsAppearanceBinding.switchHideEmailNavDrawer + .isChecked())); + // show labels in lists(issues, pr) - default is color dots activitySettingsAppearanceBinding.switchLabelsInListBadge.setChecked( Boolean.parseBoolean( diff --git a/app/src/main/java/org/mian/gitnex/adapters/AdminCronTasksAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/AdminCronTasksAdapter.java index cfef85c4..d17eda2f 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/AdminCronTasksAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/AdminCronTasksAdapter.java @@ -29,7 +29,7 @@ public class AdminCronTasksAdapter private final List tasksList; - static class CronTasksViewHolder extends RecyclerView.ViewHolder { + public static class CronTasksViewHolder extends RecyclerView.ViewHolder { private Cron cronTasks; diff --git a/app/src/main/java/org/mian/gitnex/adapters/AdminGetUsersAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/AdminGetUsersAdapter.java index a4c2ff77..a13ebc64 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/AdminGetUsersAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/AdminGetUsersAdapter.java @@ -181,7 +181,7 @@ public class AdminGetUsersAdapter extends RecyclerView.Adapter data); } - static class AssigneesViewHolder extends RecyclerView.ViewHolder { + public static class AssigneesViewHolder extends RecyclerView.ViewHolder { private final CheckBox assigneesSelection; private final TextView assigneesName; diff --git a/app/src/main/java/org/mian/gitnex/adapters/CollaboratorSearchAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/CollaboratorSearchAdapter.java index a0f1d91a..5f7b205f 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/CollaboratorSearchAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/CollaboratorSearchAdapter.java @@ -64,7 +64,7 @@ public class CollaboratorSearchAdapter int imgRadius = AppUtil.getPixelsFromDensity(context, 60); holder.userInfo = currentItem; - if (!currentItem.getFullName().equals("")) { + if (!currentItem.getFullName().isEmpty()) { holder.userFullName.setText(Html.fromHtml(currentItem.getFullName())); } else { @@ -77,7 +77,7 @@ public class CollaboratorSearchAdapter holder.userName.setText( context.getResources().getString(R.string.usernameWithAt, currentItem.getLogin())); - if (!currentItem.getAvatarUrl().equals("")) { + if (!currentItem.getAvatarUrl().isEmpty()) { PicassoService.getInstance(context) .get() .load(currentItem.getAvatarUrl()) @@ -140,7 +140,7 @@ public class CollaboratorSearchAdapter return usersSearchList.size(); } - class CollaboratorSearchViewHolder extends RecyclerView.ViewHolder { + public class CollaboratorSearchViewHolder extends RecyclerView.ViewHolder { private final ImageView userAvatar; private final TextView userFullName; diff --git a/app/src/main/java/org/mian/gitnex/adapters/CollaboratorsAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/CollaboratorsAdapter.java index d39349b8..20577147 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/CollaboratorsAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/CollaboratorsAdapter.java @@ -83,7 +83,7 @@ public class CollaboratorsAdapter extends BaseAdapter { viewHolder.userLoginId = currentItem.getLogin(); - if (!currentItem.getFullName().equals("")) { + if (!currentItem.getFullName().isEmpty()) { viewHolder.collaboratorName.setText(Html.fromHtml(currentItem.getFullName())); viewHolder.userName.setText( diff --git a/app/src/main/java/org/mian/gitnex/adapters/CommitStatusesAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/CommitStatusesAdapter.java index fedd2910..c3e22a2d 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/CommitStatusesAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/CommitStatusesAdapter.java @@ -24,7 +24,7 @@ public class CommitStatusesAdapter private final List statuses; - static class CommitStatusesViewHolder extends RecyclerView.ViewHolder { + public static class CommitStatusesViewHolder extends RecyclerView.ViewHolder { private CommitStatus status; @@ -44,7 +44,7 @@ public class CommitStatusesAdapter } private void openUrl() { - if (status.getTargetUrl() != null && !status.getTargetUrl().equals("")) { + if (status.getTargetUrl() != null && !status.getTargetUrl().isEmpty()) { AppUtil.openUrlInBrowser(itemView.getContext(), status.getTargetUrl()); } else { Toasty.info( diff --git a/app/src/main/java/org/mian/gitnex/adapters/DiffAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/DiffAdapter.java index e38b6c0d..76e7ccd7 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/DiffAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/DiffAdapter.java @@ -147,7 +147,7 @@ public class DiffAdapter extends BaseAdapter { private int getLineColor(String line) { - if (line.length() == 0) { + if (line.isEmpty()) { return COLOR_NORMAL; } diff --git a/app/src/main/java/org/mian/gitnex/adapters/DraftsAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/DraftsAdapter.java index ebb8534d..4d1e4752 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/DraftsAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/DraftsAdapter.java @@ -113,7 +113,7 @@ public class DraftsAdapter extends RecyclerView.Adapter list) { currentLabelsIds = list; @@ -116,7 +118,7 @@ public class LabelsListAdapter extends RecyclerView.Adapter data); } - static class LabelsViewHolder extends RecyclerView.ViewHolder { + public static class LabelsViewHolder extends RecyclerView.ViewHolder { private final CheckBox labelSelection; private final TextView labelText; diff --git a/app/src/main/java/org/mian/gitnex/adapters/MostVisitedReposAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/MostVisitedReposAdapter.java index 7448c1bd..ad930d6a 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/MostVisitedReposAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/MostVisitedReposAdapter.java @@ -34,7 +34,7 @@ public class MostVisitedReposAdapter private List mostVisitedReposList; private final Context ctx; - class MostVisitedViewHolder extends RecyclerView.ViewHolder { + public class MostVisitedViewHolder extends RecyclerView.ViewHolder { private Repository repository; diff --git a/app/src/main/java/org/mian/gitnex/adapters/OrganizationAddUserToTeamMemberAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/OrganizationAddUserToTeamMemberAdapter.java index d12e56f6..9e3ab0b9 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/OrganizationAddUserToTeamMemberAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/OrganizationAddUserToTeamMemberAdapter.java @@ -63,7 +63,7 @@ public class OrganizationAddUserToTeamMemberAdapter holder.userInfo = currentItem; int imgRadius = AppUtil.getPixelsFromDensity(context, 3); - if (!currentItem.getFullName().equals("")) { + if (!currentItem.getFullName().isEmpty()) { holder.userFullName.setText(Html.fromHtml(currentItem.getFullName())); } else { @@ -76,7 +76,7 @@ public class OrganizationAddUserToTeamMemberAdapter holder.userName.setText( context.getResources().getString(R.string.usernameWithAt, currentItem.getLogin())); - if (!currentItem.getAvatarUrl().equals("")) { + if (!currentItem.getAvatarUrl().isEmpty()) { PicassoService.getInstance(context) .get() .load(currentItem.getAvatarUrl()) @@ -142,7 +142,7 @@ public class OrganizationAddUserToTeamMemberAdapter return usersSearchList.size(); } - class UserSearchViewHolder extends RecyclerView.ViewHolder { + public class UserSearchViewHolder extends RecyclerView.ViewHolder { private final ImageView userAvatar; private final TextView userFullName; diff --git a/app/src/main/java/org/mian/gitnex/adapters/OrganizationTeamMembersPreviewAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/OrganizationTeamMembersPreviewAdapter.java index dcbeee4a..fab0b979 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/OrganizationTeamMembersPreviewAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/OrganizationTeamMembersPreviewAdapter.java @@ -55,7 +55,7 @@ public class OrganizationTeamMembersPreviewAdapter return userData.size(); } - static class ViewHolder extends RecyclerView.ViewHolder { + public static class ViewHolder extends RecyclerView.ViewHolder { private final ImageView avatar; diff --git a/app/src/main/java/org/mian/gitnex/adapters/OrganizationTeamRepositoriesAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/OrganizationTeamRepositoriesAdapter.java index 71a8ea74..1c693fa6 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/OrganizationTeamRepositoriesAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/OrganizationTeamRepositoriesAdapter.java @@ -50,7 +50,7 @@ public class OrganizationTeamRepositoriesAdapter reposArr = new ArrayList<>(); } - class TeamReposViewHolder extends RecyclerView.ViewHolder { + public class TeamReposViewHolder extends RecyclerView.ViewHolder { private Repository repoInfo; @@ -75,7 +75,7 @@ public class OrganizationTeamRepositoriesAdapter new Handler(Looper.getMainLooper()) .postDelayed( () -> { - if (reposArr.size() > 0) { + if (!reposArr.isEmpty()) { for (int i = 0; i < reposArr.size(); i++) { if (!reposArr.get(i).getName().equals(repoInfo.getName())) { addRepoButtonAdd.setVisibility(View.VISIBLE); @@ -144,7 +144,7 @@ public class OrganizationTeamRepositoriesAdapter .getColor(currentItem.getName()), 14); - if (currentItem.getAvatarUrl() != null && !currentItem.getAvatarUrl().equals("")) { + if (currentItem.getAvatarUrl() != null && !currentItem.getAvatarUrl().isEmpty()) { PicassoService.getInstance(context) .get() .load(currentItem.getAvatarUrl()) diff --git a/app/src/main/java/org/mian/gitnex/adapters/OrganizationTeamsAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/OrganizationTeamsAdapter.java index 1e6f143f..7a31b0c2 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/OrganizationTeamsAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/OrganizationTeamsAdapter.java @@ -1,5 +1,6 @@ package org.mian.gitnex.adapters; +import android.annotation.SuppressLint; import android.content.Context; import android.content.Intent; import android.view.LayoutInflater; @@ -38,7 +39,7 @@ public class OrganizationTeamsAdapter private final OrganizationPermissions permissions; private final String orgName; - static class OrgTeamsViewHolder extends RecyclerView.ViewHolder { + public static class OrgTeamsViewHolder extends RecyclerView.ViewHolder { private Team team; @@ -101,6 +102,7 @@ public class OrganizationTeamsAdapter return new OrganizationTeamsAdapter.OrgTeamsViewHolder(v); } + @SuppressLint("NotifyDataSetChanged") @Override public void onBindViewHolder( @NonNull OrganizationTeamsAdapter.OrgTeamsViewHolder holder, int position) { @@ -126,7 +128,7 @@ public class OrganizationTeamsAdapter @NonNull Response> response) { if (response.isSuccessful() && response.body() != null - && response.body().size() > 0) { + && !response.body().isEmpty()) { holder.membersPreviewFrame.setVisibility(View.VISIBLE); holder.userInfos.addAll( @@ -189,6 +191,7 @@ public class OrganizationTeamsAdapter return results; } + @SuppressLint("NotifyDataSetChanged") @Override protected void publishResults(CharSequence constraint, FilterResults results) { teamList.clear(); diff --git a/app/src/main/java/org/mian/gitnex/adapters/OrganizationsListAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/OrganizationsListAdapter.java index dff86717..128a4308 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/OrganizationsListAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/OrganizationsListAdapter.java @@ -182,7 +182,7 @@ public class OrganizationsListAdapter extends RecyclerView.Adapter this.userInfo = userInfo; int imgRadius = AppUtil.getPixelsFromDensity(context, 3); - if (!userInfo.getFullName().equals("")) { + if (!userInfo.getFullName().isEmpty()) { userFullName.setText(Html.fromHtml(userInfo.getFullName())); userName.setText( context.getResources() diff --git a/app/src/main/java/org/mian/gitnex/fragments/profile/DetailFragment.java b/app/src/main/java/org/mian/gitnex/fragments/profile/DetailFragment.java index 245f053c..f2898d56 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/profile/DetailFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/profile/DetailFragment.java @@ -35,6 +35,7 @@ import org.mian.gitnex.databinding.CustomEditAvatarDialogBinding; import org.mian.gitnex.databinding.CustomEditProfileBinding; import org.mian.gitnex.databinding.FragmentProfileDetailBinding; import org.mian.gitnex.helpers.AlertDialogs; +import org.mian.gitnex.helpers.AppDatabaseSettings; import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.ClickListener; import org.mian.gitnex.helpers.Markdown; @@ -62,6 +63,7 @@ public class DetailFragment extends Fragment { private int imgRadius; private static Uri avatarUri = null; public static boolean refProfile = false; + public boolean itsMe = false; public DetailFragment() {} @@ -109,6 +111,7 @@ public class DetailFragment extends Fragment { if (username.equals(((BaseActivity) context).getAccount().getAccount().getUserName())) { binding.metaProfile.setVisibility(View.VISIBLE); + itsMe = true; } else { binding.metaProfile.setVisibility(View.GONE); } @@ -366,7 +369,19 @@ public class DetailFragment extends Fragment { getString( R.string.usernameWithAt, response.body().getLogin())); - binding.userEmail.setText(email); + + if (Boolean.parseBoolean( + AppDatabaseSettings.getSettingsValue( + context, + AppDatabaseSettings + .APP_USER_PROFILE_HIDE_EMAIL_LANGUAGE_KEY)) + && itsMe) { + binding.userEmail.setText( + getString(R.string.strPrivate).toUpperCase()); + binding.userEmail.setAlpha(.4F); + } else { + binding.userEmail.setText(email); + } binding.userFollowersCount.setText( String.format( @@ -389,13 +404,25 @@ public class DetailFragment extends Fragment { String[] userLanguageCodes = response.body().getLanguage().split("-"); - if (userLanguageCodes.length >= 2) { - Locale locale = - new Locale( - userLanguageCodes[0], userLanguageCodes[1]); - binding.userLang.setText(locale.getDisplayLanguage()); + if (Boolean.parseBoolean( + AppDatabaseSettings.getSettingsValue( + context, + AppDatabaseSettings + .APP_USER_PROFILE_HIDE_EMAIL_LANGUAGE_KEY)) + && itsMe) { + binding.userLang.setText( + getString(R.string.strPrivate).toUpperCase()); + binding.userLang.setAlpha(.4F); } else { - binding.userLang.setText(locale.getDisplayLanguage()); + if (userLanguageCodes.length >= 2) { + Locale locale = + new Locale( + userLanguageCodes[0], + userLanguageCodes[1]); + binding.userLang.setText(locale.getDisplayLanguage()); + } else { + binding.userLang.setText(locale.getDisplayLanguage()); + } } PicassoService.getInstance(context) @@ -415,6 +442,30 @@ public class DetailFragment extends Fragment { TimeHelper.customDateFormatForToastDateFormat( response.body().getCreated()), context)); + if (!response.body().getWebsite().isEmpty()) { + binding.website.setText(response.body().getWebsite()); + } else { + binding.website.setText(R.string.noDataWebsite); + binding.website.setAlpha(.4F); + } + + if (!response.body().getLocation().isEmpty()) { + binding.location.setText(response.body().getLocation()); + } else { + binding.location.setText(R.string.noDataLocation); + binding.location.setAlpha(.4F); + } + + if (!response.body().getDescription().isEmpty()) { + Markdown.render( + context, + response.body().getDescription(), + binding.bio); + } else { + binding.bio.setText(R.string.noDataBio); + binding.bio.setAlpha(.4F); + } + break; case 401: diff --git a/app/src/main/java/org/mian/gitnex/helpers/AppDatabaseSettings.java b/app/src/main/java/org/mian/gitnex/helpers/AppDatabaseSettings.java index 43c4a0f8..6503aac7 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/AppDatabaseSettings.java +++ b/app/src/main/java/org/mian/gitnex/helpers/AppDatabaseSettings.java @@ -62,6 +62,11 @@ public class AppDatabaseSettings { public static String APP_IMAGES_CACHE_DEFAULT = "1"; public static String APP_IMAGES_CACHE_SIZE_KEY = "app_images_cache_size"; public static String APP_IMAGES_CACHE_SIZE_DEFAULT = "100 MB"; + public static String APP_USER_PROFILE_HIDE_EMAIL_LANGUAGE_KEY = + "app_user_profile_hide_email_lang"; + public static String APP_USER_PROFILE_HIDE_EMAIL_LANGUAGE_DEFAULT = "false"; + public static String APP_USER_HIDE_EMAIL_IN_NAV_KEY = "app_user_hide_email_nav"; + public static String APP_USER_HIDE_EMAIL_IN_NAV_DEFAULT = "false"; public static void initDefaultSettings(Context ctx) { @@ -190,6 +195,18 @@ public class AppDatabaseSettings { APP_IMAGES_CACHE_SIZE_DEFAULT, APP_IMAGES_CACHE_SIZE_DEFAULT); } + if (appSettingsApi.fetchSettingCountByKey(APP_USER_PROFILE_HIDE_EMAIL_LANGUAGE_KEY) == 0) { + appSettingsApi.insertNewSetting( + APP_USER_PROFILE_HIDE_EMAIL_LANGUAGE_KEY, + APP_USER_PROFILE_HIDE_EMAIL_LANGUAGE_DEFAULT, + APP_USER_PROFILE_HIDE_EMAIL_LANGUAGE_DEFAULT); + } + if (appSettingsApi.fetchSettingCountByKey(APP_USER_HIDE_EMAIL_IN_NAV_KEY) == 0) { + appSettingsApi.insertNewSetting( + APP_USER_HIDE_EMAIL_IN_NAV_KEY, + APP_USER_HIDE_EMAIL_IN_NAV_DEFAULT, + APP_USER_HIDE_EMAIL_IN_NAV_DEFAULT); + } if (appSettingsApi.fetchSettingCountByKey("prefsMigration") == 0) { appSettingsApi.insertNewSetting("prefsMigration", "true", "true"); diff --git a/app/src/main/java/org/mian/gitnex/helpers/SnackBar.java b/app/src/main/java/org/mian/gitnex/helpers/SnackBar.java index 162a813a..bf36330e 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/SnackBar.java +++ b/app/src/main/java/org/mian/gitnex/helpers/SnackBar.java @@ -34,7 +34,7 @@ public class SnackBar { View sbView = snackBar.getView(); TextView textView = sbView.findViewById(R.id.snackbar_text); snackBar.setBackgroundTint(context.getColor(R.color.cardBackground)); - textView.setTextColor(context.getColor(R.color.warningColor)); + textView.setTextColor(context.getColor(R.color.colorWhite)); snackBar.show(); } @@ -43,7 +43,7 @@ public class SnackBar { View sbView = snackBar.getView(); TextView textView = sbView.findViewById(R.id.snackbar_text); snackBar.setBackgroundTint(context.getColor(R.color.cardBackground)); - textView.setTextColor(context.getColor(R.color.darkRed)); + textView.setTextColor(context.getColor(R.color.colorWhite)); snackBar.show(); } } diff --git a/app/src/main/java/org/mian/gitnex/notifications/Notifications.java b/app/src/main/java/org/mian/gitnex/notifications/Notifications.java index 2a7169e0..29a6dc40 100644 --- a/app/src/main/java/org/mian/gitnex/notifications/Notifications.java +++ b/app/src/main/java/org/mian/gitnex/notifications/Notifications.java @@ -106,9 +106,17 @@ public class Notifications { MaterialAlertDialogBuilder materialAlertDialogBuilder = new MaterialAlertDialogBuilder(context) .setTitle(R.string.pageTitleNotifications) + .setCancelable(false) .setMessage(context.getString(R.string.openAppSettings)) .setNeutralButton( - R.string.cancelButton, (dialog, which) -> dialog.dismiss()) + R.string.cancelButton, + (dialog, which) -> { + dialog.dismiss(); + AppDatabaseSettings.updateSettingsValue( + context, + "false", + AppDatabaseSettings.APP_NOTIFICATIONS_KEY); + }) .setPositiveButton( R.string.isOpen, (dialog, which) -> { diff --git a/app/src/main/res/drawable/ic_file_description.xml b/app/src/main/res/drawable/ic_file_description.xml new file mode 100644 index 00000000..83140da4 --- /dev/null +++ b/app/src/main/res/drawable/ic_file_description.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/layout/activity_issue_detail.xml b/app/src/main/res/layout/activity_issue_detail.xml index a758fa50..cc7eda83 100644 --- a/app/src/main/res/layout/activity_issue_detail.xml +++ b/app/src/main/res/layout/activity_issue_detail.xml @@ -90,7 +90,7 @@ android:paddingStart="@dimen/dimen8dp" android:paddingTop="@dimen/dimen2dp" android:paddingEnd="@dimen/dimen8dp" - android:paddingBottom="@dimen/dimen104dp"> + android:paddingBottom="@dimen/dimen120dp"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_profile_detail.xml b/app/src/main/res/layout/fragment_profile_detail.xml index ad3ed811..2eaea4f4 100644 --- a/app/src/main/res/layout/fragment_profile_detail.xml +++ b/app/src/main/res/layout/fragment_profile_detail.xml @@ -51,12 +51,12 @@ tools:ignore="UseCompoundDrawables"> + app:cardCornerRadius="@dimen/dimen16dp"> @@ -203,6 +203,7 @@ android:layout_height="wrap_content" android:alpha="0.9" android:textColor="?attr/primaryTextColor" + android:autoLink="email" android:textSize="@dimen/dimen14sp" /> @@ -214,76 +215,191 @@ android:layout_marginTop="@dimen/dimen32dp" android:orientation="horizontal"> - - - - - - - - - - + + android:layout_marginStart="@dimen/dimen16dp" + android:gravity="center_vertical" + android:orientation="vertical"> - + android:text="@string/websiteText" + android:textColor="?attr/primaryTextColor" + android:textSize="@dimen/dimen16sp" /> - - - - - - - + android:alpha="0.9" + android:textColor="?attr/primaryTextColor" + android:autoLink="web" + android:textSize="@dimen/dimen14sp" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/nav_header.xml b/app/src/main/res/layout/nav_header.xml index 5ed9d1ec..ffce9148 100644 --- a/app/src/main/res/layout/nav_header.xml +++ b/app/src/main/res/layout/nav_header.xml @@ -30,7 +30,7 @@ style="?attr/materialCardViewElevatedStyle" android:layout_width="@dimen/dimen60dp" android:layout_height="@dimen/dimen60dp" - app:cardCornerRadius="@dimen/dimen32dp" + app:cardCornerRadius="@dimen/dimen16dp" app:cardElevation="@dimen/dimen0dp"> + android:title="@string/navMyIssues"/> An error occurred while restoring from the backup file. Restore from Backup You are about to restore from a GitNex-generated backup file. This will restore user accounts, settings, notes, and data related to repositories.\n\nClick Restore to start the process. + Set Email and Language as Private + You can set your email and language to private to hide them on your profile screen + Hide Email in Drawer + You can enable this option to hide your email in the app navigation drawer No more data available @@ -452,6 +456,7 @@ Watchers No website found No description found + No bio found No location found Star Watcher @@ -550,6 +555,7 @@ License Title Description + Bio Explore users diff --git a/app/src/main/res/xml/changelog.xml b/app/src/main/res/xml/changelog.xml index 36279af1..48e13ff5 100644 --- a/app/src/main/res/xml/changelog.xml +++ b/app/src/main/res/xml/changelog.xml @@ -1,15 +1,8 @@ - - New: Update user avatar - New: New popup screen to add email - New: Insert note to issue/pr/release description - New: New reply/comment UI - Improvement: Hide app contents when biometric is enabled - Bugfix: Fix back button closing the app - Bugfix: Fix profile fields data - Bugfix: Fix text color highlighting + + Under development diff --git a/build.gradle b/build.gradle index 445001b6..0257fcbd 100644 --- a/build.gradle +++ b/build.gradle @@ -20,5 +20,5 @@ allprojects { } tasks.register('clean', Delete) { - delete rootProject.buildDir + delete rootProject.layout.buildDirectory }