From 238a3a16969695983276378b48c5bee310fa36a3 Mon Sep 17 00:00:00 2001 From: M M Arif Date: Wed, 20 Sep 2023 15:30:21 +0000 Subject: [PATCH] UI enhacements (#1300) Closes #1289 Closes #1277 Closes #1286 Closes #1230 Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/1300 Co-authored-by: M M Arif Co-committed-by: M M Arif --- app/src/main/AndroidManifest.xml | 3 +- .../gitnex/actions/RepositoryActions.java | 4 +- ...java => AccountSettingsEmailActivity.java} | 43 +- .../mian/gitnex/activities/BaseActivity.java | 3 + .../mian/gitnex/activities/MainActivity.java | 73 ++- .../OrganizationDetailActivity.java | 151 ++--- .../OrganizationTeamInfoActivity.java | 6 +- .../gitnex/activities/ProfileActivity.java | 6 +- .../gitnex/activities/RepoDetailActivity.java | 4 + .../SettingsAppearanceActivity.java | 73 ++- .../activities/SettingsGeneralActivity.java | 6 +- ...java => AccountSettingsEmailsAdapter.java} | 14 +- .../mian/gitnex/adapters/SSHKeysAdapter.java | 59 ++ .../AccountSettingsEmailsFragment.java | 104 ++++ .../fragments/AccountSettingsFragment.java | 138 +++++ .../gitnex/fragments/ExploreFragment.java | 4 + .../fragments/MyProfileEmailsFragment.java | 102 ---- .../fragments/MyProfileFollowersFragment.java | 155 ------ .../fragments/MyProfileFollowingFragment.java | 157 ------ .../gitnex/fragments/MyProfileFragment.java | 177 ------ .../fragments/PullRequestChangesFragment.java | 8 + .../gitnex/fragments/SSHKeysFragment.java | 77 +++ .../WatchedRepositoriesFragment.java | 176 ++++++ .../fragments/profile/DetailFragment.java | 134 +++++ .../helpers/ViewPager2Transformers.java | 95 ++++ ...va => AccountSettingsEmailsViewModel.java} | 2 +- .../AccountSettingsSSHKeysViewModel.java | 63 +++ .../viewmodels/RepositoriesViewModel.java | 5 + .../main/res/drawable/app_logo_monochrome.xml | 16 + .../main/res/drawable/ic_account_settings.xml | 34 ++ app/src/main/res/drawable/ic_add.xml | 23 +- app/src/main/res/drawable/ic_done.xml | 24 +- app/src/main/res/drawable/ic_language.xml | 31 +- app/src/main/res/drawable/ic_markdown.xml | 30 +- app/src/main/res/drawable/ic_pin.xml | 27 +- app/src/main/res/drawable/ic_remove.xml | 16 +- app/src/main/res/drawable/ic_reply.xml | 23 +- app/src/main/res/drawable/ic_star.xml | 1 - .../main/res/drawable/ic_star_unfilled.xml | 1 - app/src/main/res/drawable/ic_unwatch.xml | 50 +- .../main/res/drawable/ic_verified_user.xml | 24 +- app/src/main/res/drawable/ic_warning.xml | 30 +- app/src/main/res/drawable/ic_watchers.xml | 24 +- .../shape_bottom_sheet_top_corners.xml | 6 +- ...ml => activity_account_settings_email.xml} | 2 +- .../main/res/layout/activity_org_detail.xml | 42 +- .../res/layout/activity_org_team_info.xml | 1 + app/src/main/res/layout/activity_profile.xml | 4 +- .../main/res/layout/activity_repo_detail.xml | 4 +- .../layout/activity_settings_appearance.xml | 33 ++ .../res/layout/activity_settings_general.xml | 2 +- .../res/layout/fragment_account_settings.xml | 40 ++ ...l => fragment_account_settings_emails.xml} | 0 .../fragment_account_settings_ssh_keys.xml | 47 ++ app/src/main/res/layout/fragment_explore.xml | 4 +- .../main/res/layout/fragment_pr_changes.xml | 5 +- app/src/main/res/layout/fragment_profile.xml | 273 --------- .../res/layout/fragment_profile_detail.xml | 524 ++++++++++-------- .../main/res/layout/fragment_repo_info.xml | 2 +- ...s.xml => list_account_settings_emails.xml} | 2 +- .../layout/list_account_settings_ssh_keys.xml | 50 ++ app/src/main/res/menu/drawer_menu.xml | 8 + .../main/res/mipmap-anydpi-v26/app_logo.xml | 3 +- app/src/main/res/values-v23/themes.xml | 46 ++ app/src/main/res/values/colors.xml | 10 + app/src/main/res/values/settings.xml | 13 +- app/src/main/res/values/strings.xml | 21 +- app/src/main/res/values/themes.xml | 68 +++ 68 files changed, 2017 insertions(+), 1389 deletions(-) rename app/src/main/java/org/mian/gitnex/activities/{MyProfileEmailActivity.java => AccountSettingsEmailActivity.java} (74%) rename app/src/main/java/org/mian/gitnex/adapters/{MyProfileEmailsAdapter.java => AccountSettingsEmailsAdapter.java} (80%) create mode 100644 app/src/main/java/org/mian/gitnex/adapters/SSHKeysAdapter.java create mode 100644 app/src/main/java/org/mian/gitnex/fragments/AccountSettingsEmailsFragment.java create mode 100644 app/src/main/java/org/mian/gitnex/fragments/AccountSettingsFragment.java delete mode 100644 app/src/main/java/org/mian/gitnex/fragments/MyProfileEmailsFragment.java delete mode 100644 app/src/main/java/org/mian/gitnex/fragments/MyProfileFollowersFragment.java delete mode 100644 app/src/main/java/org/mian/gitnex/fragments/MyProfileFollowingFragment.java delete mode 100644 app/src/main/java/org/mian/gitnex/fragments/MyProfileFragment.java create mode 100644 app/src/main/java/org/mian/gitnex/fragments/SSHKeysFragment.java create mode 100644 app/src/main/java/org/mian/gitnex/fragments/WatchedRepositoriesFragment.java create mode 100644 app/src/main/java/org/mian/gitnex/helpers/ViewPager2Transformers.java rename app/src/main/java/org/mian/gitnex/viewmodels/{ProfileEmailsViewModel.java => AccountSettingsEmailsViewModel.java} (95%) create mode 100644 app/src/main/java/org/mian/gitnex/viewmodels/AccountSettingsSSHKeysViewModel.java create mode 100644 app/src/main/res/drawable/app_logo_monochrome.xml create mode 100644 app/src/main/res/drawable/ic_account_settings.xml rename app/src/main/res/layout/{activity_profile_email.xml => activity_account_settings_email.xml} (98%) create mode 100644 app/src/main/res/layout/fragment_account_settings.xml rename app/src/main/res/layout/{fragment_profile_emails.xml => fragment_account_settings_emails.xml} (100%) create mode 100644 app/src/main/res/layout/fragment_account_settings_ssh_keys.xml delete mode 100644 app/src/main/res/layout/fragment_profile.xml rename app/src/main/res/layout/{list_profile_emails.xml => list_account_settings_emails.xml} (97%) create mode 100644 app/src/main/res/layout/list_account_settings_ssh_keys.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8ff9dba3..27eafece 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,7 +14,6 @@ android:label="@string/appName" android:networkSecurityConfig="@xml/network_security_config" android:resizeableActivity="true" - android:roundIcon="@mipmap/app_logo_round" android:supportsRtl="true" android:exported="true" tools:targetApi="n"> @@ -53,7 +52,7 @@ android:name=".activities.CreateNewUserActivity" android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|keyboard|keyboardHidden|navigation"/> call = RetrofitClient.getApiInterface(context) - .userCurrentDeleteStar(repository.getOwner(), repository.getName()); + .userCurrentDeleteSubscription(repository.getOwner(), repository.getName()); call.enqueue( new Callback<>() { @@ -166,6 +167,7 @@ public class RepositoryActions { if (response.code() == 204) { + MainActivity.reloadRepos = true; Toasty.success( context, context.getString(R.string.unWatchRepositorySuccess)); } else if (response.code() == 401) { diff --git a/app/src/main/java/org/mian/gitnex/activities/MyProfileEmailActivity.java b/app/src/main/java/org/mian/gitnex/activities/AccountSettingsEmailActivity.java similarity index 74% rename from app/src/main/java/org/mian/gitnex/activities/MyProfileEmailActivity.java rename to app/src/main/java/org/mian/gitnex/activities/AccountSettingsEmailActivity.java index 9815ae9e..94d0c725 100644 --- a/app/src/main/java/org/mian/gitnex/activities/MyProfileEmailActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/AccountSettingsEmailActivity.java @@ -6,19 +6,17 @@ import android.util.Log; import android.util.Patterns; import android.view.View; import android.view.inputmethod.InputMethodManager; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ImageView; import androidx.annotation.NonNull; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import org.gitnex.tea4j.v2.models.CreateEmailOption; import org.gitnex.tea4j.v2.models.Email; import org.mian.gitnex.R; import org.mian.gitnex.clients.RetrofitClient; -import org.mian.gitnex.databinding.ActivityProfileEmailBinding; -import org.mian.gitnex.fragments.MyProfileEmailsFragment; +import org.mian.gitnex.databinding.ActivityAccountSettingsEmailBinding; +import org.mian.gitnex.fragments.AccountSettingsEmailsFragment; import org.mian.gitnex.helpers.AlertDialogs; import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.Toasty; @@ -28,44 +26,40 @@ import retrofit2.Callback; /** * @author M M Arif */ -public class MyProfileEmailActivity extends BaseActivity { +public class AccountSettingsEmailActivity extends BaseActivity { private View.OnClickListener onClickListener; - private EditText userEmail; - private Button addEmailButton; private final View.OnClickListener addEmailListener = v -> processAddNewEmail(); + private ActivityAccountSettingsEmailBinding activityAccountSettingsEmailBinding; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - ActivityProfileEmailBinding activityProfileEmailBinding = - ActivityProfileEmailBinding.inflate(getLayoutInflater()); - setContentView(activityProfileEmailBinding.getRoot()); + activityAccountSettingsEmailBinding = + ActivityAccountSettingsEmailBinding.inflate(getLayoutInflater()); + setContentView(activityAccountSettingsEmailBinding.getRoot()); boolean connToInternet = AppUtil.hasNetworkConnection(appCtx); InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - ImageView closeActivity = activityProfileEmailBinding.close; - userEmail = activityProfileEmailBinding.userEmail; - addEmailButton = activityProfileEmailBinding.addEmailButton; - - userEmail.requestFocus(); + activityAccountSettingsEmailBinding.userEmail.requestFocus(); assert imm != null; - imm.showSoftInput(userEmail, InputMethodManager.SHOW_IMPLICIT); + imm.showSoftInput( + activityAccountSettingsEmailBinding.userEmail, InputMethodManager.SHOW_IMPLICIT); initCloseListener(); - closeActivity.setOnClickListener(onClickListener); + activityAccountSettingsEmailBinding.close.setOnClickListener(onClickListener); if (!connToInternet) { disableProcessButton(); } else { - addEmailButton.setOnClickListener(addEmailListener); + activityAccountSettingsEmailBinding.addEmailButton.setOnClickListener(addEmailListener); } } @@ -73,7 +67,10 @@ public class MyProfileEmailActivity extends BaseActivity { boolean connToInternet = AppUtil.hasNetworkConnection(appCtx); - String newUserEmail = userEmail.getText().toString().trim(); + String newUserEmail = + Objects.requireNonNull(activityAccountSettingsEmailBinding.userEmail.getText()) + .toString() + .trim(); if (!connToInternet) { @@ -115,7 +112,7 @@ public class MyProfileEmailActivity extends BaseActivity { if (response.code() == 201) { Toasty.success(ctx, getString(R.string.emailAddedText)); - MyProfileEmailsFragment.refreshEmails = true; + AccountSettingsEmailsFragment.refreshEmails = true; enableProcessButton(); finish(); } else if (response.code() == 401) { @@ -157,11 +154,11 @@ public class MyProfileEmailActivity extends BaseActivity { private void disableProcessButton() { - addEmailButton.setEnabled(false); + activityAccountSettingsEmailBinding.addEmailButton.setEnabled(false); } private void enableProcessButton() { - addEmailButton.setEnabled(true); + activityAccountSettingsEmailBinding.addEmailButton.setEnabled(true); } } 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 8f1c8b29..26c2402e 100644 --- a/app/src/main/java/org/mian/gitnex/activities/BaseActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/BaseActivity.java @@ -79,6 +79,9 @@ public abstract class BaseActivity extends AppCompatActivity { case 8: setTheme(R.style.AppThemeDynamicSystem); break; + case 9: + setTheme(R.style.AppThemeCodebergDark); + break; default: setTheme(R.style.AppThemeSystem); break; 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 b5464918..ef90eabf 100644 --- a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java @@ -36,6 +36,7 @@ import org.mian.gitnex.database.api.BaseApi; import org.mian.gitnex.database.api.UserAccountsApi; import org.mian.gitnex.database.models.UserAccount; import org.mian.gitnex.databinding.ActivityMainBinding; +import org.mian.gitnex.fragments.AccountSettingsFragment; import org.mian.gitnex.fragments.AdministrationFragment; import org.mian.gitnex.fragments.BottomSheetDraftsFragment; import org.mian.gitnex.fragments.BottomSheetMyIssuesFilterFragment; @@ -44,7 +45,6 @@ import org.mian.gitnex.fragments.DraftsFragment; import org.mian.gitnex.fragments.ExploreFragment; import org.mian.gitnex.fragments.MostVisitedReposFragment; import org.mian.gitnex.fragments.MyIssuesFragment; -import org.mian.gitnex.fragments.MyProfileFragment; import org.mian.gitnex.fragments.MyRepositoriesFragment; import org.mian.gitnex.fragments.NotesFragment; import org.mian.gitnex.fragments.NotificationsFragment; @@ -52,6 +52,7 @@ import org.mian.gitnex.fragments.OrganizationsFragment; import org.mian.gitnex.fragments.RepositoriesFragment; import org.mian.gitnex.fragments.SettingsFragment; import org.mian.gitnex.fragments.StarredRepositoriesFragment; +import org.mian.gitnex.fragments.WatchedRepositoriesFragment; import org.mian.gitnex.helpers.AlertDialogs; import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.ChangeLog; @@ -71,20 +72,17 @@ public class MainActivity extends BaseActivity public static boolean refActivity = false; public static boolean reloadRepos = false; - private DrawerLayout drawer; private TextView toolbarTitle; private Typeface myTypeface; - private boolean noConnection = false; - private View hView; private NavigationView navigationView; private MenuItem navNotifications; private TextView notificationCounter; - private BottomSheetListener profileInitListener; private FragmentRefreshListener fragmentRefreshListenerMyIssues; + private String username; @Override public void onCreate(Bundle savedInstanceState) { @@ -139,8 +137,8 @@ public class MainActivity extends BaseActivity toolbarTitle.setText(getResources().getString(R.string.pageTitleExplore)); } else if (fragmentById instanceof NotificationsFragment) { toolbarTitle.setText(R.string.pageTitleNotifications); - } else if (fragmentById instanceof MyProfileFragment) { - toolbarTitle.setText(getResources().getString(R.string.navProfile)); + } else if (fragmentById instanceof AccountSettingsFragment) { + toolbarTitle.setText(getResources().getString(R.string.navAccount)); } else if (fragmentById instanceof MostVisitedReposFragment) { toolbarTitle.setText(getResources().getString(R.string.navMostVisited)); } else if (fragmentById instanceof NotesFragment) { @@ -153,6 +151,8 @@ public class MainActivity extends BaseActivity toolbarTitle.setText(getResources().getString(R.string.navMyIssues)); } else if (fragmentById instanceof DashboardFragment) { toolbarTitle.setText(getResources().getString(R.string.dashboard)); + } else if (fragmentById instanceof WatchedRepositoriesFragment) { + toolbarTitle.setText(getResources().getString(R.string.navWatchedRepositories)); } getNotificationsCount(); @@ -241,6 +241,7 @@ public class MainActivity extends BaseActivity userFullName.setTypeface(myTypeface); if (getAccount().getUserInfo() != null) { + username = getAccount().getUserInfo().getLogin(); String userEmailNav = getAccount().getUserInfo().getEmail(); String userFullNameNav = getAccount().getFullName(); String userAvatarNav = getAccount().getUserInfo().getAvatarUrl(); @@ -270,15 +271,9 @@ public class MainActivity extends BaseActivity userAvatar.setOnClickListener( v -> { - toolbarTitle.setText( - getResources().getString(R.string.navProfile)); - getSupportFragmentManager() - .beginTransaction() - .replace( - R.id.fragment_container, - new MyProfileFragment()) - .commit(); - navigationView.setCheckedItem(R.id.nav_profile); + Intent intentProfile = new Intent(ctx, ProfileActivity.class); + intentProfile.putExtra("username", username); + ctx.startActivity(intentProfile); drawer.closeDrawers(); }); @@ -388,11 +383,9 @@ public class MainActivity extends BaseActivity return; case "profile": - getSupportFragmentManager() - .beginTransaction() - .replace(R.id.fragment_container, new MyProfileFragment()) - .commit(); - navigationView.setCheckedItem(R.id.nav_profile); + Intent intentProfile = new Intent(ctx, ProfileActivity.class); + intentProfile.putExtra("username", username); + ctx.startActivity(intentProfile); return; case "admin": @@ -442,12 +435,12 @@ public class MainActivity extends BaseActivity break; case 4: - toolbarTitle.setText(getResources().getString(R.string.navProfile)); + toolbarTitle.setText(getResources().getString(R.string.navAccount)); getSupportFragmentManager() .beginTransaction() - .replace(R.id.fragment_container, new MyProfileFragment()) + .replace(R.id.fragment_container, new AccountSettingsFragment()) .commit(); - navigationView.setCheckedItem(R.id.nav_profile); + navigationView.setCheckedItem(R.id.nav_account_settings); break; case 5: @@ -509,6 +502,14 @@ public class MainActivity extends BaseActivity .commit(); navigationView.setCheckedItem(R.id.nav_dashboard); break; + case 12: + toolbarTitle.setText(getResources().getString(R.string.navWatchedRepositories)); + getSupportFragmentManager() + .beginTransaction() + .replace(R.id.fragment_container, new WatchedRepositoriesFragment()) + .commit(); + navigationView.setCheckedItem(R.id.nav_watched_repositories); + break; default: toolbarTitle.setText(getResources().getString(R.string.navMyRepos)); getSupportFragmentManager() @@ -654,11 +655,11 @@ public class MainActivity extends BaseActivity .replace(R.id.fragment_container, new OrganizationsFragment()) .commit(); } else if (id == R.id.nav_profile) { - toolbarTitle.setText(getResources().getString(R.string.navProfile)); - getSupportFragmentManager() - .beginTransaction() - .replace(R.id.fragment_container, new MyProfileFragment()) - .commit(); + + Intent intentProfile = new Intent(ctx, ProfileActivity.class); + intentProfile.putExtra("username", username); + ctx.startActivity(intentProfile); + drawer.closeDrawers(); } else if (id == R.id.nav_repositories) { toolbarTitle.setText(getResources().getString(R.string.navRepos)); @@ -736,6 +737,20 @@ public class MainActivity extends BaseActivity .beginTransaction() .replace(R.id.fragment_container, new DashboardFragment()) .commit(); + } else if (id == R.id.nav_account_settings) { + + toolbarTitle.setText(getResources().getString(R.string.navAccount)); + getSupportFragmentManager() + .beginTransaction() + .replace(R.id.fragment_container, new AccountSettingsFragment()) + .commit(); + } else if (id == R.id.nav_watched_repositories) { + + toolbarTitle.setText(getResources().getString(R.string.navWatchedRepositories)); + getSupportFragmentManager() + .beginTransaction() + .replace(R.id.fragment_container, new WatchedRepositoriesFragment()) + .commit(); } drawer.closeDrawer(GravityCompat.START); diff --git a/app/src/main/java/org/mian/gitnex/activities/OrganizationDetailActivity.java b/app/src/main/java/org/mian/gitnex/activities/OrganizationDetailActivity.java index 1f7492f6..ed2928c8 100644 --- a/app/src/main/java/org/mian/gitnex/activities/OrganizationDetailActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/OrganizationDetailActivity.java @@ -9,17 +9,17 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentPagerAdapter; -import androidx.viewpager.widget.ViewPager; -import com.google.android.material.tabs.TabLayout; +import androidx.fragment.app.FragmentActivity; +import androidx.viewpager2.adapter.FragmentStateAdapter; +import androidx.viewpager2.widget.ViewPager2; +import com.google.android.material.tabs.TabLayoutMediator; import io.mikael.urlbuilder.UrlBuilder; import java.util.Objects; import org.gitnex.tea4j.v2.models.OrganizationPermissions; import org.mian.gitnex.R; import org.mian.gitnex.clients.RetrofitClient; +import org.mian.gitnex.databinding.ActivityOrgDetailBinding; import org.mian.gitnex.fragments.BottomSheetOrganizationFragment; import org.mian.gitnex.fragments.OrganizationInfoFragment; import org.mian.gitnex.fragments.OrganizationLabelsFragment; @@ -27,6 +27,7 @@ import org.mian.gitnex.fragments.OrganizationMembersFragment; import org.mian.gitnex.fragments.OrganizationRepositoriesFragment; import org.mian.gitnex.fragments.OrganizationTeamsFragment; import org.mian.gitnex.helpers.AppUtil; +import org.mian.gitnex.helpers.ViewPager2Transformers; import org.mian.gitnex.structs.BottomSheetListener; import retrofit2.Call; import retrofit2.Callback; @@ -41,19 +42,19 @@ public class OrganizationDetailActivity extends BaseActivity implements BottomSh public OrganizationPermissions permissions; private String orgName; private boolean isMember = false; + private ActivityOrgDetailBinding activityOrgDetailBinding; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.activity_org_detail); + activityOrgDetailBinding = ActivityOrgDetailBinding.inflate(getLayoutInflater()); + setContentView(activityOrgDetailBinding.getRoot()); orgName = getIntent().getStringExtra("orgName"); - Toolbar toolbar = findViewById(R.id.toolbar); - - setSupportActionBar(toolbar); + setSupportActionBar(activityOrgDetailBinding.toolbar); Objects.requireNonNull(getSupportActionBar()).setTitle(orgName); getSupportActionBar().setDisplayHomeAsUpEnabled(true); @@ -112,48 +113,91 @@ public class OrganizationDetailActivity extends BaseActivity implements BottomSh } public void init() { - OrganizationDetailActivity.SectionsPagerAdapter mSectionsPagerAdapter = - new OrganizationDetailActivity.SectionsPagerAdapter(getSupportFragmentManager()); - ViewPager mViewPager = findViewById(R.id.container); - mViewPager.setVisibility(View.VISIBLE); - mViewPager.setAdapter(mSectionsPagerAdapter); + ViewPager2 viewPager = activityOrgDetailBinding.container; + viewPager.setOffscreenPageLimit(1); - TabLayout tabLayout = findViewById(R.id.tabs); - tabLayout.setVisibility(View.VISIBLE); + ViewGroup vg = (ViewGroup) activityOrgDetailBinding.tabs.getChildAt(0); + + Typeface myTypeface = AppUtil.getTypeface(ctx); + + activityOrgDetailBinding.toolbarTitle.setTypeface(myTypeface); + activityOrgDetailBinding.toolbarTitle.setText(orgName); + + viewPager.setAdapter(new OrganizationDetailActivity.ViewPagerAdapter(this)); + + ViewPager2Transformers.returnSelectedTransformer( + viewPager, tinyDB.getInt("fragmentTabsAnimationId", 0)); + + String[] tabTitles = { + getResources().getString(R.string.tabTextInfo), + getResources().getString(R.string.navRepos), + getResources().getString(R.string.newIssueLabelsTitle), + getResources().getString(R.string.orgTabTeams), + getResources().getString(R.string.orgTabMembers) + }; if (!isMember) { - tabLayout.removeTabAt(3); + activityOrgDetailBinding.tabs.removeTabAt(3); } - Typeface myTypeface = AppUtil.getTypeface(this); - TextView toolbarTitle = findViewById(R.id.toolbar_title); + new TabLayoutMediator( + activityOrgDetailBinding.tabs, + viewPager, + (tab, position) -> tab.setText(tabTitles[position])) + .attach(); - toolbarTitle.setTypeface(myTypeface); - toolbarTitle.setText(orgName); - - ViewGroup vg = (ViewGroup) tabLayout.getChildAt(0); - int tabsCount = vg.getChildCount(); - - for (int j = 0; j < tabsCount; j++) { + for (int j = 0; j < tabTitles.length; j++) { ViewGroup vgTab = (ViewGroup) vg.getChildAt(j); int tabChildCount = vgTab.getChildCount(); for (int i = 0; i < tabChildCount; i++) { - View tabViewChild = vgTab.getChildAt(i); - if (tabViewChild instanceof TextView) { - ((TextView) tabViewChild).setTypeface(myTypeface); } } } + } - mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout)); - tabLayout.addOnTabSelectedListener( - new TabLayout.ViewPagerOnTabSelectedListener(mViewPager)); + public class ViewPagerAdapter extends FragmentStateAdapter { + + public ViewPagerAdapter(@NonNull FragmentActivity fa) { + super(fa); + } + + @NonNull @Override + public Fragment createFragment(int position) { + switch (position) { + case 0: // info + return OrganizationInfoFragment.newInstance(orgName); + case 1: // repos + return OrganizationRepositoriesFragment.newInstance(orgName, permissions); + case 2: // labels + return OrganizationLabelsFragment.newInstance(orgName, permissions); + case 3: // teams / members + if (isMember) { + return OrganizationTeamsFragment.newInstance(orgName, permissions); + } else { + return OrganizationMembersFragment.newInstance(orgName); + } + case 4: // members + if (isMember) { + return OrganizationMembersFragment.newInstance(orgName); + } + } + return null; + } + + @Override + public int getItemCount() { + if (isMember) { + return 5; + } else { + return 4; + } + } } @Override @@ -205,47 +249,4 @@ public class OrganizationDetailActivity extends BaseActivity implements BottomSh break; } } - - public class SectionsPagerAdapter extends FragmentPagerAdapter { - - SectionsPagerAdapter(FragmentManager fm) { - super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); - } - - @NonNull @Override - public Fragment getItem(int position) { - - String orgName = getIntent().getStringExtra("orgName"); - - Fragment fragment = null; - switch (position) { - case 0: // info - return OrganizationInfoFragment.newInstance(orgName); - case 1: // repos - return OrganizationRepositoriesFragment.newInstance(orgName, permissions); - case 2: // labels - return OrganizationLabelsFragment.newInstance(orgName, permissions); - case 3: // teams / members - if (isMember) { - return OrganizationTeamsFragment.newInstance(orgName, permissions); - } else { - return OrganizationMembersFragment.newInstance(orgName); - } - case 4: // members - if (isMember) { - return OrganizationMembersFragment.newInstance(orgName); - } - } - return fragment; - } - - @Override - public int getCount() { - if (isMember) { - return 5; - } else { - return 4; - } - } - } } diff --git a/app/src/main/java/org/mian/gitnex/activities/OrganizationTeamInfoActivity.java b/app/src/main/java/org/mian/gitnex/activities/OrganizationTeamInfoActivity.java index 33eb961b..5df687c5 100644 --- a/app/src/main/java/org/mian/gitnex/activities/OrganizationTeamInfoActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/OrganizationTeamInfoActivity.java @@ -14,6 +14,7 @@ import org.mian.gitnex.databinding.ActivityOrgTeamInfoBinding; import org.mian.gitnex.fragments.OrganizationTeamInfoMembersFragment; import org.mian.gitnex.fragments.OrganizationTeamInfoPermissionsFragment; import org.mian.gitnex.fragments.OrganizationTeamInfoReposFragment; +import org.mian.gitnex.helpers.ViewPager2Transformers; /** * @author M M Arif @@ -28,7 +29,7 @@ public class OrganizationTeamInfoActivity extends BaseActivity { super.onCreate(savedInstanceState); - org.mian.gitnex.databinding.ActivityOrgTeamInfoBinding binding = + ActivityOrgTeamInfoBinding binding = ActivityOrgTeamInfoBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); @@ -66,6 +67,9 @@ public class OrganizationTeamInfoActivity extends BaseActivity { } }); + ViewPager2Transformers.returnSelectedTransformer( + binding.pager, tinyDB.getInt("fragmentTabsAnimationId", 0)); + new TabLayoutMediator( binding.tabs, binding.pager, 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 d0eaa364..c5d71e4b 100644 --- a/app/src/main/java/org/mian/gitnex/activities/ProfileActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/ProfileActivity.java @@ -28,6 +28,7 @@ import org.mian.gitnex.fragments.profile.RepositoriesFragment; import org.mian.gitnex.fragments.profile.StarredRepositoriesFragment; import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.Toasty; +import org.mian.gitnex.helpers.ViewPager2Transformers; import org.mian.gitnex.structs.BottomSheetListener; import retrofit2.Call; import retrofit2.Callback; @@ -73,6 +74,9 @@ public class ProfileActivity extends BaseActivity implements BottomSheetListener viewPager.setAdapter(new ViewPagerAdapter(this)); + ViewPager2Transformers.returnSelectedTransformer( + viewPager, tinyDB.getInt("fragmentTabsAnimationId", 0)); + String[] tabTitles = { ctx.getResources().getString(R.string.tabTextInfo), ctx.getResources().getString(R.string.navRepos), @@ -147,7 +151,7 @@ public class ProfileActivity extends BaseActivity implements BottomSheetListener } call.enqueue( - new Callback() { + new Callback<>() { @Override public void onResponse( diff --git a/app/src/main/java/org/mian/gitnex/activities/RepoDetailActivity.java b/app/src/main/java/org/mian/gitnex/activities/RepoDetailActivity.java index 7b2b6777..cf72355d 100644 --- a/app/src/main/java/org/mian/gitnex/activities/RepoDetailActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/RepoDetailActivity.java @@ -52,6 +52,7 @@ import org.mian.gitnex.fragments.RepoInfoFragment; import org.mian.gitnex.fragments.WikiFragment; import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.Toasty; +import org.mian.gitnex.helpers.ViewPager2Transformers; import org.mian.gitnex.helpers.contexts.RepositoryContext; import org.mian.gitnex.structs.BottomSheetListener; import org.mian.gitnex.structs.FragmentRefreshListener; @@ -537,6 +538,9 @@ public class RepoDetailActivity extends BaseActivity implements BottomSheetListe viewPager.setAdapter(new ViewPagerAdapter(this)); + ViewPager2Transformers.returnSelectedTransformer( + viewPager, tinyDB.getInt("fragmentTabsAnimationId", 0)); + String[] tabTitles = { ctx.getResources().getString(R.string.tabTextInfo), ctx.getResources().getString(R.string.tabTextFiles), 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 1f57e765..4df8b749 100644 --- a/app/src/main/java/org/mian/gitnex/activities/SettingsAppearanceActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/SettingsAppearanceActivity.java @@ -5,8 +5,6 @@ import android.app.TimePickerDialog; import android.os.Build; import android.os.Bundle; import android.view.View; -import android.widget.ImageView; -import android.widget.LinearLayout; import android.widget.TimePicker; import androidx.annotation.NonNull; import androidx.fragment.app.DialogFragment; @@ -32,6 +30,8 @@ public class SettingsAppearanceActivity extends BaseActivity { private static int themeSelectedChoice = 0; private View.OnClickListener onClickListener; private static int langSelectedChoice = 0; + private static String[] fragmentTabsAnimationList; + private static int fragmentTabsAnimationSelectedChoice = 0; @Override public void onCreate(Bundle savedInstanceState) { @@ -48,16 +48,10 @@ public class SettingsAppearanceActivity extends BaseActivity { lang.put(langCode, getLanguageDisplayName(langCode)); } - ImageView closeActivity = activitySettingsAppearanceBinding.close; - - LinearLayout customFontFrame = activitySettingsAppearanceBinding.customFontFrame; - LinearLayout themeFrame = activitySettingsAppearanceBinding.themeSelectionFrame; - LinearLayout lightTimeFrame = - activitySettingsAppearanceBinding.lightThemeTimeSelectionFrame; - LinearLayout darkTimeFrame = activitySettingsAppearanceBinding.darkThemeTimeSelectionFrame; - customFontList = getResources().getStringArray(R.array.fonts); + fragmentTabsAnimationList = getResources().getStringArray(R.array.fragmentTabsAnimation); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S || "S".equals(Build.VERSION.CODENAME)) { themeList = getResources().getStringArray(R.array.themesAndroid12); } else { @@ -65,7 +59,7 @@ public class SettingsAppearanceActivity extends BaseActivity { } initCloseListener(); - closeActivity.setOnClickListener(onClickListener); + activitySettingsAppearanceBinding.close.setOnClickListener(onClickListener); String lightMinute = String.valueOf(tinyDB.getInt("lightThemeTimeMinute")); String lightHour = String.valueOf(tinyDB.getInt("lightThemeTimeHour")); @@ -85,6 +79,7 @@ public class SettingsAppearanceActivity extends BaseActivity { darkHour = "0" + darkHour; } + fragmentTabsAnimationSelectedChoice = tinyDB.getInt("fragmentTabsAnimationId", 0); customFontSelectedChoice = tinyDB.getInt("customFontId", 1); themeSelectedChoice = tinyDB.getInt("themeId", 6); // use system theme as default @@ -97,13 +92,17 @@ public class SettingsAppearanceActivity extends BaseActivity { activitySettingsAppearanceBinding.customFontSelected.setText( customFontList[customFontSelectedChoice]); activitySettingsAppearanceBinding.themeSelected.setText(themeList[themeSelectedChoice]); + activitySettingsAppearanceBinding.fragmentTabsAnimationFrameSelected.setText( + fragmentTabsAnimationList[fragmentTabsAnimationSelectedChoice]); if (themeList[themeSelectedChoice].startsWith("Auto")) { - darkTimeFrame.setVisibility(View.VISIBLE); - lightTimeFrame.setVisibility(View.VISIBLE); + activitySettingsAppearanceBinding.darkThemeTimeSelectionFrame.setVisibility( + View.VISIBLE); + activitySettingsAppearanceBinding.lightThemeTimeSelectionFrame.setVisibility( + View.VISIBLE); } else { - darkTimeFrame.setVisibility(View.GONE); - lightTimeFrame.setVisibility(View.GONE); + activitySettingsAppearanceBinding.darkThemeTimeSelectionFrame.setVisibility(View.GONE); + activitySettingsAppearanceBinding.lightThemeTimeSelectionFrame.setVisibility(View.GONE); } activitySettingsAppearanceBinding.switchCounterBadge.setChecked( @@ -136,7 +135,7 @@ public class SettingsAppearanceActivity extends BaseActivity { .isChecked())); // theme selection dialog - themeFrame.setOnClickListener( + activitySettingsAppearanceBinding.themeSelectionFrame.setOnClickListener( view -> { MaterialAlertDialogBuilder materialAlertDialogBuilder = new MaterialAlertDialogBuilder(ctx) @@ -163,20 +162,20 @@ public class SettingsAppearanceActivity extends BaseActivity { materialAlertDialogBuilder.create().show(); }); - lightTimeFrame.setOnClickListener( + activitySettingsAppearanceBinding.lightThemeTimeSelectionFrame.setOnClickListener( view -> { LightTimePicker timePicker = new LightTimePicker(); timePicker.show(getSupportFragmentManager(), "timePicker"); }); - darkTimeFrame.setOnClickListener( + activitySettingsAppearanceBinding.darkThemeTimeSelectionFrame.setOnClickListener( view -> { DarkTimePicker timePicker = new DarkTimePicker(); timePicker.show(getSupportFragmentManager(), "timePicker"); }); // custom font dialog - customFontFrame.setOnClickListener( + activitySettingsAppearanceBinding.customFontFrame.setOnClickListener( view -> { MaterialAlertDialogBuilder materialAlertDialogBuilder = new MaterialAlertDialogBuilder(ctx) @@ -206,9 +205,39 @@ public class SettingsAppearanceActivity extends BaseActivity { materialAlertDialogBuilder.create().show(); }); - // language selector dialog - LinearLayout langFrame = activitySettingsAppearanceBinding.langFrame; + // fragment tabs animation dialog + activitySettingsAppearanceBinding.fragmentTabsAnimationFrame.setOnClickListener( + view -> { + MaterialAlertDialogBuilder materialAlertDialogBuilder = + new MaterialAlertDialogBuilder(ctx) + .setTitle(R.string.fragmentTabsAnimationHeader) + .setCancelable(fragmentTabsAnimationSelectedChoice != -1) + .setSingleChoiceItems( + fragmentTabsAnimationList, + fragmentTabsAnimationSelectedChoice, + (dialogInterfaceCustomFont, i) -> { + fragmentTabsAnimationSelectedChoice = i; + activitySettingsAppearanceBinding + .fragmentTabsAnimationFrameSelected.setText( + fragmentTabsAnimationList[i]); + tinyDB.putInt("fragmentTabsAnimationId", i); + AppUtil.typeface = null; // reset typeface + FontsOverride.setDefaultFont(this); + SettingsFragment.refreshParent = true; + this.recreate(); + this.overridePendingTransition(0, 0); + dialogInterfaceCustomFont.dismiss(); + Toasty.success( + appCtx, + appCtx.getResources() + .getString(R.string.settingsSave)); + }); + + materialAlertDialogBuilder.create().show(); + }); + + // language selector dialog activitySettingsAppearanceBinding.helpTranslate.setOnClickListener( v12 -> { AppUtil.openUrlInBrowser(this, getResources().getString(R.string.crowdInLink)); @@ -219,7 +248,7 @@ public class SettingsAppearanceActivity extends BaseActivity { lang.get(lang.keySet().toArray(new String[0])[langSelectedChoice])); // language dialog - langFrame.setOnClickListener( + activitySettingsAppearanceBinding.langFrame.setOnClickListener( view -> { MaterialAlertDialogBuilder materialAlertDialogBuilder = new MaterialAlertDialogBuilder(ctx) diff --git a/app/src/main/java/org/mian/gitnex/activities/SettingsGeneralActivity.java b/app/src/main/java/org/mian/gitnex/activities/SettingsGeneralActivity.java index 29590950..a12c1a6d 100644 --- a/app/src/main/java/org/mian/gitnex/activities/SettingsGeneralActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/SettingsGeneralActivity.java @@ -71,7 +71,7 @@ public class SettingsGeneralActivity extends BaseActivity { viewBinding.homeScreenSelected.setText(getResources().getString(R.string.navRepos)); } else if (homeScreenSelectedChoice == 4) { - viewBinding.homeScreenSelected.setText(getResources().getString(R.string.navProfile)); + viewBinding.homeScreenSelected.setText(getResources().getString(R.string.navAccount)); } else if (homeScreenSelectedChoice == 5) { viewBinding.homeScreenSelected.setText( @@ -96,6 +96,10 @@ public class SettingsGeneralActivity extends BaseActivity { } else if (homeScreenSelectedChoice == 11) { viewBinding.homeScreenSelected.setText(getResources().getString(R.string.dashboard)); + } else if (homeScreenSelectedChoice == 12) { + + viewBinding.homeScreenSelected.setText( + getResources().getString(R.string.navWatchedRepositories)); } viewBinding.homeScreenFrame.setOnClickListener( diff --git a/app/src/main/java/org/mian/gitnex/adapters/MyProfileEmailsAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/AccountSettingsEmailsAdapter.java similarity index 80% rename from app/src/main/java/org/mian/gitnex/adapters/MyProfileEmailsAdapter.java rename to app/src/main/java/org/mian/gitnex/adapters/AccountSettingsEmailsAdapter.java index ccd13c0c..20de1b8f 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/MyProfileEmailsAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/AccountSettingsEmailsAdapter.java @@ -17,29 +17,29 @@ import org.mian.gitnex.R; /** * @author M M Arif */ -public class MyProfileEmailsAdapter - extends RecyclerView.Adapter { +public class AccountSettingsEmailsAdapter + extends RecyclerView.Adapter { private final List emailsList; private final Context context; - public MyProfileEmailsAdapter(Context ctx, List emailsListMain) { + public AccountSettingsEmailsAdapter(Context ctx, List emailsListMain) { this.context = ctx; this.emailsList = emailsListMain; } @NonNull @Override - public MyProfileEmailsAdapter.EmailsViewHolder onCreateViewHolder( + public AccountSettingsEmailsAdapter.EmailsViewHolder onCreateViewHolder( @NonNull ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()) - .inflate(R.layout.list_profile_emails, parent, false); - return new MyProfileEmailsAdapter.EmailsViewHolder(v); + .inflate(R.layout.list_account_settings_emails, parent, false); + return new AccountSettingsEmailsAdapter.EmailsViewHolder(v); } @Override public void onBindViewHolder( - @NonNull MyProfileEmailsAdapter.EmailsViewHolder holder, int position) { + @NonNull AccountSettingsEmailsAdapter.EmailsViewHolder holder, int position) { Email currentItem = emailsList.get(position); diff --git a/app/src/main/java/org/mian/gitnex/adapters/SSHKeysAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/SSHKeysAdapter.java new file mode 100644 index 00000000..2cc3ef1f --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/adapters/SSHKeysAdapter.java @@ -0,0 +1,59 @@ +package org.mian.gitnex.adapters; + +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import java.util.List; +import org.gitnex.tea4j.v2.models.PublicKey; +import org.mian.gitnex.R; + +/** + * @author M M Arif + */ +public class SSHKeysAdapter extends RecyclerView.Adapter { + + private final List keysList; + + public SSHKeysAdapter(List keysListMain) { + this.keysList = keysListMain; + } + + @NonNull @Override + public SSHKeysAdapter.KeysViewHolder onCreateViewHolder( + @NonNull ViewGroup parent, int viewType) { + View v = + LayoutInflater.from(parent.getContext()) + .inflate(R.layout.list_account_settings_ssh_keys, parent, false); + return new SSHKeysAdapter.KeysViewHolder(v); + } + + @Override + public void onBindViewHolder(@NonNull SSHKeysAdapter.KeysViewHolder holder, int position) { + + PublicKey currentItem = keysList.get(position); + + holder.keyName.setText(currentItem.getTitle()); + holder.key.setText(currentItem.getKey()); + } + + @Override + public int getItemCount() { + return keysList.size(); + } + + static class KeysViewHolder extends RecyclerView.ViewHolder { + + private final TextView keyName; + private final TextView key; + + private KeysViewHolder(View itemView) { + super(itemView); + + keyName = itemView.findViewById(R.id.keyName); + key = itemView.findViewById(R.id.key); + } + } +} diff --git a/app/src/main/java/org/mian/gitnex/fragments/AccountSettingsEmailsFragment.java b/app/src/main/java/org/mian/gitnex/fragments/AccountSettingsEmailsFragment.java new file mode 100644 index 00000000..66599551 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/fragments/AccountSettingsEmailsFragment.java @@ -0,0 +1,104 @@ +package org.mian.gitnex.fragments; + +import android.annotation.SuppressLint; +import android.content.Intent; +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 androidx.swiperefreshlayout.widget.SwipeRefreshLayout; +import org.mian.gitnex.activities.AccountSettingsEmailActivity; +import org.mian.gitnex.adapters.AccountSettingsEmailsAdapter; +import org.mian.gitnex.databinding.FragmentAccountSettingsEmailsBinding; +import org.mian.gitnex.viewmodels.AccountSettingsEmailsViewModel; + +/** + * @author M M Arif + */ +public class AccountSettingsEmailsFragment extends Fragment { + + public static boolean refreshEmails = false; + private AccountSettingsEmailsViewModel accountSettingsEmailsViewModel; + private AccountSettingsEmailsAdapter adapter; + + public AccountSettingsEmailsFragment() {} + + private FragmentAccountSettingsEmailsBinding fragmentAccountSettingsEmailsBinding; + + @Override + public View onCreateView( + @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + fragmentAccountSettingsEmailsBinding = + FragmentAccountSettingsEmailsBinding.inflate(inflater, container, false); + accountSettingsEmailsViewModel = + new ViewModelProvider(this).get(AccountSettingsEmailsViewModel.class); + + final SwipeRefreshLayout swipeRefresh = fragmentAccountSettingsEmailsBinding.pullToRefresh; + + fragmentAccountSettingsEmailsBinding.recyclerView.setHasFixedSize(true); + fragmentAccountSettingsEmailsBinding.recyclerView.setLayoutManager( + new LinearLayoutManager(getContext())); + + swipeRefresh.setOnRefreshListener( + () -> + new Handler(Looper.getMainLooper()) + .postDelayed( + () -> { + swipeRefresh.setRefreshing(false); + accountSettingsEmailsViewModel.loadEmailsList( + getContext()); + }, + 200)); + + fetchDataAsync(); + + fragmentAccountSettingsEmailsBinding.addNewEmailAddress.setOnClickListener( + v1 -> startActivity(new Intent(getContext(), AccountSettingsEmailActivity.class))); + + return fragmentAccountSettingsEmailsBinding.getRoot(); + } + + @SuppressLint("NotifyDataSetChanged") + private void fetchDataAsync() { + + accountSettingsEmailsViewModel + .getEmailsList(getContext()) + .observe( + getViewLifecycleOwner(), + emailsListMain -> { + adapter = + new AccountSettingsEmailsAdapter(getContext(), emailsListMain); + if (adapter.getItemCount() > 0) { + fragmentAccountSettingsEmailsBinding.recyclerView.setAdapter( + adapter); + fragmentAccountSettingsEmailsBinding.noDataEmails.setVisibility( + View.GONE); + } else { + adapter.notifyDataSetChanged(); + fragmentAccountSettingsEmailsBinding.recyclerView.setAdapter( + adapter); + fragmentAccountSettingsEmailsBinding.noDataEmails.setVisibility( + View.VISIBLE); + } + fragmentAccountSettingsEmailsBinding.progressBar.setVisibility( + View.GONE); + }); + } + + @Override + public void onResume() { + super.onResume(); + + if (refreshEmails) { + accountSettingsEmailsViewModel.loadEmailsList(getContext()); + refreshEmails = false; + } + } +} diff --git a/app/src/main/java/org/mian/gitnex/fragments/AccountSettingsFragment.java b/app/src/main/java/org/mian/gitnex/fragments/AccountSettingsFragment.java new file mode 100644 index 00000000..5fa523be --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/fragments/AccountSettingsFragment.java @@ -0,0 +1,138 @@ +package org.mian.gitnex.fragments; + +import android.content.Context; +import android.graphics.Typeface; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.viewpager2.adapter.FragmentStateAdapter; +import androidx.viewpager2.widget.ViewPager2; +import com.google.android.material.tabs.TabLayout; +import com.google.android.material.tabs.TabLayoutMediator; +import org.mian.gitnex.R; +import org.mian.gitnex.activities.BaseActivity; +import org.mian.gitnex.activities.MainActivity; +import org.mian.gitnex.helpers.AppUtil; +import org.mian.gitnex.helpers.TinyDB; +import org.mian.gitnex.helpers.ViewPager2Transformers; +import org.mian.gitnex.helpers.contexts.AccountContext; + +/** + * @author M M Arif + */ +public class AccountSettingsFragment extends Fragment { + + public ViewPager2 viewPager; + private Context ctx; + private View view; + private Typeface myTypeface; + private TinyDB tinyDB; + + @Nullable @Override + public View onCreateView( + @NonNull LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + + ctx = getContext(); + tinyDB = TinyDB.getInstance(ctx); + + view = inflater.inflate(R.layout.fragment_account_settings, container, false); + setHasOptionsMenu(false); + + ((MainActivity) requireActivity()) + .setActionBarTitle(getResources().getString(R.string.navAccount)); + + myTypeface = AppUtil.getTypeface(ctx); + + AccountContext account = ((BaseActivity) requireActivity()).getAccount(); + if (account.getUserInfo() != null) { + viewData(); + } else { + ((MainActivity) requireActivity()) + .setProfileInitListener( + (text) -> { + viewData(); + }); + } + + return view; + } + + public void viewData() { + + TabLayout tabLayout = view.findViewById(R.id.tabs); + + if (viewPager == null) { + + viewPager = view.findViewById(R.id.accountSettingsContainer); + viewPager.setOffscreenPageLimit(1); + + viewPager.setAdapter(new ViewPagerAdapter(this)); + + ViewPager2Transformers.returnSelectedTransformer( + viewPager, tinyDB.getInt("fragmentTabsAnimationId", 0)); + + String[] tabTitles = { + ctx.getResources().getString(R.string.accountEmails), + ctx.getResources().getString(R.string.sshKeys) + }; + new TabLayoutMediator( + tabLayout, + viewPager, + (tab, position) -> tab.setText(tabTitles[position])) + .attach(); + + ViewGroup viewGroup = (ViewGroup) tabLayout.getChildAt(0); + int tabsCount_ = viewGroup.getChildCount(); + + for (int j = 0; j < tabsCount_; j++) { + + ViewGroup vgTab = (ViewGroup) viewGroup.getChildAt(j); + int tabChildCount = vgTab.getChildCount(); + + for (int i = 0; i < tabChildCount; i++) { + + View tabViewChild = vgTab.getChildAt(i); + + if (tabViewChild instanceof TextView) { + + ((TextView) tabViewChild).setTypeface(myTypeface); + } + } + } + } + } + + public static class ViewPagerAdapter extends FragmentStateAdapter { + + public ViewPagerAdapter(@NonNull Fragment fa) { + super(fa); + } + + @NonNull @Override + public Fragment createFragment(int position) { + + Fragment fragment = null; + + switch (position) { + case 0: // Emails + return new AccountSettingsEmailsFragment(); + case 1: // SSH keys + return new SSHKeysFragment(); + } + assert false; + return fragment; + } + + @Override + public int getItemCount() { + return 2; + } + } +} diff --git a/app/src/main/java/org/mian/gitnex/fragments/ExploreFragment.java b/app/src/main/java/org/mian/gitnex/fragments/ExploreFragment.java index 8657194d..6c0520b0 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/ExploreFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/ExploreFragment.java @@ -18,6 +18,7 @@ import org.mian.gitnex.R; import org.mian.gitnex.activities.MainActivity; import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.TinyDB; +import org.mian.gitnex.helpers.ViewPager2Transformers; /** * @author M M Arif @@ -47,6 +48,9 @@ public class ExploreFragment extends Fragment { Typeface myTypeface = AppUtil.getTypeface(requireContext()); viewPager.setAdapter(new ViewPagerAdapter(this)); + ViewPager2Transformers.returnSelectedTransformer( + viewPager, tinyDB.getInt("fragmentTabsAnimationId", 0)); + String[] tabTitles = { getResources().getString(R.string.navRepos), getResources().getString(R.string.pageTitleIssues), diff --git a/app/src/main/java/org/mian/gitnex/fragments/MyProfileEmailsFragment.java b/app/src/main/java/org/mian/gitnex/fragments/MyProfileEmailsFragment.java deleted file mode 100644 index 18797f9f..00000000 --- a/app/src/main/java/org/mian/gitnex/fragments/MyProfileEmailsFragment.java +++ /dev/null @@ -1,102 +0,0 @@ -package org.mian.gitnex.fragments; - -import android.content.Intent; -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 android.widget.ProgressBar; -import android.widget.TextView; -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; -import androidx.lifecycle.ViewModelProvider; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; -import org.mian.gitnex.activities.MyProfileEmailActivity; -import org.mian.gitnex.adapters.MyProfileEmailsAdapter; -import org.mian.gitnex.databinding.FragmentProfileEmailsBinding; -import org.mian.gitnex.viewmodels.ProfileEmailsViewModel; - -/** - * @author M M Arif - */ -public class MyProfileEmailsFragment extends Fragment { - - public static boolean refreshEmails = false; - private ProfileEmailsViewModel profileEmailsViewModel; - private ProgressBar mProgressBar; - private MyProfileEmailsAdapter adapter; - private RecyclerView mRecyclerView; - private TextView noDataEmails; - - public MyProfileEmailsFragment() {} - - @Override - public View onCreateView( - @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - - FragmentProfileEmailsBinding fragmentProfileEmailsBinding = - FragmentProfileEmailsBinding.inflate(inflater, container, false); - profileEmailsViewModel = new ViewModelProvider(this).get(ProfileEmailsViewModel.class); - - final SwipeRefreshLayout swipeRefresh = fragmentProfileEmailsBinding.pullToRefresh; - - noDataEmails = fragmentProfileEmailsBinding.noDataEmails; - mRecyclerView = fragmentProfileEmailsBinding.recyclerView; - - mRecyclerView.setHasFixedSize(true); - mRecyclerView.setLayoutManager(new LinearLayoutManager(getContext())); - - mProgressBar = fragmentProfileEmailsBinding.progressBar; - - swipeRefresh.setOnRefreshListener( - () -> - new Handler(Looper.getMainLooper()) - .postDelayed( - () -> { - swipeRefresh.setRefreshing(false); - profileEmailsViewModel.loadEmailsList(getContext()); - }, - 200)); - - fetchDataAsync(); - - fragmentProfileEmailsBinding.addNewEmailAddress.setOnClickListener( - v1 -> startActivity(new Intent(getContext(), MyProfileEmailActivity.class))); - - return fragmentProfileEmailsBinding.getRoot(); - } - - private void fetchDataAsync() { - - profileEmailsViewModel - .getEmailsList(getContext()) - .observe( - getViewLifecycleOwner(), - emailsListMain -> { - adapter = new MyProfileEmailsAdapter(getContext(), emailsListMain); - if (adapter.getItemCount() > 0) { - mRecyclerView.setAdapter(adapter); - noDataEmails.setVisibility(View.GONE); - } else { - adapter.notifyDataSetChanged(); - mRecyclerView.setAdapter(adapter); - noDataEmails.setVisibility(View.VISIBLE); - } - mProgressBar.setVisibility(View.GONE); - }); - } - - @Override - public void onResume() { - super.onResume(); - - if (refreshEmails) { - profileEmailsViewModel.loadEmailsList(getContext()); - refreshEmails = false; - } - } -} diff --git a/app/src/main/java/org/mian/gitnex/fragments/MyProfileFollowersFragment.java b/app/src/main/java/org/mian/gitnex/fragments/MyProfileFollowersFragment.java deleted file mode 100644 index 31586d2e..00000000 --- a/app/src/main/java/org/mian/gitnex/fragments/MyProfileFollowersFragment.java +++ /dev/null @@ -1,155 +0,0 @@ -package org.mian.gitnex.fragments; - -import android.content.Context; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; -import androidx.recyclerview.widget.LinearLayoutManager; -import java.util.ArrayList; -import java.util.List; -import org.gitnex.tea4j.v2.models.User; -import org.mian.gitnex.R; -import org.mian.gitnex.adapters.UsersAdapter; -import org.mian.gitnex.clients.RetrofitClient; -import org.mian.gitnex.databinding.FragmentProfileFollowersFollowingBinding; -import org.mian.gitnex.helpers.Constants; -import org.mian.gitnex.helpers.SnackBar; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; - -/** - * @author M M Arif - */ -public class MyProfileFollowersFragment extends Fragment { - - private final String TAG = "MyProfileFollowersFragment"; - private FragmentProfileFollowersFollowingBinding viewBinding; - private Context context; - private List dataList; - private UsersAdapter adapter; - private int pageSize; - private int resultLimit; - - @Override - public View onCreateView( - @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - - viewBinding = FragmentProfileFollowersFollowingBinding.inflate(inflater, container, false); - context = getContext(); - - dataList = new ArrayList<>(); - adapter = new UsersAdapter(dataList, context); - resultLimit = Constants.getCurrentResultLimit(context); - - viewBinding.pullToRefresh.setOnRefreshListener( - () -> - new Handler(Looper.getMainLooper()) - .postDelayed( - () -> { - viewBinding.pullToRefresh.setRefreshing(false); - loadInitial(resultLimit); - adapter.notifyDataChanged(); - }, - 200)); - - adapter.setLoadMoreListener( - () -> - viewBinding.recyclerView.post( - () -> { - if (dataList.size() == resultLimit || pageSize == resultLimit) { - int page = (dataList.size() + resultLimit) / resultLimit; - loadMore(resultLimit, page); - } - })); - - viewBinding.recyclerView.setHasFixedSize(true); - viewBinding.recyclerView.setLayoutManager(new LinearLayoutManager(context)); - viewBinding.recyclerView.setAdapter(adapter); - - loadInitial(resultLimit); - - return viewBinding.getRoot(); - } - - private void loadInitial(int resultLimit) { - - Call> call = - RetrofitClient.getApiInterface(context).userCurrentListFollowers(1, resultLimit); - call.enqueue( - new Callback>() { - @Override - public void onResponse( - @NonNull Call> call, - @NonNull Response> response) { - if (response.isSuccessful()) { - if (response.body() != null && response.body().size() > 0) { - dataList.clear(); - dataList.addAll(response.body()); - adapter.notifyDataChanged(); - viewBinding.noData.setVisibility(View.GONE); - } else { - dataList.clear(); - adapter.notifyDataChanged(); - viewBinding.noData.setVisibility(View.VISIBLE); - } - viewBinding.progressBar.setVisibility(View.GONE); - } else if (response.code() == 404) { - viewBinding.noData.setVisibility(View.VISIBLE); - viewBinding.progressBar.setVisibility(View.GONE); - } else { - Log.e(TAG, String.valueOf(response.code())); - } - } - - @Override - public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - Log.e(TAG, t.toString()); - } - }); - } - - private void loadMore(int resultLimit, int page) { - - viewBinding.progressBar.setVisibility(View.VISIBLE); - Call> call = - RetrofitClient.getApiInterface(context).userCurrentListFollowers(page, resultLimit); - call.enqueue( - new Callback>() { - @Override - public void onResponse( - @NonNull Call> call, - @NonNull Response> response) { - if (response.isSuccessful()) { - assert response.body() != null; - List result = response.body(); - if (result.size() > 0) { - pageSize = result.size(); - dataList.addAll(result); - } else { - SnackBar.info( - context, - viewBinding.getRoot(), - getString(R.string.noMoreData)); - adapter.setMoreDataAvailable(false); - } - adapter.notifyDataChanged(); - viewBinding.progressBar.setVisibility(View.GONE); - } else { - Log.e(TAG, String.valueOf(response.code())); - } - } - - @Override - public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - Log.e(TAG, t.toString()); - } - }); - } -} diff --git a/app/src/main/java/org/mian/gitnex/fragments/MyProfileFollowingFragment.java b/app/src/main/java/org/mian/gitnex/fragments/MyProfileFollowingFragment.java deleted file mode 100644 index 1a972c3f..00000000 --- a/app/src/main/java/org/mian/gitnex/fragments/MyProfileFollowingFragment.java +++ /dev/null @@ -1,157 +0,0 @@ -package org.mian.gitnex.fragments; - -import android.content.Context; -import android.os.Bundle; -import android.os.Handler; -import android.os.Looper; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import androidx.annotation.NonNull; -import androidx.fragment.app.Fragment; -import androidx.recyclerview.widget.LinearLayoutManager; -import java.util.ArrayList; -import java.util.List; -import org.gitnex.tea4j.v2.models.User; -import org.mian.gitnex.R; -import org.mian.gitnex.adapters.UsersAdapter; -import org.mian.gitnex.clients.RetrofitClient; -import org.mian.gitnex.databinding.FragmentProfileFollowersFollowingBinding; -import org.mian.gitnex.helpers.Constants; -import org.mian.gitnex.helpers.SnackBar; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; - -/** - * @author M M Arif - */ -public class MyProfileFollowingFragment extends Fragment { - - private final String TAG = "MyProfileFollowingFragment"; - private FragmentProfileFollowersFollowingBinding viewBinding; - private Context context; - private List dataList; - private UsersAdapter adapter; - private int pageSize; - private int resultLimit; - - @Override - public View onCreateView( - @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - - viewBinding = FragmentProfileFollowersFollowingBinding.inflate(inflater, container, false); - context = getContext(); - - dataList = new ArrayList<>(); - adapter = new UsersAdapter(dataList, context); - resultLimit = Constants.getCurrentResultLimit(context); - - viewBinding.pullToRefresh.setOnRefreshListener( - () -> - new Handler(Looper.getMainLooper()) - .postDelayed( - () -> { - viewBinding.pullToRefresh.setRefreshing(false); - loadInitial(resultLimit); - adapter.notifyDataChanged(); - }, - 200)); - - adapter.setLoadMoreListener( - () -> - viewBinding.recyclerView.post( - () -> { - if (dataList.size() == resultLimit || pageSize == resultLimit) { - int page = (dataList.size() + resultLimit) / resultLimit; - loadMore(resultLimit, page); - } - })); - - viewBinding.recyclerView.setHasFixedSize(true); - viewBinding.recyclerView.setLayoutManager(new LinearLayoutManager(context)); - viewBinding.recyclerView.setAdapter(adapter); - - loadInitial(resultLimit); - - return viewBinding.getRoot(); - } - - private void loadInitial(int resultLimit) { - - Call> call = - RetrofitClient.getApiInterface(context).userCurrentListFollowing(1, resultLimit); - call.enqueue( - new Callback>() { - - @Override - public void onResponse( - @NonNull Call> call, - @NonNull Response> response) { - if (response.isSuccessful()) { - if (response.body() != null && response.body().size() > 0) { - dataList.clear(); - dataList.addAll(response.body()); - adapter.notifyDataChanged(); - viewBinding.noData.setVisibility(View.GONE); - } else { - dataList.clear(); - adapter.notifyDataChanged(); - viewBinding.noData.setVisibility(View.VISIBLE); - } - viewBinding.progressBar.setVisibility(View.GONE); - } else if (response.code() == 404) { - viewBinding.noData.setVisibility(View.VISIBLE); - viewBinding.progressBar.setVisibility(View.GONE); - } else { - Log.e(TAG, String.valueOf(response.code())); - } - } - - @Override - public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - Log.e(TAG, t.toString()); - } - }); - } - - private void loadMore(int resultLimit, int page) { - - viewBinding.progressBar.setVisibility(View.VISIBLE); - Call> call = - RetrofitClient.getApiInterface(context).userCurrentListFollowing(page, resultLimit); - call.enqueue( - new Callback>() { - - @Override - public void onResponse( - @NonNull Call> call, - @NonNull Response> response) { - if (response.isSuccessful()) { - assert response.body() != null; - List result = response.body(); - if (result.size() > 0) { - pageSize = result.size(); - dataList.addAll(result); - } else { - SnackBar.info( - context, - viewBinding.getRoot(), - getString(R.string.noMoreData)); - adapter.setMoreDataAvailable(false); - } - adapter.notifyDataChanged(); - viewBinding.progressBar.setVisibility(View.GONE); - } else { - Log.e(TAG, String.valueOf(response.code())); - } - } - - @Override - public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - Log.e(TAG, t.toString()); - } - }); - } -} diff --git a/app/src/main/java/org/mian/gitnex/fragments/MyProfileFragment.java b/app/src/main/java/org/mian/gitnex/fragments/MyProfileFragment.java deleted file mode 100644 index 83042f36..00000000 --- a/app/src/main/java/org/mian/gitnex/fragments/MyProfileFragment.java +++ /dev/null @@ -1,177 +0,0 @@ -package org.mian.gitnex.fragments; - -import android.content.Context; -import android.graphics.Typeface; -import android.os.Bundle; -import android.text.Html; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ImageView; -import android.widget.TextView; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentStatePagerAdapter; -import androidx.viewpager.widget.ViewPager; -import com.google.android.material.progressindicator.LinearProgressIndicator; -import com.google.android.material.tabs.TabLayout; -import java.util.Locale; -import org.mian.gitnex.R; -import org.mian.gitnex.activities.BaseActivity; -import org.mian.gitnex.activities.MainActivity; -import org.mian.gitnex.clients.PicassoService; -import org.mian.gitnex.helpers.AppUtil; -import org.mian.gitnex.helpers.RoundedTransformation; -import org.mian.gitnex.helpers.contexts.AccountContext; - -/** - * @author M M Arif - */ -public class MyProfileFragment extends Fragment { - - private Context ctx; - - @Nullable @Override - public View onCreateView( - @NonNull LayoutInflater inflater, - @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - - ctx = getContext(); - - View v = inflater.inflate(R.layout.fragment_profile, container, false); - setHasOptionsMenu(false); - - ((MainActivity) requireActivity()) - .setActionBarTitle(getResources().getString(R.string.navProfile)); - - AccountContext account = ((BaseActivity) requireActivity()).getAccount(); - if (account.getUserInfo() != null) { - viewData(v, account); - } else { - // we have to wait until loading is finished - LinearProgressIndicator loading = v.findViewById(R.id.loadingIndicator); - loading.setVisibility(View.VISIBLE); - ((MainActivity) requireActivity()) - .setProfileInitListener( - (text) -> { - loading.setVisibility(View.GONE); - viewData(v, account); - }); - } - - return v; - } - - public void viewData(View v, AccountContext account) { - - TextView userFullName = v.findViewById(R.id.userFullName); - ImageView userAvatar = v.findViewById(R.id.userAvatar); - TextView userLogin = v.findViewById(R.id.userLogin); - TextView userLanguage = v.findViewById(R.id.userLanguage); - TextView userFollowersCount = v.findViewById(R.id.user_followers_count); - TextView userFollowingCount = v.findViewById(R.id.user_following_count); - TextView userStarredReposCount = v.findViewById(R.id.user_starred_repos_count); - - String[] userLanguageCodes = - account.getUserInfo().getLanguage() != null - ? account.getUserInfo().getLanguage().split("-") - : new String[] {""}; - - if (userLanguageCodes.length >= 2) { - Locale locale = new Locale(userLanguageCodes[0], userLanguageCodes[1]); - userLanguage.setText(locale.getDisplayLanguage()); - } else { - userLanguage.setText(getResources().getConfiguration().locale.getDisplayLanguage()); - } - - userAvatar.setOnClickListener( - loginId -> - AppUtil.copyToClipboard( - ctx, - account.getAccount().getUserName(), - ctx.getString( - R.string.copyLoginIdToClipBoard, - account.getAccount().getUserName()))); - - userFullName.setText(Html.fromHtml(account.getFullName())); - userLogin.setText(getString(R.string.usernameWithAt, account.getAccount().getUserName())); - - int avatarRadius = AppUtil.getPixelsFromDensity(ctx, 60); - - PicassoService.getInstance(ctx) - .get() - .load(account.getUserInfo().getAvatarUrl()) - .transform(new RoundedTransformation(avatarRadius, 0)) - .placeholder(R.drawable.loader_animated) - .resize(120, 120) - .centerCrop() - .into(userAvatar); - - userFollowersCount.setText(String.valueOf(account.getUserInfo().getFollowersCount())); - userFollowingCount.setText(String.valueOf(account.getUserInfo().getFollowingCount())); - userStarredReposCount.setText(String.valueOf(account.getUserInfo().getStarredReposCount())); - - MyProfileFragment.SectionsPagerAdapter mSectionsPagerAdapter = - new SectionsPagerAdapter(getChildFragmentManager()); - - ViewPager mViewPager = v.findViewById(R.id.container); - mViewPager.setAdapter(mSectionsPagerAdapter); - - Typeface myTypeface = AppUtil.getTypeface(requireContext()); - TabLayout tabLayout = v.findViewById(R.id.tabs); - - ViewGroup vg = (ViewGroup) tabLayout.getChildAt(0); - int tabsCount = vg.getChildCount(); - - for (int j = 0; j < tabsCount; j++) { - - ViewGroup vgTab = (ViewGroup) vg.getChildAt(j); - int tabChildCount = vgTab.getChildCount(); - - for (int i = 0; i < tabChildCount; i++) { - - View tabViewChild = vgTab.getChildAt(i); - - if (tabViewChild instanceof TextView) { - ((TextView) tabViewChild).setTypeface(myTypeface); - } - } - } - - mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout)); - tabLayout.addOnTabSelectedListener( - new TabLayout.ViewPagerOnTabSelectedListener(mViewPager)); - } - - public static class SectionsPagerAdapter extends FragmentStatePagerAdapter { - - SectionsPagerAdapter(FragmentManager fm) { - super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT); - } - - @NonNull @Override - public Fragment getItem(int position) { - - switch (position) { - case 0: // followers - return new MyProfileFollowersFragment(); - - case 1: // following - return new MyProfileFollowingFragment(); - - case 2: // emails - return new MyProfileEmailsFragment(); - } - - return null; - } - - @Override - public int getCount() { - return 3; - } - } -} diff --git a/app/src/main/java/org/mian/gitnex/fragments/PullRequestChangesFragment.java b/app/src/main/java/org/mian/gitnex/fragments/PullRequestChangesFragment.java index 0ec55756..21c34218 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/PullRequestChangesFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/PullRequestChangesFragment.java @@ -10,6 +10,8 @@ import androidx.viewpager2.adapter.FragmentStateAdapter; import com.google.android.material.tabs.TabLayoutMediator; import org.mian.gitnex.R; import org.mian.gitnex.databinding.FragmentPrChangesBinding; +import org.mian.gitnex.helpers.TinyDB; +import org.mian.gitnex.helpers.ViewPager2Transformers; /** * @author qwerty287 @@ -33,6 +35,8 @@ public class PullRequestChangesFragment extends Fragment { binding = FragmentPrChangesBinding.inflate(inflater, container, false); + TinyDB tinyDB = TinyDB.getInstance(getContext()); + binding.close.setOnClickListener(v -> requireActivity().finish()); binding.container.setAdapter( @@ -56,6 +60,10 @@ public class PullRequestChangesFragment extends Fragment { }); String[] tabs = new String[] {getString(R.string.tabTextFiles), getString(R.string.commits)}; + + ViewPager2Transformers.returnSelectedTransformer( + binding.container, tinyDB.getInt("fragmentTabsAnimationId", 0)); + new TabLayoutMediator( binding.tabs, binding.container, diff --git a/app/src/main/java/org/mian/gitnex/fragments/SSHKeysFragment.java b/app/src/main/java/org/mian/gitnex/fragments/SSHKeysFragment.java new file mode 100644 index 00000000..c061dc07 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/fragments/SSHKeysFragment.java @@ -0,0 +1,77 @@ +package org.mian.gitnex.fragments; + +import android.annotation.SuppressLint; +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 org.mian.gitnex.adapters.SSHKeysAdapter; +import org.mian.gitnex.databinding.FragmentAccountSettingsSshKeysBinding; +import org.mian.gitnex.viewmodels.AccountSettingsSSHKeysViewModel; + +/** + * @author M M Arif + */ +public class SSHKeysFragment extends Fragment { + + private FragmentAccountSettingsSshKeysBinding viewBinding; + private Context context; + private SSHKeysAdapter adapter; + private AccountSettingsSSHKeysViewModel accountSettingsSSHKeysViewModel; + + @Override + public View onCreateView( + @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + viewBinding = FragmentAccountSettingsSshKeysBinding.inflate(inflater, container, false); + context = getContext(); + + accountSettingsSSHKeysViewModel = + new ViewModelProvider(this).get(AccountSettingsSSHKeysViewModel.class); + + viewBinding.recyclerView.setHasFixedSize(true); + viewBinding.recyclerView.setLayoutManager(new LinearLayoutManager(context)); + + viewBinding.pullToRefresh.setOnRefreshListener( + () -> + new Handler(Looper.getMainLooper()) + .postDelayed( + () -> { + viewBinding.pullToRefresh.setRefreshing(false); + accountSettingsSSHKeysViewModel.loadKeysList(context); + }, + 200)); + + fetchDataAsync(); + + return viewBinding.getRoot(); + } + + @SuppressLint("NotifyDataSetChanged") + private void fetchDataAsync() { + + accountSettingsSSHKeysViewModel + .getKeysList(context) + .observe( + getViewLifecycleOwner(), + keysListMain -> { + adapter = new SSHKeysAdapter(keysListMain); + if (adapter.getItemCount() > 0) { + viewBinding.recyclerView.setAdapter(adapter); + viewBinding.noData.setVisibility(View.GONE); + } else { + adapter.notifyDataSetChanged(); + viewBinding.recyclerView.setAdapter(adapter); + viewBinding.noData.setVisibility(View.VISIBLE); + } + viewBinding.progressBar.setVisibility(View.GONE); + }); + } +} diff --git a/app/src/main/java/org/mian/gitnex/fragments/WatchedRepositoriesFragment.java b/app/src/main/java/org/mian/gitnex/fragments/WatchedRepositoriesFragment.java new file mode 100644 index 00000000..9c0d0d4f --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/fragments/WatchedRepositoriesFragment.java @@ -0,0 +1,176 @@ +package org.mian.gitnex.fragments; + +import android.content.Intent; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.EditorInfo; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; +import androidx.recyclerview.widget.LinearLayoutManager; +import org.mian.gitnex.R; +import org.mian.gitnex.activities.CreateRepoActivity; +import org.mian.gitnex.activities.MainActivity; +import org.mian.gitnex.adapters.ReposListAdapter; +import org.mian.gitnex.databinding.FragmentRepositoriesBinding; +import org.mian.gitnex.helpers.Constants; +import org.mian.gitnex.viewmodels.RepositoriesViewModel; + +/** + * @author M M Arif + */ +public class WatchedRepositoriesFragment extends Fragment { + + private RepositoriesViewModel repositoriesViewModel; + private FragmentRepositoriesBinding fragmentRepositoriesBinding; + private ReposListAdapter adapter; + private int page = 1; + private int resultLimit; + + @Override + public View onCreateView( + @NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + fragmentRepositoriesBinding = + FragmentRepositoriesBinding.inflate(inflater, container, false); + setHasOptionsMenu(true); + ((MainActivity) requireActivity()) + .setActionBarTitle(getResources().getString(R.string.navWatchedRepositories)); + repositoriesViewModel = new ViewModelProvider(this).get(RepositoriesViewModel.class); + + resultLimit = Constants.getCurrentResultLimit(getContext()); + + fragmentRepositoriesBinding.addNewRepo.setOnClickListener( + view -> { + Intent intent = new Intent(view.getContext(), CreateRepoActivity.class); + startActivity(intent); + }); + + fragmentRepositoriesBinding.recyclerView.setHasFixedSize(true); + fragmentRepositoriesBinding.recyclerView.setLayoutManager( + new LinearLayoutManager(getContext())); + + fragmentRepositoriesBinding.recyclerView.setPadding(0, 0, 0, 220); + fragmentRepositoriesBinding.recyclerView.setClipToPadding(false); + + fragmentRepositoriesBinding.pullToRefresh.setOnRefreshListener( + () -> + new Handler(Looper.getMainLooper()) + .postDelayed( + () -> { + page = 1; + fragmentRepositoriesBinding.pullToRefresh.setRefreshing( + false); + fetchDataAsync(); + fragmentRepositoriesBinding.progressBar.setVisibility( + View.VISIBLE); + }, + 50)); + + fetchDataAsync(); + + return fragmentRepositoriesBinding.getRoot(); + } + + private void fetchDataAsync() { + + repositoriesViewModel + .getRepositories( + page, + resultLimit, + "", + "watched", + null, + getContext(), + fragmentRepositoriesBinding) + .observe( + getViewLifecycleOwner(), + reposListMain -> { + adapter = new ReposListAdapter(reposListMain, getContext()); + adapter.setLoadMoreListener( + new ReposListAdapter.OnLoadMoreListener() { + + @Override + public void onLoadMore() { + + page += 1; + repositoriesViewModel.loadMoreRepos( + page, + resultLimit, + "", + "watched", + null, + getContext(), + adapter); + fragmentRepositoriesBinding.progressBar.setVisibility( + View.VISIBLE); + } + + @Override + public void onLoadFinished() { + + fragmentRepositoriesBinding.progressBar.setVisibility( + View.GONE); + } + }); + + if (adapter.getItemCount() > 0) { + fragmentRepositoriesBinding.recyclerView.setAdapter(adapter); + fragmentRepositoriesBinding.noData.setVisibility(View.GONE); + } else { + adapter.notifyDataChanged(); + fragmentRepositoriesBinding.recyclerView.setAdapter(adapter); + fragmentRepositoriesBinding.noData.setVisibility(View.VISIBLE); + } + + fragmentRepositoriesBinding.progressBar.setVisibility(View.GONE); + }); + } + + @Override + public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { + + inflater.inflate(R.menu.search_menu, menu); + super.onCreateOptionsMenu(menu, inflater); + + MenuItem searchItem = menu.findItem(R.id.action_search); + androidx.appcompat.widget.SearchView searchView = + (androidx.appcompat.widget.SearchView) searchItem.getActionView(); + searchView.setImeOptions(EditorInfo.IME_ACTION_DONE); + + searchView.setOnQueryTextListener( + new androidx.appcompat.widget.SearchView.OnQueryTextListener() { + + @Override + public boolean onQueryTextSubmit(String query) { + return false; + } + + @Override + public boolean onQueryTextChange(String newText) { + if (fragmentRepositoriesBinding.recyclerView.getAdapter() != null) { + adapter.getFilter().filter(newText); + } + return false; + } + }); + } + + @Override + public void onResume() { + super.onResume(); + + if (MainActivity.reloadRepos) { + page = 1; + fetchDataAsync(); + MainActivity.reloadRepos = false; + } + } +} 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 d037dcab..7040139c 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 @@ -7,7 +7,10 @@ import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; +import java.io.IOException; import java.util.Locale; +import okhttp3.ResponseBody; +import org.gitnex.tea4j.v2.models.Repository; import org.gitnex.tea4j.v2.models.User; import org.mian.gitnex.R; import org.mian.gitnex.clients.PicassoService; @@ -16,6 +19,7 @@ import org.mian.gitnex.databinding.FragmentProfileDetailBinding; import org.mian.gitnex.helpers.AlertDialogs; import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.ClickListener; +import org.mian.gitnex.helpers.Markdown; import org.mian.gitnex.helpers.RoundedTransformation; import org.mian.gitnex.helpers.TimeHelper; import org.mian.gitnex.helpers.TinyDB; @@ -63,6 +67,7 @@ public class DetailFragment extends Fragment { locale = getResources().getConfiguration().locale; getProfileDetail(username); + getProfileRepository(username); return binding.getRoot(); } @@ -162,4 +167,133 @@ public class DetailFragment extends Fragment { } }); } + + public void getProfileRepository(String username) { + + Call call = + RetrofitClient.getApiInterface(context).repoGet(username, ".profile"); + + call.enqueue( + new Callback<>() { + + @Override + public void onResponse( + @NonNull Call call, + @NonNull retrofit2.Response response) { + + if (response.isSuccessful() && response.body() != null) { + + switch (response.code()) { + case 200: + String defBranch = response.body().getDefaultBranch(); + binding.profileRepoView.setVisibility(View.VISIBLE); + + Call call_profile = + RetrofitClient.getWebInterface(getContext()) + .getFileContents( + username, + ".profile", + defBranch, + "README.md"); + + call_profile.enqueue( + new Callback<>() { + + @Override + public void onResponse( + @NonNull Call call_profile, + @NonNull retrofit2.Response + response) { + + if (isAdded()) { + + switch (response.code()) { + case 200: + assert response.body() != null; + new Thread( + () -> { + try { + Markdown + .render( + context, + response.body() + .string(), + binding.profileRepoContent); + } catch ( + IOException + e) { + requireActivity() + .runOnUiThread( + () -> + Toasty + .error( + context, + context + .getString( + R + .string + .genericError))); + } + }) + .start(); + break; + + case 401: + binding.profileRepoView + .setVisibility(View.GONE); + AlertDialogs + .authorizationTokenRevokedDialog( + context); + break; + + case 403: + binding.profileRepoView + .setVisibility(View.GONE); + Toasty.error( + context, + context.getString( + R.string + .authorizeError)); + break; + + default: + break; + } + } + } + + @Override + public void onFailure( + @NonNull Call call_profile, + @NonNull Throwable t) {} + }); + + break; + + case 401: + AlertDialogs.authorizationTokenRevokedDialog(context); + binding.profileRepoView.setVisibility(View.GONE); + break; + + case 403: + binding.profileRepoView.setVisibility(View.GONE); + Toasty.error( + context, context.getString(R.string.authorizeError)); + break; + + default: + binding.profileRepoView.setVisibility(View.GONE); + Toasty.error(context, getString(R.string.genericError)); + break; + } + } + binding.progressBar.setVisibility(View.GONE); + } + + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + binding.profileRepoView.setVisibility(View.GONE); + } + }); + } } diff --git a/app/src/main/java/org/mian/gitnex/helpers/ViewPager2Transformers.java b/app/src/main/java/org/mian/gitnex/helpers/ViewPager2Transformers.java new file mode 100644 index 00000000..86b2d4e6 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/helpers/ViewPager2Transformers.java @@ -0,0 +1,95 @@ +package org.mian.gitnex.helpers; + +import android.view.View; +import androidx.viewpager2.widget.ViewPager2; + +/** + * @author M M Arif + */ +public abstract class ViewPager2Transformers { + + public static void returnSelectedTransformer(ViewPager2 viewPager, int selection) { + + if (selection == 0) { + viewPager.setPageTransformer(new DepthPageTransformer()); + } else if (selection == 1) { + viewPager.setPageTransformer(new ZoomOutPageTransformer()); + } else if (selection == 2) { + viewPager.setPageTransformer(null); + } + } + + public static class DepthPageTransformer implements ViewPager2.PageTransformer { + private static final float MIN_SCALE = 0.75f; + + public void transformPage(View view, float position) { + int pageWidth = view.getWidth(); + + if (position < -1) { // [-Infinity,-1) + // This page is way off-screen to the left. + view.setAlpha(0f); + + } else if (position <= 0) { // [-1,0] + // Use the default slide transition when moving to the left page + view.setAlpha(1f); + view.setTranslationX(0f); + view.setScaleX(1f); + view.setScaleY(1f); + + } else if (position <= 1) { // (0,1] + // Fade the page out. + view.setAlpha(1 - position); + + // Counteract the default slide transition + view.setTranslationX(pageWidth * -position); + + // Scale the page down (between MIN_SCALE and 1) + float scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position)); + view.setScaleX(scaleFactor); + view.setScaleY(scaleFactor); + + } else { // (1,+Infinity] + // This page is way off-screen to the right. + view.setAlpha(0f); + } + } + } + + public static class ZoomOutPageTransformer implements ViewPager2.PageTransformer { + private static final float MIN_SCALE = 0.85f; + private static final float MIN_ALPHA = 0.5f; + + public void transformPage(View view, float position) { + int pageWidth = view.getWidth(); + int pageHeight = view.getHeight(); + + if (position < -1) { // [-Infinity,-1) + // This page is way off-screen to the left. + view.setAlpha(0f); + + } else if (position <= 1) { // [-1,1] + // Modify the default slide transition to shrink the page as well. + float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position)); + float vertMargin = pageHeight * (1 - scaleFactor) / 2; + float horzMargin = pageWidth * (1 - scaleFactor) / 2; + if (position < 0) { + view.setTranslationX(horzMargin - vertMargin / 2); + } else { + view.setTranslationX(-horzMargin + vertMargin / 2); + } + + // Scale the page down (between MIN_SCALE and 1). + view.setScaleX(scaleFactor); + view.setScaleY(scaleFactor); + + // Fade the page relative to its size. + view.setAlpha( + MIN_ALPHA + (scaleFactor - MIN_SCALE) / (1 - MIN_SCALE) * (1 - MIN_ALPHA)); + + } else { // (1,+Infinity] + // This page is way off-screen to the right. + view.setAlpha(0f); + } + } + } +} diff --git a/app/src/main/java/org/mian/gitnex/viewmodels/ProfileEmailsViewModel.java b/app/src/main/java/org/mian/gitnex/viewmodels/AccountSettingsEmailsViewModel.java similarity index 95% rename from app/src/main/java/org/mian/gitnex/viewmodels/ProfileEmailsViewModel.java rename to app/src/main/java/org/mian/gitnex/viewmodels/AccountSettingsEmailsViewModel.java index c442025e..a0b7e00d 100644 --- a/app/src/main/java/org/mian/gitnex/viewmodels/ProfileEmailsViewModel.java +++ b/app/src/main/java/org/mian/gitnex/viewmodels/AccountSettingsEmailsViewModel.java @@ -17,7 +17,7 @@ import retrofit2.Response; /** * @author M M Arif */ -public class ProfileEmailsViewModel extends ViewModel { +public class AccountSettingsEmailsViewModel extends ViewModel { private MutableLiveData> emailsList; diff --git a/app/src/main/java/org/mian/gitnex/viewmodels/AccountSettingsSSHKeysViewModel.java b/app/src/main/java/org/mian/gitnex/viewmodels/AccountSettingsSSHKeysViewModel.java new file mode 100644 index 00000000..0ff5f8b1 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/viewmodels/AccountSettingsSSHKeysViewModel.java @@ -0,0 +1,63 @@ +package org.mian.gitnex.viewmodels; + +import android.content.Context; +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.PublicKey; +import org.mian.gitnex.R; +import org.mian.gitnex.clients.RetrofitClient; +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 AccountSettingsSSHKeysViewModel extends ViewModel { + + private MutableLiveData> keysList; + private int resultLimit; + + public LiveData> getKeysList(Context ctx) { + + keysList = new MutableLiveData<>(); + resultLimit = Constants.getCurrentResultLimit(ctx); + loadKeysList(ctx); + + return keysList; + } + + public void loadKeysList(Context ctx) { + + Call> call = + RetrofitClient.getApiInterface(ctx).userCurrentListKeys("", 1, resultLimit); + + call.enqueue( + new Callback<>() { + + @Override + public void onResponse( + @NonNull Call> call, + @NonNull Response> response) { + + if (response.isSuccessful()) { + keysList.postValue(response.body()); + } else { + Toasty.error(ctx, ctx.getString(R.string.genericError)); + } + } + + @Override + public void onFailure( + @NonNull Call> call, @NonNull Throwable t) { + + Toasty.error(ctx, ctx.getString(R.string.genericServerResponseError)); + } + }); + } +} diff --git a/app/src/main/java/org/mian/gitnex/viewmodels/RepositoriesViewModel.java b/app/src/main/java/org/mian/gitnex/viewmodels/RepositoriesViewModel.java index b69c148e..67e65335 100644 --- a/app/src/main/java/org/mian/gitnex/viewmodels/RepositoriesViewModel.java +++ b/app/src/main/java/org/mian/gitnex/viewmodels/RepositoriesViewModel.java @@ -70,6 +70,11 @@ public class RepositoriesViewModel extends ViewModel { RetrofitClient.getApiInterface(ctx) .orgListTeamRepos(Long.valueOf(userLogin), page, resultLimit); break; + case "watched": + call = + RetrofitClient.getApiInterface(ctx) + .userCurrentListSubscriptions(page, resultLimit); + break; default: call = RetrofitClient.getApiInterface(ctx).userCurrentListRepos(page, resultLimit); break; diff --git a/app/src/main/res/drawable/app_logo_monochrome.xml b/app/src/main/res/drawable/app_logo_monochrome.xml new file mode 100644 index 00000000..5b98d2c3 --- /dev/null +++ b/app/src/main/res/drawable/app_logo_monochrome.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_account_settings.xml b/app/src/main/res/drawable/ic_account_settings.xml new file mode 100644 index 00000000..976c1618 --- /dev/null +++ b/app/src/main/res/drawable/ic_account_settings.xml @@ -0,0 +1,34 @@ + + + + + + diff --git a/app/src/main/res/drawable/ic_add.xml b/app/src/main/res/drawable/ic_add.xml index 6e1d4eda..6ebb51f6 100644 --- a/app/src/main/res/drawable/ic_add.xml +++ b/app/src/main/res/drawable/ic_add.xml @@ -1,5 +1,20 @@ - - + + + diff --git a/app/src/main/res/drawable/ic_done.xml b/app/src/main/res/drawable/ic_done.xml index 770e2652..b98ac290 100644 --- a/app/src/main/res/drawable/ic_done.xml +++ b/app/src/main/res/drawable/ic_done.xml @@ -1,6 +1,20 @@ - - + + + diff --git a/app/src/main/res/drawable/ic_language.xml b/app/src/main/res/drawable/ic_language.xml index 4d56c51a..b8760cfb 100644 --- a/app/src/main/res/drawable/ic_language.xml +++ b/app/src/main/res/drawable/ic_language.xml @@ -1,6 +1,27 @@ - - + + + + diff --git a/app/src/main/res/drawable/ic_markdown.xml b/app/src/main/res/drawable/ic_markdown.xml index b999100a..2ab34692 100644 --- a/app/src/main/res/drawable/ic_markdown.xml +++ b/app/src/main/res/drawable/ic_markdown.xml @@ -1,5 +1,27 @@ - - + + + + diff --git a/app/src/main/res/drawable/ic_pin.xml b/app/src/main/res/drawable/ic_pin.xml index 752919a6..07b1453a 100644 --- a/app/src/main/res/drawable/ic_pin.xml +++ b/app/src/main/res/drawable/ic_pin.xml @@ -1,11 +1,20 @@ - +android:width="24dp" +android:height="24dp" +android:viewportWidth="24" +android:viewportHeight="24"> + + diff --git a/app/src/main/res/drawable/ic_remove.xml b/app/src/main/res/drawable/ic_remove.xml index 7d4ed879..d39378d2 100644 --- a/app/src/main/res/drawable/ic_remove.xml +++ b/app/src/main/res/drawable/ic_remove.xml @@ -1,5 +1,13 @@ - - + + diff --git a/app/src/main/res/drawable/ic_reply.xml b/app/src/main/res/drawable/ic_reply.xml index 26538623..227306a2 100644 --- a/app/src/main/res/drawable/ic_reply.xml +++ b/app/src/main/res/drawable/ic_reply.xml @@ -1,5 +1,20 @@ - - + + + diff --git a/app/src/main/res/drawable/ic_star.xml b/app/src/main/res/drawable/ic_star.xml index cdf6b04e..c06bb8aa 100644 --- a/app/src/main/res/drawable/ic_star.xml +++ b/app/src/main/res/drawable/ic_star.xml @@ -2,7 +2,6 @@ android:width="24dp" android:height="24dp" android:viewportWidth="24" - android:viewportHeight="24"> - - +android:width="24dp" +android:height="24dp" +android:viewportWidth="24" +android:viewportHeight="24"> + + + + diff --git a/app/src/main/res/drawable/ic_verified_user.xml b/app/src/main/res/drawable/ic_verified_user.xml index ccced5da..a7723da3 100644 --- a/app/src/main/res/drawable/ic_verified_user.xml +++ b/app/src/main/res/drawable/ic_verified_user.xml @@ -1,6 +1,20 @@ - - + + + diff --git a/app/src/main/res/drawable/ic_warning.xml b/app/src/main/res/drawable/ic_warning.xml index d35666b4..b2dedfed 100644 --- a/app/src/main/res/drawable/ic_warning.xml +++ b/app/src/main/res/drawable/ic_warning.xml @@ -1,5 +1,27 @@ - - + + + + diff --git a/app/src/main/res/drawable/ic_watchers.xml b/app/src/main/res/drawable/ic_watchers.xml index 3a307142..82973b4c 100644 --- a/app/src/main/res/drawable/ic_watchers.xml +++ b/app/src/main/res/drawable/ic_watchers.xml @@ -1,6 +1,20 @@ - - + + + diff --git a/app/src/main/res/drawable/shape_bottom_sheet_top_corners.xml b/app/src/main/res/drawable/shape_bottom_sheet_top_corners.xml index ee5b9710..68443243 100644 --- a/app/src/main/res/drawable/shape_bottom_sheet_top_corners.xml +++ b/app/src/main/res/drawable/shape_bottom_sheet_top_corners.xml @@ -3,10 +3,10 @@ android:shape="rectangle"> + android:topLeftRadius="@dimen/dimen32dp" + android:topRightRadius="@dimen/dimen32dp"/> - + diff --git a/app/src/main/res/layout/activity_profile_email.xml b/app/src/main/res/layout/activity_account_settings_email.xml similarity index 98% rename from app/src/main/res/layout/activity_profile_email.xml rename to app/src/main/res/layout/activity_account_settings_email.xml index d1e08692..a4b5c8dd 100644 --- a/app/src/main/res/layout/activity_profile_email.xml +++ b/app/src/main/res/layout/activity_account_settings_email.xml @@ -60,7 +60,7 @@ android:layout_height="wrap_content" android:layout_marginTop="@dimen/dimen8dp" android:layout_marginBottom="@dimen/dimen8dp" - android:hint="@string/profileEmailTitle" + android:hint="@string/accountEmailTitle" android:textColorHint="?attr/hintColor" app:boxBackgroundColor="?attr/inputBackgroundColor" app:boxStrokeErrorColor="@color/darkRed" diff --git a/app/src/main/res/layout/activity_org_detail.xml b/app/src/main/res/layout/activity_org_detail.xml index 54e57a11..6b948f98 100644 --- a/app/src/main/res/layout/activity_org_detail.xml +++ b/app/src/main/res/layout/activity_org_detail.xml @@ -40,51 +40,19 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/primaryBackgroundColor" - android:visibility="gone" app:tabIndicatorColor="?attr/pagerTabIndicatorColor" - app:tabMode="scrollable" + app:tabIndicatorFullWidth="true" + app:tabMode="auto" + app:tabIndicatorHeight="@dimen/dimen4dp" app:tabTextAppearance="@style/customTabLayout" - app:tabTextColor="?attr/primaryTextColor"> - - - - - - - - - - - - + app:tabTextColor="?attr/primaryTextColor"/> - diff --git a/app/src/main/res/layout/activity_org_team_info.xml b/app/src/main/res/layout/activity_org_team_info.xml index 7a74161f..e5def625 100644 --- a/app/src/main/res/layout/activity_org_team_info.xml +++ b/app/src/main/res/layout/activity_org_team_info.xml @@ -51,6 +51,7 @@ app:tabIndicatorColor="?attr/pagerTabIndicatorColor" app:tabIndicatorFullWidth="true" app:tabMode="auto" + app:tabIndicatorHeight="@dimen/dimen4dp" app:tabTextAppearance="@style/customTabLayout" app:tabTextColor="?attr/primaryTextColor"/> diff --git a/app/src/main/res/layout/activity_profile.xml b/app/src/main/res/layout/activity_profile.xml index 363d8a80..cb6a05c8 100644 --- a/app/src/main/res/layout/activity_profile.xml +++ b/app/src/main/res/layout/activity_profile.xml @@ -39,7 +39,9 @@ android:layout_height="wrap_content" android:background="?attr/primaryBackgroundColor" app:tabIndicatorColor="?attr/pagerTabIndicatorColor" - app:tabMode="scrollable" + app:tabIndicatorFullWidth="true" + app:tabMode="auto" + app:tabIndicatorHeight="@dimen/dimen4dp" app:tabTextAppearance="@style/customTabLayout" app:tabTextColor="?attr/primaryTextColor"/> diff --git a/app/src/main/res/layout/activity_repo_detail.xml b/app/src/main/res/layout/activity_repo_detail.xml index f726decd..364b35f9 100644 --- a/app/src/main/res/layout/activity_repo_detail.xml +++ b/app/src/main/res/layout/activity_repo_detail.xml @@ -58,7 +58,9 @@ android:layout_height="wrap_content" android:background="?attr/primaryBackgroundColor" app:tabIndicatorColor="?attr/pagerTabIndicatorColor" - app:tabMode="scrollable" + app:tabIndicatorFullWidth="true" + app:tabMode="auto" + app:tabIndicatorHeight="@dimen/dimen4dp" app:tabTextAppearance="@style/customTabLayout" app:tabTextColor="?attr/primaryTextColor"> diff --git a/app/src/main/res/layout/activity_settings_appearance.xml b/app/src/main/res/layout/activity_settings_appearance.xml index 0d65497c..7ef42d6e 100644 --- a/app/src/main/res/layout/activity_settings_appearance.xml +++ b/app/src/main/res/layout/activity_settings_appearance.xml @@ -180,6 +180,39 @@ + + + + + + + + diff --git a/app/src/main/res/layout/fragment_account_settings.xml b/app/src/main/res/layout/fragment_account_settings.xml new file mode 100644 index 00000000..c8160860 --- /dev/null +++ b/app/src/main/res/layout/fragment_account_settings.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_profile_emails.xml b/app/src/main/res/layout/fragment_account_settings_emails.xml similarity index 100% rename from app/src/main/res/layout/fragment_profile_emails.xml rename to app/src/main/res/layout/fragment_account_settings_emails.xml diff --git a/app/src/main/res/layout/fragment_account_settings_ssh_keys.xml b/app/src/main/res/layout/fragment_account_settings_ssh_keys.xml new file mode 100644 index 00000000..7c6ac3a6 --- /dev/null +++ b/app/src/main/res/layout/fragment_account_settings_ssh_keys.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_explore.xml b/app/src/main/res/layout/fragment_explore.xml index 332df9ed..4a14f5f7 100644 --- a/app/src/main/res/layout/fragment_explore.xml +++ b/app/src/main/res/layout/fragment_explore.xml @@ -20,7 +20,9 @@ android:layout_height="wrap_content" android:background="?attr/primaryBackgroundColor" app:tabIndicatorColor="?attr/pagerTabIndicatorColor" - app:tabMode="scrollable" + app:tabIndicatorFullWidth="true" + app:tabMode="auto" + app:tabIndicatorHeight="@dimen/dimen4dp" app:tabTextAppearance="@style/customTabLayout" app:tabTextColor="?attr/primaryTextColor"/> diff --git a/app/src/main/res/layout/fragment_pr_changes.xml b/app/src/main/res/layout/fragment_pr_changes.xml index ebb85c0c..f18d3922 100644 --- a/app/src/main/res/layout/fragment_pr_changes.xml +++ b/app/src/main/res/layout/fragment_pr_changes.xml @@ -42,9 +42,10 @@ android:background="?attr/primaryBackgroundColor" app:tabGravity="fill" app:tabIndicatorColor="?attr/pagerTabIndicatorColor" - app:tabIndicatorFullWidth="false" app:tabMaxWidth="0dp" - app:tabMode="fixed" + app:tabIndicatorFullWidth="true" + app:tabMode="auto" + app:tabIndicatorHeight="@dimen/dimen4dp" app:tabTextAppearance="@style/customTabLayout" app:tabTextColor="?attr/primaryTextColor"/> diff --git a/app/src/main/res/layout/fragment_profile.xml b/app/src/main/res/layout/fragment_profile.xml deleted file mode 100644 index 04d49892..00000000 --- a/app/src/main/res/layout/fragment_profile.xml +++ /dev/null @@ -1,273 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_profile_detail.xml b/app/src/main/res/layout/fragment_profile_detail.xml index 461ccd2c..f502a159 100644 --- a/app/src/main/res/layout/fragment_profile_detail.xml +++ b/app/src/main/res/layout/fragment_profile_detail.xml @@ -17,276 +17,330 @@ style="@style/Widget.Material3.LinearProgressIndicator" app:indicatorColor="?attr/progressIndicatorColor" /> - + android:layout_height="match_parent"> + android:layout_height="wrap_content"> - - - - - - - + style="?attr/materialCardViewFilledStyle" + app:cardElevation="@dimen/dimen0dp"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:orientation="horizontal" + tools:ignore="UseCompoundDrawables"> - + - + + + + + android:paddingStart="@dimen/dimen24dp" + android:paddingEnd="@dimen/dimen10dp" + android:orientation="vertical"> + + + + + + + - - - - - - + android:layout_marginTop="@dimen/dimen20dp" + android:layout_marginBottom="@dimen/dimen0dp" + android:orientation="horizontal" + android:baselineAligned="false"> - + android:layout_weight="1" + android:gravity="center" + android:orientation="vertical"> - + + + + + + + android:layout_weight="1" + android:gravity="center" + android:orientation="vertical"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/app/src/main/res/layout/fragment_repo_info.xml b/app/src/main/res/layout/fragment_repo_info.xml index eb2edd36..0131bd08 100644 --- a/app/src/main/res/layout/fragment_repo_info.xml +++ b/app/src/main/res/layout/fragment_repo_info.xml @@ -254,7 +254,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:contentDescription="@string/repoWatchersInMenu" - app:srcCompat="@drawable/ic_unwatch"/> + app:srcCompat="@drawable/ic_watchers"/> diff --git a/app/src/main/res/layout/list_account_settings_ssh_keys.xml b/app/src/main/res/layout/list_account_settings_ssh_keys.xml new file mode 100644 index 00000000..5528b163 --- /dev/null +++ b/app/src/main/res/layout/list_account_settings_ssh_keys.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/menu/drawer_menu.xml b/app/src/main/res/menu/drawer_menu.xml index 5be95c19..0b3e6210 100644 --- a/app/src/main/res/menu/drawer_menu.xml +++ b/app/src/main/res/menu/drawer_menu.xml @@ -29,6 +29,10 @@ android:icon="@drawable/ic_repo" android:title="@string/navRepos"/> + + + + - \ No newline at end of file + + diff --git a/app/src/main/res/values-v23/themes.xml b/app/src/main/res/values-v23/themes.xml index a9b4b8cb..7ae108e8 100644 --- a/app/src/main/res/values-v23/themes.xml +++ b/app/src/main/res/values-v23/themes.xml @@ -91,4 +91,50 @@ + + + + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 11ad1b9c..142929e3 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -65,4 +65,14 @@ #151515 #161918 #0C0C0C + + #0c1a24 + #7f8699 + #082437 + #0c1a24 + #082437 + #04121b + #9dacb6 + #14507d + #04121b diff --git a/app/src/main/res/values/settings.xml b/app/src/main/res/values/settings.xml index 2d985ff3..94c2649d 100644 --- a/app/src/main/res/values/settings.xml +++ b/app/src/main/res/values/settings.xml @@ -38,6 +38,7 @@ Pitch Black @string/followSystem @string/followSystemBlack + @@ -50,6 +51,7 @@ @string/followSystem @string/followSystemBlack @string/dynamicColorsFollowSystem + @string/codebergDark @@ -67,7 +69,7 @@ @string/pageTitleStarredRepos @string/navOrg @string/navRepos - @string/navProfile + @string/navAccount @string/pageTitleExplore @string/titleDrafts @string/pageTitleNotifications @@ -75,10 +77,11 @@ @string/navMostVisited @string/navNotes @string/dashboard + @string/navWatchedRepositories - @string/generalDeepLinkSelectedText + @string/none @string/navRepos @string/navOrg @string/pageTitleNotifications @@ -109,4 +112,10 @@ 6 8 + + + @string/fadeOut + @string/zoomOut + @string/none + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f2a757ae..7dbbe187 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -35,6 +35,8 @@ My Issues Most Visited Repos Notes + Account Settings + Watched Repositories @@ -246,7 +248,6 @@ Home screen, drafts, crash reports Default Link Handler Choose what screen should be loaded if the app cannot handle external links. It will redirect you automatically. - N/A Select Default Link Handler Screen Biometric Support Labels With Text Support @@ -255,6 +256,9 @@ Indentation Tabs Width System Default Font + Tabs Animation + Fade Out + Zoom Out No more data available @@ -342,16 +346,19 @@ Followers Following - Add Email Address - Email Address + \u0040%1$s + + + + Emails + Email Address New email added successfully Email address is empty Email address is not valid Email address is already in use Primary - Emails - \u0040%1$s - + SSH Keys + Add / Remove Labels @@ -516,6 +523,7 @@ Delete %s Reset BETA + None Explore users @@ -778,6 +786,7 @@ Follow system (Light/Dark) Follow system (Light/Pitch Black) Dynamic colors - Follow system (Light/Dark) + Codeberg (Dark) Fork of: %s Adopt Adopted repository %s diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 30dbb06a..c0cf3ca4 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -182,6 +182,52 @@ + + + + @@ -389,6 +435,28 @@ + + + + + +