Cherry-pick changes from #897 (#1032)

Cherry-pick enhancements and features from #897. I didn't pick database-related stuff because I don't think it's a good idea to do this (see discord conversation).
 Closes #598
 Closes #887
 Closes #888
 Closes #901
 Closes #1024
 Closes #1035

Co-authored-by: opyale <opyale@noreply.codeberg.org>
Co-authored-by: qwerty287 <ndev@web.de>
Co-authored-by: M M Arif <mmarif@noreply.codeberg.org>
Co-authored-by: M M Arif <mmarif@swatian.com>
Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/1032
Co-authored-by: qwerty287 <qwerty287@noreply.codeberg.org>
Co-committed-by: qwerty287 <qwerty287@noreply.codeberg.org>
This commit is contained in:
qwerty287 2022-02-22 07:23:46 +01:00 committed by M M Arif
parent 24c7176753
commit 56ff09d44d
84 changed files with 2104 additions and 1933 deletions

View File

@ -31,7 +31,7 @@ labels:
<!-- Screenshots and stacktrace's can go here. -->
<br><br>
- [ ] 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).
<br>
<!-- Thank you for your time. -->
<!-- Thank you for your time. -->

View File

@ -11,7 +11,7 @@ labels:
## # Describe your matter briefly
<br><br>
- [ ] 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).
<br>
<!-- Thank you for your time. -->

View File

@ -31,7 +31,7 @@ labels:
<!-- Screenshots and stacktrace's can go here. -->
<br><br>
- [ ] 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).
<br>
<!-- Thank you for your time. -->

View File

@ -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.**
<br>
### 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.<br><br>
**Note:** Please contact the project directly via [email](mailto:gitnex@swatian.com) if have to share sensitive and security related details.
<br>
### 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**<br><br>
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.

View File

@ -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:

View File

@ -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.

View File

@ -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'
}

View File

@ -18,7 +18,6 @@
android:roundIcon="@mipmap/app_logo_round"
android:supportsRtl="true"
tools:targetApi="n">
<activity
android:name=".activities.MergePullRequestActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|uiMode|keyboard|keyboardHidden|navigation" />
@ -60,7 +59,7 @@
android:name=".activities.CreateTeamByOrgActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|uiMode|keyboard|keyboardHidden|navigation" />
<activity
android:name=".activities.OrganizationTeamMembersActivity"
android:name=".activities.OrganizationTeamInfoActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|uiMode|keyboard|keyboardHidden|navigation" />
<activity
android:name=".activities.OrganizationDetailActivity"
@ -85,12 +84,11 @@
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name=".activities.MainActivity"
android:theme="@android:style/Theme.NoTitleBar"
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|uiMode|keyboard|keyboardHidden|navigation"
android:theme="@android:style/Theme.NoTitleBar"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
@ -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" />
<activity
android:name=".activities.CreateRepoActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|uiMode|keyboard|keyboardHidden|navigation" />
@ -108,6 +106,10 @@
<activity
android:name=".activities.FileDiffActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|uiMode|keyboard|keyboardHidden|navigation" />
<activity
android:name=".activities.DiffActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|uiMode|keyboard|keyboardHidden|navigation"
android:theme="@android:style/Theme.NoTitleBar" />
<activity
android:name=".activities.CommitsActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|uiMode|keyboard|keyboardHidden|navigation" />
@ -154,28 +156,28 @@
<activity
android:name=".activities.SettingsNotificationsActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|uiMode|keyboard|keyboardHidden|navigation" />
<activity
android:name=".activities.AdminCronTasksActivity"
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|uiMode|keyboard|keyboardHidden|navigation" />
<!-- Version < 3.0. DeX Mode and Screen Mirroring support -->
<meta-data android:name="com.samsung.android.keepalive.density" android:value="true"/>
<!-- Version >= 3.0. DeX Dual Mode support -->
<meta-data android:name="com.samsung.android.multidisplay.keep_process_alive" android:value="true"/>
<!-- deep links -->
android:configChanges="orientation|screenSize|smallestScreenSize|density|screenLayout|uiMode|keyboard|keyboardHidden|navigation" /> <!-- Version < 3.0. DeX Mode and Screen Mirroring support -->
<meta-data
android:name="com.samsung.android.keepalive.density"
android:value="true" /> <!-- Version >= 3.0. DeX Dual Mode support -->
<meta-data
android:name="com.samsung.android.multidisplay.keep_process_alive"
android:value="true" /> <!-- deep links -->
<activity
android:name=".activities.DeepLinksActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:noHistory="true"
android:launchMode="singleTask"
android:noHistory="true"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" />
<data android:scheme="https" />
<data android:host="codeberg.org" />
@ -185,17 +187,7 @@
<data android:host="git.fsfe.org" />
<data android:host="opendev.org" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="gitnex" />
</intent-filter>
</activity>
<!-- deep links -->
</application>
</manifest>

View File

@ -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<Void> 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<Void> call = RetrofitClient.getApiInterface(context)
.markNotificationThreadsAsRead(instanceToken, AppUtil.getTimestampFromDate(context, date), true,
new String[]{"unread", "pinned"}, "read");
return call.execute().isSuccessful();
}
}

View File

@ -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());

View File

@ -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();
}
}

View File

