GitNex-Android-App/app/src/main/java/org/mian/gitnex/activities/LoginActivity.java

847 lines
24 KiB
Java
Raw Normal View History

package org.mian.gitnex.activities;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioGroup;
import androidx.annotation.NonNull;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import io.mikael.urlbuilder.UrlBuilder;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.UUID;
import okhttp3.Credentials;
import org.gitnex.tea4j.v2.models.AccessToken;
import org.gitnex.tea4j.v2.models.CreateAccessTokenOption;
import org.gitnex.tea4j.v2.models.GeneralAPISettings;
import org.gitnex.tea4j.v2.models.ServerVersion;
import org.gitnex.tea4j.v2.models.User;
import org.mian.gitnex.R;
import org.mian.gitnex.clients.RetrofitClient;
import org.mian.gitnex.database.api.BaseApi;
Implement drafts, introduce Room persistence library for db (#139) Fix no draft message translation updates format improvements typo update some renaming refactors Use better naming convention remove duplicate source arrange draft titles enhance click listener area Launch drafts from reply screen and clean up Add message draft saved update repositories tasks Update user accounts repository with thread, remove async tasks remove async task in drafts update layout, change async to thread in drafts Merge branch 'master' into pull_139 # Conflicts: # app/build.gradle # app/src/main/java/org/mian/gitnex/activities/LoginActivity.java Merge branch 'master' into pull_139 # Conflicts: # app/src/main/java/org/mian/gitnex/activities/ReplyToIssueActivity.java Merge branch 'pull_139' of codeberg.org:gitnex/GitNex into pull_139 # Conflicts: # app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java Merge branch 'master' into pull_139 # Conflicts: # app/src/main/java/org/mian/gitnex/activities/LoginActivity.java # app/src/main/java/org/mian/gitnex/activities/ReplyToIssueActivity.java # app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java Merge branch 'master' into pull_139 Merge branch 'master' into pull_139 Merge branch 'master' into pull_139 and fix conflicts # Conflicts: # app/build.gradle # app/src/main/java/org/mian/gitnex/activities/LoginActivity.java # app/src/main/java/org/mian/gitnex/activities/MainActivity.java # app/src/main/java/org/mian/gitnex/activities/ReplyToIssueActivity.java # app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java # app/src/main/java/org/mian/gitnex/helpers/StaticGlobalVariables.java # app/src/main/res/values/strings.xml Code Format Merge branch 'master' into 15-comments-draft Merge branch 'master' into 15-comments-draft Merge branch 'master' into 15-comments-draft # Conflicts: # app/src/main/java/org/mian/gitnex/activities/MainActivity.java # app/src/main/res/values/strings.xml Go to draft, save on type and other fixes delete all drafts, added messages where needed delete draft Force logout Merge branch 'master' into 15-comments-draft Merge branch 'master' into 15-comments-draft # Conflicts: # app/src/main/java/org/mian/gitnex/activities/MainActivity.java # app/src/main/java/org/mian/gitnex/helpers/StaticGlobalVariables.java check if account data is null, we need to log the user out for the 1st time. Merge branch 'master' into 15-comments-draft fix repo owner, name sequence Add comments for test, show drafts list Add repos to db Add account to db and other refactors to the code Merge branch 'master' into 15-comments-draft Merge branch 'master' into 15-comments-draft Merge branch 'master' into 15-comments-draft Merge branch 'master' into 15-comments-draft # Conflicts: # app/build.gradle # app/src/main/AndroidManifest.xml Merge branch 'master' into 15-comments-draft merge more queries, added dao repositories, layout update Added queries in dao some refactor. added models, dao, entities (accounts, repositories, drafts) WIP on implementing drafts, introduced Room persistence library for db. Co-authored-by: M M Arif <mmarif@swatian.com> Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/139 Reviewed-by: opyale <opyale@noreply.codeberg.org>
2020-07-04 22:51:55 +02:00
import org.mian.gitnex.database.api.UserAccountsApi;
import org.mian.gitnex.database.models.UserAccount;
import org.mian.gitnex.databinding.ActivityLoginBinding;
import org.mian.gitnex.helpers.AppUtil;
import org.mian.gitnex.helpers.NetworkStatusObserver;
import org.mian.gitnex.helpers.PathsHelper;
import org.mian.gitnex.helpers.Toasty;
import org.mian.gitnex.helpers.UrlHelper;
import org.mian.gitnex.helpers.Version;
import org.mian.gitnex.structs.Protocol;
import retrofit2.Call;
import retrofit2.Callback;
/**
* @author M M Arif
*/
public class LoginActivity extends BaseActivity {
private Button loginButton;
private EditText instanceUrlET, loginUidET, loginPassword, otpCode, loginTokenCode;
private AutoCompleteTextView protocolSpinner;
private RadioGroup loginMethod;
private String device_id = "token";
private String selectedProtocol;
Don't use TinyDB as cache (#1034) Do not use TinyDB as a cache or a way to send data between activities. ### How is this working Instead of saving everything into the TinyDB, I created three `Context`s (a `RepositoryContext`, an `IssueContext` and an `AccountContext`). All are used to store things like API or database values/models and additional data, e.g. the `RepositoryContext` also contains information about the current filter state of a repository (issues, pull requests, releases/tags and milestones). These are sent using `Intent`s and `Bundle`s between activities and fragments. Changing a field (e.g. filter state) in any fragment changes it also for the whole repository (or at least it should do so). Due to the size of the changes (after https://codeberg.org/gitnex/GitNex/commit/c9172f85efafd9f25739fdd8385e1904b711ea41, Git says `154 files changed, 3318 insertions(+), 3835 deletions(-)`) **I highly recommend you to create a beta/pre release before releasing a stable version**. Additional changes: * after logging out, the account remains in the account list (with a note) and you can log in again (you can't switch to this account) * repositories and organizations are clickable on user profiles * deleted two unused classes Once finished, hopefully * closes #354 * replaces #897 * fixes #947 * closes #1001 * closes #1015 * marks #876 and #578 as `Wontfix` since they are not necessary at this point * and all the other TinyDB issues Co-authored-by: qwerty287 <ndev@web.de> Co-authored-by: M M Arif <mmarif@noreply.codeberg.org> Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/1034 Reviewed-by: 6543 <6543@noreply.codeberg.org> Co-authored-by: qwerty287 <qwerty287@noreply.codeberg.org> Co-committed-by: qwerty287 <qwerty287@noreply.codeberg.org>
2022-03-13 03:59:13 +01:00
private URI instanceUrl;
private Version giteaVersion;
private int maxResponseItems = 50;
private int defaultPagingNumber = 25;
Don't use TinyDB as cache (#1034) Do not use TinyDB as a cache or a way to send data between activities. ### How is this working Instead of saving everything into the TinyDB, I created three `Context`s (a `RepositoryContext`, an `IssueContext` and an `AccountContext`). All are used to store things like API or database values/models and additional data, e.g. the `RepositoryContext` also contains information about the current filter state of a repository (issues, pull requests, releases/tags and milestones). These are sent using `Intent`s and `Bundle`s between activities and fragments. Changing a field (e.g. filter state) in any fragment changes it also for the whole repository (or at least it should do so). Due to the size of the changes (after https://codeberg.org/gitnex/GitNex/commit/c9172f85efafd9f25739fdd8385e1904b711ea41, Git says `154 files changed, 3318 insertions(+), 3835 deletions(-)`) **I highly recommend you to create a beta/pre release before releasing a stable version**. Additional changes: * after logging out, the account remains in the account list (with a note) and you can log in again (you can't switch to this account) * repositories and organizations are clickable on user profiles * deleted two unused classes Once finished, hopefully * closes #354 * replaces #897 * fixes #947 * closes #1001 * closes #1015 * marks #876 and #578 as `Wontfix` since they are not necessary at this point * and all the other TinyDB issues Co-authored-by: qwerty287 <ndev@web.de> Co-authored-by: M M Arif <mmarif@noreply.codeberg.org> Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/1034 Reviewed-by: 6543 <6543@noreply.codeberg.org> Co-authored-by: qwerty287 <qwerty287@noreply.codeberg.org> Co-committed-by: qwerty287 <qwerty287@noreply.codeberg.org>
2022-03-13 03:59:13 +01:00
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityLoginBinding activityLoginBinding =
ActivityLoginBinding.inflate(getLayoutInflater());
setContentView(activityLoginBinding.getRoot());
NetworkStatusObserver networkStatusObserver = NetworkStatusObserver.getInstance(ctx);
loginButton = activityLoginBinding.loginButton;
instanceUrlET = activityLoginBinding.instanceUrl;
loginUidET = activityLoginBinding.loginUid;
loginPassword = activityLoginBinding.loginPasswd;
otpCode = activityLoginBinding.otpCode;
protocolSpinner = activityLoginBinding.httpsSpinner;
loginMethod = activityLoginBinding.loginMethod;
loginTokenCode = activityLoginBinding.loginTokenCode;
activityLoginBinding.appVersion.setText(AppUtil.getAppVersion(appCtx));
ArrayAdapter<Protocol> adapterProtocols =
new ArrayAdapter<>(
LoginActivity.this, R.layout.list_spinner_items, Protocol.values());
instanceUrlET.setText(getIntent().getStringExtra("instanceUrl"));
protocolSpinner.setAdapter(adapterProtocols);
protocolSpinner.setSelection(0);
protocolSpinner.setOnItemClickListener(
(parent, view, position, id) -> {
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 {
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 {
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);
}
});
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();
});
}
private void login() {
try {
if (selectedProtocol == null) {
Toasty.error(ctx, getResources().getString(R.string.protocolEmptyError));
enableProcessButton();
return;
}
String loginUid = loginUidET.getText().toString().replaceAll("[\\uFEFF]", "").trim();
String loginPass = loginPassword.getText().toString().trim();
String loginToken =
loginTokenCode.getText().toString().replaceAll("[\\uFEFF|#]", "").trim();
LoginType loginType =
(loginMethod.getCheckedRadioButtonId() == R.id.loginUsernamePassword)
? LoginType.BASIC
: LoginType.TOKEN;
URI rawInstanceUrl =
UrlBuilder.fromString(
UrlHelper.fixScheme(
instanceUrlET
.getText()
.toString()
.replaceAll("[\\uFEFF|#]", "")
.trim(),
"http"))
.toUri();
instanceUrl =
UrlBuilder.fromUri(rawInstanceUrl)
.withScheme(selectedProtocol.toLowerCase())
.withPath(PathsHelper.join(rawInstanceUrl.getPath(), "/api/v1/"))
.toUri();
Don't use TinyDB as cache (#1034) Do not use TinyDB as a cache or a way to send data between activities. ### How is this working Instead of saving everything into the TinyDB, I created three `Context`s (a `RepositoryContext`, an `IssueContext` and an `AccountContext`). All are used to store things like API or database values/models and additional data, e.g. the `RepositoryContext` also contains information about the current filter state of a repository (issues, pull requests, releases/tags and milestones). These are sent using `Intent`s and `Bundle`s between activities and fragments. Changing a field (e.g. filter state) in any fragment changes it also for the whole repository (or at least it should do so). Due to the size of the changes (after https://codeberg.org/gitnex/GitNex/commit/c9172f85efafd9f25739fdd8385e1904b711ea41, Git says `154 files changed, 3318 insertions(+), 3835 deletions(-)`) **I highly recommend you to create a beta/pre release before releasing a stable version**. Additional changes: * after logging out, the account remains in the account list (with a note) and you can log in again (you can't switch to this account) * repositories and organizations are clickable on user profiles * deleted two unused classes Once finished, hopefully * closes #354 * replaces #897 * fixes #947 * closes #1001 * closes #1015 * marks #876 and #578 as `Wontfix` since they are not necessary at this point * and all the other TinyDB issues Co-authored-by: qwerty287 <ndev@web.de> Co-authored-by: M M Arif <mmarif@noreply.codeberg.org> Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/1034 Reviewed-by: 6543 <6543@noreply.codeberg.org> Co-authored-by: qwerty287 <qwerty287@noreply.codeberg.org> Co-committed-by: qwerty287 <qwerty287@noreply.codeberg.org>
2022-03-13 03:59:13 +01:00
// cache values to make them available the next time the user wants to log in
tinyDB.putString("loginType", loginType.name().toLowerCase());
tinyDB.putString("instanceUrlRaw", instanceUrlET.getText().toString());
if (instanceUrlET.getText().toString().equals("")) {
Toasty.error(ctx, getResources().getString(R.string.emptyFieldURL));
enableProcessButton();
return;
}
if (loginType == LoginType.BASIC) {
if (otpCode.length() != 0 && otpCode.length() != 6) {
Toasty.warning(ctx, getResources().getString(R.string.loginOTPTypeError));
enableProcessButton();
return;
}
if (loginUid.equals("")) {
Toasty.error(ctx, getResources().getString(R.string.emptyFieldUsername));
enableProcessButton();
return;
}
if (loginPass.equals("")) {
Toasty.error(ctx, getResources().getString(R.string.emptyFieldPassword));
enableProcessButton();
return;
}
int loginOTP =
(otpCode.length() > 0)
? Integer.parseInt(otpCode.getText().toString().trim())
: 0;
versionCheck(loginUid, loginPass, loginOTP, loginToken, loginType);
} else {
if (loginToken.equals("")) {
Toasty.error(ctx, getResources().getString(R.string.loginTokenError));
enableProcessButton();
return;
}
versionCheck(loginUid, loginPass, 123, loginToken, loginType);
serverPageLimitSettings();
}
} catch (Exception e) {
Toasty.error(ctx, getResources().getString(R.string.malformedUrl));
enableProcessButton();
}
}
private void serverPageLimitSettings() {
Call<GeneralAPISettings> generalAPISettings =
RetrofitClient.getApiInterface(ctx).getGeneralAPISettings();
generalAPISettings.enqueue(
new Callback<>() {
@Override
public void onResponse(
@NonNull final Call<GeneralAPISettings> generalAPISettings,
@NonNull retrofit2.Response<GeneralAPISettings> response) {
if (response.code() == 200 && response.body() != null) {
if (response.body().getMaxResponseItems() != null) {
maxResponseItems =
Math.toIntExact(response.body().getMaxResponseItems());
}
if (response.body().getDefaultPagingNum() != null) {
defaultPagingNumber =
Math.toIntExact(response.body().getDefaultPagingNum());
}
}
}
@Override
public void onFailure(
@NonNull Call<GeneralAPISettings> generalAPISettings,
@NonNull Throwable t) {}
});
}
private void versionCheck(
final String loginUid,
final String loginPass,
final int loginOTP,
final String loginToken,
final LoginType loginType) {
Call<ServerVersion> callVersion;
if (!loginToken.equals("")) {
callVersion =
RetrofitClient.getApiInterface(
ctx, instanceUrl.toString(), "token " + loginToken, null)
.getVersion();
} else {
String credential = Credentials.basic(loginUid, loginPass, StandardCharsets.UTF_8);
if (loginOTP != 0) {
callVersion =
RetrofitClient.getApiInterface(
ctx, instanceUrl.toString(), credential, null)
.getVersion(loginOTP);
} else {
callVersion =
RetrofitClient.getApiInterface(
ctx, instanceUrl.toString(), credential, null)
.getVersion();
}
}
callVersion.enqueue(
new Callback<>() {
@Override
public void onResponse(
@NonNull final Call<ServerVersion> callVersion,
@NonNull retrofit2.Response<ServerVersion> responseVersion) {
if (responseVersion.code() == 200) {
ServerVersion version = responseVersion.body();
assert version != null;
2020-03-31 16:41:50 +02:00
if (!Version.valid(version.getVersion())) {
Toasty.error(
ctx, getResources().getString(R.string.versionUnknown));
enableProcessButton();
return;
}
giteaVersion = new Version(version.getVersion());
if (giteaVersion.less(getString(R.string.versionLow))) {
MaterialAlertDialogBuilder materialAlertDialogBuilder =
new MaterialAlertDialogBuilder(ctx)
.setTitle(
getString(
R.string.versionAlertDialogHeader))
.setMessage(
getResources()
.getString(
R.string
.versionUnsupportedOld,
version.getVersion()))
.setNeutralButton(
getString(R.string.cancelButton),
(dialog, which) -> {
dialog.dismiss();
enableProcessButton();
})
.setPositiveButton(
getString(R.string.textContinue),
(dialog, which) -> {
dialog.dismiss();
login(
loginType,
loginUid,
loginPass,
loginOTP,
loginToken);
});
materialAlertDialogBuilder.create().show();
} else if (giteaVersion.lessOrEqual(getString(R.string.versionHigh))) {
login(loginType, loginUid, loginPass, loginOTP, loginToken);
} else {
Toasty.warning(
ctx,
getResources().getString(R.string.versionUnsupportedNew));
login(loginType, loginUid, loginPass, loginOTP, loginToken);
}
} else if (responseVersion.code() == 403) {
login(loginType, loginUid, loginPass, loginOTP, loginToken);
}
}
private void login(
LoginType loginType,
String loginUid,
String loginPass,
int loginOTP,
String loginToken) {
2020-03-31 16:41:50 +02:00
// ToDo: before store/create token: get UserInfo to check DB/AccountManager
// if there already exist a token
// the setup methods then can better handle all different cases
if (loginType == LoginType.BASIC) {
setup(loginUid, loginPass, loginOTP);
} else if (loginType == LoginType.TOKEN) { // Token
setupUsingExistingToken(loginToken);
}
}
@Override
public void onFailure(
@NonNull Call<ServerVersion> callVersion, @NonNull Throwable t) {
Toasty.error(
ctx, getResources().getString(R.string.genericServerResponseError));
enableProcessButton();
}
});
}
private void setupUsingExistingToken(final String loginToken) {
Call<User> call =
RetrofitClient.getApiInterface(
ctx, instanceUrl.toString(), "token " + loginToken, null)
.userGetCurrent();
call.enqueue(
new Callback<>() {
@Override
public void onResponse(
@NonNull Call<User> call, @NonNull retrofit2.Response<User> response) {
User userDetails = response.body();
switch (response.code()) {
case 200:
assert userDetails != null;
// insert new account to db if does not exist
String accountName = userDetails.getLogin() + "@" + instanceUrl;
UserAccountsApi userAccountsApi =
BaseApi.getInstance(ctx, UserAccountsApi.class);
assert userAccountsApi != null;
boolean userAccountExists =
userAccountsApi.userAccountExists(accountName);
UserAccount account;
if (!userAccountExists) {
long accountId =
userAccountsApi.createNewAccount(
accountName,
instanceUrl.toString(),
userDetails.getLogin(),
loginToken,
giteaVersion.toString(),
maxResponseItems,
defaultPagingNumber);
account = userAccountsApi.getAccountById((int) accountId);
} else {
userAccountsApi.updateTokenByAccountName(
accountName, loginToken);
userAccountsApi.login(
userAccountsApi
.getAccountByName(accountName)
.getAccountId());
account = userAccountsApi.getAccountByName(accountName);
}
AppUtil.switchToAccount(LoginActivity.this, account);
enableProcessButton();
startActivity(new Intent(LoginActivity.this, MainActivity.class));
finish();
break;
case 401:
Toasty.error(
ctx,
getResources().getString(R.string.unauthorizedApiError));
enableProcessButton();
break;
default:
Toasty.error(
ctx,
getResources()
.getString(
R.string.genericApiError, response.code()));
enableProcessButton();
}
}
Implement drafts, introduce Room persistence library for db (#139) Fix no draft message translation updates format improvements typo update some renaming refactors Use better naming convention remove duplicate source arrange draft titles enhance click listener area Launch drafts from reply screen and clean up Add message draft saved update repositories tasks Update user accounts repository with thread, remove async tasks remove async task in drafts update layout, change async to thread in drafts Merge branch 'master' into pull_139 # Conflicts: # app/build.gradle # app/src/main/java/org/mian/gitnex/activities/LoginActivity.java Merge branch 'master' into pull_139 # Conflicts: # app/src/main/java/org/mian/gitnex/activities/ReplyToIssueActivity.java Merge branch 'pull_139' of codeberg.org:gitnex/GitNex into pull_139 # Conflicts: # app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java Merge branch 'master' into pull_139 # Conflicts: # app/src/main/java/org/mian/gitnex/activities/LoginActivity.java # app/src/main/java/org/mian/gitnex/activities/ReplyToIssueActivity.java # app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java Merge branch 'master' into pull_139 Merge branch 'master' into pull_139 Merge branch 'master' into pull_139 and fix conflicts # Conflicts: # app/build.gradle # app/src/main/java/org/mian/gitnex/activities/LoginActivity.java # app/src/main/java/org/mian/gitnex/activities/MainActivity.java # app/src/main/java/org/mian/gitnex/activities/ReplyToIssueActivity.java # app/src/main/java/org/mian/gitnex/adapters/MyReposListAdapter.java # app/src/main/java/org/mian/gitnex/helpers/StaticGlobalVariables.java # app/src/main/res/values/strings.xml Code Format Merge branch 'master' into 15-comments-draft Merge branch 'master' into 15-comments-draft Merge branch 'master' into 15-comments-draft # Conflicts: # app/src/main/java/org/mian/gitnex/activities/MainActivity.java # app/src/main/res/values/strings.xml Go to draft, save on type and other fixes delete all drafts, added messages where needed delete draft Force logout Merge branch 'master' into 15-comments-draft Merge branch 'master' into 15-comments-draft # Conflicts: # app/src/main/java/org/mian/gitnex/activities/MainActivity.java # app/src/main/java/org/mian/gitnex/helpers/StaticGlobalVariables.java check if account data is null, we need to log the user out for the 1st time. Merge branch 'master' into 15-comments-draft fix repo owner, name sequence Add comments for test, show drafts list Add repos to db Add account to db and other refactors to the code Merge branch 'master' into 15-comments-draft Merge branch 'master' into 15-comments-draft Merge branch 'master' into 15-comments-draft Merge branch 'master' into 15-comments-draft # Conflicts: # app/build.gradle # app/src/main/AndroidManifest.xml Merge branch 'master' into 15-comments-draft merge more queries, added dao repositories, layout update Added queries in dao some refactor. added models, dao, entities (accounts, repositories, drafts) WIP on implementing drafts, introduced Room persistence library for db. Co-authored-by: M M Arif <mmarif@swatian.com> Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/139 Reviewed-by: opyale <opyale@noreply.codeberg.org>
2020-07-04 22:51:55 +02:00
@Override
public void onFailure(@NonNull Call<User> call, @NonNull Throwable t) {
Toasty.error(ctx, ctx.getString(R.string.genericServerResponseError));
enableProcessButton();
}
});
}
private void setup(final String loginUid, final String loginPass, final int loginOTP) {
final String credential = Credentials.basic(loginUid, loginPass, StandardCharsets.UTF_8);
final String tokenName = "gitnex-app-" + device_id;
Call<List<AccessToken>> call;
if (loginOTP != 0) {
call =
RetrofitClient.getApiInterface(ctx, instanceUrl.toString(), credential, null)
.userGetTokens(loginOTP, loginUid, null, null);
} else {
call =
RetrofitClient.getApiInterface(ctx, instanceUrl.toString(), credential, null)
.userGetTokens(loginUid, null, null);
}
call.enqueue(
new Callback<>() {
@Override
public void onResponse(
@NonNull Call<List<AccessToken>> call,
@NonNull retrofit2.Response<List<AccessToken>> response) {
List<AccessToken> userTokens = response.body();
if (response.code() == 200) {
assert userTokens != null;
for (AccessToken t : userTokens) {
if (t.getName().equals(tokenName)) {
// this app had created an token on this instance before
// -> since it looks like GitNex forgot the secret we have to
// delete it first
Call<Void> delToken;
if (loginOTP != 0) {
delToken =
RetrofitClient.getApiInterface(
ctx,
instanceUrl.toString(),
credential,
null)
.userDeleteAccessToken(
loginOTP,
loginUid,
String.valueOf(t.getId()));
} else {
delToken =
RetrofitClient.getApiInterface(
ctx,
instanceUrl.toString(),
credential,
null)
.userDeleteAccessToken(
loginUid,
String.valueOf(t.getId()));
}
2020-03-31 21:04:53 +02:00
delToken.enqueue(
new Callback<>() {
@Override
public void onResponse(
@NonNull Call<Void> delToken,
@NonNull retrofit2.Response<Void> response) {
if (response.code() == 204) {
setupToken(
loginUid, loginPass, loginOTP,
tokenName);
} else {
Toasty.error(
ctx,
getResources()
.getString(
R.string
.genericApiError,
response.code()));
enableProcessButton();
}
}
@Override
public void onFailure(
@NonNull Call<Void> delToken,
@NonNull Throwable t) {
Toasty.error(
ctx,
getResources()
.getString(
R.string
.malformedJson));
enableProcessButton();
}
});
return;
}
}
2020-03-31 21:04:53 +02:00
setupToken(loginUid, loginPass, loginOTP, tokenName);
} else {
2020-03-31 21:04:53 +02:00
Toasty.error(
ctx,
getResources()
.getString(R.string.genericApiError, response.code()));
enableProcessButton();
}
}
2020-03-31 21:04:53 +02:00
@Override
public void onFailure(
@NonNull Call<List<AccessToken>> call, @NonNull Throwable t) {
2020-03-31 21:04:53 +02:00
Toasty.error(ctx, getResources().getString(R.string.malformedJson));
enableProcessButton();
}
});
}
2020-03-31 21:04:53 +02:00
private void setupToken(
final String loginUid,
final String loginPass,
final int loginOTP,
final String tokenName) {
2020-03-31 21:04:53 +02:00
final String credential = Credentials.basic(loginUid, loginPass, StandardCharsets.UTF_8);
2020-03-31 21:04:53 +02:00
CreateAccessTokenOption createUserToken = new CreateAccessTokenOption().name(tokenName);
Call<AccessToken> callCreateToken;
2020-03-31 21:04:53 +02:00
if (loginOTP != 0) {
2020-03-31 21:04:53 +02:00
callCreateToken =
RetrofitClient.getApiInterface(ctx, instanceUrl.toString(), credential, null)
.userCreateToken(loginOTP, loginUid, createUserToken);
} else {
callCreateToken =
RetrofitClient.getApiInterface(ctx, instanceUrl.toString(), credential, null)
.userCreateToken(loginUid, createUserToken);
}
callCreateToken.enqueue(
new Callback<>() {
@Override
public void onResponse(
@NonNull Call<AccessToken> callCreateToken,
@NonNull retrofit2.Response<AccessToken> responseCreate) {
if (responseCreate.code() == 201) {
AccessToken newToken = responseCreate.body();
assert newToken != null;
if (!newToken.getSha1().equals("")) {
Call<User> call =
RetrofitClient.getApiInterface(
ctx,
instanceUrl.toString(),
"token " + newToken.getSha1(),
null)
.userGetCurrent();
call.enqueue(
new Callback<>() {
@Override
public void onResponse(
@NonNull Call<User> call,
@NonNull retrofit2.Response<User> response) {
User userDetails = response.body();
switch (response.code()) {
case 200:
assert userDetails != null;
// insert new account to db if does not
// exist
String accountName =
userDetails.getLogin()
+ "@"
+ instanceUrl;
UserAccountsApi userAccountsApi =
BaseApi.getInstance(
ctx, UserAccountsApi.class);
assert userAccountsApi != null;
boolean userAccountExists =
userAccountsApi.userAccountExists(
accountName);
UserAccount account;
if (!userAccountExists) {
long accountId =
userAccountsApi
.createNewAccount(
accountName,
instanceUrl
.toString(),
userDetails
.getLogin(),
newToken
.getSha1(),
giteaVersion
.toString(),
maxResponseItems,
defaultPagingNumber);
account =
userAccountsApi.getAccountById(
(int) accountId);
} else {
userAccountsApi
.updateTokenByAccountName(
accountName,
newToken.getSha1());
account =
userAccountsApi
.getAccountByName(
accountName);
}
AppUtil.switchToAccount(
LoginActivity.this, account);
startActivity(
new Intent(
LoginActivity.this,
MainActivity.class));
finish();
break;
case 401:
Toasty.error(
ctx,
getResources()
.getString(
R.string
.unauthorizedApiError));
enableProcessButton();
break;
default:
Toasty.error(
ctx,
getResources()
.getString(
R.string
.genericApiError,
response.code()));
enableProcessButton();
}
}
@Override
public void onFailure(
@NonNull Call<User> call,
@NonNull Throwable t) {
Toasty.error(
ctx,
getResources()
.getString(R.string.genericError));
enableProcessButton();
}
});
}
} else if (responseCreate.code() == 500) {
Toasty.error(
ctx,
getResources()
.getString(
R.string.genericApiError,
responseCreate.code()));
enableProcessButton();
}
}
@Override
public void onFailure(
@NonNull Call<AccessToken> createUserToken, @NonNull Throwable t) {
Toasty.error(ctx, ctx.getString(R.string.genericServerResponseError));
}
});
}
private void loadDefaults() {
if (tinyDB.getString("loginType").equals(LoginType.BASIC.name().toLowerCase())) {
loginMethod.check(R.id.loginUsernamePassword);
} else {
loginMethod.check(R.id.loginToken);
}
if (!tinyDB.getString("instanceUrlRaw").equals("")) {
instanceUrlET.setText(tinyDB.getString("instanceUrlRaw"));
}
if (getAccount() != null && getAccount().getAccount() != null) {
Don't use TinyDB as cache (#1034) Do not use TinyDB as a cache or a way to send data between activities. ### How is this working Instead of saving everything into the TinyDB, I created three `Context`s (a `RepositoryContext`, an `IssueContext` and an `AccountContext`). All are used to store things like API or database values/models and additional data, e.g. the `RepositoryContext` also contains information about the current filter state of a repository (issues, pull requests, releases/tags and milestones). These are sent using `Intent`s and `Bundle`s between activities and fragments. Changing a field (e.g. filter state) in any fragment changes it also for the whole repository (or at least it should do so). Due to the size of the changes (after https://codeberg.org/gitnex/GitNex/commit/c9172f85efafd9f25739fdd8385e1904b711ea41, Git says `154 files changed, 3318 insertions(+), 3835 deletions(-)`) **I highly recommend you to create a beta/pre release before releasing a stable version**. Additional changes: * after logging out, the account remains in the account list (with a note) and you can log in again (you can't switch to this account) * repositories and organizations are clickable on user profiles * deleted two unused classes Once finished, hopefully * closes #354 * replaces #897 * fixes #947 * closes #1001 * closes #1015 * marks #876 and #578 as `Wontfix` since they are not necessary at this point * and all the other TinyDB issues Co-authored-by: qwerty287 <ndev@web.de> Co-authored-by: M M Arif <mmarif@noreply.codeberg.org> Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://codeberg.org/gitnex/GitNex/pulls/1034 Reviewed-by: 6543 <6543@noreply.codeberg.org> Co-authored-by: qwerty287 <qwerty287@noreply.codeberg.org> Co-committed-by: qwerty287 <qwerty287@noreply.codeberg.org>
2022-03-13 03:59:13 +01:00
loginUidET.setText(getAccount().getAccount().getUserName());
}
if (!tinyDB.getString("uniqueAppId").isEmpty()) {
device_id = tinyDB.getString("uniqueAppId");
} else {
device_id = UUID.randomUUID().toString();
tinyDB.putString("uniqueAppId", device_id);
}
}
private void disableProcessButton() {
loginButton.setText(R.string.processingText);
loginButton.setEnabled(false);
}
private void enableProcessButton() {
loginButton.setText(R.string.btnLogin);
loginButton.setEnabled(true);
}
private enum LoginType {
BASIC,
TOKEN
}
}