diff --git a/.gitea/issue_template/bug.md b/.gitea/issue_template/bug.md index 05c82f17..9ae53483 100644 --- a/.gitea/issue_template/bug.md +++ b/.gitea/issue_template/bug.md @@ -31,7 +31,7 @@ labels:

-- [ ] I carefully read the [contribution guidelines](https://codeberg.org/GitNex/GitNex/src/branch/main/CONTRIBUTING.md). +- [ ] I carefully read the [contribution guidelines](https://codeberg.org/gitnex/GitNex/wiki/Contributing).
- \ No newline at end of file + diff --git a/.gitea/issue_template/feature.md b/.gitea/issue_template/feature.md index ff712012..14d225b9 100644 --- a/.gitea/issue_template/feature.md +++ b/.gitea/issue_template/feature.md @@ -11,7 +11,7 @@ labels: ## # Describe your matter briefly

-- [ ] I carefully read the [contribution guidelines](https://codeberg.org/GitNex/GitNex/src/branch/main/CONTRIBUTING.md). +- [ ] I carefully read the [contribution guidelines](https://codeberg.org/gitnex/GitNex/wiki/Contributing).
diff --git a/.gitea/issue_template/suggestion.md b/.gitea/issue_template/suggestion.md index d40f242a..0e00e914 100644 --- a/.gitea/issue_template/suggestion.md +++ b/.gitea/issue_template/suggestion.md @@ -31,7 +31,7 @@ labels:

-- [ ] I carefully read the [contribution guidelines](https://codeberg.org/GitNex/GitNex/src/branch/main/CONTRIBUTING.md). +- [ ] I carefully read the [contribution guidelines](https://codeberg.org/gitnex/GitNex/wiki/Contributing).
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index de3a1a9c..00000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,36 +0,0 @@ -# Contributing to GitNex - -Please take a few minutes to read this document to make the process of contribution more easy and healthy for all involved. - -### General -> **Be polite and gentle while commenting or creating new issues to maintain a healthy environment in which __everyone__ is able to feel comfortable.** -
- -### Issues and Reports -Before creating an issue please take a moment and search the repository issues(open/closed) to avoid duplicate issues either it's a bug or feature. -In case you want to submit a bug report, please provide as much details as possible to better debug the problem. The important part is how to reproduce the bug and steps to reproduce are appreciated.

-**Note:** Please contact the project directly via [email](mailto:gitnex@swatian.com) if have to share sensitive and security related details. -
- -### Pull Requests -Patches, enhancements and features are always welcome. -The PR should focus on the scope of work and avoid many unnecessary commits. -Please provide as much detail and context as possible to explain the work submitted. - -**Please ask if you are not sure about the scope of work to be submitted to avoid waste of time spent on the work.** (Submit an issue, __before__ submitting a PR) - -**Code Standards**

-Please follow the code standards, this will help other developers to understand your code too. -It also helps maintaining the code afterwards. -It is documented in the Wiki: [Code-Standards](https://codeberg.org/gitnex/GitNex/wiki/Code-Standards) - -**How to submit a PR (Pull Request)** -1. Fork this repository. -2. Clone the forked repository from your namespace to your local machine. -3. Create a new branch and work on your feature, enhancement or patch. -4. Push your commits to your forked version. -5. You can now create a PR using the web interface against **main** branch. - -For more information, click [here](http://makeapullrequest.com/). - -**IMPORTANT:** By submitting PR, you agree to allow GitNex to license your work under the same license as that used by GitNex. \ No newline at end of file diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md deleted file mode 100644 index 73b19345..00000000 --- a/CONTRIBUTORS.md +++ /dev/null @@ -1,37 +0,0 @@ -# Contributors -This part lists all PUBLIC individuals having contributed content to the code. - - * M M Arif (mmarif) - * 6543 - * opyale - * Unpublished - -# Translators -This part lists all PUBLIC individuals having contributed content to the translation. -*Entries are in alphabetical order* - - * 6543 - * acrylicpaintboy - * Antoine GIRARD (sapk) - * BaRaN6161_TURK - * ButterflyOfFire (BoFFire) - * dadosch - * erardiflorian - * IndeedNotJames - * jaqra - * Lafriks - * ljoonal - * Lunny Xiao (xiaolunwen) - * lxs - * Marcos de Oliveira (markkrj) - * mmarif - * Nadezhda Moiseeva (digitalkiller) - * Oleg Popenkov (FanHamMer) - * PsychotherapistSam - * Rodion Borisov (vintproykt) - * s4ne - * valeriezhao1013 - * Vladislav Glinsky (cl0ne) - * Voyvode - -**Thank you for all your work** :+1: diff --git a/README.md b/README.md index ba0f8b7b..ea568674 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ Option 2 - Open terminal(Linux) and cd to the project dir. Run `./gradlew assemb - [MANY MORE](https://codeberg.org/gitnex/GitNex/wiki/Features) ## Contributing -[CONTRIBUTING](https://codeberg.org/gitnex/GitNex/src/branch/main/CONTRIBUTING.md) +[Contributing](https://codeberg.org/gitnex/GitNex/wiki/Contributing) ## Translation Help us translate GitNex to your native language. diff --git a/app/build.gradle b/app/build.gradle index 8c2c58a8..3eadd0f8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -6,7 +6,7 @@ android { applicationId "org.mian.gitnex" minSdkVersion 21 targetSdkVersion 31 - versionCode 415 + versionCode 420 versionName "4.2.0" multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -54,9 +54,9 @@ configurations { } dependencies { - def lifecycle_version = '2.4.0' + def lifecycle_version = '2.4.1' def markwon_version = '4.6.2' - def work_version = "2.7.0-alpha05" + def work_version = "2.7.1" def acra = "5.7.0" implementation fileTree(include: ['*.jar'], dir: 'libs') @@ -114,5 +114,4 @@ dependencies { implementation 'androidx.biometric:biometric:1.1.0' implementation 'com.github.chrisvest:stormpot:2.4.2' implementation 'androidx.browser:browser:1.4.0' - } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 58fcc5d1..cf2c0600 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -18,7 +18,6 @@ android:roundIcon="@mipmap/app_logo_round" android:supportsRtl="true" tools:targetApi="n"> - @@ -60,7 +59,7 @@ android:name=".activities.CreateTeamByOrgActivity" android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|uiMode|keyboard|keyboardHidden|navigation" /> - @@ -98,7 +96,7 @@ android:name=".activities.LoginActivity" android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|uiMode|keyboard|keyboardHidden|navigation" android:launchMode="singleTask" - android:theme="@android:style/Theme.NoTitleBar"/> + android:theme="@android:style/Theme.NoTitleBar" /> @@ -108,6 +106,10 @@ + @@ -154,28 +156,28 @@ - - - - - - - - + android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|uiMode|keyboard|keyboardHidden|navigation" /> + + + + @@ -185,17 +187,7 @@ - - - - - - - - - - diff --git a/app/src/main/java/org/mian/gitnex/actions/NotificationsActions.java b/app/src/main/java/org/mian/gitnex/actions/NotificationsActions.java deleted file mode 100644 index 8c5e2946..00000000 --- a/app/src/main/java/org/mian/gitnex/actions/NotificationsActions.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.mian.gitnex.actions; - -import android.content.Context; -import org.gitnex.tea4j.models.NotificationThread; -import org.mian.gitnex.clients.RetrofitClient; -import org.mian.gitnex.helpers.AppUtil; -import org.mian.gitnex.helpers.TinyDB; -import java.io.IOException; -import java.util.Date; -import retrofit2.Call; - -/** - * Author opyale - */ - -public class NotificationsActions { - - public enum NotificationStatus {READ, UNREAD, PINNED} - - private TinyDB tinyDB; - private Context context; - private String instanceToken; - - public NotificationsActions(Context context) { - - this.context = context; - this.tinyDB = TinyDB.getInstance(context); - - String loginUid = tinyDB.getString("loginUid"); - - instanceToken = "token " + tinyDB.getString(loginUid + "-token"); - - } - - public void setNotificationStatus(NotificationThread notificationThread, NotificationStatus notificationStatus) throws IOException { - - Call call = RetrofitClient.getApiInterface(context) - .markNotificationThreadAsRead(instanceToken, notificationThread.getId(), notificationStatus.name()); - - if(!call.execute().isSuccessful()) { - - throw new IllegalStateException(); - } - } - - public boolean setAllNotificationsRead(Date date) throws IOException { - - Call call = RetrofitClient.getApiInterface(context) - .markNotificationThreadsAsRead(instanceToken, AppUtil.getTimestampFromDate(context, date), true, - new String[]{"unread", "pinned"}, "read"); - - return call.execute().isSuccessful(); - - } - -} diff --git a/app/src/main/java/org/mian/gitnex/activities/CreateFileActivity.java b/app/src/main/java/org/mian/gitnex/activities/CreateFileActivity.java index 380fa92f..674824f9 100644 --- a/app/src/main/java/org/mian/gitnex/activities/CreateFileActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/CreateFileActivity.java @@ -124,7 +124,7 @@ public class CreateFileActivity extends BaseActivity { disableProcessButton(); NetworkStatusObserver networkStatusObserver = NetworkStatusObserver.getInstance(ctx); - networkStatusObserver.registerNetworkStatusListener(binding.newFileCreate::setEnabled); + networkStatusObserver.registerNetworkStatusListener(hasNetworkConnection -> runOnUiThread(() -> binding.newFileCreate.setEnabled(hasNetworkConnection))); binding.newFileCreate.setOnClickListener(v -> processNewFile()); diff --git a/app/src/main/java/org/mian/gitnex/activities/DiffActivity.java b/app/src/main/java/org/mian/gitnex/activities/DiffActivity.java new file mode 100644 index 00000000..a3211398 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/activities/DiffActivity.java @@ -0,0 +1,49 @@ +package org.mian.gitnex.activities; + +import android.os.Bundle; +import androidx.activity.OnBackPressedCallback; +import org.mian.gitnex.R; +import org.mian.gitnex.databinding.ActivityDiffBinding; +import org.mian.gitnex.fragments.DiffFilesFragment; +import org.mian.gitnex.fragments.DiffFragment; + +/** + * @author opyale + */ + +public class DiffActivity extends BaseActivity { + + @Override + public void onCreate(Bundle savedInstanceState) { + + super.onCreate(savedInstanceState); + + ActivityDiffBinding binding = ActivityDiffBinding.inflate(getLayoutInflater()); + + setContentView(binding.getRoot()); + + DiffFilesFragment fragment = DiffFilesFragment.newInstance(); + + getOnBackPressedDispatcher().addCallback(new OnBackPressedCallback(true) { + + @Override + public void handleOnBackPressed() { + if(getSupportFragmentManager().findFragmentById(R.id.fragment_container) instanceof DiffFragment) { + getSupportFragmentManager() + .beginTransaction() + .replace(R.id.fragment_container, fragment) + .commit(); + } else { + finish(); + } + } + }); + + getSupportFragmentManager() + .beginTransaction() + .replace(R.id.fragment_container, fragment) + .commit(); + + } + +} diff --git a/app/src/main/java/org/mian/gitnex/activities/FileDiffActivity.java b/app/src/main/java/org/mian/gitnex/activities/FileDiffActivity.java deleted file mode 100644 index fd951dd5..00000000 --- a/app/src/main/java/org/mian/gitnex/activities/FileDiffActivity.java +++ /dev/null @@ -1,152 +0,0 @@ -package org.mian.gitnex.activities; - -import android.os.Bundle; -import android.view.View; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.ProgressBar; -import android.widget.TextView; -import androidx.appcompat.widget.Toolbar; -import org.gitnex.tea4j.models.FileDiffView; -import org.mian.gitnex.R; -import org.mian.gitnex.adapters.FilesDiffAdapter; -import org.mian.gitnex.clients.RetrofitClient; -import org.mian.gitnex.databinding.ActivityFileDiffBinding; -import org.mian.gitnex.helpers.AlertDialogs; -import org.mian.gitnex.helpers.Authorization; -import org.mian.gitnex.helpers.ParseDiff; -import org.mian.gitnex.helpers.TinyDB; -import org.mian.gitnex.helpers.Toasty; -import org.mian.gitnex.helpers.Version; -import java.io.IOException; -import java.util.List; -import okhttp3.ResponseBody; -import retrofit2.Call; -import retrofit2.Response; - -/** - * Author M M Arif - */ - -public class FileDiffActivity extends BaseActivity { - - private View.OnClickListener onClickListener; - private TextView toolbarTitle; - private ListView mListView; - private ProgressBar mProgressBar; - - @Override - public void onCreate(Bundle savedInstanceState) { - - super.onCreate(savedInstanceState); - - ActivityFileDiffBinding activityFileDiffBinding = ActivityFileDiffBinding.inflate(getLayoutInflater()); - setContentView(activityFileDiffBinding.getRoot()); - - Toolbar toolbar = activityFileDiffBinding.toolbar; - setSupportActionBar(toolbar); - - final TinyDB tinyDb = TinyDB.getInstance(appCtx); - String repoFullName = tinyDb.getString("repoFullName"); - String[] parts = repoFullName.split("/"); - final String repoOwner = parts[0]; - final String repoName = parts[1]; - - ImageView closeActivity = activityFileDiffBinding.close; - toolbarTitle = activityFileDiffBinding.toolbarTitle; - mListView = activityFileDiffBinding.listView; - mProgressBar = activityFileDiffBinding.progressBar; - - mListView.setDivider(null); - - toolbarTitle.setText(R.string.processingText); - initCloseListener(); - closeActivity.setOnClickListener(onClickListener); - - mProgressBar.setVisibility(View.VISIBLE); - - String pullIndex = tinyDb.getString("issueNumber"); - - boolean apiCall = !new Version(tinyDb.getString("giteaVersion")).less("1.13.0"); - getPullDiffContent(repoOwner, repoName, pullIndex, apiCall); - - } - - private void getPullDiffContent(String owner, String repo, String pullIndex, boolean apiCall) { - - Thread thread = new Thread(() -> { - - Call call = apiCall ? - RetrofitClient.getApiInterface(ctx).getPullDiffContent(Authorization.get(ctx), owner, repo, pullIndex) : - RetrofitClient.getWebInterface(ctx).getPullDiffContent(Authorization.getWeb(ctx), owner, repo, pullIndex); - - try { - - Response response = call.execute(); - assert response.body() != null; - - switch(response.code()) { - - case 200: - List fileDiffViews = ParseDiff.getFileDiffViewArray(response.body().string()); - - int filesCount = fileDiffViews.size(); - - String toolbarTitleText = (filesCount > 1) ? - getResources().getString(R.string.fileDiffViewHeader, Integer.toString(filesCount)) : - getResources().getString(R.string.fileDiffViewHeaderSingle, Integer.toString(filesCount)); - - FilesDiffAdapter adapter = new FilesDiffAdapter(ctx, getSupportFragmentManager(), fileDiffViews); - - runOnUiThread(() -> { - toolbarTitle.setText(toolbarTitleText); - mListView.setAdapter(adapter); - mProgressBar.setVisibility(View.GONE); - }); - break; - - case 401: - runOnUiThread(() -> AlertDialogs.authorizationTokenRevokedDialog(ctx, - getString(R.string.alertDialogTokenRevokedTitle), - getString(R.string.alertDialogTokenRevokedMessage), - getString(R.string.cancelButton), - getString(R.string.navLogout))); - break; - - case 403: - runOnUiThread(() -> { - Toasty.error(ctx, ctx.getString(R.string.authorizeError)); - finish(); - }); - break; - - case 404: - runOnUiThread(() -> { - Toasty.warning(ctx, ctx.getString(R.string.apiNotFound)); - finish(); - }); - break; - - default: - runOnUiThread(() -> Toasty.error(ctx, getString(R.string.labelGeneralError))); - - } - } catch(IOException ignored) {} - - }); - - thread.start(); - - } - - private void initCloseListener() { - - onClickListener = view -> { - - getIntent().removeExtra("singleFileName"); - finish(); - - }; - } - -} diff --git a/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java b/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java index 7c84b55c..866a2444 100644 --- a/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/IssueDetailActivity.java @@ -202,9 +202,8 @@ public class IssueDetailActivity extends BaseActivity implements LabelsListAdapt fetchDataAsync(repoOwner, repoName, issueIndex); if(getIntent().getStringExtra("openPrDiff") != null && getIntent().getStringExtra("openPrDiff").equals("true")) { - startActivity(new Intent(ctx, FileDiffActivity.class)); + startActivity(new Intent(ctx, DiffActivity.class)); } - } @Override @@ -224,12 +223,10 @@ public class IssueDetailActivity extends BaseActivity implements LabelsListAdapt showAssignees(); break; } - } @Override - public void labelsInterface(List data) { - } + public void labelsInterface(List data) { } @Override public void labelsIdsInterface(List data) { diff --git a/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java b/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java index f47d7445..77ed11f1 100644 --- a/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java @@ -2,7 +2,6 @@ package org.mian.gitnex.activities; import android.content.Intent; import android.os.Bundle; -import android.os.Handler; import android.util.Log; import android.view.View; import android.widget.ArrayAdapter; @@ -85,60 +84,41 @@ public class LoginActivity extends BaseActivity { selectedProtocol = String.valueOf(parent.getItemAtPosition(position)); if(selectedProtocol.equals(String.valueOf(Protocol.HTTP))) { - Toasty.warning(ctx, getResources().getString(R.string.protocolError)); } }); if(R.id.loginToken == loginMethod.getCheckedRadioButtonId()) { - AppUtil.setMultiVisibility(View.GONE, findViewById(R.id.login_uidLayout), findViewById(R.id.login_passwdLayout), findViewById(R.id.otpCodeLayout)); findViewById(R.id.loginTokenCodeLayout).setVisibility(View.VISIBLE); - } - else { - + } else { AppUtil.setMultiVisibility(View.VISIBLE, findViewById(R.id.login_uidLayout), findViewById(R.id.login_passwdLayout), findViewById(R.id.otpCodeLayout)); findViewById(R.id.loginTokenCodeLayout).setVisibility(View.GONE); } loginMethod.setOnCheckedChangeListener((group, checkedId) -> { - if(checkedId == R.id.loginToken) { - AppUtil.setMultiVisibility(View.GONE, findViewById(R.id.login_uidLayout), findViewById(R.id.login_passwdLayout), findViewById(R.id.otpCodeLayout)); findViewById(R.id.loginTokenCodeLayout).setVisibility(View.VISIBLE); - } - else { - + } else { AppUtil.setMultiVisibility(View.VISIBLE, findViewById(R.id.login_uidLayout), findViewById(R.id.login_passwdLayout), findViewById(R.id.otpCodeLayout)); findViewById(R.id.loginTokenCodeLayout).setVisibility(View.GONE); } }); - Handler handler = new Handler(getMainLooper()); - - networkStatusObserver.registerNetworkStatusListener(hasNetworkConnection -> { - - handler.post(() -> { - - if(hasNetworkConnection) { - - enableProcessButton(); - } - else { - - disableProcessButton(); - loginButton.setText(getResources().getString(R.string.btnLogin)); - Toasty.error(ctx, getResources().getString(R.string.checkNetConnection)); - } - - }); - }); + networkStatusObserver.registerNetworkStatusListener(hasNetworkConnection -> runOnUiThread(() -> { + if(hasNetworkConnection) { + enableProcessButton(); + } else { + disableProcessButton(); + loginButton.setText(getResources().getString(R.string.btnLogin)); + Toasty.error(ctx, getResources().getString(R.string.checkNetConnection)); + } + })); loadDefaults(); loginButton.setOnClickListener(view -> { - disableProcessButton(); login(); }); 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 19dfcd6e..36f49e12 100644 --- a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java @@ -5,6 +5,7 @@ import android.content.Context; import android.content.Intent; import android.graphics.Typeface; import android.os.Bundle; +import android.os.Handler; import android.text.Html; import android.util.Log; import android.view.Menu; @@ -88,6 +89,7 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig setContentView(activityMainBinding.getRoot()); Intent mainIntent = getIntent(); + Handler handler = new Handler(); // DO NOT MOVE if(mainIntent.hasExtra("switchAccountId") && @@ -97,7 +99,6 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig mainIntent.removeExtra("switchAccountId"); recreate(); return; - } // DO NOT MOVE @@ -106,8 +107,6 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig loginUid = tinyDB.getString("loginUid"); instanceToken = "token " + tinyDB.getString(loginUid + "-token"); - boolean connToInternet = AppUtil.hasNetworkConnection(appCtx); - if(!tinyDB.getBoolean("loggedInMode")) { logout(this, ctx); @@ -136,7 +135,6 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig default: myTypeface = Typeface.createFromAsset(getAssets(), "fonts/manroperegular.ttf"); break; - } toolbarTitle.setTypeface(myTypeface); @@ -191,12 +189,6 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig @Override public void onDrawerOpened(@NonNull View drawerView) { - if(tinyDB.getBoolean("noConnection")) { - - Toasty.error(ctx, getResources().getString(R.string.checkNetConnection)); - tinyDB.putBoolean("noConnection", false); - } - String userEmailNav = tinyDB.getString("userEmail"); String userFullNameNav = tinyDB.getString("userFullname"); String userAvatarNav = tinyDB.getString("userAvatar"); @@ -418,21 +410,24 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig } } - if(!connToInternet) { + handler.postDelayed(() -> { - if(!tinyDB.getBoolean("noConnection")) { + boolean connToInternet = AppUtil.hasNetworkConnection(appCtx); + if(!connToInternet) { - Toasty.error(ctx, getResources().getString(R.string.checkNetConnection)); + if(!tinyDB.getBoolean("noConnection")) { + Toasty.error(ctx, getResources().getString(R.string.checkNetConnection)); + } + tinyDB.putBoolean("noConnection", true); } + else { - tinyDB.putBoolean("noConnection", true); - } - else { - - loadUserInfo(instanceToken, loginUid); - giteaVersion(); - tinyDB.putBoolean("noConnection", false); - } + loadUserInfo(instanceToken, loginUid); + giteaVersion(); + tinyDB.putBoolean("noConnection", false); + } + Log.e("Network status is: ", String.valueOf(connToInternet)); + }, 1500); // Changelog popup int versionCode = AppUtil.getAppBuildNo(appCtx); diff --git a/app/src/main/java/org/mian/gitnex/activities/OrganizationTeamInfoActivity.java b/app/src/main/java/org/mian/gitnex/activities/OrganizationTeamInfoActivity.java new file mode 100644 index 00000000..98890d7f --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/activities/OrganizationTeamInfoActivity.java @@ -0,0 +1,136 @@ +package org.mian.gitnex.activities; + +import android.annotation.SuppressLint; +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.widget.TextView; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.viewpager2.adapter.FragmentStateAdapter; +import com.google.android.material.tabs.TabLayoutMediator; +import org.gitnex.tea4j.models.OrgPermissions; +import org.gitnex.tea4j.models.Teams; +import org.mian.gitnex.R; +import org.mian.gitnex.databinding.ActivityOrgTeamInfoBinding; +import org.mian.gitnex.fragments.BottomSheetOrganizationTeamsFragment; +import org.mian.gitnex.fragments.OrganizationTeamInfoMembersFragment; +import org.mian.gitnex.fragments.OrganizationTeamInfoPermissionsFragment; +import org.mian.gitnex.helpers.TinyDB; +import org.mian.gitnex.structs.BottomSheetListener; + +/** + * Author M M Arif + */ + +public class OrganizationTeamInfoActivity extends BaseActivity implements BottomSheetListener { + + private ActivityOrgTeamInfoBinding binding; + private Teams team; + + @SuppressLint("SetTextI18n") + @Override + public void onCreate(Bundle savedInstanceState) { + + super.onCreate(savedInstanceState); + + binding = ActivityOrgTeamInfoBinding.inflate(getLayoutInflater()); + + setContentView(binding.getRoot()); + setSupportActionBar(binding.toolbar); + + team = (Teams) getIntent().getSerializableExtra("team"); + + if(team.getName() != null && !team.getName().isEmpty()) { + binding.toolbarTitle.setText(team.getName()); + } + else { + binding.toolbarTitle.setText(R.string.orgTeamMembers); + } + + binding.close.setOnClickListener(view -> finish()); + binding.pager.setAdapter(new FragmentStateAdapter(getSupportFragmentManager(), getLifecycle()) { + @NonNull + @Override + public Fragment createFragment(int position) { + switch(position) { + case 0: + return OrganizationTeamInfoMembersFragment.newInstance(team); + case 1: + return OrganizationTeamInfoPermissionsFragment.newInstance(team); + } + return null; + } + + @Override + public int getItemCount() { + return 2; + } + }); + + new TabLayoutMediator(binding.tabs, binding.pager, (tab, position) -> { + TextView textView = (TextView) LayoutInflater.from(ctx).inflate(R.layout.layout_tab_text, null); + + switch(position) { + case 0: + textView.setText(R.string.orgTabMembers); + break; + case 1: + textView.setText(R.string.teamPermissions); + break; + } + + tab.setCustomView(textView); + }).attach(); + } + + @Override + public void onResume() { + super.onResume(); + TinyDB tinyDb = TinyDB.getInstance(appCtx); + + if(tinyDb.getBoolean("teamActionFlag")) { + tinyDb.putBoolean("teamActionFlag", false); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + OrgPermissions permissions = (OrgPermissions) getIntent().getSerializableExtra("permissions"); + if(permissions == null || permissions.isOwner()) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.generic_nav_dotted_menu, menu); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + int id = item.getItemId(); + + if(id == android.R.id.home) { + finish(); + return true; + } + else if(id == R.id.genericMenu) { + BottomSheetOrganizationTeamsFragment bottomSheet = new BottomSheetOrganizationTeamsFragment(); + bottomSheet.show(getSupportFragmentManager(), "orgTeamsBottomSheet"); + return true; + } + else { + return super.onOptionsItemSelected(item); + } + } + + @Override + public void onButtonClicked(String text) { + if("newMember".equals(text)) { + Intent intent = new Intent(OrganizationTeamInfoActivity.this, AddNewTeamMemberActivity.class); + intent.putExtra("team", team); + startActivity(intent); + } + } +} diff --git a/app/src/main/java/org/mian/gitnex/activities/OrganizationTeamMembersActivity.java b/app/src/main/java/org/mian/gitnex/activities/OrganizationTeamMembersActivity.java deleted file mode 100644 index 3d18624f..00000000 --- a/app/src/main/java/org/mian/gitnex/activities/OrganizationTeamMembersActivity.java +++ /dev/null @@ -1,164 +0,0 @@ -package org.mian.gitnex.activities; - -import android.content.Intent; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.widget.GridView; -import android.widget.ImageView; -import android.widget.ProgressBar; -import android.widget.TextView; -import androidx.appcompat.widget.Toolbar; -import androidx.lifecycle.ViewModelProvider; -import org.gitnex.tea4j.models.OrgPermissions; -import org.mian.gitnex.R; -import org.mian.gitnex.adapters.UserGridAdapter; -import org.mian.gitnex.databinding.ActivityOrgTeamMembersBinding; -import org.mian.gitnex.fragments.BottomSheetOrganizationTeamsFragment; -import org.mian.gitnex.helpers.Authorization; -import org.mian.gitnex.helpers.TinyDB; -import org.mian.gitnex.structs.BottomSheetListener; -import org.mian.gitnex.viewmodels.TeamMembersByOrgViewModel; -import java.util.Objects; - -/** - * Author M M Arif - */ - -public class OrganizationTeamMembersActivity extends BaseActivity implements BottomSheetListener { - - private TextView noDataMembers; - private View.OnClickListener onClickListener; - private UserGridAdapter adapter; - private GridView mGridView; - private ProgressBar progressBar; - - private String teamId; - - @Override - public void onCreate(Bundle savedInstanceState) { - - super.onCreate(savedInstanceState); - - ActivityOrgTeamMembersBinding activityOrgTeamMembersBinding = ActivityOrgTeamMembersBinding.inflate(getLayoutInflater()); - setContentView(activityOrgTeamMembersBinding.getRoot()); - - Toolbar toolbar = activityOrgTeamMembersBinding.toolbar; - setSupportActionBar(toolbar); - - ImageView closeActivity = activityOrgTeamMembersBinding.close; - TextView toolbarTitle = activityOrgTeamMembersBinding.toolbarTitle; - noDataMembers = activityOrgTeamMembersBinding.noDataMembers; - mGridView = activityOrgTeamMembersBinding.gridView; - progressBar = activityOrgTeamMembersBinding.progressBar; - - initCloseListener(); - closeActivity.setOnClickListener(onClickListener); - - if(getIntent().getStringExtra("teamTitle") != null && !Objects.requireNonNull(getIntent().getStringExtra("teamTitle")).equals("")) { - - toolbarTitle.setText(getIntent().getStringExtra("teamTitle")); - } - else { - - toolbarTitle.setText(R.string.orgTeamMembers); - } - - if(getIntent().getStringExtra("teamId") != null && !Objects.requireNonNull(getIntent().getStringExtra("teamId")).equals("")){ - - teamId = getIntent().getStringExtra("teamId"); - } - else { - - teamId = "0"; - } - - assert teamId != null; - fetchDataAsync(Authorization.get(ctx), Integer.parseInt(teamId)); - } - - @Override - public void onResume() { - - super.onResume(); - TinyDB tinyDb = TinyDB.getInstance(appCtx); - - if(tinyDb.getBoolean("teamActionFlag")) { - - fetchDataAsync(Authorization.get(ctx), Integer.parseInt(teamId)); - tinyDb.putBoolean("teamActionFlag", false); - } - } - - private void fetchDataAsync(String instanceToken, int teamId) { - - TeamMembersByOrgViewModel teamMembersModel = new ViewModelProvider(this).get(TeamMembersByOrgViewModel.class); - - teamMembersModel.getMembersByOrgList(instanceToken, teamId, ctx, noDataMembers, progressBar).observe(this, teamMembersListMain -> { - - adapter = new UserGridAdapter(ctx, teamMembersListMain); - - if(adapter.getCount() > 0) { - - mGridView.setAdapter(adapter); - noDataMembers.setVisibility(View.GONE); - } - else { - - adapter.notifyDataSetChanged(); - mGridView.setAdapter(adapter); - noDataMembers.setVisibility(View.VISIBLE); - } - - progressBar.setVisibility(View.GONE); - }); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - if(((OrgPermissions) getIntent().getSerializableExtra("permissions")).isOwner()) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.generic_nav_dotted_menu, menu); - } - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - int id = item.getItemId(); - - if(id == android.R.id.home) { - - finish(); - return true; - } - else if(id == R.id.genericMenu) { - - BottomSheetOrganizationTeamsFragment bottomSheet = new BottomSheetOrganizationTeamsFragment(); - bottomSheet.show(getSupportFragmentManager(), "orgTeamsBottomSheet"); - return true; - } - else { - - return super.onOptionsItemSelected(item); - } - } - - @Override - public void onButtonClicked(String text) { - - if("newMember".equals(text)) { - - Intent intent = new Intent(OrganizationTeamMembersActivity.this, AddNewTeamMemberActivity.class); - intent.putExtra("teamId", teamId); - startActivity(intent); - } - } - - private void initCloseListener() { - onClickListener = view -> finish(); - } -} diff --git a/app/src/main/java/org/mian/gitnex/activities/RepoForksActivity.java b/app/src/main/java/org/mian/gitnex/activities/RepoForksActivity.java index 0789e96c..b8aab2a0 100644 --- a/app/src/main/java/org/mian/gitnex/activities/RepoForksActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/RepoForksActivity.java @@ -4,7 +4,6 @@ import android.annotation.SuppressLint; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.text.method.ScrollingMovementMethod; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; @@ -42,7 +41,6 @@ import retrofit2.Response; public class RepoForksActivity extends BaseActivity { - private View.OnClickListener onClickListener; private TextView noData; private ProgressBar progressBar; private final String TAG = "RepositoryForks"; @@ -74,9 +72,7 @@ public class RepoForksActivity extends BaseActivity { final String repoOwner = parts[0]; final String repoName = parts[1]; - TextView toolbar_title = activityRepoForksBinding.toolbarTitle; - toolbar_title.setMovementMethod(new ScrollingMovementMethod()); - toolbar_title.setText(String.format("%s : %s", ctx.getResources().getString(R.string.infoTabRepoForksCount), repoName)); + activityRepoForksBinding.toolbarTitle.setText(ctx.getResources().getString(R.string.infoTabRepoForksCount)); ImageView closeActivity = activityRepoForksBinding.close; noData = activityRepoForksBinding.noData; @@ -84,21 +80,20 @@ public class RepoForksActivity extends BaseActivity { progressBar = activityRepoForksBinding.progressBar; SwipeRefreshLayout swipeRefresh = activityRepoForksBinding.pullToRefresh; - initCloseListener(); - closeActivity.setOnClickListener(onClickListener); + closeActivity.setOnClickListener(v -> { + getIntent().removeExtra("repoFullNameForForks"); + finish(); + }); // if gitea is 1.12 or higher use the new limit (resultLimitNewGiteaInstances) if(new Version(tinyDb.getString("giteaVersion")).higherOrEqual("1.12")) { - resultLimit = Constants.resultLimitNewGiteaInstances; } recyclerView = activityRepoForksBinding.recyclerView; forksList = new ArrayList<>(); - DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), - DividerItemDecoration.VERTICAL); - recyclerView.addItemDecoration(dividerItemDecoration); + recyclerView.addItemDecoration(new DividerItemDecoration(recyclerView.getContext(), DividerItemDecoration.VERTICAL)); swipeRefresh.setOnRefreshListener(() -> new Handler(Looper.getMainLooper()).postDelayed(() -> { @@ -141,30 +136,24 @@ public class RepoForksActivity extends BaseActivity { assert response.body() != null; if(response.body().size() > 0) { - forksList.clear(); forksList.addAll(response.body()); adapter.notifyDataChanged(); noData.setVisibility(View.GONE); - } - else { - + } else { forksList.clear(); adapter.notifyDataChanged(); noData.setVisibility(View.VISIBLE); } progressBar.setVisibility(View.GONE); - } - else { - + } else { Log.e(TAG, String.valueOf(response.code())); } } @Override public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - Log.e(TAG, t.toString()); } }); @@ -193,27 +182,21 @@ public class RepoForksActivity extends BaseActivity { assert result != null; if(result.size() > 0) { - pageSize = result.size(); forksList.addAll(result); - } - else { - + } else { adapter.setMoreDataAvailable(false); } adapter.notifyDataChanged(); progressLoadMore.setVisibility(View.GONE); - } - else { - + } else { Log.e(TAG, String.valueOf(response.code())); } } @Override public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - Log.e(TAG, t.toString()); } @@ -235,44 +218,31 @@ public class RepoForksActivity extends BaseActivity { @Override public boolean onQueryTextSubmit(String query) { - return false; } @Override public boolean onQueryTextChange(String newText) { - filter(newText); return true; } - }); return super.onCreateOptionsMenu(menu); } private void filter(String text) { - - List arr = new ArrayList<>(); + List userRepositories = new ArrayList<>(); for(UserRepositories d : forksList) { + if(d.getName().toLowerCase().contains(text) || + d.getDescription().toLowerCase().contains(text)) { - if(d.getName().toLowerCase().contains(text) || d.getDescription().toLowerCase().contains(text)) { - - arr.add(d); + userRepositories.add(d); } } - adapter.updateList(arr); - } - - private void initCloseListener() { - - onClickListener = view -> { - - getIntent().removeExtra("repoFullNameForForks"); - finish(); - }; + adapter.updateList(userRepositories); } } diff --git a/app/src/main/java/org/mian/gitnex/adapters/CollaboratorsAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/CollaboratorsAdapter.java index 6e90df0f..42cc7eef 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/CollaboratorsAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/CollaboratorsAdapter.java @@ -77,7 +77,7 @@ public class CollaboratorsAdapter extends BaseAdapter { @Override public View getView(int position, View finalView, ViewGroup parent) { - ViewHolder viewHolder = null; + ViewHolder viewHolder; if (finalView == null) { diff --git a/app/src/main/java/org/mian/gitnex/adapters/CommitsAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/CommitsAdapter.java index fc056177..ca223ff3 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/CommitsAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/CommitsAdapter.java @@ -1,25 +1,22 @@ package org.mian.gitnex.adapters; -import android.annotation.SuppressLint; import android.content.Context; -import android.content.Intent; -import android.net.Uri; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.Button; +import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.core.text.HtmlCompat; import androidx.recyclerview.widget.RecyclerView; import com.vdurmont.emoji.EmojiParser; import org.gitnex.tea4j.models.Commits; import org.mian.gitnex.R; +import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.helpers.AppUtil; -import org.mian.gitnex.helpers.ClickListener; +import org.mian.gitnex.helpers.RoundedTransformation; import org.mian.gitnex.helpers.TimeHelper; -import org.mian.gitnex.helpers.TinyDB; import java.util.List; -import java.util.Locale; /** * Author M M Arif @@ -48,8 +45,7 @@ public class CommitsAdapter extends RecyclerView.Adapter 1 && !commitMessageParts[1].trim().isEmpty()) { + commitBody.setVisibility(View.VISIBLE); + commitSubject.setText(EmojiParser.parseToUnicode(commitMessageParts[0].trim())); + commitBody.setText(EmojiParser.parseToUnicode(commitMessageParts[1].trim())); + } else { + commitSubject.setText(EmojiParser.parseToUnicode(commitMessageParts[0].trim())); + commitBody.setVisibility(View.GONE); } - commitHtmlUrl.setOnClickListener(v -> AppUtil.openUrlInBrowser(context, commitsModel.getHtml_url())); - } + if(commitsModel.getCommitter().getId() != commitsModel.getAuthor().getId()) { + commitAuthorAndCommitter.setText(HtmlCompat.fromHtml(context + .getString(R.string.commitAuthoredByAndCommittedByWhen, commitsModel.getAuthor().getUsername(), commitsModel.getCommitter().getUsername(), + TimeHelper + .formatTime(commitsModel.getCommit().getCommitter().getDate(), context.getResources().getConfiguration().locale, "pretty", + context)), HtmlCompat.FROM_HTML_MODE_COMPACT)); + } else { + commitAuthorAndCommitter.setText(HtmlCompat.fromHtml(context + .getString(R.string.commitCommittedByWhen, commitsModel.getCommitter().getUsername(), + TimeHelper + .formatTime(commitsModel.getCommit().getCommitter().getDate(), context.getResources().getConfiguration().locale, "pretty", + context)), HtmlCompat.FROM_HTML_MODE_COMPACT)); + + } + + if(commitsModel.getAuthor().getAvatar_url() != null && + !commitsModel.getAuthor().getAvatar_url().isEmpty()) { + + commitAuthorAvatar.setVisibility(View.VISIBLE); + + int imgRadius = AppUtil.getPixelsFromDensity(context, 3); + + PicassoService.getInstance(context).get() + .load(commitsModel.getAuthor().getAvatar_url()) + .placeholder(R.drawable.loader_animated) + .transform(new RoundedTransformation(imgRadius, 0)) + .resize(120, 120) + .centerCrop().into(commitAuthorAvatar); + + } else { + commitAuthorAvatar.setImageDrawable(null); + commitAuthorAvatar.setVisibility(View.GONE); + } + + if(!commitsModel.getAuthor().getLogin().equals(commitsModel.getCommitter().getLogin()) && + commitsModel.getCommitter().getAvatar_url() != null && + !commitsModel.getCommitter().getAvatar_url().isEmpty()) { + + commitCommitterAvatar.setVisibility(View.VISIBLE); + + int imgRadius = AppUtil.getPixelsFromDensity(context, 3); + + PicassoService.getInstance(context).get() + .load(commitsModel.getCommitter().getAvatar_url()) + .placeholder(R.drawable.loader_animated) + .transform(new RoundedTransformation(imgRadius, 0)) + .resize(120, 120) + .centerCrop().into(commitCommitterAvatar); + + } else { + commitCommitterAvatar.setImageDrawable(null); + commitCommitterAvatar.setVisibility(View.GONE); + } + + commitSha.setText(commitsModel.getSha().substring(0, Math.min(commitsModel.getSha().length(), 10))); + rootView.setOnClickListener(v -> AppUtil.openUrlInBrowser(context, commitsModel.getHtml_url())); + + } } static class LoadHolder extends RecyclerView.ViewHolder { - LoadHolder(View itemView) { super(itemView); } diff --git a/app/src/main/java/org/mian/gitnex/adapters/DiffAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/DiffAdapter.java new file mode 100644 index 00000000..3130f806 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/adapters/DiffAdapter.java @@ -0,0 +1,151 @@ +package org.mian.gitnex.adapters; + +import android.content.Context; +import android.graphics.Typeface; +import android.os.Bundle; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; +import androidx.fragment.app.FragmentManager; +import org.mian.gitnex.R; +import org.mian.gitnex.fragments.BottomSheetReplyFragment; +import org.mian.gitnex.helpers.AppUtil; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Author opyale + */ + +public class DiffAdapter extends BaseAdapter { + + private final Context context; + private final FragmentManager fragmentManager; + private final List lines; + + private final List selectedLines; + private final Typeface typeface; + + private static int COLOR_ADDED; + private static int COLOR_REMOVED; + private static int COLOR_NORMAL; + private static int COLOR_SELECTED; + private static int COLOR_FONT; + + public DiffAdapter(Context context, FragmentManager fragmentManager, List lines) { + + this.context = context; + this.fragmentManager = fragmentManager; + this.lines = lines; + + selectedLines = new ArrayList<>(); + typeface = Typeface.createFromAsset(context.getAssets(), "fonts/sourcecodeproregular.ttf"); + + COLOR_ADDED = AppUtil.getColorFromAttribute(context, R.attr.diffAddedColor); + COLOR_REMOVED = AppUtil.getColorFromAttribute(context, R.attr.diffRemovedColor); + COLOR_NORMAL = AppUtil.getColorFromAttribute(context, R.attr.primaryBackgroundColor); + COLOR_SELECTED = AppUtil.getColorFromAttribute(context, R.attr.diffSelectedColor); + COLOR_FONT = AppUtil.getColorFromAttribute(context, R.attr.inputTextColor); + + } + + @Override + public int getCount() { + return lines.size(); + } + + @Override + public Object getItem(int position) { + return lines.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + + if(convertView == null) { + + TextView textView = new TextView(context); + + textView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + textView.setTextColor(COLOR_FONT); + textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); + textView.setPadding(15, 0, 15, 0); + textView.setTypeface(typeface); + + convertView = textView; + + } + + convertView.setOnClickListener(v -> { + + if(selectedLines.contains(position)) { + + selectedLines.remove((Object) position); + v.setBackgroundColor(getLineColor(lines.get(position))); + } else { + + selectedLines.add(position); + v.setBackgroundColor(COLOR_SELECTED); + } + }); + + convertView.setOnLongClickListener(v -> { + + if(selectedLines.contains(position)) { + + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("```\n"); + + for(Integer selectedLine : selectedLines.stream().sorted().collect(Collectors.toList())) { + stringBuilder.append(lines.get(selectedLine)); + stringBuilder.append("\n"); + } + + stringBuilder.append("```\n\n"); + selectedLines.clear(); + + Bundle bundle = new Bundle(); + bundle.putString("commentBody", stringBuilder.toString()); + bundle.putBoolean("cursorToEnd", true); + + BottomSheetReplyFragment.newInstance(bundle).show(fragmentManager, "replyBottomSheet"); + } + + return true; + + }); + + String line = lines.get(position); + + int backgroundColor = selectedLines.contains(position) ? COLOR_SELECTED : getLineColor(line); + + convertView.setBackgroundColor(backgroundColor); + ((TextView) convertView).setText(line); + + return convertView; + + } + + private int getLineColor(String line) { + + if(line.length() == 0) { + return COLOR_NORMAL; + } + + switch(line.charAt(0)) { + case '+': return COLOR_ADDED; + case '-': return COLOR_REMOVED; + + default: return COLOR_NORMAL; + } + } + +} diff --git a/app/src/main/java/org/mian/gitnex/adapters/DiffFilesAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/DiffFilesAdapter.java new file mode 100644 index 00000000..75888cd6 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/adapters/DiffFilesAdapter.java @@ -0,0 +1,90 @@ +package org.mian.gitnex.adapters; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; +import org.gitnex.tea4j.models.FileDiffView; +import org.mian.gitnex.R; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author opyale + */ + +public class DiffFilesAdapter extends BaseAdapter { + + private static final Pattern statisticsPattern = Pattern.compile("(\\d+).*?,.*?(\\d+)"); + + private final Context context; + private final List fileDiffViews; + + public DiffFilesAdapter(Context context, List fileDiffViews) { + this.context = context; + this.fileDiffViews = fileDiffViews; + } + + private static class ViewHolder { + private final TextView fileName; + private final TextView fileStatistics; + + public ViewHolder(TextView fileName, TextView fileStatistics) { + this.fileName = fileName; + this.fileStatistics = fileStatistics; + } + } + + @Override + public int getCount() { + return fileDiffViews.size(); + } + + @Override + public Object getItem(int position) { + return fileDiffViews.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + + ViewHolder viewHolder; + + if(convertView == null) { + convertView = LayoutInflater.from(context).inflate(R.layout.list_diff_files, parent, false); + + viewHolder = new ViewHolder( + convertView.findViewById(R.id.fileName), + convertView.findViewById(R.id.fileStatistics) + ); + + convertView.setTag(viewHolder); + } else { + viewHolder = (ViewHolder) convertView.getTag(); + } + + FileDiffView fileDiffView = fileDiffViews.get(position); + + viewHolder.fileName.setText(fileDiffView.getFileName()); + + Matcher matcher = statisticsPattern.matcher(fileDiffView.getFileInfo()); + + if(matcher.find() && matcher.groupCount() == 2) { + viewHolder.fileStatistics.setText(context.getString(R.string.diffStatistics, matcher.group(1), matcher.group(2))); + } else { + viewHolder.fileStatistics.setText(fileDiffView.getFileInfo()); + } + + return convertView; + + } + +} diff --git a/app/src/main/java/org/mian/gitnex/adapters/FilesDiffAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/FilesDiffAdapter.java deleted file mode 100644 index 681934da..00000000 --- a/app/src/main/java/org/mian/gitnex/adapters/FilesDiffAdapter.java +++ /dev/null @@ -1,252 +0,0 @@ -package org.mian.gitnex.adapters; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.graphics.Typeface; -import android.os.Bundle; -import android.util.TypedValue; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; -import androidx.fragment.app.FragmentManager; -import org.gitnex.tea4j.models.FileDiffView; -import org.mian.gitnex.R; -import org.mian.gitnex.fragments.BottomSheetReplyFragment; -import org.mian.gitnex.helpers.AppUtil; -import org.mian.gitnex.views.DiffTextView; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentSkipListMap; - -/** - * Author opyale - */ - -public class FilesDiffAdapter extends BaseAdapter { - - private static Map selectedViews; - private static final int MAXIMUM_LINES = 5000; - - private static int COLOR_ADDED; - private static int COLOR_REMOVED; - private static int COLOR_NORMAL; - private static int COLOR_SELECTED; - private static int COLOR_FONT; - - private final Context context; - private final FragmentManager fragmentManager; - private final List fileDiffViews; - - public FilesDiffAdapter(Context context, FragmentManager fragmentManager, List fileDiffViews) { - - this.context = context; - this.fragmentManager = fragmentManager; - this.fileDiffViews = fileDiffViews; - - selectedViews = new ConcurrentSkipListMap<>(); - - COLOR_ADDED = AppUtil.getColorFromAttribute(context, R.attr.diffAddedColor); - COLOR_REMOVED = AppUtil.getColorFromAttribute(context, R.attr.diffRemovedColor); - COLOR_NORMAL = AppUtil.getColorFromAttribute(context, R.attr.primaryBackgroundColor); - COLOR_SELECTED = AppUtil.getColorFromAttribute(context, R.attr.diffSelectedColor); - COLOR_FONT = AppUtil.getColorFromAttribute(context, R.attr.inputTextColor); - } - - @Override - public int getCount() { - - return fileDiffViews.size(); - } - - @Override - public Object getItem(int position) { - - return fileDiffViews.get(position); - } - - @Override - public long getItemId(int position) { - - return position; - } - - @SuppressLint({"ViewHolder", "InflateParams"}) - @Override - public View getView(int position, View convertView, ViewGroup parent) { - - convertView = LayoutInflater.from(context).inflate(R.layout.list_files_diffs, null, false); - - TextView headerFileName = convertView.findViewById(R.id.headerFileName); - TextView headerFileInfo = convertView.findViewById(R.id.headerFileInfo); - ImageView footerImage = convertView.findViewById(R.id.footerImage); - LinearLayout diffStats = convertView.findViewById(R.id.diff_stats); - LinearLayout diffLines = convertView.findViewById(R.id.diffLines); - - FileDiffView data = (FileDiffView) getItem(position); - headerFileName.setText(data.getFileName()); - - if(data.isFileBinary()) { - - diffStats.setVisibility(View.GONE); - diffLines.addView(getMessageView(context.getResources().getString(R.string.binaryFileError))); - } - else { - - diffStats.setVisibility(View.VISIBLE); - headerFileInfo.setText(data.getFileInfo()); - - String[] codeLines = getLines(data.toString()); - - if(MAXIMUM_LINES > codeLines.length) { - - for(int l=0; l 0) { - - int uniquePosition = l + (position * MAXIMUM_LINES); - - DiffTextView diffTextView = new DiffTextView(context); - - diffTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 15); - diffTextView.setPadding(15, 2, 15, 2); - diffTextView.setTypeface(Typeface.createFromAsset(context.getAssets(), "fonts/sourcecodeproregular.ttf")); - diffTextView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - diffTextView.setPosition(uniquePosition); - - boolean isSelected = false; - - for(View view : selectedViews.values()) { - - if(((DiffTextView) view).getPosition() == uniquePosition) { - - diffTextView.setBackgroundColor(COLOR_SELECTED); - isSelected = true; - break; - - } - - } - - - if(codeLines[l].startsWith("+")) { - - diffTextView.setText(codeLines[l]); - diffTextView.setTextColor(COLOR_FONT); - - if(!isSelected) { - - diffTextView.setInitialBackgroundColor(COLOR_ADDED); - } - - } - else if(codeLines[l].startsWith("-")) { - - diffTextView.setText(codeLines[l]); - diffTextView.setTextColor(COLOR_FONT); - - if(!isSelected) { - - diffTextView.setInitialBackgroundColor(COLOR_REMOVED); - } - - } - else { - - diffTextView.setText(codeLines[l]); - diffTextView.setTextColor(COLOR_FONT); - - if(!isSelected) { - - diffTextView.setInitialBackgroundColor(COLOR_NORMAL); - } - - } - - diffTextView.setOnClickListener(v -> { - - if(((DiffTextView) v).getCurrentBackgroundColor() != COLOR_SELECTED) { - - selectedViews.put(((DiffTextView) v).getPosition(), v); - v.setBackgroundColor(COLOR_SELECTED); - - } - else { - - selectedViews.remove(((DiffTextView) v).getPosition()); - v.setBackgroundColor(((DiffTextView) v).getInitialBackgroundColor()); - - } - - }); - - diffTextView.setOnLongClickListener(v -> { - - if(((DiffTextView) v).getCurrentBackgroundColor() == COLOR_SELECTED) { - - StringBuilder stringBuilder = new StringBuilder(); - stringBuilder.append("```\n"); - - for(View view : selectedViews.values()) { - - stringBuilder.append(((DiffTextView) view).getText()); - stringBuilder.append("\n"); - } - - stringBuilder.append("```\n\n"); - - selectedViews.clear(); - - Bundle bundle = new Bundle(); - bundle.putString("commentBody", stringBuilder.toString()); - bundle.putBoolean("cursorToEnd", true); - - BottomSheetReplyFragment.newInstance(bundle).show(fragmentManager, "replyBottomSheet"); - } - - return true; - - }); - - diffLines.addView(diffTextView); - - } - - } - - } - else { - - diffLines.addView(getMessageView(context.getResources().getString(R.string.fileTooLarge))); - } - - } - - return convertView; - - } - - private TextView getMessageView(String message) { - - TextView textView = new TextView(context); - - textView.setTextColor(COLOR_FONT); - textView.setBackgroundColor(COLOR_NORMAL); - textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14); - textView.setPadding(15, 15, 15, 15); - textView.setTypeface(Typeface.DEFAULT); - textView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - textView.setText(message); - - return textView; - } - - private String[] getLines(String content) { - - return content.split("\\R"); - } - -} diff --git a/app/src/main/java/org/mian/gitnex/adapters/IssueCommentsAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/IssueCommentsAdapter.java index d6d86dbc..edd885b5 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/IssueCommentsAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/IssueCommentsAdapter.java @@ -1,12 +1,12 @@ package org.mian.gitnex.adapters; import android.annotation.SuppressLint; +import android.app.Activity; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.os.Handler; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; @@ -140,12 +140,11 @@ public class IssueCommentsAdapter extends RecyclerView.Adapter { + reactionSpinner.setOnLoadingFinishedListener(() -> { linearLayout.removeView(loadReactions); reactionSpinner.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 160)); linearLayout.addView(reactionSpinner); - }, 2500); + }); commentMenuEdit.setOnClickListener(v1 -> { Bundle bundle = new Bundle(); diff --git a/app/src/main/java/org/mian/gitnex/adapters/NotificationsAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/NotificationsAdapter.java index 3ab321d7..a953dd00 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/NotificationsAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/NotificationsAdapter.java @@ -2,6 +2,7 @@ package org.mian.gitnex.adapters; import android.annotation.SuppressLint; import android.content.Context; +import android.content.res.ColorStateList; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -11,6 +12,7 @@ import android.widget.TextView; import androidx.annotation.NonNull; import androidx.core.content.res.ResourcesCompat; import androidx.core.text.HtmlCompat; +import androidx.core.widget.ImageViewCompat; import androidx.recyclerview.widget.RecyclerView; import org.apache.commons.lang3.StringUtils; import org.gitnex.tea4j.models.NotificationThread; @@ -18,6 +20,7 @@ import org.mian.gitnex.R; import org.mian.gitnex.database.api.BaseApi; import org.mian.gitnex.database.api.RepositoriesApi; import org.mian.gitnex.database.models.Repository; +import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.TinyDB; import java.util.List; @@ -89,11 +92,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter { + + private final Context context; + private final List userInfos; + + public ReactionAuthorsAdapter(Context context, List userInfos) { + this.context = context; + this.userInfos = userInfos; + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + return new ViewHolder(LayoutInflater.from(context).inflate(R.layout.list_reaction_authors, parent, false)); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + UserInfo userInfo = userInfos.get(position); + + PicassoService.getInstance(context).get() + .load(userInfo.getAvatar()) + .placeholder(R.drawable.loader_animated) + .resize(240, 240) + .transform(new RoundedTransformation(AppUtil.getPixelsFromDensity(context, 6), 0)) + .centerCrop().into(holder.authorAvatar); + + if(userInfo.getFullname() == null || userInfo.getFullname().isEmpty()) { + holder.authorFullName.setVisibility(View.GONE); + } else { + holder.authorFullName.setText(userInfo.getFullname()); + holder.authorFullName.setVisibility(View.VISIBLE); + } + + holder.authorLogin.setText(userInfo.getLogin()); + } + + @Override + public int getItemCount() { + return userInfos.size(); + } + + static class ViewHolder extends RecyclerView.ViewHolder { + + private final ImageView authorAvatar; + + private final TextView authorFullName; + private final TextView authorLogin; + + public ViewHolder(@NonNull View itemView) { + super(itemView); + + authorAvatar = itemView.findViewById(R.id.authorAvatar); + authorFullName = itemView.findViewById(R.id.authorFullName); + authorLogin = itemView.findViewById(R.id.authorLogin); + } + } + +} diff --git a/app/src/main/java/org/mian/gitnex/adapters/RepoForksAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/RepoForksAdapter.java index 2589aedb..789c40ad 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/RepoForksAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/RepoForksAdapter.java @@ -24,6 +24,7 @@ import org.mian.gitnex.database.api.BaseApi; import org.mian.gitnex.database.api.RepositoriesApi; import org.mian.gitnex.database.models.Repository; import org.mian.gitnex.helpers.AppUtil; +import org.mian.gitnex.helpers.Authorization; import org.mian.gitnex.helpers.ClickListener; import org.mian.gitnex.helpers.RoundedTransformation; import org.mian.gitnex.helpers.TimeHelper; @@ -64,8 +65,7 @@ public class RepoForksAdapter extends RecyclerView.Adapter() { - WatchInfo watch = new WatchInfo(); + @Override + public void onResponse(@NonNull Call call, @NonNull retrofit2.Response response) { - Call call; + if(response.isSuccessful() && response.body() != null) { - call = RetrofitClient.getApiInterface(context).checkRepoWatchStatus(token, repoOwner, repoName); - - call.enqueue(new Callback() { - - @Override - public void onResponse(@NonNull Call call, @NonNull retrofit2.Response response) { - - if(response.isSuccessful()) { - - assert response.body() != null; - tinyDb.putBoolean("repoWatch", response.body().getSubscribed()); - - } - else { - - tinyDb.putBoolean("repoWatch", false); - - if(response.code() != 404) { - - Toasty.error(context, context.getString(R.string.genericApiStatusError)); + tinyDb.putBoolean("repoWatch", response.body().getSubscribed()); + } else { + tinyDb.putBoolean("repoWatch", false); + if(response.code() != 404) { + Toasty.error(context, context.getString(R.string.genericApiStatusError)); + } } - } - } - - @Override - public void onFailure(@NonNull Call call, @NonNull Throwable t) { - - tinyDb.putBoolean("repoWatch", false); - Toasty.error(context, context.getString(R.string.genericApiStatusError)); - - } + @Override + public void onFailure(@NonNull Call call, @NonNull Throwable t) { + tinyDb.putBoolean("repoWatch", false); + Toasty.error(context, context.getString(R.string.genericApiStatusError)); + } }); - } context.startActivity(intent); diff --git a/app/src/main/java/org/mian/gitnex/adapters/TeamMembersByOrgPreviewAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/TeamMembersByOrgPreviewAdapter.java new file mode 100644 index 00000000..180a481e --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/adapters/TeamMembersByOrgPreviewAdapter.java @@ -0,0 +1,64 @@ +package org.mian.gitnex.adapters; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import org.gitnex.tea4j.models.UserInfo; +import org.mian.gitnex.R; +import org.mian.gitnex.clients.PicassoService; +import org.mian.gitnex.helpers.AppUtil; +import org.mian.gitnex.helpers.RoundedTransformation; +import java.util.List; + +/** + * @author opyale + */ + +public class TeamMembersByOrgPreviewAdapter extends RecyclerView.Adapter { + + private final Context context; + private final List userData; + + public TeamMembersByOrgPreviewAdapter(Context context, List userInfo) { + this.context = context; + this.userData = userInfo; + } + + @NonNull + @Override + public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + View v = LayoutInflater.from(context).inflate(R.layout.list_members_by_org_preview, parent, false); + return new ViewHolder(v); + } + + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int position) { + UserInfo userInfo = userData.get(position); + + PicassoService.getInstance(context).get() + .load(userInfo.getAvatar()) + .placeholder(R.drawable.loader_animated) + .transform(new RoundedTransformation(AppUtil.getPixelsFromDensity(context, 3), 0)) + .resize(120, 120) + .centerCrop().into(holder.avatar); + } + + @Override + public int getItemCount() { + return userData.size(); + } + + static class ViewHolder extends RecyclerView.ViewHolder { + + private final ImageView avatar; + + public ViewHolder(@NonNull View itemView) { + super(itemView); + avatar = itemView.findViewById(R.id.avatar); + } + } +} diff --git a/app/src/main/java/org/mian/gitnex/adapters/TeamsByOrgAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/TeamsByOrgAdapter.java index eaba32e4..37ac127d 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/TeamsByOrgAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/TeamsByOrgAdapter.java @@ -7,15 +7,24 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Filter; import android.widget.Filterable; +import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import org.gitnex.tea4j.models.OrgPermissions; import org.gitnex.tea4j.models.Teams; +import org.gitnex.tea4j.models.UserInfo; import org.mian.gitnex.R; -import org.mian.gitnex.activities.OrganizationTeamMembersActivity; +import org.mian.gitnex.activities.OrganizationTeamInfoActivity; +import org.mian.gitnex.clients.RetrofitClient; +import org.mian.gitnex.helpers.Authorization; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; /** * Author M M Arif @@ -30,33 +39,40 @@ public class TeamsByOrgAdapter extends RecyclerView.Adapter userInfos; + private final TeamMembersByOrgPreviewAdapter adapter; private OrgTeamsViewHolder(View itemView) { - super(itemView); + teamTitle = itemView.findViewById(R.id.teamTitle); teamDescription = itemView.findViewById(R.id.teamDescription); - teamPermission = itemView.findViewById(R.id.teamPermission); + membersPreviewFrame = itemView.findViewById(R.id.membersPreviewFrame); + + RecyclerView membersPreview = itemView.findViewById(R.id.membersPreview); + + userInfos = new ArrayList<>(); + adapter = new TeamMembersByOrgPreviewAdapter(itemView.getContext(), userInfos); + + membersPreview.setLayoutManager(new LinearLayoutManager(itemView.getContext(), RecyclerView.HORIZONTAL, false)); + membersPreview.setAdapter(adapter); itemView.setOnClickListener(v -> { - Context context = v.getContext(); - Intent intent = new Intent(context, OrganizationTeamMembersActivity.class); - intent.putExtra("teamTitle", teams.getName()); - intent.putExtra("teamId", String.valueOf(teams.getId())); + Intent intent = new Intent(context, OrganizationTeamInfoActivity.class); + intent.putExtra("team", team); intent.putExtra("permissions", permissions); context.startActivity(intent); }); - } - } public TeamsByOrgAdapter(Context ctx, List teamListMain, OrgPermissions permissions) { @@ -78,19 +94,42 @@ public class TeamsByOrgAdapter extends RecyclerView.Adapter>() { + @Override + public void onResponse(@NonNull Call> call, @NonNull Response> response) { + if(response.isSuccessful() && + response.body() != null && + response.body().size() > 0) { + + holder.membersPreviewFrame.setVisibility(View.VISIBLE); + holder.userInfos.addAll(response.body().stream() + .limit(Math.min(response.body().size(), 6)) + .collect(Collectors.toList())); + + holder.adapter.notifyDataSetChanged(); + } + } + + @Override public void onFailure(@NonNull Call> call, @NonNull Throwable t) {} + }); + + if (currentItem.getDescription() != null && !currentItem.getDescription().isEmpty()) { holder.teamDescription.setVisibility(View.VISIBLE); holder.teamDescription.setText(currentItem.getDescription()); - } - else { + } else { holder.teamDescription.setVisibility(View.GONE); holder.teamDescription.setText(""); } - holder.teamPermission.setText(context.getResources().getString(R.string.teamPermission, currentItem.getPermission())); } @Override diff --git a/app/src/main/java/org/mian/gitnex/adapters/UserGridAdapter.java b/app/src/main/java/org/mian/gitnex/adapters/UserGridAdapter.java index d8bc98bb..826d0f80 100644 --- a/app/src/main/java/org/mian/gitnex/adapters/UserGridAdapter.java +++ b/app/src/main/java/org/mian/gitnex/adapters/UserGridAdapter.java @@ -82,7 +82,7 @@ public class UserGridAdapter extends BaseAdapter implements Filterable { @Override public View getView(int position, View finalView, ViewGroup parent) { - UserGridAdapter.ViewHolder viewHolder = null; + UserGridAdapter.ViewHolder viewHolder; if (finalView == null) { diff --git a/app/src/main/java/org/mian/gitnex/fragments/BottomSheetNotificationsFragment.java b/app/src/main/java/org/mian/gitnex/fragments/BottomSheetNotificationsFragment.java index 5f1c3169..c4f8e0d5 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/BottomSheetNotificationsFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/BottomSheetNotificationsFragment.java @@ -1,9 +1,7 @@ package org.mian.gitnex.fragments; -import android.app.Activity; import android.content.Context; import android.os.Bundle; -import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -13,9 +11,11 @@ import androidx.annotation.Nullable; import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import org.gitnex.tea4j.models.NotificationThread; import org.mian.gitnex.R; -import org.mian.gitnex.actions.NotificationsActions; +import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.databinding.BottomSheetNotificationsBinding; import org.mian.gitnex.helpers.AppUtil; +import org.mian.gitnex.helpers.Authorization; +import org.mian.gitnex.helpers.SimpleCallback; import org.mian.gitnex.helpers.Toasty; /** @@ -48,94 +48,55 @@ public class BottomSheetNotificationsFragment extends BottomSheetDialogFragment TextView markUnread = bottomSheetNotificationsBinding.markUnread; TextView markPinned = bottomSheetNotificationsBinding.markPinned; - NotificationsActions notificationsActions = new NotificationsActions(context); - Activity activity = requireActivity(); - if(notificationThread.isPinned()) { - AppUtil.setMultiVisibility(View.GONE, markUnread, markPinned); } else if(notificationThread.isUnread()) { - markUnread.setVisibility(View.GONE); } else { - markRead.setVisibility(View.GONE); } - markPinned.setOnClickListener(v12 -> { + markPinned.setOnClickListener(v12 -> + RetrofitClient.getApiInterface(context) + .markNotificationThreadAsRead(Authorization.get(context), notificationThread.getId(), "pinned") + .enqueue((SimpleCallback) (call, voidResponse) -> { - Thread thread = new Thread(() -> { - - try { - - notificationsActions.setNotificationStatus(notificationThread, NotificationsActions.NotificationStatus.PINNED); - activity.runOnUiThread(() -> onOptionSelectedListener.onSelected()); - - } - catch(Exception e) { - - activity.runOnUiThread(() -> Toasty.error(context, getString(R.string.genericError))); - Log.e("onError", e.toString()); - - } finally { + if(voidResponse.isPresent() && voidResponse.get().isSuccessful()) { + onOptionSelectedListener.onSelected(); + } else { + Toasty.error(context, getString(R.string.genericError)); + } dismiss(); - } - }); + })); - thread.start(); + markRead.setOnClickListener(v1 -> + RetrofitClient.getApiInterface(context) + .markNotificationThreadAsRead(Authorization.get(context), notificationThread.getId(), "read") + .enqueue((SimpleCallback) (call, voidResponse) -> { - }); - - markRead.setOnClickListener(v1 -> { - - Thread thread = new Thread(() -> { - - try { - - notificationsActions.setNotificationStatus(notificationThread, NotificationsActions.NotificationStatus.READ); - activity.runOnUiThread(() -> onOptionSelectedListener.onSelected()); - - } - catch(Exception e) { - - activity.runOnUiThread(() -> Toasty.error(context, getString(R.string.genericError))); - Log.e("onError", e.toString()); - - } finally { + if(voidResponse.isPresent() && voidResponse.get().isSuccessful()) { + onOptionSelectedListener.onSelected(); + } else { + Toasty.error(context, getString(R.string.genericError)); + } dismiss(); - } - }); + })); - thread.start(); + markUnread.setOnClickListener(v13 -> + RetrofitClient.getApiInterface(context) + .markNotificationThreadAsRead(Authorization.get(context), notificationThread.getId(), "unread") + .enqueue((SimpleCallback) (call, voidResponse) -> { - }); - - markUnread.setOnClickListener(v13 -> { - - Thread thread = new Thread(() -> { - - try { - - notificationsActions.setNotificationStatus(notificationThread, NotificationsActions.NotificationStatus.UNREAD); - activity.runOnUiThread(() -> onOptionSelectedListener.onSelected()); - - } - catch(Exception e) { - - activity.runOnUiThread(() -> Toasty.error(context, getString(R.string.genericError))); - Log.e("onError", e.toString()); - - } finally { + if(voidResponse.isPresent() && voidResponse.get().isSuccessful()) { + onOptionSelectedListener.onSelected(); + } else { + Toasty.error(context, getString(R.string.genericError)); + } dismiss(); - } - }); - - thread.start(); - - }); + })); return bottomSheetNotificationsBinding.getRoot(); diff --git a/app/src/main/java/org/mian/gitnex/fragments/BottomSheetSingleIssueFragment.java b/app/src/main/java/org/mian/gitnex/fragments/BottomSheetSingleIssueFragment.java index 6bc18676..0f48b794 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/BottomSheetSingleIssueFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/BottomSheetSingleIssueFragment.java @@ -5,12 +5,10 @@ import android.content.ClipboardManager; import android.content.Context; import android.content.Intent; import android.os.Bundle; -import android.os.Handler; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -18,8 +16,8 @@ import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import org.mian.gitnex.R; import org.mian.gitnex.actions.IssueActions; import org.mian.gitnex.actions.PullRequestActions; +import org.mian.gitnex.activities.DiffActivity; import org.mian.gitnex.activities.EditIssueActivity; -import org.mian.gitnex.activities.FileDiffActivity; import org.mian.gitnex.activities.MergePullRequestActivity; import org.mian.gitnex.databinding.BottomSheetSingleIssueBinding; import org.mian.gitnex.helpers.AlertDialogs; @@ -47,7 +45,7 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment { @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { - BottomSheetSingleIssueBinding bottomSheetSingleIssueBinding = BottomSheetSingleIssueBinding.inflate(inflater, container, false); + BottomSheetSingleIssueBinding binding = BottomSheetSingleIssueBinding.inflate(inflater, container, false); final Context ctx = getContext(); final TinyDB tinyDB = TinyDB.getInstance(ctx); @@ -57,38 +55,22 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment { boolean canPush = tinyDB.getBoolean("canPush"); boolean archived = tinyDB.getBoolean("isArchived"); - TextView editIssue = bottomSheetSingleIssueBinding.editIssue; - TextView editLabels = bottomSheetSingleIssueBinding.editLabels; - TextView closeIssue = bottomSheetSingleIssueBinding.closeIssue; - TextView addRemoveAssignees = bottomSheetSingleIssueBinding.addRemoveAssignees; - TextView copyIssueUrl = bottomSheetSingleIssueBinding.copyIssueUrl; - TextView openFilesDiff = bottomSheetSingleIssueBinding.openFilesDiff; - TextView updatePullRequest = bottomSheetSingleIssueBinding.updatePullRequest; - TextView mergePullRequest = bottomSheetSingleIssueBinding.mergePullRequest; - TextView deletePullRequestBranch = bottomSheetSingleIssueBinding.deletePrHeadBranch; - TextView shareIssue = bottomSheetSingleIssueBinding.shareIssue; - TextView subscribeIssue = bottomSheetSingleIssueBinding.subscribeIssue; - TextView unsubscribeIssue = bottomSheetSingleIssueBinding.unsubscribeIssue; - View closeReopenDivider = bottomSheetSingleIssueBinding.dividerCloseReopenIssue; - - LinearLayout linearLayout = bottomSheetSingleIssueBinding.commentReactionButtons; - - Bundle bundle1 = new Bundle(); - String repoFullName = tinyDB.getString("repoFullName"); String[] parts = repoFullName.split("/"); - bundle1.putString("repoOwner", parts[0]); - bundle1.putString("repoName", parts[1]); - bundle1.putInt("issueId", Integer.parseInt(tinyDB.getString("issueNumber"))); + Bundle bundle = new Bundle(); + + bundle.putString("repoOwner", parts[0]); + bundle.putString("repoName", parts[1]); + bundle.putInt("issueId", Integer.parseInt(tinyDB.getString("issueNumber"))); TextView loadReactions = new TextView(ctx); loadReactions.setText(Objects.requireNonNull(ctx).getString(R.string.genericWaitFor)); loadReactions.setGravity(Gravity.CENTER); - loadReactions.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 160)); - linearLayout.addView(loadReactions); + loadReactions.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 80)); + binding.commentReactionButtons.addView(loadReactions); - ReactionSpinner reactionSpinner = new ReactionSpinner(ctx, bundle1); + ReactionSpinner reactionSpinner = new ReactionSpinner(ctx, bundle); reactionSpinner.setOnInteractedListener(() -> { tinyDB.putBoolean("singleIssueUpdate", true); @@ -96,74 +78,72 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment { bmListener.onButtonClicked("onResume"); dismiss(); }); - - Handler handler = new Handler(); - handler.postDelayed(() -> { - linearLayout.removeView(loadReactions); + reactionSpinner.setOnLoadingFinishedListener(() -> { reactionSpinner.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 160)); - linearLayout.addView(reactionSpinner); - }, 2500); + binding.commentReactionButtons.removeView(loadReactions); + binding.commentReactionButtons.addView(reactionSpinner); + }); if(tinyDB.getString("issueType").equalsIgnoreCase("Pull")) { - editIssue.setText(R.string.editPrText); - copyIssueUrl.setText(R.string.copyPrUrlText); - shareIssue.setText(R.string.sharePr); + binding.editIssue.setText(R.string.editPrText); + binding.copyIssueUrl.setText(R.string.copyPrUrlText); + binding.shareIssue.setText(R.string.sharePr); boolean canPushPullSource = tinyDB.getBoolean("canPushPullSource"); if(tinyDB.getBoolean("prMerged") || tinyDB.getString("repoPrState").equals("closed")) { - updatePullRequest.setVisibility(View.GONE); - mergePullRequest.setVisibility(View.GONE); + binding.updatePullRequest.setVisibility(View.GONE); + binding.mergePullRequest.setVisibility(View.GONE); if(canPushPullSource) { - deletePullRequestBranch.setVisibility(View.VISIBLE); + binding.deletePrHeadBranch.setVisibility(View.VISIBLE); } else { if(!canPush) { - editIssue.setVisibility(View.GONE); + binding.editIssue.setVisibility(View.GONE); } - deletePullRequestBranch.setVisibility(View.GONE); + binding.deletePrHeadBranch.setVisibility(View.GONE); } } else { if(canPushPullSource) { - updatePullRequest.setVisibility(View.VISIBLE); + binding.updatePullRequest.setVisibility(View.VISIBLE); } else { - updatePullRequest.setVisibility(View.GONE); + binding.updatePullRequest.setVisibility(View.GONE); } if(!userIsCreator && !canPush) { - editIssue.setVisibility(View.GONE); + binding.editIssue.setVisibility(View.GONE); } if(canPush && !tinyDB.getString("prMergeable").equals("false")) { - mergePullRequest.setVisibility(View.VISIBLE); + binding.mergePullRequest.setVisibility(View.VISIBLE); } else { - mergePullRequest.setVisibility(View.GONE); + binding.mergePullRequest.setVisibility(View.GONE); } - deletePullRequestBranch.setVisibility(View.GONE); + binding.deletePrHeadBranch.setVisibility(View.GONE); } if(new Version(tinyDB.getString("giteaVersion")).higherOrEqual("1.13.0")) { - openFilesDiff.setVisibility(View.VISIBLE); + binding.openFilesDiff.setVisibility(View.VISIBLE); } else if(tinyDB.getString("repoType").equals("public")) { - openFilesDiff.setVisibility(View.VISIBLE); + binding.openFilesDiff.setVisibility(View.VISIBLE); } else { - openFilesDiff.setVisibility(View.GONE); + binding.openFilesDiff.setVisibility(View.GONE); } } else { if(!userIsCreator && !canPush) { - editIssue.setVisibility(View.GONE); + binding.editIssue.setVisibility(View.GONE); } - updatePullRequest.setVisibility(View.GONE); - mergePullRequest.setVisibility(View.GONE); - deletePullRequestBranch.setVisibility(View.GONE); + binding.updatePullRequest.setVisibility(View.GONE); + binding.mergePullRequest.setVisibility(View.GONE); + binding.deletePrHeadBranch.setVisibility(View.GONE); } - updatePullRequest.setOnClickListener(v -> { + binding.updatePullRequest.setOnClickListener(v -> { if(new Version(tinyDB.getString("giteaVersion")).higherOrEqual("1.16.0")) { AlertDialogs.selectPullUpdateStrategy(requireContext(), parts[0], parts[1], tinyDB.getString("issueNumber")); } @@ -173,43 +153,40 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment { dismiss(); }); - mergePullRequest.setOnClickListener(v13 -> { - + binding.mergePullRequest.setOnClickListener(v13 -> { startActivity(new Intent(ctx, MergePullRequestActivity.class)); dismiss(); }); - deletePullRequestBranch.setOnClickListener(v -> { + binding.openFilesDiff.setOnClickListener(v14 -> { + startActivity(new Intent(ctx, DiffActivity.class)); + dismiss(); + }); + + binding.deletePrHeadBranch.setOnClickListener(v -> { PullRequestActions.deleteHeadBranch(ctx, parts[0], parts[1], tinyDB.getString("prHeadBranch"), true); dismiss(); }); - openFilesDiff.setOnClickListener(v14 -> { - startActivity(new Intent(ctx, FileDiffActivity.class)); - dismiss(); - }); - - editIssue.setOnClickListener(v15 -> { + binding.editIssue.setOnClickListener(v15 -> { startActivity(new Intent(ctx, EditIssueActivity.class)); dismiss(); }); - editLabels.setOnClickListener(v16 -> { - + binding.editLabels.setOnClickListener(v16 -> { bmListener.onButtonClicked("showLabels"); dismiss(); }); - addRemoveAssignees.setOnClickListener(v17 -> { - + binding.addRemoveAssignees.setOnClickListener(v17 -> { bmListener.onButtonClicked("showAssignees"); dismiss(); }); - shareIssue.setOnClickListener(v1 -> { + binding.shareIssue.setOnClickListener(v1 -> { Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND); sharingIntent.setType("text/plain"); @@ -220,7 +197,7 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment { dismiss(); }); - copyIssueUrl.setOnClickListener(v12 -> { + binding.copyIssueUrl.setOnClickListener(v12 -> { // copy to clipboard ClipboardManager clipboard = (ClipboardManager) Objects.requireNonNull(ctx).getSystemService(Context.CLIPBOARD_SERVICE); @@ -235,13 +212,13 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment { if(tinyDB.getString("issueState").equals("open")) { // close issue if(!userIsCreator && !canPush) { - closeIssue.setVisibility(View.GONE); - closeReopenDivider.setVisibility(View.GONE); + binding.closeIssue.setVisibility(View.GONE); + binding.dividerCloseReopenIssue.setVisibility(View.GONE); } else if(tinyDB.getString("issueType").equalsIgnoreCase("Pull")) { - closeIssue.setText(R.string.closePr); + binding.closeIssue.setText(R.string.closePr); } - closeIssue.setOnClickListener(closeSingleIssue -> { + binding.closeIssue.setOnClickListener(closeSingleIssue -> { IssueActions.closeReopenIssue(ctx, Integer.parseInt(tinyDB.getString("issueNumber")), "closed"); dismiss(); }); @@ -249,60 +226,60 @@ public class BottomSheetSingleIssueFragment extends BottomSheetDialogFragment { else if(tinyDB.getString("issueState").equals("closed")) { if(userIsCreator || canPush) { if(tinyDB.getString("issueType").equalsIgnoreCase("Pull")) { - closeIssue.setText(R.string.reopenPr); + binding.closeIssue.setText(R.string.reopenPr); } else { - closeIssue.setText(R.string.reOpenIssue); + binding.closeIssue.setText(R.string.reOpenIssue); } } else { - closeIssue.setVisibility(View.GONE); - closeReopenDivider.setVisibility(View.GONE); + binding.closeIssue.setVisibility(View.GONE); + binding.dividerCloseReopenIssue.setVisibility(View.GONE); } - closeIssue.setOnClickListener(closeSingleIssue -> { + binding.closeIssue.setOnClickListener(closeSingleIssue -> { IssueActions.closeReopenIssue(ctx, Integer.parseInt(tinyDB.getString("issueNumber")), "open"); dismiss(); }); } - subscribeIssue.setOnClickListener(subscribeToIssue -> { + binding.subscribeIssue.setOnClickListener(subscribeToIssue -> { IssueActions.subscribe(ctx); dismiss(); }); - unsubscribeIssue.setOnClickListener(unsubscribeToIssue -> { + binding.unsubscribeIssue.setOnClickListener(unsubscribeToIssue -> { IssueActions.unsubscribe(ctx); dismiss(); }); if(new Version(tinyDB.getString("giteaVersion")).less("1.12.0")) { - subscribeIssue.setVisibility(View.GONE); - unsubscribeIssue.setVisibility(View.GONE); + binding.subscribeIssue.setVisibility(View.GONE); + binding.unsubscribeIssue.setVisibility(View.GONE); } else if(tinyDB.getBoolean("issueSubscribed")) { - subscribeIssue.setVisibility(View.GONE); - unsubscribeIssue.setVisibility(View.VISIBLE); + binding.subscribeIssue.setVisibility(View.GONE); + binding.unsubscribeIssue.setVisibility(View.VISIBLE); } else { - subscribeIssue.setVisibility(View.VISIBLE); - unsubscribeIssue.setVisibility(View.GONE); + binding.subscribeIssue.setVisibility(View.VISIBLE); + binding.unsubscribeIssue.setVisibility(View.GONE); } if(archived) { - subscribeIssue.setVisibility(View.GONE); - unsubscribeIssue.setVisibility(View.GONE); - editIssue.setVisibility(View.GONE); - editLabels.setVisibility(View.GONE); - closeIssue.setVisibility(View.GONE); - closeReopenDivider.setVisibility(View.GONE); - addRemoveAssignees.setVisibility(View.GONE); - linearLayout.setVisibility(View.GONE); - bottomSheetSingleIssueBinding.shareDivider.setVisibility(View.GONE); + binding.subscribeIssue.setVisibility(View.GONE); + binding.unsubscribeIssue.setVisibility(View.GONE); + binding.editIssue.setVisibility(View.GONE); + binding.editLabels.setVisibility(View.GONE); + binding.closeIssue.setVisibility(View.GONE); + binding.dividerCloseReopenIssue.setVisibility(View.GONE); + binding.addRemoveAssignees.setVisibility(View.GONE); + binding.commentReactionButtons.setVisibility(View.GONE); + binding.shareDivider.setVisibility(View.GONE); } - return bottomSheetSingleIssueBinding.getRoot(); + return binding.getRoot(); } @Override diff --git a/app/src/main/java/org/mian/gitnex/fragments/DiffFilesFragment.java b/app/src/main/java/org/mian/gitnex/fragments/DiffFilesFragment.java new file mode 100644 index 00000000..750bc917 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/fragments/DiffFilesFragment.java @@ -0,0 +1,134 @@ +package org.mian.gitnex.fragments; + +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import org.gitnex.tea4j.models.FileDiffView; +import org.mian.gitnex.R; +import org.mian.gitnex.adapters.DiffFilesAdapter; +import org.mian.gitnex.clients.RetrofitClient; +import org.mian.gitnex.databinding.FragmentDiffFilesBinding; +import org.mian.gitnex.helpers.AlertDialogs; +import org.mian.gitnex.helpers.Authorization; +import org.mian.gitnex.helpers.ParseDiff; +import org.mian.gitnex.helpers.TinyDB; +import org.mian.gitnex.helpers.Toasty; +import org.mian.gitnex.helpers.Version; +import java.io.IOException; +import java.util.List; +import okhttp3.ResponseBody; +import retrofit2.Call; +import retrofit2.Response; + +/** + * @author opyale + */ + +public class DiffFilesFragment extends Fragment { + + private FragmentDiffFilesBinding binding; + private Context ctx; + private TinyDB tinyDB; + + public DiffFilesFragment() {} + + public static DiffFilesFragment newInstance() { + return new DiffFilesFragment(); + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + binding = FragmentDiffFilesBinding.inflate(inflater, container, false); + ctx = requireContext(); + tinyDB = TinyDB.getInstance(ctx); + + String repoFullName = tinyDB.getString("repoFullName"); + String[] parts = repoFullName.split("/"); + final String repoOwner = parts[0]; + final String repoName = parts[1]; + + String pullIndex = tinyDB.getString("issueNumber"); + + binding.progressBar.setVisibility(View.VISIBLE); + binding.toolbarTitle.setText(R.string.processingText); + binding.close.setOnClickListener(v -> requireActivity().finish()); + + binding.diffFiles.setOnItemClickListener((parent, view, position, id) -> requireActivity().getSupportFragmentManager() + .beginTransaction() + .replace(R.id.fragment_container, DiffFragment.newInstance((FileDiffView) parent.getItemAtPosition(position))) + .commit()); + + getPullDiffFiles(repoOwner, repoName, pullIndex); + + return binding.getRoot(); + + } + + private void getPullDiffFiles(String owner, String repo, String pullIndex) { + + Thread thread = new Thread(() -> { + + Call call = new Version(tinyDB.getString("giteaVersion")).higherOrEqual("1.13.0") ? + RetrofitClient.getApiInterface(ctx).getPullDiffContent(Authorization.get(ctx), owner, repo, pullIndex) : + RetrofitClient.getWebInterface(ctx).getPullDiffContent(Authorization.getWeb(ctx), owner, repo, pullIndex); + + try { + + Response response = call.execute(); + assert response.body() != null; + + switch(response.code()) { + + case 200: + List fileDiffViews = ParseDiff.getFileDiffViewArray(response.body().string()); + + int filesCount = fileDiffViews.size(); + + String toolbarTitleText = (filesCount > 1) ? + getResources().getString(R.string.fileDiffViewHeader, Integer.toString(filesCount)) : + getResources().getString(R.string.fileDiffViewHeaderSingle, Integer.toString(filesCount)); + + DiffFilesAdapter adapter = new DiffFilesAdapter(ctx, fileDiffViews); + + requireActivity().runOnUiThread(() -> { + binding.progressBar.setVisibility(View.GONE); + binding.diffFiles.setAdapter(adapter); + binding.toolbarTitle.setText(toolbarTitleText); + }); + break; + + case 401: + requireActivity().runOnUiThread(() -> AlertDialogs.authorizationTokenRevokedDialog(ctx, + getString(R.string.alertDialogTokenRevokedTitle), + getString(R.string.alertDialogTokenRevokedMessage), + getString(R.string.cancelButton), + getString(R.string.cancelButton))); + break; + + case 403: + requireActivity().runOnUiThread(() -> Toasty.error(ctx, ctx.getString(R.string.authorizeError))); + break; + + case 404: + requireActivity().runOnUiThread(() -> Toasty.warning(ctx, ctx.getString(R.string.apiNotFound))); + break; + + default: + requireActivity().runOnUiThread(() -> Toasty.error(ctx, getString(R.string.labelGeneralError))); + + } + } catch(IOException ignored) {} + + }); + + thread.start(); + + } + +} diff --git a/app/src/main/java/org/mian/gitnex/fragments/DiffFragment.java b/app/src/main/java/org/mian/gitnex/fragments/DiffFragment.java new file mode 100644 index 00000000..db224c6a --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/fragments/DiffFragment.java @@ -0,0 +1,59 @@ +package org.mian.gitnex.fragments; + +import android.content.Context; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import org.gitnex.tea4j.models.FileDiffView; +import org.mian.gitnex.R; +import org.mian.gitnex.adapters.DiffAdapter; +import org.mian.gitnex.databinding.FragmentDiffBinding; +import java.util.Arrays; + +/** + * @author opyale + */ + +public class DiffFragment extends Fragment { + + private FragmentDiffBinding binding; + private Context ctx; + + private FileDiffView fileDiffView; + + public DiffFragment() {} + + public void setFileDiffView(FileDiffView fileDiffView) { + this.fileDiffView = fileDiffView; + } + + public static DiffFragment newInstance(FileDiffView fileDiffView) { + + DiffFragment fragment = new DiffFragment(); + fragment.setFileDiffView(fileDiffView); + return fragment; + + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + binding = FragmentDiffBinding.inflate(inflater, container, false); + ctx = requireContext(); + + binding.close.setOnClickListener(v -> requireActivity().getSupportFragmentManager() + .beginTransaction() + .replace(R.id.fragment_container, DiffFilesFragment.newInstance()) + .commit()); + + binding.toolbarTitle.setText(fileDiffView.getFileName()); + binding.diff.setDivider(null); + binding.diff.setAdapter(new DiffAdapter(ctx, getChildFragmentManager(), Arrays.asList(fileDiffView.toString().split("\\R")))); + + return binding.getRoot(); + + } +} diff --git a/app/src/main/java/org/mian/gitnex/fragments/FilesFragment.java b/app/src/main/java/org/mian/gitnex/fragments/FilesFragment.java index e7f82578..482949ec 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/FilesFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/FilesFragment.java @@ -13,6 +13,7 @@ import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import androidx.activity.OnBackPressedCallback; import androidx.annotation.NonNull; +import androidx.appcompat.widget.SearchView; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.DividerItemDecoration; @@ -104,9 +105,7 @@ public class FilesFragment extends Fragment implements FilesAdapter.FilesAdapter binding.recyclerView.setHasFixedSize(true); binding.recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); binding.recyclerView.setAdapter(filesAdapter); - - DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(binding.recyclerView.getContext(), DividerItemDecoration.VERTICAL); - binding.recyclerView.addItemDecoration(dividerItemDecoration); + binding.recyclerView.addItemDecoration(new DividerItemDecoration(binding.recyclerView.getContext(), DividerItemDecoration.VERTICAL)); binding.breadcrumbsView.setItems(new ArrayList<>(Collections.singletonList(BreadcrumbItem.createSimpleItem(getResources().getString(R.string.filesBreadcrumbRoot) + getResources().getString(R.string.colonDivider) + ref)))); // noinspection unchecked @@ -117,16 +116,11 @@ public class FilesFragment extends Fragment implements FilesAdapter.FilesAdapter public void onNavigateBack(BreadcrumbItem item, int position) { if(position == 0) { - path.clear(); - fetchDataAsync(Authorization.get(getContext()), repoOwner, repoName, ref); - return; - + } else { + path.pop(path.size() - position); } - - path.pop(path.size() - position); - fetchDataAsyncSub(Authorization.get(getContext()), repoOwner, repoName, path.toString(), ref); - + refresh(); } @Override public void onNavigateNewLocation(BreadcrumbItem newItem, int changedPosition) {} @@ -151,34 +145,32 @@ public class FilesFragment extends Fragment implements FilesAdapter.FilesAdapter } }); + binding.pullToRefresh.setOnRefreshListener(() -> { + refresh(); + binding.pullToRefresh.setRefreshing(false); + }); + ((RepoDetailActivity) requireActivity()).setFragmentRefreshListenerFiles(repoBranch -> { path.clear(); ref = repoBranch; binding.breadcrumbsView.setItems(new ArrayList<>(Collections.singletonList(BreadcrumbItem.createSimpleItem(getResources().getString(R.string.filesBreadcrumbRoot) + getResources().getString(R.string.colonDivider) + ref)))); - fetchDataAsync(Authorization.get(getContext()), repoOwner, repoName, repoBranch); + refresh(); }); + String dir = requireActivity().getIntent().getStringExtra("dir"); if(dir != null) { - fetchDataAsyncSub(Authorization.get(getContext()), repoOwner, repoName, dir, ref); for(String segment: dir.split("/")) { binding.breadcrumbsView.addItem(new BreadcrumbItem(Collections.singletonList(segment))); path.add(segment); } } - else { - fetchDataAsync(Authorization.get(getContext()), repoOwner, repoName, ref); - } + refresh(); return binding.getRoot(); - } - @Override - public void onResume() { - - super.onResume(); } @Override @@ -189,8 +181,7 @@ public class FilesFragment extends Fragment implements FilesAdapter.FilesAdapter case "dir": path.add(file.getName()); binding.breadcrumbsView.addItem(new BreadcrumbItem(Collections.singletonList(file.getName()))); - - fetchDataAsyncSub(Authorization.get(getContext()), repoOwner, repoName, path.toString(), ref); + refresh(); break; case "file": @@ -302,6 +293,14 @@ public class FilesFragment extends Fragment implements FilesAdapter.FilesAdapter } } + public void refresh() { + if(path.size() > 0) { + fetchDataAsyncSub(Authorization.get(getContext()), repoOwner, repoName, path.toString(), ref); + } else { + fetchDataAsync(Authorization.get(getContext()), repoOwner, repoName, ref); + } + } + private void fetchDataAsync(String instanceToken, String owner, String repo, String ref) { binding.recyclerView.setVisibility(View.GONE); @@ -365,15 +364,17 @@ public class FilesFragment extends Fragment implements FilesAdapter.FilesAdapter public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) { menu.clear(); + inflater.inflate(R.menu.search_menu, menu); inflater.inflate(R.menu.files_switch_branches_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() { + SearchView searchView = (SearchView) searchItem.getActionView(); + searchView.setImeOptions(EditorInfo.IME_ACTION_DONE); + searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() { @Override public boolean onQueryTextChange(String newText) { @@ -401,7 +402,6 @@ public class FilesFragment extends Fragment implements FilesAdapter.FilesAdapter @Override public void onDetach() { - super.onDetach(); mListener = null; } diff --git a/app/src/main/java/org/mian/gitnex/fragments/NotificationsFragment.java b/app/src/main/java/org/mian/gitnex/fragments/NotificationsFragment.java index 67b099ab..d211528a 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/NotificationsFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/NotificationsFragment.java @@ -6,7 +6,6 @@ import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Looper; -import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; @@ -22,24 +21,20 @@ import androidx.recyclerview.widget.RecyclerView; import org.apache.commons.lang3.StringUtils; import org.gitnex.tea4j.models.NotificationThread; import org.mian.gitnex.R; -import org.mian.gitnex.actions.NotificationsActions; import org.mian.gitnex.activities.IssueDetailActivity; import org.mian.gitnex.activities.RepoDetailActivity; import org.mian.gitnex.adapters.NotificationsAdapter; import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.databinding.FragmentNotificationsBinding; import org.mian.gitnex.helpers.AppUtil; +import org.mian.gitnex.helpers.Authorization; import org.mian.gitnex.helpers.Constants; -import org.mian.gitnex.helpers.SnackBar; +import org.mian.gitnex.helpers.SimpleCallback; import org.mian.gitnex.helpers.TinyDB; import org.mian.gitnex.helpers.Toasty; -import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.List; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; /** * Author opyale @@ -49,17 +44,16 @@ import retrofit2.Response; public class NotificationsFragment extends Fragment implements NotificationsAdapter.OnNotificationClickedListener, NotificationsAdapter.OnMoreClickedListener, BottomSheetNotificationsFragment.OnOptionSelectedListener { private FragmentNotificationsBinding viewBinding; - private List notificationThreads; + private final List notificationThreads = new ArrayList<>(); private NotificationsAdapter notificationsAdapter; - private NotificationsActions notificationsActions; private Activity activity; private Context context; private TinyDB tinyDB; private Menu menu; - private int resultLimit; - private int pageSize; + private int pageCurrentIndex = 1; + private int pageResultLimit; private String currentFilterMode = "unread"; private final String TAG = Constants.tagNotifications; private String instanceToken; @@ -81,14 +75,11 @@ public class NotificationsFragment extends Fragment implements NotificationsAdap context = getContext(); tinyDB = TinyDB.getInstance(context); - String loginUid = tinyDB.getString("loginUid"); - instanceToken = "token " + tinyDB.getString(loginUid + "-token"); + instanceToken = Authorization.get(context); - resultLimit = Constants.getCurrentResultLimit(context); + pageResultLimit = Constants.getCurrentResultLimit(context); tinyDB.putString("notificationsFilterState", currentFilterMode); - notificationThreads = new ArrayList<>(); - notificationsActions = new NotificationsActions(context); notificationsAdapter = new NotificationsAdapter(context, notificationThreads, this, this); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(context); @@ -100,18 +91,10 @@ public class NotificationsFragment extends Fragment implements NotificationsAdap viewBinding.notifications.setAdapter(notificationsAdapter); viewBinding.notifications.addItemDecoration(dividerItemDecoration); - viewBinding.pullToRefresh.setOnRefreshListener(() -> new Handler(Looper.getMainLooper()).postDelayed(() -> { - viewBinding.pullToRefresh.setRefreshing(false); - loadInitial(resultLimit); - notificationsAdapter.notifyDataChanged(); - }, 200)); - - notificationsAdapter.setLoadMoreListener(() -> viewBinding.notifications.post(() -> { - if(notificationThreads.size() == resultLimit || pageSize == resultLimit) { - int page = (notificationThreads.size() + resultLimit) / resultLimit; - loadMore(resultLimit, page); - } - })); + notificationsAdapter.setLoadMoreListener(() -> { + pageCurrentIndex++; + loadNotifications(true); + }); viewBinding.notifications.addOnScrollListener(new RecyclerView.OnScrollListener() { @@ -133,141 +116,78 @@ public class NotificationsFragment extends Fragment implements NotificationsAdap } }); - viewBinding.markAllAsRead.setOnClickListener(v1 -> { + viewBinding.markAllAsRead.setOnClickListener(v1 -> + RetrofitClient.getApiInterface(context) + .markNotificationThreadsAsRead(Authorization.get(context), AppUtil.getTimestampFromDate(context, new Date()), true, new String[]{"unread", "pinned"}, "read") + .enqueue((SimpleCallback) (call, voidResponse) -> { - Thread thread = new Thread(() -> { - try { - if(notificationsActions.setAllNotificationsRead(new Date())) { - activity.runOnUiThread(() -> { - Toasty.success(context, getString(R.string.markedNotificationsAsRead)); - loadInitial(resultLimit); - }); + if(voidResponse.isPresent() && voidResponse.get().isSuccessful()) { + Toasty.success(context, getString(R.string.markedNotificationsAsRead)); + pageCurrentIndex = 1; + loadNotifications(false); + } else { + activity.runOnUiThread(() -> Toasty.error(context, getString(R.string.genericError))); } - } - catch(IOException e) { - activity.runOnUiThread(() -> Toasty.error(context, getString(R.string.genericError))); - Log.e("onError", e.toString()); - } - }); - thread.start(); - }); + })); viewBinding.pullToRefresh.setOnRefreshListener(() -> { - loadInitial(resultLimit); + viewBinding.pullToRefresh.setRefreshing(false); + pageCurrentIndex = 1; + loadNotifications(false); }); - loadInitial(resultLimit); + loadNotifications(true); return viewBinding.getRoot(); } - private void loadInitial(int resultLimit) { + private void loadNotifications(boolean append) { - notificationThreads.clear(); - notificationsAdapter.notifyDataChanged(); + viewBinding.noDataNotifications.setVisibility(View.GONE); viewBinding.progressBar.setVisibility(View.VISIBLE); - notificationThreads.clear(); + String[] filter = tinyDB.getString("notificationsFilterState").equals("read") ? new String[]{"pinned", "read"} : new String[]{"pinned", "unread"}; - viewBinding.pullToRefresh.setRefreshing(false); - Call> call = RetrofitClient + RetrofitClient .getApiInterface(context) - .getNotificationThreads(instanceToken, false, filter, - Constants.defaultOldestTimestamp, "", - 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) { - notificationThreads.addAll(response.body()); - notificationsAdapter.notifyDataChanged(); - viewBinding.noDataNotifications.setVisibility(View.GONE); + .getNotificationThreads(instanceToken, false, filter, Constants.defaultOldestTimestamp, "", pageCurrentIndex, pageResultLimit) + .enqueue((SimpleCallback>) (call1, listResponse) -> { + + if(listResponse.isPresent() && listResponse.get().isSuccessful() && listResponse.get().body() != null) { + System.out.println(listResponse.get().body()); + if(!append) { + notificationThreads.clear(); + } + + if(listResponse.get().body().size() > 0) { + notificationThreads.addAll(listResponse.get().body()); + notificationsAdapter.notifyDataSetChanged(); } else { - notificationsAdapter.notifyDataChanged(); - viewBinding.noDataNotifications.setVisibility(View.VISIBLE); - } - viewBinding.progressBar.setVisibility(View.GONE); - } - else if(response.code() == 404) { - viewBinding.noDataNotifications.setVisibility(View.VISIBLE); - viewBinding.progressBar.setVisibility(View.GONE); - } - else { - notificationsAdapter.notifyDataChanged(); - Log.e(TAG, String.valueOf(response.code())); - } - onCleanup(); - } - - @Override - public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - Log.e(TAG, t.toString()); - onCleanup(); - } - }); - } - - private void loadMore(int resultLimit, int page) { - - String[] filter = tinyDB.getString("notificationsFilterState").equals("read") ? - new String[]{"pinned", "read"} : - new String[]{"pinned", "unread"}; - - viewBinding.progressBar.setVisibility(View.VISIBLE); - Call> call = RetrofitClient.getApiInterface(context) - .getNotificationThreads(instanceToken, false, filter, - Constants.defaultOldestTimestamp, "", - page, resultLimit); - call.enqueue(new Callback>() { - @Override - public void onResponse(@NonNull Call> call, @NonNull Response> response) { - if(response.code() == 200) { - assert response.body() != null; - List result = response.body(); - - if(result.size() > 0) { - pageSize = result.size(); - notificationThreads.addAll(result); - } - else { - SnackBar.info(context, viewBinding.getRoot(), getString(R.string.noMoreData)); notificationsAdapter.setMoreDataAvailable(false); } - notificationsAdapter.notifyDataChanged(); - viewBinding.progressBar.setVisibility(View.GONE); + + } + + AppUtil.setMultiVisibility(View.GONE, viewBinding.progressBar); + + if(notificationThreads.isEmpty()) { + viewBinding.noDataNotifications.setVisibility(View.VISIBLE); } else { - Log.e(TAG, String.valueOf(response.code())); + viewBinding.noDataNotifications.setVisibility(View.GONE); } - onCleanup(); - } - @Override - public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - Log.e(TAG, t.toString()); - onCleanup(); - } - }); - } - - private void onCleanup() { - - AppUtil.setMultiVisibility(View.GONE, viewBinding.progressBar, viewBinding.progressBar); - viewBinding.pullToRefresh.setRefreshing(false); - - if(currentFilterMode.equalsIgnoreCase("unread")) { - - if(notificationThreads.isEmpty()) { - viewBinding.noDataNotifications.setVisibility(View.VISIBLE); - viewBinding.markAllAsRead.setVisibility(View.GONE); - } - else { - viewBinding.markAllAsRead.setVisibility(View.VISIBLE); - } - } + if(currentFilterMode.equalsIgnoreCase("unread")) { + if(notificationThreads.isEmpty()) { + viewBinding.markAllAsRead.setVisibility(View.GONE); + } + else { + viewBinding.markAllAsRead.setVisibility(View.VISIBLE); + } + } + }); } private void changeFilterMode() { @@ -280,8 +200,7 @@ public class NotificationsFragment extends Fragment implements NotificationsAdap if(currentFilterMode.equalsIgnoreCase("read")) { viewBinding.markAllAsRead.setVisibility(View.GONE); - } - else { + } else { viewBinding.markAllAsRead.setVisibility(View.VISIBLE); } } @@ -291,7 +210,6 @@ public class NotificationsFragment extends Fragment implements NotificationsAdap this.menu = menu; inflater.inflate(R.menu.filter_menu_notifications, menu); - currentFilterMode = tinyDB.getString("notificationsFilterState"); changeFilterMode(); super.onCreateOptionsMenu(menu, inflater); @@ -308,7 +226,9 @@ public class NotificationsFragment extends Fragment implements NotificationsAdap currentFilterMode = tinyDB.getString("notificationsFilterState"); changeFilterMode(); - loadInitial(resultLimit); + pageCurrentIndex = 1; + loadNotifications(false); + }); return true; } @@ -318,16 +238,14 @@ public class NotificationsFragment extends Fragment implements NotificationsAdap @Override public void onNotificationClicked(NotificationThread notificationThread) { - Thread thread = new Thread(() -> { - try { - if(notificationThread.isUnread()) { - notificationsActions.setNotificationStatus(notificationThread, NotificationsActions.NotificationStatus.READ); - activity.runOnUiThread(() -> loadInitial(resultLimit)); + if(notificationThread.isUnread() && !notificationThread.isPinned()) { + RetrofitClient.getApiInterface(context).markNotificationThreadAsRead(Authorization.get(context), notificationThread.getId(), "read").enqueue((SimpleCallback) (call, voidResponse) -> { + if(voidResponse.isPresent() && voidResponse.get().isSuccessful()) { + pageCurrentIndex = 1; + loadNotifications(false); } - } catch(IOException ignored) {} - }); - - thread.start(); + }); + } if(StringUtils.containsAny(notificationThread.getSubject().getType().toLowerCase(), "pull", "issue")) { @@ -356,6 +274,7 @@ public class NotificationsFragment extends Fragment implements NotificationsAdap @Override public void onSelected() { - loadInitial(resultLimit); + pageCurrentIndex = 1; + loadNotifications(false); } } diff --git a/app/src/main/java/org/mian/gitnex/fragments/OrganizationTeamInfoMembersFragment.java b/app/src/main/java/org/mian/gitnex/fragments/OrganizationTeamInfoMembersFragment.java new file mode 100644 index 00000000..2386efe5 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/fragments/OrganizationTeamInfoMembersFragment.java @@ -0,0 +1,99 @@ +package org.mian.gitnex.fragments; + +import android.content.Context; +import android.os.Bundle; +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 org.gitnex.tea4j.models.Teams; +import org.gitnex.tea4j.models.UserInfo; +import org.mian.gitnex.adapters.UserGridAdapter; +import org.mian.gitnex.clients.RetrofitClient; +import org.mian.gitnex.databinding.FragmentOrganizationTeamInfoMembersBinding; +import org.mian.gitnex.helpers.Authorization; +import java.util.ArrayList; +import java.util.List; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +/** + * @author opyale + */ + +public class OrganizationTeamInfoMembersFragment extends Fragment { + + private Context ctx; + + private FragmentOrganizationTeamInfoMembersBinding binding; + private Teams team; + + private UserGridAdapter adapter; + private final List teamUserInfo = new ArrayList<>(); + + public OrganizationTeamInfoMembersFragment() {} + + public static OrganizationTeamInfoMembersFragment newInstance(Teams team) { + OrganizationTeamInfoMembersFragment fragment = new OrganizationTeamInfoMembersFragment(); + + Bundle bundle = new Bundle(); + bundle.putSerializable("team", team); + fragment.setArguments(bundle); + + return fragment; + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + binding = FragmentOrganizationTeamInfoMembersBinding.inflate(inflater, container, false); + ctx = getContext(); + + team = (Teams) requireArguments().getSerializable("team"); + + adapter = new UserGridAdapter(ctx, teamUserInfo); + binding.members.setAdapter(adapter); + fetchMembersAsync(); + + return binding.getRoot(); + } + + private void fetchMembersAsync() { + + Call> call = RetrofitClient + .getApiInterface(ctx) + .getTeamMembersByOrg(Authorization.get(ctx), team.getId()); + + binding.progressBar.setVisibility(View.VISIBLE); + + call.enqueue(new Callback>() { + + @Override + public void onResponse(@NonNull Call> call, @NonNull Response> response) { + if(response.isSuccessful() && response.body() != null && response.body().size() > 0) { + teamUserInfo.clear(); + teamUserInfo.addAll(response.body()); + + adapter.notifyDataSetChanged(); + + binding.noDataMembers.setVisibility(View.GONE); + binding.members.setVisibility(View.VISIBLE); + } else { + binding.members.setVisibility(View.GONE); + binding.noDataMembers.setVisibility(View.VISIBLE); + } + + binding.progressBar.setVisibility(View.GONE); + } + + @Override + public void onFailure(@NonNull Call> call, @NonNull Throwable t) { + Log.i("onFailure", t.toString()); + } + + }); + } + +} diff --git a/app/src/main/java/org/mian/gitnex/fragments/OrganizationTeamInfoPermissionsFragment.java b/app/src/main/java/org/mian/gitnex/fragments/OrganizationTeamInfoPermissionsFragment.java new file mode 100644 index 00000000..faa4f93a --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/fragments/OrganizationTeamInfoPermissionsFragment.java @@ -0,0 +1,71 @@ +package org.mian.gitnex.fragments; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import org.gitnex.tea4j.models.Teams; +import org.mian.gitnex.R; +import org.mian.gitnex.databinding.FragmentOrganizationTeamInfoPermissionsBinding; +import java.util.Collections; + +/** + * @author opyale + */ + +public class OrganizationTeamInfoPermissionsFragment extends Fragment { + + private FragmentOrganizationTeamInfoPermissionsBinding binding; + private Teams team; + + public OrganizationTeamInfoPermissionsFragment() {} + + public static OrganizationTeamInfoPermissionsFragment newInstance(Teams team) { + OrganizationTeamInfoPermissionsFragment fragment = new OrganizationTeamInfoPermissionsFragment(); + + Bundle bundle = new Bundle(); + bundle.putSerializable("team", team); + fragment.setArguments(bundle); + + return fragment; + } + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + binding = FragmentOrganizationTeamInfoPermissionsBinding.inflate(inflater, container, false); + + team = (Teams) requireArguments().getSerializable("team"); + + StringBuilder permissions = new StringBuilder(); + + // Future proofing in case of gitea becoming able to assign multiple permissions per team + for(String permission : Collections.singletonList(team.getPermission())) { + + switch(permission) { + case "none": + permissions.append(getString(R.string.teamPermissionNone)).append("\n"); + break; + case "read": + permissions.append(getString(R.string.teamPermissionRead)).append("\n"); + break; + case "write": + permissions.append(getString(R.string.teamPermissionWrite)).append("\n"); + break; + case "admin": + permissions.append(getString(R.string.teamPermissionAdmin)).append("\n"); + break; + case "owner": + permissions.append(getString(R.string.teamPermissionOwner)).append("\n"); + break; + } + } + + binding.permissions.setText(permissions.toString()); + binding.progressBar.setVisibility(View.GONE); + + return binding.getRoot(); + } + +} diff --git a/app/src/main/java/org/mian/gitnex/fragments/RepoInfoFragment.java b/app/src/main/java/org/mian/gitnex/fragments/RepoInfoFragment.java index 601390d5..2c95c9af 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/RepoInfoFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/RepoInfoFragment.java @@ -17,6 +17,7 @@ import org.apache.commons.io.FileUtils; import org.gitnex.tea4j.models.UserRepositories; import org.mian.gitnex.R; import org.mian.gitnex.activities.RepoDetailActivity; +import org.mian.gitnex.activities.RepoForksActivity; import org.mian.gitnex.activities.RepoStargazersActivity; import org.mian.gitnex.activities.RepoWatchersActivity; import org.mian.gitnex.clients.RetrofitClient; @@ -111,6 +112,13 @@ public class RepoInfoFragment extends Fragment { ctx.startActivity(intent); }); + binding.repoMetaForksFrame.setOnClickListener(v -> { + + Intent intent = new Intent(ctx, RepoForksActivity.class); + intent.putExtra("repoFullNameForForks", repoOwner + "/" + repoName); + ctx.startActivity(intent); + }); + binding.repoMetaPullRequestsFrame.setOnClickListener(metaPR -> RepoDetailActivity.mViewPager.setCurrentItem(3)); return binding.getRoot(); diff --git a/app/src/main/java/org/mian/gitnex/fragments/TeamsByOrgFragment.java b/app/src/main/java/org/mian/gitnex/fragments/TeamsByOrgFragment.java index ae9b7c24..d4192cf7 100644 --- a/app/src/main/java/org/mian/gitnex/fragments/TeamsByOrgFragment.java +++ b/app/src/main/java/org/mian/gitnex/fragments/TeamsByOrgFragment.java @@ -14,9 +14,7 @@ import android.view.inputmethod.EditorInfo; import android.widget.ProgressBar; import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; -import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; import androidx.recyclerview.widget.DividerItemDecoration; import androidx.recyclerview.widget.LinearLayoutManager; @@ -30,7 +28,6 @@ import org.mian.gitnex.databinding.FragmentTeamsByOrgBinding; import org.mian.gitnex.helpers.Authorization; import org.mian.gitnex.helpers.TinyDB; import org.mian.gitnex.viewmodels.TeamsByOrgViewModel; -import java.util.List; /** * Author M M Arif @@ -117,21 +114,17 @@ public class TeamsByOrgFragment extends Fragment { TeamsByOrgViewModel teamModel = new ViewModelProvider(this).get(TeamsByOrgViewModel.class); - teamModel.getTeamsByOrg(instanceToken, owner, getContext(), noDataTeams, mProgressBar).observe(getViewLifecycleOwner(), new Observer>() { - @Override - public void onChanged(@Nullable List orgTeamsListMain) { - adapter = new TeamsByOrgAdapter(getContext(), orgTeamsListMain, permissions); - if(adapter.getItemCount() > 0) { - mRecyclerView.setAdapter(adapter); - noDataTeams.setVisibility(View.GONE); - } - else { - adapter.notifyDataSetChanged(); - mRecyclerView.setAdapter(adapter); - noDataTeams.setVisibility(View.VISIBLE); - } - mProgressBar.setVisibility(View.GONE); + teamModel.getTeamsByOrg(instanceToken, owner, getContext(), noDataTeams, mProgressBar).observe(getViewLifecycleOwner(), orgTeamsListMain -> { + adapter = new TeamsByOrgAdapter(getContext(), orgTeamsListMain, permissions); + if(adapter.getItemCount() > 0) { + mRecyclerView.setAdapter(adapter); + noDataTeams.setVisibility(View.GONE); + } else { + adapter.notifyDataSetChanged(); + mRecyclerView.setAdapter(adapter); + noDataTeams.setVisibility(View.VISIBLE); } + mProgressBar.setVisibility(View.GONE); }); } diff --git a/app/src/main/java/org/mian/gitnex/helpers/AppUtil.java b/app/src/main/java/org/mian/gitnex/helpers/AppUtil.java index 899c8e56..67ac40af 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/AppUtil.java +++ b/app/src/main/java/org/mian/gitnex/helpers/AppUtil.java @@ -302,7 +302,7 @@ public class AppUtil { long lines = 0; - Pattern pattern = Pattern.compile("(\r\n|\r|\n)"); + Pattern pattern = Pattern.compile("(\r\n|\n)"); Matcher matcher = pattern.matcher(s); while(matcher.find()) diff --git a/app/src/main/java/org/mian/gitnex/helpers/ColorInverter.java b/app/src/main/java/org/mian/gitnex/helpers/ColorInverter.java index 00925b0e..8ec4dffa 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/ColorInverter.java +++ b/app/src/main/java/org/mian/gitnex/helpers/ColorInverter.java @@ -16,7 +16,7 @@ public class ColorInverter { @ColorInt public int getContrastColor(@ColorInt int color) { - double a = 1 - (0.299 * Color.red(color) + 0.587 * Color.green(color) + 0.114 * Color.blue(color)) / 255; + double a = 1 - (0.2126 * Color.red(color) + 0.7152 * Color.green(color) + 0.0722 * Color.blue(color)) / 255; int d = (a < 0.30) ? 30 : // almost black diff --git a/app/src/main/java/org/mian/gitnex/helpers/Markdown.java b/app/src/main/java/org/mian/gitnex/helpers/Markdown.java index 51e03a9d..64fb73ab 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/Markdown.java +++ b/app/src/main/java/org/mian/gitnex/helpers/Markdown.java @@ -26,7 +26,7 @@ import org.mian.gitnex.clients.PicassoService; import org.mian.gitnex.core.MainGrammarLocator; import java.util.Objects; import java.util.concurrent.ExecutorService; -import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; @@ -70,14 +70,13 @@ import stormpot.Timeout; public class Markdown { - private static final int MAX_POOL_SIZE = 45; - private static final int MAX_THREAD_KEEP_ALIVE_SECONDS = 120; - private static final int MAX_CLAIM_TIMEOUT_SECONDS = 120; + private static final int MAX_OBJECT_POOL_SIZE = 45; + private static final int MAX_THREAD_POOL_SIZE = Runtime.getRuntime().availableProcessors(); - private static final Timeout timeout = new Timeout(MAX_CLAIM_TIMEOUT_SECONDS, TimeUnit.SECONDS); + private static final Timeout OBJECT_POOL_CLAIM_TIMEOUT = new Timeout(240, TimeUnit.SECONDS); - private static final ExecutorService executorService = new ThreadPoolExecutor(MAX_POOL_SIZE / 2, MAX_POOL_SIZE, MAX_THREAD_KEEP_ALIVE_SECONDS, - TimeUnit.SECONDS, new SynchronousQueue<>()); + private static final ExecutorService executorService = + new ThreadPoolExecutor(MAX_THREAD_POOL_SIZE, MAX_THREAD_POOL_SIZE, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>()); private static final Pool rendererPool; private static final Pool rvRendererPool; @@ -88,19 +87,15 @@ public class Markdown { config.setBackgroundExpirationEnabled(true); config.setPreciseLeakDetectionEnabled(true); - config.setSize(MAX_POOL_SIZE); + config.setSize(MAX_OBJECT_POOL_SIZE); config.setAllocator(new Allocator() { @Override public Renderer allocate(Slot slot) { - return new Renderer(slot); } - @Override - public void deallocate(Renderer poolable) { - - } + @Override public void deallocate(Renderer poolable) {} }); @@ -110,7 +105,7 @@ public class Markdown { configRv.setBackgroundExpirationEnabled(true); configRv.setPreciseLeakDetectionEnabled(true); - configRv.setSize(MAX_POOL_SIZE); + configRv.setSize(MAX_OBJECT_POOL_SIZE); configRv.setAllocator(new Allocator() { @Override @@ -133,7 +128,7 @@ public class Markdown { public static void render(Context context, String markdown, TextView textView) { try { - Renderer renderer = rendererPool.claim(timeout); + Renderer renderer = rendererPool.claim(OBJECT_POOL_CLAIM_TIMEOUT); if(renderer != null) { renderer.setParameters(context, markdown, textView); @@ -147,7 +142,7 @@ public class Markdown { public static void render(Context context, String markdown, RecyclerView recyclerView) { try { - RecyclerViewRenderer renderer = rvRendererPool.claim(timeout); + RecyclerViewRenderer renderer = rvRendererPool.claim(OBJECT_POOL_CLAIM_TIMEOUT); if(renderer != null) { renderer.setParameters(context, markdown, recyclerView); @@ -234,7 +229,6 @@ public class Markdown { } public void setParameters(Context context, String markdown, TextView textView) { - this.context = context; this.markdown = markdown; this.textView = textView; @@ -242,7 +236,6 @@ public class Markdown { @Override public void run() { - Objects.requireNonNull(context); Objects.requireNonNull(markdown); Objects.requireNonNull(textView); @@ -257,18 +250,15 @@ public class Markdown { localReference.post(() -> localReference.setText(processedMarkdown)); release(); - } @Override public void release() { - context = null; markdown = null; textView = null; slot.release(this); - } public void expire() { diff --git a/app/src/main/java/org/mian/gitnex/helpers/ParseDiff.java b/app/src/main/java/org/mian/gitnex/helpers/ParseDiff.java index ffde46db..ef703172 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/ParseDiff.java +++ b/app/src/main/java/org/mian/gitnex/helpers/ParseDiff.java @@ -2,6 +2,7 @@ package org.mian.gitnex.helpers; import org.gitnex.tea4j.models.FileDiffView; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -53,10 +54,11 @@ public class ParseDiff { public static List getFileDiffViewArray(String raw) { - List fileContentsArray = new ArrayList<>(); - String[] lines = raw.split("(^|\\n)diff --git a/"); + List fileContentsArray; + if(lines.length > 1) { + fileContentsArray = new ArrayList<>(lines.length); // for each file in diff for(int i = 1; i < lines.length; i++) { @@ -137,6 +139,8 @@ public class ParseDiff { fileContentsArray.add(new FileDiffView(lines2[1], lines2[2].split("\\n")[0], "rename", "rename", null)); } } + } else { + fileContentsArray = Collections.emptyList(); } return fileContentsArray; diff --git a/app/src/main/java/org/mian/gitnex/helpers/RecyclerViewEmptySupport.java b/app/src/main/java/org/mian/gitnex/helpers/RecyclerViewEmptySupport.java index 72529559..4be152db 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/RecyclerViewEmptySupport.java +++ b/app/src/main/java/org/mian/gitnex/helpers/RecyclerViewEmptySupport.java @@ -57,7 +57,7 @@ public class RecyclerViewEmptySupport extends RecyclerView { @Override public void setAdapter(Adapter adapter) { - final Adapter oldAdapter = getAdapter(); + final Adapter oldAdapter = getAdapter(); if (oldAdapter != null) { oldAdapter.unregisterAdapterDataObserver(observer); } @@ -74,4 +74,4 @@ public class RecyclerViewEmptySupport extends RecyclerView { this.emptyView = emptyView; checkIfEmpty(); } -} \ No newline at end of file +} diff --git a/app/src/main/java/org/mian/gitnex/helpers/SimpleCallback.java b/app/src/main/java/org/mian/gitnex/helpers/SimpleCallback.java new file mode 100644 index 00000000..ca6e8b23 --- /dev/null +++ b/app/src/main/java/org/mian/gitnex/helpers/SimpleCallback.java @@ -0,0 +1,33 @@ +package org.mian.gitnex.helpers; + +import android.util.Log; +import androidx.annotation.NonNull; +import java.io.File; +import java.util.Optional; +import java.util.stream.Collectors; +import retrofit2.Call; +import retrofit2.Callback; +import retrofit2.Response; + +/** + * @author opyale + */ + +public interface SimpleCallback extends Callback { + + void onFinished(@NonNull Call call, @NonNull Optional> optionalResponse); + + default void onResponse(@NonNull Call call, @NonNull Response response) { + onFinished(call, Optional.of(response)); + } + + default void onFailure(@NonNull Call call, @NonNull Throwable throwable) { + onFinished(call, Optional.empty()); + + Log.e(call.request().url() + .pathSegments() + .stream() + .collect(Collectors.joining(File.pathSeparator)), throwable.toString()); + } + +} diff --git a/app/src/main/java/org/mian/gitnex/helpers/TimeHelper.java b/app/src/main/java/org/mian/gitnex/helpers/TimeHelper.java index 37d08332..09db44c7 100644 --- a/app/src/main/java/org/mian/gitnex/helpers/TimeHelper.java +++ b/app/src/main/java/org/mian/gitnex/helpers/TimeHelper.java @@ -22,44 +22,41 @@ public class TimeHelper { String part1 = parts[0] + "Z"; SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.ENGLISH); Date createdTime = null; + try { createdTime = formatter.parse(part1); - } - catch(ParseException e) { - e.printStackTrace(); - } + } catch(ParseException ignored) {} DateFormat format = DateFormat.getDateTimeInstance(); + assert createdTime != null; + return format.format(createdTime); } public static String formatTime(Date date, Locale locale, String timeFormat, Context context) { - if(date == null) { - return ""; - } + if(date != null) { + switch(timeFormat) { - switch(timeFormat) { + case "pretty": { + PrettyTime prettyTime = new PrettyTime(locale); + return prettyTime.format(date); + } + case "normal": { + DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd '" + context.getResources().getString(R.string.timeAtText) + "' HH:mm", locale); + return formatter.format(date); + } + case "normal1": { + DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy '" + context.getResources().getString(R.string.timeAtText) + "' HH:mm", locale); + return formatter.format(date); + } - case "pretty": { - PrettyTime prettyTime = new PrettyTime(locale); - return prettyTime.format(date); } - - case "normal": { - DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd '" + context.getResources().getString(R.string.timeAtText) + "' HH:mm", locale); - return formatter.format(date); - } - - case "normal1": { - DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy '" + context.getResources().getString(R.string.timeAtText) + "' HH:mm", locale); - return formatter.format(date); - } - } return ""; + } public static String customDateFormatForToastDateFormat(Date customDate) { @@ -84,8 +81,7 @@ public class TimeHelper { if(to.before(from)) { if(cal.after(to)) { to.add(Calendar.DATE, 1); - } - else { + } else { from.add(Calendar.DATE, -1); } } diff --git a/app/src/main/java/org/mian/gitnex/viewmodels/TeamMembersByOrgViewModel.java b/app/src/main/java/org/mian/gitnex/viewmodels/TeamMembersByOrgViewModel.java deleted file mode 100644 index c6c9cf1b..00000000 --- a/app/src/main/java/org/mian/gitnex/viewmodels/TeamMembersByOrgViewModel.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.mian.gitnex.viewmodels; - -import android.content.Context; -import android.util.Log; -import android.view.View; -import android.widget.ProgressBar; -import android.widget.TextView; -import androidx.annotation.NonNull; -import androidx.lifecycle.LiveData; -import androidx.lifecycle.MutableLiveData; -import androidx.lifecycle.ViewModel; -import org.gitnex.tea4j.models.UserInfo; -import org.mian.gitnex.R; -import org.mian.gitnex.clients.RetrofitClient; -import java.util.List; -import retrofit2.Call; -import retrofit2.Callback; -import retrofit2.Response; - -/** - * Author M M Arif - */ - -public class TeamMembersByOrgViewModel extends ViewModel { - - private static MutableLiveData> teamMembersList; - - public LiveData> getMembersByOrgList(String token, int teamId, Context ctx, TextView noDataMembers, ProgressBar progressBar) { - - teamMembersList = new MutableLiveData<>(); - loadMembersByOrgList(token, teamId, ctx, noDataMembers, progressBar); - - return teamMembersList; - } - - private static void loadMembersByOrgList(String token, int teamId, Context ctx, TextView noDataMembers, ProgressBar progressBar) { - - Call> call = RetrofitClient - .getApiInterface(ctx) - .getTeamMembersByOrg(token, teamId); - - call.enqueue(new Callback>() { - - @Override - public void onResponse(@NonNull Call> call, @NonNull Response> response) { - - if (response.isSuccessful()) { - teamMembersList.postValue(response.body()); - } else { - Log.i("onResponse", String.valueOf(response.code())); - progressBar.setVisibility(View.GONE); - if(response.code() == 403) { - noDataMembers.setText(R.string.authorizeError); - } else { - noDataMembers.setText(R.string.genericError); - } - } - - } - - @Override - public void onFailure(@NonNull Call> call, @NonNull Throwable t) { - Log.i("onFailure", t.toString()); - progressBar.setVisibility(View.GONE); - noDataMembers.setText(R.string.genericError); - } - - }); - } - -} diff --git a/app/src/main/java/org/mian/gitnex/views/DiffTextView.java b/app/src/main/java/org/mian/gitnex/views/DiffTextView.java deleted file mode 100644 index 2e9503c3..00000000 --- a/app/src/main/java/org/mian/gitnex/views/DiffTextView.java +++ /dev/null @@ -1,64 +0,0 @@ -package org.mian.gitnex.views; - -import android.content.Context; -import android.util.AttributeSet; - -/** - * Author opyale - */ - -public class DiffTextView extends androidx.appcompat.widget.AppCompatTextView { - - private int initialBackgroundColor; - private int currentBackgroundColor; - private long position; - - public DiffTextView(Context context) { - - super(context); - } - - public DiffTextView(Context context, AttributeSet attrs) { - - super(context, attrs); - } - - public DiffTextView(Context context, AttributeSet attrs, int defStyleAttr) { - - super(context, attrs, defStyleAttr); - } - - @Override - public void setBackgroundColor(int color) { - - currentBackgroundColor = color; - super.setBackgroundColor(color); - } - - public void setInitialBackgroundColor(int initialBackgroundColor) { - - setBackgroundColor(initialBackgroundColor); - this.initialBackgroundColor = initialBackgroundColor; - } - - public int getInitialBackgroundColor() { - - return initialBackgroundColor; - } - - public int getCurrentBackgroundColor() { - - return currentBackgroundColor; - } - - public long getPosition() { - - return position; - } - - public void setPosition(int position) { - - this.position = position; - } - -} diff --git a/app/src/main/java/org/mian/gitnex/views/ReactionList.java b/app/src/main/java/org/mian/gitnex/views/ReactionList.java index 4e5cfdcb..f4800055 100644 --- a/app/src/main/java/org/mian/gitnex/views/ReactionList.java +++ b/app/src/main/java/org/mian/gitnex/views/ReactionList.java @@ -9,11 +9,17 @@ import android.view.ViewGroup; import android.widget.HorizontalScrollView; import android.widget.LinearLayout; import android.widget.TextView; +import androidx.appcompat.app.AlertDialog; import androidx.cardview.widget.CardView; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import com.google.gson.Gson; import com.vdurmont.emoji.Emoji; import com.vdurmont.emoji.EmojiManager; import org.gitnex.tea4j.models.IssueReaction; +import org.gitnex.tea4j.models.UserInfo; import org.mian.gitnex.R; +import org.mian.gitnex.adapters.ReactionAuthorsAdapter; import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.Authorization; @@ -23,6 +29,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import retrofit2.Response; /** @@ -63,7 +70,8 @@ public class ReactionList extends HorizontalScrollView { if(bundle.containsKey("commentId")) { id = bundle.getInt("commentId"); reactionType = ReactionType.COMMENT; - } else { + } + else { id = bundle.getInt("issueId"); reactionType = ReactionType.ISSUE; } @@ -89,17 +97,15 @@ public class ReactionList extends HorizontalScrollView { .getIssueCommentReactions(Authorization.get(context), repoOwner, repoName, id) .execute(); break; - } - Map> sortedReactions = new HashMap<>(); + if(response.isSuccessful() && response.body() != null && !response.body().isEmpty()) { - if(response.isSuccessful() && response.body() != null) { + Map> sortedReactions = new HashMap<>(); for(IssueReaction issueReaction : response.body()) { if(sortedReactions.containsKey(issueReaction.getContent())) { - sortedReactions.get(issueReaction.getContent()).add(issueReaction); } else { List issueReactions = new ArrayList<>(); @@ -108,30 +114,56 @@ public class ReactionList extends HorizontalScrollView { sortedReactions.put(issueReaction.getContent(), issueReactions); } } - } - for(String content : sortedReactions.keySet()) { + for(String content : sortedReactions.keySet()) { - List issueReactions = sortedReactions.get(content); + List issueReactions = sortedReactions.get(content); - @SuppressLint("InflateParams") CardView reactionBadge = (CardView) LayoutInflater.from(context) - .inflate(R.layout.layout_reaction_badge, this, false); + @SuppressLint("InflateParams") CardView reactionBadge = (CardView) LayoutInflater.from(context).inflate(R.layout.layout_reaction_badge, this, false); - for(IssueReaction issueReaction : issueReactions) { - - if(issueReaction.getUser().getLogin().equals(loginUid)) { - reactionBadge.setCardBackgroundColor(AppUtil.getColorFromAttribute(context, R.attr.inputSelectedColor)); - break; + for(IssueReaction issueReaction : issueReactions) { + if(issueReaction.getUser().getLogin().equals(loginUid)) { + reactionBadge.setCardBackgroundColor(AppUtil.getColorFromAttribute(context, R.attr.inputSelectedColor)); + break; + } } + + Emoji emoji = EmojiManager.getForAlias(content); + + ((TextView) reactionBadge.findViewById(R.id.symbol)).setText(((emoji == null) ? content : emoji.getUnicode()) + " " + issueReactions.size()); + + reactionBadge.setOnClickListener(v -> { + + List userData = issueReactions.stream().map(issueReaction -> { + Gson gson = new Gson(); + return gson.fromJson(gson.toJson(issueReaction.getUser()), UserInfo.class); // FIXME Remove when transitioned to tea4j-autodeploy + }).collect(Collectors.toList()); + + ReactionAuthorsAdapter adapter = new ReactionAuthorsAdapter(context, userData); + + int paddingTop = AppUtil.getPixelsFromDensity(context, 10); + + RecyclerView recyclerView = new RecyclerView(context); + recyclerView.setPadding(0, paddingTop, 0, 0); + recyclerView.setLayoutManager(new LinearLayoutManager(context)); + recyclerView.setAdapter(adapter); + + assert emoji != null; + AlertDialog alertDialog = new AlertDialog.Builder(context) + .setView(recyclerView) + .setTitle(emoji.getUnicode()) + .setPositiveButton(R.string.okButton, (dialog, which) -> dialog.cancel()) + .setCancelable(true) + .create(); + + alertDialog.show(); + + }); + + root.post(() -> root.addView(reactionBadge)); + onReactionAddedListener.reactionAdded(); + } - - Emoji emoji = EmojiManager.getForAlias(content); - - ((TextView) reactionBadge.findViewById(R.id.symbol)).setText(((emoji == null) ? content : emoji.getUnicode()) + " " + issueReactions.size()); - - root.post(() -> root.addView(reactionBadge)); - onReactionAddedListener.reactionAdded(); - } } catch (IOException ignored) {} @@ -145,5 +177,4 @@ public class ReactionList extends HorizontalScrollView { } public interface OnReactionAddedListener { void reactionAdded(); } - } diff --git a/app/src/main/java/org/mian/gitnex/views/ReactionSpinner.java b/app/src/main/java/org/mian/gitnex/views/ReactionSpinner.java index 1e074180..84118d5b 100644 --- a/app/src/main/java/org/mian/gitnex/views/ReactionSpinner.java +++ b/app/src/main/java/org/mian/gitnex/views/ReactionSpinner.java @@ -1,6 +1,7 @@ package org.mian.gitnex.views; import android.annotation.SuppressLint; +import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.view.Gravity; @@ -18,7 +19,6 @@ import org.mian.gitnex.R; import org.mian.gitnex.clients.RetrofitClient; import org.mian.gitnex.helpers.AppUtil; import org.mian.gitnex.helpers.Authorization; -import org.mian.gitnex.helpers.Constants; import org.mian.gitnex.helpers.TinyDB; import java.io.IOException; import java.util.ArrayList; @@ -30,14 +30,16 @@ import retrofit2.Response; /** * @author opyale */ - @SuppressLint("ViewConstructor") public class ReactionSpinner extends HorizontalScrollView { + private static final List allowedReactionsCache = new ArrayList<>(); + private enum ReactionType { COMMENT, ISSUE } private enum ReactionAction { REMOVE, ADD } private OnInteractedListener onInteractedListener; + private Runnable onLoadingFinishedListener; public ReactionSpinner(Context context, Bundle bundle) { @@ -45,12 +47,12 @@ public class ReactionSpinner extends HorizontalScrollView { LinearLayout root = new LinearLayout(context); - int dens = AppUtil.getPixelsFromDensity(context, 10); + int sidesPadding = AppUtil.getPixelsFromDensity(context, 10); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); root.setOrientation(LinearLayout.HORIZONTAL); - root.setPadding(dens, 0, dens, 0); + root.setPadding(sidesPadding, 0, sidesPadding, 0); root.setGravity(Gravity.START); root.setLayoutParams(layoutParams); @@ -76,56 +78,61 @@ public class ReactionSpinner extends HorizontalScrollView { try { List allReactions = getReactions(repoOwner, repoName, reactionType, id); + List allowedReactions = getAllowedReactions(); - for(String allowedReaction : getAllowedReactions()) { + if(!allowedReactions.isEmpty()) { + // Show all allowed reactions + for(String allowedReaction : allowedReactions) { - @SuppressLint("InflateParams") CardView reactionButton = (CardView) LayoutInflater.from(context) - .inflate(R.layout.layout_reaction_button, root, false); + @SuppressLint("InflateParams") CardView reactionButton = (CardView) LayoutInflater.from(context) + .inflate(R.layout.layout_reaction_button, root, false); - IssueReaction myReaction = null; + // Checks if current user reacted with 'allowedReaction' + boolean myReaction = allReactions.stream().anyMatch(issueReaction -> + issueReaction.getContent().equals(allowedReaction) && + issueReaction.getUser().getLogin().equals(loginUid)); - for(IssueReaction issueReaction : allReactions) { + ReactionAction reactionAction; - if(issueReaction.getContent().equals(allowedReaction) && issueReaction.getUser().getLogin().equals(loginUid)) { - myReaction = issueReaction; - break; + if(myReaction) { + reactionButton.setCardBackgroundColor(AppUtil.getColorFromAttribute(context, R.attr.inputSelectedColor)); + reactionAction = ReactionAction.REMOVE; + } else { + reactionAction = ReactionAction.ADD; } + + reactionButton.setOnClickListener(v -> new Thread(() -> { + + try { + if(react(repoOwner, repoName, reactionType, reactionAction, new IssueReaction(allowedReaction), id)) { + v.post(() -> onInteractedListener.onInteracted()); + } + } catch(IOException ignored) {} + + }).start()); + + Emoji emoji = EmojiManager.getForAlias(allowedReaction); + + ((TextView) reactionButton.findViewById(R.id.symbol)).setText((emoji == null) ? allowedReaction : emoji.getUnicode()); + root.post(() -> root.addView(reactionButton)); + } - ReactionAction reactionAction; - - if(myReaction != null) { - - reactionButton.setCardBackgroundColor(AppUtil.getColorFromAttribute(context, R.attr.inputSelectedColor)); - reactionAction = ReactionAction.REMOVE; - } else { - reactionAction = ReactionAction.ADD; - } - - reactionButton.setOnClickListener(v -> new Thread(() -> { - - try { - if(react(repoOwner, repoName, reactionType, reactionAction, new IssueReaction(allowedReaction), id)) { - v.post(() -> onInteractedListener.onInteracted()); - } - } catch(IOException ignored) {} - - }).start()); - - Emoji emoji = EmojiManager.getForAlias(allowedReaction); - - ((TextView) reactionButton.findViewById(R.id.symbol)).setText((emoji == null) ? allowedReaction : emoji.getUnicode()); - root.post(() -> root.addView(reactionButton)); + this.post(() -> { + setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); + addView(root); + setVisibility(VISIBLE); + }); + } else { + this.post(() -> setVisibility(GONE)); } - } catch(IOException ignored) {} - + if(onLoadingFinishedListener != null) { + ((Activity) context).runOnUiThread(() -> onLoadingFinishedListener.run()); + } }).start(); - setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); - addView(root); - } private boolean react(String repoOwner, String repoName, ReactionType reactionType, ReactionAction reactionAction, IssueReaction issueReaction, int id) throws IOException { @@ -204,29 +211,29 @@ public class ReactionSpinner extends HorizontalScrollView { } - if(response.isSuccessful() && response.body() != null) + if(response.isSuccessful() && response.body() != null) { return response.body(); - else + } else { return Collections.emptyList(); - + } } + // Assumes that there's something wrong when no allowed reactions are returned by the server private List getAllowedReactions() throws IOException { - List allowedReactions = new ArrayList<>(); + if(allowedReactionsCache.isEmpty()) { - Response response = RetrofitClient - .getApiInterface(getContext()) - .getUISettings(Authorization.get(getContext())) - .execute(); + Response response = RetrofitClient + .getApiInterface(getContext()) + .getUISettings(Authorization.get(getContext())) + .execute(); - if(response.isSuccessful() && response.body() != null) { - allowedReactions.addAll(Arrays.asList(response.body().getAllowed_reactions())); - } else { - allowedReactions.addAll(Arrays.asList(Constants.fallbackReactions)); + if(response.isSuccessful() && response.body() != null) { + allowedReactionsCache.addAll(Arrays.asList(response.body().getAllowed_reactions())); + } } - return allowedReactions; + return allowedReactionsCache; } @@ -236,4 +243,8 @@ public class ReactionSpinner extends HorizontalScrollView { public interface OnInteractedListener { void onInteracted(); } + public void setOnLoadingFinishedListener(Runnable onLoadingFinishedListener) { + this.onLoadingFinishedListener = onLoadingFinishedListener; + } + } diff --git a/app/src/main/res/drawable/ic_diff.xml b/app/src/main/res/drawable/ic_diff.xml new file mode 100644 index 00000000..45b3785f --- /dev/null +++ b/app/src/main/res/drawable/ic_diff.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/shape_badge_background.xml b/app/src/main/res/drawable/shape_badge_background.xml index 896ae05a..200f3a02 100644 --- a/app/src/main/res/drawable/shape_badge_background.xml +++ b/app/src/main/res/drawable/shape_badge_background.xml @@ -2,7 +2,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> - + - - - - - - diff --git a/app/src/main/res/layout/activity_diff.xml b/app/src/main/res/layout/activity_diff.xml new file mode 100644 index 00000000..fe7bf0e2 --- /dev/null +++ b/app/src/main/res/layout/activity_diff.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/layout/activity_org_team_members.xml b/app/src/main/res/layout/activity_org_team_info.xml similarity index 66% rename from app/src/main/res/layout/activity_org_team_members.xml rename to app/src/main/res/layout/activity_org_team_info.xml index 0f92988f..a074efad 100644 --- a/app/src/main/res/layout/activity_org_team_members.xml +++ b/app/src/main/res/layout/activity_org_team_info.xml @@ -23,13 +23,13 @@ android:id="@+id/close" android:layout_width="@dimen/close_button_size" android:layout_height="@dimen/close_button_size" - android:layout_marginRight="15dp" android:layout_marginLeft="15dp" - android:gravity="center_vertical" - android:contentDescription="@string/close" + android:layout_marginRight="15dp" android:background="?android:attr/selectableItemBackgroundBorderless" - android:focusable="true" android:clickable="true" + android:contentDescription="@string/close" + android:focusable="true" + android:gravity="center_vertical" android:src="@drawable/ic_close" /> + + - - - - - + app:layout_behavior="@string/appbar_scrolling_view_behavior" /> diff --git a/app/src/main/res/layout/activity_repo_stargazers.xml b/app/src/main/res/layout/activity_repo_stargazers.xml index cb4306a6..7d1a33c8 100644 --- a/app/src/main/res/layout/activity_repo_stargazers.xml +++ b/app/src/main/res/layout/activity_repo_stargazers.xml @@ -47,7 +47,6 @@ + android:gravity="center" /> - + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:orientation="horizontal"> + android:textColor="@color/lightGray" + android:textSize="16sp" + app:textAllCaps="true" /> - + diff --git a/app/src/main/res/layout/badge_release.xml b/app/src/main/res/layout/badge_release.xml index 37515ae1..e1ae71a2 100644 --- a/app/src/main/res/layout/badge_release.xml +++ b/app/src/main/res/layout/badge_release.xml @@ -1,30 +1,29 @@ - + android:layout_height="wrap_content" + android:gravity="center_vertical" + android:orientation="horizontal"> + android:textColor="@color/lightGray" + android:textSize="16sp" + app:textAllCaps="true" /> - + diff --git a/app/src/main/res/layout/bottom_sheet_issue_comments.xml b/app/src/main/res/layout/bottom_sheet_issue_comments.xml index d49a94b1..a090ffd7 100644 --- a/app/src/main/res/layout/bottom_sheet_issue_comments.xml +++ b/app/src/main/res/layout/bottom_sheet_issue_comments.xml @@ -22,7 +22,6 @@ android:id="@+id/commentReactionButtons" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="10dp" android:orientation="horizontal" /> + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_file_diff.xml b/app/src/main/res/layout/fragment_diff_files.xml similarity index 80% rename from app/src/main/res/layout/activity_file_diff.xml rename to app/src/main/res/layout/fragment_diff_files.xml index 1607502f..fca6669d 100644 --- a/app/src/main/res/layout/activity_file_diff.xml +++ b/app/src/main/res/layout/fragment_diff_files.xml @@ -1,11 +1,9 @@ - + android:layout_width="match_parent" + android:layout_height="match_parent"> - - - - - - + + diff --git a/app/src/main/res/layout/fragment_files.xml b/app/src/main/res/layout/fragment_files.xml index e3cc0fd1..e18d6289 100644 --- a/app/src/main/res/layout/fragment_files.xml +++ b/app/src/main/res/layout/fragment_files.xml @@ -19,17 +19,25 @@ android:id="@+id/breadcrumbs_view" android:layout_width="match_parent" android:layout_height="wrap_content" + android:saveEnabled="false" android:text="@string/filesBreadcrumbRoot" app:CustomTextSize="16sp" app:SelectedTextColor="?attr/primaryTextColor" app:UnSelectedTextColor="@color/lightGray" /> - + android:layout_height="wrap_content"> + + + + diff --git a/app/src/main/res/layout/fragment_members_by_org.xml b/app/src/main/res/layout/fragment_members_by_org.xml index 9b846206..281e7474 100644 --- a/app/src/main/res/layout/fragment_members_by_org.xml +++ b/app/src/main/res/layout/fragment_members_by_org.xml @@ -8,14 +8,14 @@ + android:background="?attr/primaryBackgroundColor" + android:gravity="center" /> + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_organization_team_info_permissions.xml b/app/src/main/res/layout/fragment_organization_team_info_permissions.xml new file mode 100644 index 00000000..c9818785 --- /dev/null +++ b/app/src/main/res/layout/fragment_organization_team_info_permissions.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/app/src/main/res/layout/layout_reaction_badge.xml b/app/src/main/res/layout/layout_reaction_badge.xml index b20c5436..f169749e 100644 --- a/app/src/main/res/layout/layout_reaction_badge.xml +++ b/app/src/main/res/layout/layout_reaction_badge.xml @@ -4,19 +4,19 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginEnd="5dp" + android:layout_marginEnd="6dp" app:cardBackgroundColor="?attr/inputBackgroundColor" - app:cardCornerRadius="10dp" + app:cardCornerRadius="15dp" app:cardElevation="0dp"> diff --git a/app/src/main/res/layout/layout_reaction_button.xml b/app/src/main/res/layout/layout_reaction_button.xml index 9e5df599..f054cdaa 100644 --- a/app/src/main/res/layout/layout_reaction_button.xml +++ b/app/src/main/res/layout/layout_reaction_button.xml @@ -2,13 +2,12 @@ + app:cardCornerRadius="20dp" + app:cardElevation="0dp"> diff --git a/app/src/main/res/layout/layout_tab_text.xml b/app/src/main/res/layout/layout_tab_text.xml new file mode 100644 index 00000000..1a24e38c --- /dev/null +++ b/app/src/main/res/layout/layout_tab_text.xml @@ -0,0 +1,9 @@ + + diff --git a/app/src/main/res/layout/list_commits.xml b/app/src/main/res/layout/list_commits.xml index 0fe9b501..f6cac1d0 100644 --- a/app/src/main/res/layout/list_commits.xml +++ b/app/src/main/res/layout/list_commits.xml @@ -1,84 +1,96 @@ - + android:background="?android:attr/selectableItemBackground" + android:orientation="vertical"> + android:textSize="18sp" + tools:text="This is a test" /> - + + + android:layout_weight="1" + android:gravity="center_vertical" + android:orientation="horizontal" + tools:ignore="UseCompoundDrawables"> + + + + + tools:text="opyale authored and opyale committed" /> + app:drawableLeftCompat="@drawable/ic_commit" + tools:text="357f3qd5s" /> - - - -