@ -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<ResponseBody> call = apiCall ?
RetrofitClient.getApiInterface(ctx).getPullDiffContent(Authorization.get(ctx), owner, repo, pullIndex) :
RetrofitClient.getWebInterface(ctx).getPullDiffContent(Authorization.getWeb(ctx), owner, repo, pullIndex);
try {
Response<ResponseBody> response = call.execute();
assert response.body() != null;
switch(response.code()) {
case 200:
List<FileDiffView> 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();
};
}
}

View File

@ -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<String> data) {
}
public void labelsInterface(List<String> data) { }
@Override
public void labelsIdsInterface(List<Integer> data) {

View File

@ -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();
});

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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();
}
}

View File

@ -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<List<UserRepositories>> 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<List<UserRepositories>> 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<UserRepositories> arr = new ArrayList<>();
List<UserRepositories> 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);
}
}

View File

@ -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) {

View File

@ -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<RecyclerView.ViewHolder
if(viewType == TYPE_LOAD) {
return new CommitsHolder(inflater.inflate(R.layout.list_commits, parent, false));
}
else {
} else {
return new LoadHolder(inflater.inflate(R.layout.row_load, parent, false));
}
}
@ -72,56 +68,116 @@ public class CommitsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder
if(commitsList.get(position).getSha() != null) {
return TYPE_LOAD;
}
else {
} else {
return 1;
}
}
@Override
public int getItemCount() {
return commitsList.size();
}
class CommitsHolder extends RecyclerView.ViewHolder {
TextView commitTitle;
TextView commitCommitter;
TextView commitDate;
Button commitHtmlUrl;
View rootView;
TextView commitSubject;
TextView commitBody;
TextView commitAuthorAndCommitter;
ImageView commitAuthorAvatar;
ImageView commitCommitterAvatar;
TextView commitSha;
CommitsHolder(View itemView) {
super(itemView);
commitTitle = itemView.findViewById(R.id.commitTitleVw);
commitCommitter = itemView.findViewById(R.id.commitCommitterVw);
commitDate = itemView.findViewById(R.id.commitDateVw);
commitHtmlUrl = itemView.findViewById(R.id.commitHtmlUrlVw);
rootView = itemView;
commitSubject = itemView.findViewById(R.id.commitSubject);
commitBody = itemView.findViewById(R.id.commitBody);
commitAuthorAndCommitter = itemView.findViewById(R.id.commitAuthorAndCommitter);
commitAuthorAvatar = itemView.findViewById(R.id.commitAuthorAvatar);
commitCommitterAvatar = itemView.findViewById(R.id.commitCommitterAvatar);
commitSha = itemView.findViewById(R.id.commitSha);
}
@SuppressLint("SetTextI18n")
void bindData(Commits commitsModel) {
final TinyDB tinyDb = TinyDB.getInstance(context);
Locale locale = context.getResources().getConfiguration().locale;
final String timeFormat = tinyDb.getString("dateFormat");
String[] commitMessageParts = commitsModel.getCommit().getMessage().split("(\r\n|\n)", 2);
commitTitle.setText(EmojiParser.parseToUnicode(commitsModel.getCommit().getMessage()));
commitCommitter.setText(context.getString(R.string.commitCommittedBy, commitsModel.getCommit().getCommitter().getName()));
commitDate.setText(TimeHelper.formatTime(commitsModel.getCommit().getCommitter().getDate(), locale, timeFormat, context));
if(timeFormat.equals("pretty")) {
commitDate.setOnClickListener(new ClickListener(TimeHelper.customDateFormatForToastDateFormat(commitsModel.getCommit().getCommitter().getDate()), context));
if(commitMessageParts.length > 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);
}

View File

@ -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<String> lines;
private final List<Integer> 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<String> 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;
}
}
}

View File

@ -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<FileDiffView> fileDiffViews;
public DiffFilesAdapter(Context context, List<FileDiffView> 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;
}
}

View File

@ -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<Long, View> 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<FileDiffView> fileDiffViews;
public FilesDiffAdapter(Context context, FragmentManager fragmentManager, List<FileDiffView> 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<codeLines.length; l++) {
if(codeLines[l].length() > 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");
}
}

View File

@ -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<IssueCommentsAdap
dialog.dismiss();
});
Handler handler = new Handler();
handler.postDelayed(() -> {
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();

View File

@ -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<RecyclerView.View
private final LinearLayout frame;
private final TextView subject;
private final TextView repository;
private final ImageView typePr;
private final ImageView typeIssue;
private final ImageView typeRepo;
private final ImageView typeCommit;
private final ImageView typeUnknown;
private final ImageView type;
private ImageView pinned;
private final ImageView more;
@ -103,11 +102,7 @@ public class NotificationsAdapter extends RecyclerView.Adapter<RecyclerView.View
frame = itemView.findViewById(R.id.frame);
subject = itemView.findViewById(R.id.subject);
repository = itemView.findViewById(R.id.repository);
typePr = itemView.findViewById(R.id.typePr);
typeIssue = itemView.findViewById(R.id.typeIssue);
typeRepo = itemView.findViewById(R.id.typeRepo);
typeCommit = itemView.findViewById(R.id.typeCommit);
typeUnknown = itemView.findViewById(R.id.typeUnknown);
type = itemView.findViewById(R.id.type);
pinned = itemView.findViewById(R.id.pinned);
more = itemView.findViewById(R.id.more);
}
@ -138,41 +133,39 @@ public class NotificationsAdapter extends RecyclerView.Adapter<RecyclerView.View
pinned.setVisibility(View.GONE);
}
switch(notificationThread.getSubject().getType()) {
case "Pull":
typePr.setVisibility(View.VISIBLE);
typeIssue.setVisibility(View.GONE);
typeRepo.setVisibility(View.GONE);
typeCommit.setVisibility(View.GONE);
typeUnknown.setVisibility(View.GONE);
switch(notificationThread.getSubject().getType().toLowerCase()) {
case "pull":
type.setImageResource(R.drawable.ic_pull_request);
break;
case "Issue":
typePr.setVisibility(View.GONE);
typeRepo.setVisibility(View.GONE);
typeCommit.setVisibility(View.GONE);
typeIssue.setVisibility(View.VISIBLE);
typeUnknown.setVisibility(View.GONE);
case "issue":
type.setImageResource(R.drawable.ic_issue);
break;
case "Repository":
typeUnknown.setVisibility(View.GONE);
typeIssue.setVisibility(View.GONE);
typePr.setVisibility(View.GONE);
typeRepo.setVisibility(View.VISIBLE);
typeCommit.setVisibility(View.GONE);
case "commit":
type.setImageResource(R.drawable.ic_commit);
break;
case "Commit":
typeUnknown.setVisibility(View.GONE);
typeIssue.setVisibility(View.GONE);
typePr.setVisibility(View.GONE);
typeRepo.setVisibility(View.GONE);
typeCommit.setVisibility(View.VISIBLE);
case "repository":
type.setImageResource(R.drawable.ic_repo);
break;
default:
typePr.setVisibility(View.GONE);
typeRepo.setVisibility(View.GONE);
typeCommit.setVisibility(View.GONE);
typeIssue.setVisibility(View.GONE);
typeUnknown.setVisibility(View.VISIBLE);
type.setImageResource(R.drawable.ic_question);
break;
}
switch(notificationThread.getSubject().getState().toLowerCase()) {
case "closed":
ImageViewCompat.setImageTintList(type, ColorStateList.valueOf(context.getResources().getColor(R.color.iconIssuePrClosedColor)));
break;
case "merged":
ImageViewCompat.setImageTintList(type, ColorStateList.valueOf(context.getResources().getColor(R.color.iconPrMergedColor)));
break;
default:
case "open":
ImageViewCompat.setImageTintList(type, ColorStateList.valueOf(AppUtil.getColorFromAttribute(context, R.attr.iconsColor)));
break;
}

View File

@ -0,0 +1,80 @@
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 android.widget.TextView;
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 ReactionAuthorsAdapter extends RecyclerView.Adapter<ReactionAuthorsAdapter.ViewHolder> {
private final Context context;
private final List<UserInfo> userInfos;
public ReactionAuthorsAdapter(Context context, List<UserInfo> 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);
}
}
}

View File

@ -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<RecyclerView.ViewHold
if(viewType == TYPE_LOAD) {
return new RepoForksAdapter.ForksHolder(inflater.inflate(R.layout.list_repositories, parent, false));
}
else {
} else {
return new LoadHolder(inflater.inflate(R.layout.row_load, parent, false));
}
}
@ -139,7 +139,6 @@ public class RepoForksAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
repoName.setText(forksModel.getFullName().split("/")[1]);
repoStars.setText(forksModel.getStars_count());
ColorGenerator generator = ColorGenerator.MATERIAL;
int color = generator.getColor(forksModel.getName());
String firstCharacter = String.valueOf(forksModel.getFullName().charAt(0));
@ -244,48 +243,31 @@ public class RepoForksAdapter extends RecyclerView.Adapter<RecyclerView.ViewHold
//store if user is watching this repo
{
final String token = "token " + tinyDb.getString(tinyDb.getString("loginUid") + "-token");
RetrofitClient.getApiInterface(context)
.checkRepoWatchStatus(Authorization.get(context), repoOwner, repoName)
.enqueue(new Callback<WatchInfo>() {
WatchInfo watch = new WatchInfo();
@Override
public void onResponse(@NonNull Call<WatchInfo> call, @NonNull retrofit2.Response<WatchInfo> response) {
Call<WatchInfo> call;
if(response.isSuccessful() && response.body() != null) {
call = RetrofitClient.getApiInterface(context).checkRepoWatchStatus(token, repoOwner, repoName);
call.enqueue(new Callback<WatchInfo>() {
@Override
public void onResponse(@NonNull Call<WatchInfo> call, @NonNull retrofit2.Response<WatchInfo> 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<WatchInfo> call, @NonNull Throwable t) {
tinyDb.putBoolean("repoWatch", false);
Toasty.error(context, context.getString(R.string.genericApiStatusError));
}
@Override
public void onFailure(@NonNull Call<WatchInfo> call, @NonNull Throwable t) {
tinyDb.putBoolean("repoWatch", false);
Toasty.error(context, context.getString(R.string.genericApiStatusError));
}
});
}
context.startActivity(intent);

View File

@ -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<TeamMembersByOrgPreviewAdapter.ViewHolder> {
private final Context context;
private final List<UserInfo> userData;
public TeamMembersByOrgPreviewAdapter(Context context, List<UserInfo> 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);
}
}
}

View File

@ -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<TeamsByOrgAdapter.Or
static class OrgTeamsViewHolder extends RecyclerView.ViewHolder {
private Teams teams;
private Teams team;
private OrgPermissions permissions;
private final TextView teamTitle;
private final TextView teamDescription;
private final TextView teamPermission;
private final LinearLayout membersPreviewFrame;
private final List<UserInfo> 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<Teams> teamListMain, OrgPermissions permissions) {
@ -78,19 +94,42 @@ public class TeamsByOrgAdapter extends RecyclerView.Adapter<TeamsByOrgAdapter.Or
Teams currentItem = teamList.get(position);
holder.teams = currentItem;
holder.team = currentItem;
holder.teamTitle.setText(currentItem.getName());
holder.permissions = permissions;
if (!currentItem.getDescription().equals("")) {
holder.membersPreviewFrame.setVisibility(View.GONE);
holder.userInfos.clear();
holder.adapter.notifyDataSetChanged();
RetrofitClient.getApiInterface(context)
.getTeamMembersByOrg(Authorization.get(context), currentItem.getId())
.enqueue(new Callback<List<UserInfo>>() {
@Override
public void onResponse(@NonNull Call<List<UserInfo>> call, @NonNull Response<List<UserInfo>> 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<List<UserInfo>> 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

View File

@ -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) {

View File

@ -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<Void>) (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<Void>) (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<Void>) (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();

View File

@ -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

View File

@ -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<ResponseBody> 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<ResponseBody> response = call.execute();
assert response.body() != null;
switch(response.code()) {
case 200:
List<FileDiffView> 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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}

View File

@ -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<NotificationThread> notificationThreads;
private final List<NotificationThread> 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<Void>) (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<List<NotificationThread>> call = RetrofitClient
RetrofitClient
.getApiInterface(context)
.getNotificationThreads(instanceToken, false, filter,
Constants.defaultOldestTimestamp, "",
1, resultLimit);
call.enqueue(new Callback<List<NotificationThread>>() {
@Override
public void onResponse(@NonNull Call<List<NotificationThread>> call, @NonNull Response<List<NotificationThread>> 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<List<NotificationThread>>) (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<List<NotificationThread>> 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<List<NotificationThread>> call = RetrofitClient.getApiInterface(context)
.getNotificationThreads(instanceToken, false, filter,
Constants.defaultOldestTimestamp, "",
page, resultLimit);
call.enqueue(new Callback<List<NotificationThread>>() {
@Override
public void onResponse(@NonNull Call<List<NotificationThread>> call, @NonNull Response<List<NotificationThread>> response) {
if(response.code() == 200) {
assert response.body() != null;
List<NotificationThread> 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<List<NotificationThread>> 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<Void>) (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);
}
}

View File

@ -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<UserInfo> 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<List<UserInfo>> call = RetrofitClient
.getApiInterface(ctx)
.getTeamMembersByOrg(Authorization.get(ctx), team.getId());
binding.progressBar.setVisibility(View.VISIBLE);
call.enqueue(new Callback<List<UserInfo>>() {
@Override
public void onResponse(@NonNull Call<List<UserInfo>> call, @NonNull Response<List<UserInfo>> 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<List<UserInfo>> call, @NonNull Throwable t) {
Log.i("onFailure", t.toString());
}
});
}
}

View File

@ -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();
}
}

View File

@ -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();

View File

@ -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<List<Teams>>() {
@Override
public void onChanged(@Nullable List<Teams> 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);
});
}

View File

@ -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())

View File

@ -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

View File

@ -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<Renderer> rendererPool;
private static final Pool<RecyclerViewRenderer> 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<Renderer>() {
@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<RecyclerViewRenderer>() {
@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() {

View File

@ -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<FileDiffView> getFileDiffViewArray(String raw) {
List<FileDiffView> fileContentsArray = new ArrayList<>();
String[] lines = raw.split("(^|\\n)diff --git a/");
List<FileDiffView> 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;

View File

@ -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();
}
}
}

View File

@ -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<T> extends Callback<T> {
void onFinished(@NonNull Call<T> call, @NonNull Optional<Response<T>> optionalResponse);
default void onResponse(@NonNull Call<T> call, @NonNull Response<T> response) {
onFinished(call, Optional.of(response));
}
default void onFailure(@NonNull Call<T> call, @NonNull Throwable throwable) {
onFinished(call, Optional.empty());
Log.e(call.request().url()
.pathSegments()
.stream()
.collect(Collectors.joining(File.pathSeparator)), throwable.toString());
}
}

View File

@ -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);
}
}

View File

@ -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<List<UserInfo>> teamMembersList;
public LiveData<List<UserInfo>> 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<List<UserInfo>> call = RetrofitClient
.getApiInterface(ctx)
.getTeamMembersByOrg(token, teamId);
call.enqueue(new Callback<List<UserInfo>>() {
@Override
public void onResponse(@NonNull Call<List<UserInfo>> call, @NonNull Response<List<UserInfo>> 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<List<UserInfo>> call, @NonNull Throwable t) {
Log.i("onFailure", t.toString());
progressBar.setVisibility(View.GONE);
noDataMembers.setText(R.string.genericError);
}
});
}
}

View File

@ -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;
}
}

View File

@ -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<String, List<IssueReaction>> sortedReactions = new HashMap<>();
if(response.isSuccessful() && response.body() != null && !response.body().isEmpty()) {
if(response.isSuccessful() && response.body() != null) {
Map<String, List<IssueReaction>> sortedReactions = new HashMap<>();
for(IssueReaction issueReaction : response.body()) {
if(sortedReactions.containsKey(issueReaction.getContent())) {
sortedReactions.get(issueReaction.getContent()).add(issueReaction);
} else {
List<IssueReaction> 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<IssueReaction> issueReactions = sortedReactions.get(content);
List<IssueReaction> 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<UserInfo> 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(); }
}

View File

@ -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<String> 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<IssueReaction> allReactions = getReactions(repoOwner, repoName, reactionType, id);
List<String> 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<String> getAllowedReactions() throws IOException {
List<String> allowedReactions = new ArrayList<>();
if(allowedReactionsCache.isEmpty()) {
Response<UISettings> response = RetrofitClient
.getApiInterface(getContext())
.getUISettings(Authorization.get(getContext()))
.execute();
Response<UISettings> 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;
}
}

View File

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?attr/iconsColor"
android:pathData="M12.25,3.5a0.75,0.75 0,0 1,0.75 0.75V8.5h4.25a0.75,0.75 0,0 1,0 1.5H13v4.25a0.75,0.75 0,0 1,-1.5 0V10H7.25a0.75,0.75 0,0 1,0 -1.5h4.25V4.25a0.75,0.75 0,0 1,0.75 -0.75zM6.562,19.25a0.75,0.75 0,0 1,0.75 -0.75h9.938a0.75,0.75 0,0 1,0 1.5H7.312a0.75,0.75 0,0 1,-0.75 -0.75z"
android:fillType="evenOdd"/>
</vector>

View File

@ -2,7 +2,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="2dp" />
<corners android:radius="8dp" />
<solid
android:color="?attr/iconsColor"/>
<padding

View File

@ -1,14 +0,0 @@
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="0dp" />
<solid
android:color="?attr/iconsColor"/>
<padding
android:bottom="2dp"
android:left="5dp"
android:right="5dp"
android:top="2dp" />
</shape>

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="?attr/primaryBackgroundColor">
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

View File

@ -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" />
<TextView
@ -37,43 +37,29 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:textColor="?attr/primaryTextColor"
android:maxLines="1"
android:textColor="?attr/primaryTextColor"
android:textSize="20sp" />
</com.google.android.material.appbar.MaterialToolbar>
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/primaryBackgroundColor"
app:tabIndicatorColor="?attr/pagerTabIndicatorColor"
app:tabIndicatorFullWidth="true"
app:tabMode="auto"
app:tabTextAppearance="@style/customTabLayout"
app:tabTextColor="?attr/primaryTextColor" />
</com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
style="@style/Widget.MaterialComponents.LinearProgressIndicator"
app:indicatorColor="?attr/progressIndicatorColor" />
<GridView
android:id="@+id/gridView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:horizontalSpacing="10dp"
android:numColumns="4"
android:columnWidth="80dp"
android:stretchMode="columnWidth"
android:background="?attr/primaryBackgroundColor"
android:gravity="center"/>
<TextView
android:id="@+id/noDataMembers"
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="15dp"
android:gravity="center"
android:text="@string/noDataMembers"
android:textColor="?attr/primaryTextColor"
android:textSize="20sp"
android:visibility="gone" />
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</LinearLayout>

View File

@ -47,7 +47,6 @@
</com.google.android.material.appbar.AppBarLayout>
<GridView
android:padding="10dp"
android:id="@+id/gridView"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@ -47,7 +47,6 @@
</com.google.android.material.appbar.AppBarLayout>
<GridView
android:padding="10dp"
android:id="@+id/gridView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@ -56,7 +55,7 @@
android:columnWidth="80dp"
android:stretchMode="columnWidth"
android:background="?attr/primaryBackgroundColor"
android:gravity="center"/>
android:gravity="center" />
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progress_bar"

View File

@ -1,30 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/counterBadgePullText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="16sp"
app:textAllCaps="true"
android:text="@string/tabPullRequests"
android:textColor="@color/lightGray" />
android:textColor="@color/lightGray"
android:textSize="16sp"
app:textAllCaps="true" />
<TextView
android:id="@+id/counterBadgePull"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="1dp"
android:layout_toEndOf="@id/counterBadgePullText"
android:background="@drawable/shape_badge_background"
android:gravity="center"
android:text="@string/infoTabRepoZero"
android:textColor="@color/colorWhite"
android:textSize="12sp" />
</RelativeLayout>
</LinearLayout>

View File

@ -1,30 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/counterBadgeReleaseText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="16sp"
app:textAllCaps="true"
android:text="@string/tabTextReleases"
android:textColor="@color/lightGray" />
android:textColor="@color/lightGray"
android:textSize="16sp"
app:textAllCaps="true" />
<TextView
android:id="@+id/counterBadgeRelease"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="1dp"
android:layout_toEndOf="@id/counterBadgeReleaseText"
android:background="@drawable/shape_badge_background"
android:gravity="center"
android:text="@string/infoTabRepoZero"
android:textColor="@color/colorWhite"
android:textSize="12sp" />
</RelativeLayout>
</LinearLayout>

View File

@ -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" />
<TextView

View File

@ -23,7 +23,6 @@
android:id="@+id/commentReactionButtons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:orientation="horizontal" />
<TextView

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/Widget.AppCompat.SearchView">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/primaryBackgroundColor">
<ImageView
android:id="@+id/close"
android:layout_width="@dimen/close_button_size"
android:layout_height="@dimen/close_button_size"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:contentDescription="@string/close"
android:focusable="true"
android:gravity="center_vertical"
android:src="@drawable/ic_close" />
<TextView
android:id="@+id/toolbar_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="20dp"
android:ellipsize="start"
android:singleLine="true"
android:textColor="?attr/primaryTextColor"
android:textSize="18sp" />
</com.google.android.material.appbar.MaterialToolbar>
</com.google.android.material.appbar.AppBarLayout>
<ListView
android:id="@+id/diff"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fastScrollEnabled="true" />
</LinearLayout>

View File

@ -1,11 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:background="?attr/primaryBackgroundColor">
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
@ -50,27 +48,18 @@
</com.google.android.material.appbar.AppBarLayout>
<LinearLayout
android:id="@+id/filesDiffFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/primaryBackgroundColor"
android:scrollbars="vertical" />
</LinearLayout>
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progress_bar"
style="@style/Widget.MaterialComponents.LinearProgressIndicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
style="@style/Widget.MaterialComponents.LinearProgressIndicator"
app:indicatorColor="?attr/progressIndicatorColor" />
<ListView
android:id="@+id/diff_files"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fastScrollEnabled="true" />
</LinearLayout>

View File

@ -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" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/pullToRefresh"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/primaryBackgroundColor"
android:scrollbars="vertical" />
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/primaryBackgroundColor"
android:scrollbars="vertical" />
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</LinearLayout>

View File

@ -8,14 +8,14 @@
<GridView
android:id="@+id/gridView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:horizontalSpacing="10dp"
android:numColumns="4"
android:columnWidth="80dp"
android:stretchMode="columnWidth"
android:gravity="center"/>
android:background="?attr/primaryBackgroundColor"
android:gravity="center" />
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progressBar"

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/primaryBackgroundColor"
android:orientation="vertical">
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progressBar"
style="@style/Widget.MaterialComponents.LinearProgressIndicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
app:indicatorColor="?attr/progressIndicatorColor" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<GridView
android:id="@+id/members"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:horizontalSpacing="10dp"
android:numColumns="4"
android:columnWidth="80dp"
android:stretchMode="columnWidth"
android:background="?attr/primaryBackgroundColor"
android:gravity="center"/>
<TextView
android:id="@+id/noDataMembers"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="15dp"
android:gravity="center"
android:text="@string/noDataMembers"
android:textColor="?attr/primaryTextColor"
android:textSize="20sp"
android:visibility="gone" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/primaryBackgroundColor"
android:orientation="vertical">
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progressBar"
style="@style/Widget.MaterialComponents.LinearProgressIndicator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
app:indicatorColor="?attr/progressIndicatorColor" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/permissions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:textColor="?attr/primaryTextColor"
android:textSize="15sp"
tools:text="• Members of this team can view team repositories." />
</LinearLayout>
</LinearLayout>

View File

@ -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">
<TextView
android:id="@+id/symbol"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="8dp"
android:paddingTop="3dp"
android:paddingRight="8dp"
android:paddingBottom="3dp"
android:paddingLeft="10dp"
android:paddingTop="5dp"
android:paddingRight="10dp"
android:paddingBottom="5dp"
android:textColor="?attr/primaryTextColor"
android:textSize="14sp"
tools:text="👍" />

View File

@ -2,13 +2,12 @@
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginEnd="6dp"
app:cardBackgroundColor="?attr/inputBackgroundColor"
app:cardCornerRadius="25sp"
app:cardElevation="0dp"
app:contentPadding="10dp">
app:cardCornerRadius="20dp"
app:cardElevation="0dp">
<TextView
android:id="@+id/symbol"
@ -16,7 +15,8 @@
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textColor="?attr/primaryTextColor"
android:textSize="18sp"
android:textSize="20dp"
tools:ignore="SpUsage"
tools:text="👍" />
</androidx.cardview.widget.CardView>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:maxLines="1"
android:textAllCaps="true"
android:textColor="?attr/primaryTextColor"
android:textSize="16sp" />

View File

@ -1,84 +1,96 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/commitList"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:background="?attr/primaryBackgroundColor">
android:background="?android:attr/selectableItemBackground"
android:orientation="vertical">
<LinearLayout
android:id="@+id/linearLayoutFrame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:padding="16dp"
android:orientation="vertical">
<TextView
android:id="@+id/commitTitleVw"
android:id="@+id/commitSubject"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/commitTitle"
android:layout_marginBottom="8dp"
android:textColor="?attr/primaryTextColor"
android:textSize="18sp" />
android:textSize="18sp"
tools:text="This is a test" />
<LinearLayout
android:id="@+id/frameCommitterAndDate"
<TextView
android:id="@+id/commitBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:alpha=".8"
android:textColor="?attr/primaryTextColor"
android:textSize="14sp"
tools:text="This is a test commit message" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<LinearLayout
android:id="@+id/frameCommitter"
android:layout_width="0dp"
android:layout_weight=".25"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="horizontal"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/commitAuthorAvatar"
android:layout_width="@dimen/list_avatar_size"
android:layout_height="@dimen/list_avatar_size"
android:contentDescription="@string/generalImgContentText"
tools:srcCompat="@tools:sample/avatars" />
<ImageView
android:id="@+id/commitCommitterAvatar"
android:layout_width="@dimen/list_avatar_size"
android:layout_height="@dimen/list_avatar_size"
android:layout_marginStart="5dp"
android:contentDescription="@string/generalImgContentText"
tools:srcCompat="@tools:sample/avatars" />
<TextView
android:id="@+id/commitCommitterVw"
android:id="@+id/commitAuthorAndCommitter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:textColor="?attr/primaryTextColor"
android:textSize="14sp" />
tools:text="opyale authored and opyale committed" />
</LinearLayout>
<TextView
android:id="@+id/commitDateVw"
android:layout_width="0dp"
android:layout_weight=".25"
android:id="@+id/commitSha"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="end"
android:layout_weight="0"
android:alpha=".8"
android:drawablePadding="10dp"
android:gravity="center_vertical"
android:textColor="?attr/primaryTextColor"
android:textSize="14sp" />
app:drawableLeftCompat="@drawable/ic_commit"
tools:text="357f3qd5s" />
</LinearLayout>
<LinearLayout
android:id="@+id/frameViewCommits"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/commitHtmlUrlVw"
android:layout_width="wrap_content"
android:layout_height="60dp"
android:text="@string/viewInBrowser"
android:layout_marginTop="15dp"
android:textColor="@color/btnTextColor"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
<View
android:layout_below="@id/linearLayoutFrame"
android:layout_width="match_parent"
android:layout_height="1dp"
android:id="@+id/divider"
android:background="?attr/dividerColor" />
</RelativeLayout>
</LinearLayout>

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="15dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="@string/generalImgContentText"
app:srcCompat="@drawable/ic_diff" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/fileName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="start"
android:singleLine="true"
android:textColor="?attr/primaryTextColor"
android:textSize="16sp"
tools:text="/src/test1/test2/test3/test4/test.txt" />
<TextView
android:id="@+id/fileStatistics"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?attr/primaryTextColor"
android:textSize="13sp"
tools:text="@string/diffStatistics" />
</LinearLayout>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_chevron_right"
android:contentDescription="@string/generalImgContentText" />
</LinearLayout>

View File

@ -1,108 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/shape_files_diffs"
android:orientation="vertical"
android:paddingLeft="15dp"
android:paddingTop="7dp"
android:paddingRight="15dp"
android:paddingBottom="7dp">
<TextView
android:id="@+id/headerFileName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="start"
android:fontFamily="monospace"
android:singleLine="true"
android:textColor="@color/colorWhite"
android:textSize="16sp" />
<LinearLayout
android:id="@+id/diff_stats"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:orientation="horizontal">
<LinearLayout
android:layout_width="16dp"
android:layout_height="5dp"
android:layout_marginEnd="10dp"
android:background="@color/colorLightGreen"
android:orientation="horizontal">
<Space
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0.5" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="0.5"
android:background="@color/colorRed"
android:orientation="horizontal" />
</LinearLayout>
<TextView
android:id="@+id/headerFileInfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:textColor="@color/colorWhite"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/dividerColor" />
<LinearLayout
android:id="@+id/footer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="@+id/footerImage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:scaleType="fitXY"
android:visibility="gone"
android:contentDescription="@string/generalImgContentText" />
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
tools:visibility="visible">
<LinearLayout
android:id="@+id/diffLines"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"/>
</HorizontalScrollView>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?attr/dividerColor" />
</LinearLayout>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:orientation="vertical">
<ImageView
android:id="@+id/avatar"
android:layout_width="@dimen/list_avatar_size"
android:layout_height="@dimen/list_avatar_size"
android:contentDescription="@string/generalImgContentText" />
</LinearLayout>

View File

@ -4,7 +4,7 @@
android:id="@+id/frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/primaryBackgroundColor"
android:background="?android:attr/selectableItemBackground"
android:padding="16dp"
android:orientation="horizontal">
@ -21,45 +21,13 @@
android:orientation="vertical">
<ImageView
android:id="@+id/typePr"
android:id="@+id/type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:contentDescription="@string/generalImgContentText"
app:srcCompat="@drawable/ic_pull_request" />
<ImageView
android:id="@+id/typeIssue"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:contentDescription="@string/generalImgContentText"
app:srcCompat="@drawable/ic_issue" />
<ImageView
android:id="@+id/typeRepo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:contentDescription="@string/generalImgContentText"
app:srcCompat="@drawable/ic_repo" />
<ImageView
android:id="@+id/typeCommit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:contentDescription="@string/generalImgContentText"
app:srcCompat="@drawable/ic_commit" />
<ImageView
android:id="@+id/typeUnknown"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:contentDescription="@string/generalImgContentText"
app:srcCompat="@drawable/ic_question" />
</LinearLayout>
<LinearLayout

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingLeft="15dp"
android:paddingTop="5dp"
android:paddingRight="15dp"
android:paddingBottom="5dp">
<ImageView
android:id="@+id/authorAvatar"
android:layout_width="@dimen/list_avatar_size_medium"
android:layout_height="@dimen/list_avatar_size_medium"
android:layout_marginEnd="10dp"
android:contentDescription="@string/generalImgContentText"
tools:src="@tools:sample/avatars" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/authorFullName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?attr/primaryTextColor"
android:textSize="14sp"
android:textStyle="bold"
tools:text="opyale" />
<TextView
android:id="@+id/authorLogin"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="?attr/primaryTextColor"
tools:text="opyale" />
</LinearLayout>
</LinearLayout>

View File

@ -1,36 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout android:layout_width="match_parent"
<LinearLayout xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/orgTeamsFrame"
android:padding="16dp"
android:orientation="vertical"
android:background="?attr/primaryBackgroundColor"
android:background="?android:attr/selectableItemBackground"
xmlns:android="http://schemas.android.com/apk/res/android">
<TextView
android:id="@+id/teamTitle"
android:text="@string/teamTitle"
android:layout_width="match_parent"
<TextView
android:id="@+id/teamTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/teamTitle"
android:textColor="?attr/primaryTextColor"
android:textSize="18sp"
android:textStyle="bold"
tools:text="PR-Managers" />
<TextView
android:id="@+id/teamDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/teamDescription"
android:textColor="?attr/primaryTextColor"
android:textSize="15sp"
tools:text="Managing pull requests and related issues" />
<LinearLayout
android:id="@+id/membersPreviewFrame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp"
android:gravity="bottom"
android:orientation="horizontal"
android:visibility="gone"
tools:visibility="visible">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/membersPreview"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="10dp"
android:textColor="?attr/primaryTextColor"
android:textSize="18sp" />
android:layout_weight="1" />
<TextView
android:id="@+id/teamDescription"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/teamDescription"
android:gravity="center_vertical"
android:text="@string/teamShowAll"
android:textColor="?attr/primaryTextColor"
android:layout_marginBottom="10dp"
android:textSize="14sp" />
<TextView
android:id="@+id/teamPermission"
android:text="@string/teamPermission"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:textColor="?attr/primaryTextColor"
android:textSize="14sp" />
android:textSize="15sp"
app:drawableEndCompat="@drawable/ic_chevron_right"
app:drawableTint="?attr/primaryTextColor" />
</LinearLayout>
</LinearLayout>

View File

@ -1,11 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/gridViewData"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/primaryBackgroundColor"
android:orientation="vertical">
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:orientation="vertical"
tools:ignore="UseCompoundDrawables">
<ImageView
android:id="@+id/userAvatarImageView"
@ -21,7 +24,7 @@
android:gravity="top|center_horizontal"
android:layout_marginTop="1dp"
android:text="@string/userName"
android:textSize="14sp"
android:textColor="?attr/primaryTextColor" />
android:textColor="?attr/primaryTextColor"
android:textSize="14sp" />
</LinearLayout>

View File

@ -28,8 +28,8 @@
<ImageView
android:id="@+id/userAvatar"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_width="@dimen/list_avatar_size_large"
android:layout_height="@dimen/list_avatar_size_large"
android:contentDescription="@string/generalImgContentText"
tools:srcCompat="@tools:sample/avatars" />

View File

@ -4,4 +4,6 @@
<dimen name="fab_padding">15dp</dimen>
<dimen name="list_avatar_size">24dp</dimen>
<dimen name="list_avatar_size_medium">34dp</dimen>
<dimen name="list_avatar_size_large">60dp</dimen>
</resources>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="versionLow" translatable="false">1.14</string>
<string name="versionHigh" translatable="false">1.16</string>
<string name="versionLow" translatable="false">1.15</string>
<string name="versionHigh" translatable="false">1.17</string>
</resources>

View File

@ -107,8 +107,7 @@
<string name="orgCreatedError">Something went wrong, please try again</string>
<string name="orgExistsError">Organization already exists</string>
<string name="binaryFileError">Binary files are not supported yet.</string>
<string name="fileTooLarge">This file exceeds the maximum possible diff lines</string>
<string name="diffStatistics">%s addition(s) and %s deletion(s)</string>
<string name="processingText">Processing</string>
<string name="search">Search</string>
@ -300,7 +299,13 @@
<string name="noDataTeams">No teams found</string>
<string name="teamTitle">Team name</string>
<string name="teamDescription">Team desc</string>
<string name="teamPermission">Permission : %1$s</string>
<string name="teamPermissions">Permissions</string>
<string name="teamPermissionNone">• Members of this team do not have any permissions.</string>
<string name="teamPermissionRead">• Members of this team can view team repositories.</string>
<string name="teamPermissionWrite">• Members of this team can view and push to team repositories.</string>
<string name="teamPermissionAdmin">• Members of this team can push to and from team repositories and add collaborators.</string>
<string name="teamPermissionOwner">• Members of this team have owner permissions.</string>
<string name="teamShowAll">show all</string>
<string name="noDataMembers">No members found</string>
<string name="orgMember">Org members</string>
<string name="orgTeamMembers">Organization team members</string>
@ -540,8 +545,8 @@
<string name="editPrSuccessMessage">Pull Request updated</string>
<string name="fileDiffViewHeader">%1$s Files Changed</string>
<string name="fileDiffViewHeaderSingle">%1$s File Changed</string>
<string name="openFileDiffText">Files Changed</string>
<string name="updatePullRequestText">Update Pull Request</string>
<string name="openFileDiffText">Show Changed Files</string>
<string name="mergePullRequestText">Merge Pull Request</string>
<string name="deletePrHeadBranch">Delete head branch</string>
<string name="deleteBranchSuccess">Branch deleted successfully</string>
@ -580,7 +585,8 @@
<string name="shareRepository">Share Repository</string>
<string name="createRepository">Create Repository</string>
<string name="commitTitle">Commits</string>
<string name="commitCommittedBy">Committed by %1$s</string>
<string name="commitAuthoredByAndCommittedByWhen"><![CDATA[<b>%s</b> authored and <b>%s</b> committed %s]]></string>
<string name="commitCommittedByWhen"><![CDATA[<b>%s</b> committed %s]]></string>
<string name="viewCommits">View Commits</string>
<string name="changelogTitle" translatable="false">Changelog</string>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<changelog>
<release version="4.2.0" versioncode="415">
<release version="4.2.0" versioncode="420">
<change>Under development</change>
</release>