Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/passman-android.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcos Zuriaga <wolfi@wolfi.es>2022-08-16 22:14:08 +0300
committerMarcos Zuriaga <wolfi@wolfi.es>2022-08-16 22:14:08 +0300
commitae462545e561fce71506bc78cdd23769c000d007 (patch)
tree7a484669fc8d5fd605c6433591b98cf1591512c1
parent02f32bfd5a6e4fb5afd64eec9d128e03779d3715 (diff)
parent47cbed18475f1ec9b2cbb8e0882b5cbc26bc9e49 (diff)
Merge branch 'binsky08-sso-3-backport'
-rw-r--r--app/build.gradle3
-rw-r--r--app/src/main/AndroidManifest.xml4
-rw-r--r--app/src/main/java/es/wolfi/app/ResponseHandlers/AutofillCredentialSaveResponseHandler.java105
-rw-r--r--app/src/main/java/es/wolfi/app/ResponseHandlers/CoreAPIGETResponseHandler.java55
-rw-r--r--app/src/main/java/es/wolfi/app/ResponseHandlers/CredentialAddFileResponseHandler.java76
-rw-r--r--app/src/main/java/es/wolfi/app/ResponseHandlers/CredentialDeleteResponseHandler.java45
-rw-r--r--app/src/main/java/es/wolfi/app/ResponseHandlers/CredentialSaveForNewVaultResponseHandler.java128
-rw-r--r--app/src/main/java/es/wolfi/app/ResponseHandlers/CredentialSaveResponseHandler.java45
-rw-r--r--app/src/main/java/es/wolfi/app/ResponseHandlers/CustomFieldFileDeleteResponseHandler.java16
-rw-r--r--app/src/main/java/es/wolfi/app/ResponseHandlers/FileDeleteResponseHandler.java18
-rw-r--r--app/src/main/java/es/wolfi/app/ResponseHandlers/VaultDeleteResponseHandler.java93
-rw-r--r--app/src/main/java/es/wolfi/app/ResponseHandlers/VaultSaveResponseHandler.java143
-rw-r--r--app/src/main/java/es/wolfi/app/passman/activities/LoginActivity.java180
-rw-r--r--app/src/main/java/es/wolfi/app/passman/activities/PasswordListActivity.java62
-rw-r--r--app/src/main/java/es/wolfi/app/passman/fragments/CredentialAddFragment.java1
-rw-r--r--app/src/main/java/es/wolfi/app/passman/fragments/SettingsFragment.java81
-rw-r--r--app/src/main/java/es/wolfi/passman/API/Core.java268
-rw-r--r--app/src/main/java/es/wolfi/passman/API/Credential.java1
-rw-r--r--app/src/main/res/drawable/ic_baseline_logout_24_grey.xml5
-rw-r--r--app/src/main/res/drawable/login_decision_button.xml28
-rw-r--r--app/src/main/res/drawable/nc_background.pngbin0 -> 102736 bytes
-rw-r--r--app/src/main/res/layout-land/content_legacy_login.xml (renamed from app/src/main/res/layout-land/content_login.xml)0
-rw-r--r--app/src/main/res/layout/activity_login.xml47
-rw-r--r--app/src/main/res/layout/content_legacy_login.xml (renamed from app/src/main/res/layout/content_login.xml)23
-rw-r--r--app/src/main/res/layout/content_manual_server_connection_settings.xml92
-rw-r--r--app/src/main/res/layout/content_sso_settings.xml68
-rw-r--r--app/src/main/res/layout/fragment_settings.xml64
-rw-r--r--app/src/main/res/values/strings.xml3
-rw-r--r--build.gradle2
29 files changed, 1196 insertions, 460 deletions
diff --git a/app/build.gradle b/app/build.gradle
index d7116d7..3a55966 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -105,6 +105,9 @@ dependencies {
androidTestImplementation('androidx.test.espresso:espresso-core:3.4.0', {
exclude group: 'com.android.support', module: 'support-annotations'
})
+ // Nextcloud SSO
+ implementation "com.github.nextcloud:Android-SingleSignOn:0.5.6"
+
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
implementation 'com.jakewharton:butterknife:10.2.3'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 89b8eb1..50b0a58 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -5,6 +5,10 @@
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+ <queries>
+ <package android:name="com.nextcloud.client" />
+ <package android:name="com.nextcloud.android.beta" />
+ </queries>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
diff --git a/app/src/main/java/es/wolfi/app/ResponseHandlers/AutofillCredentialSaveResponseHandler.java b/app/src/main/java/es/wolfi/app/ResponseHandlers/AutofillCredentialSaveResponseHandler.java
index 0c139e9..da8b1d7 100644
--- a/app/src/main/java/es/wolfi/app/ResponseHandlers/AutofillCredentialSaveResponseHandler.java
+++ b/app/src/main/java/es/wolfi/app/ResponseHandlers/AutofillCredentialSaveResponseHandler.java
@@ -24,6 +24,8 @@ package es.wolfi.app.ResponseHandlers;
import android.content.Context;
import android.content.SharedPreferences;
+import android.os.Handler;
+import android.os.Looper;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;
@@ -63,66 +65,71 @@ public class AutofillCredentialSaveResponseHandler extends AsyncHttpResponseHand
@Override
public void onSuccess(int statusCode, cz.msebera.android.httpclient.Header[] headers, byte[] responseBody) {
String result = new String(responseBody);
- if (statusCode == 200) {
- try {
- JSONObject credentialObject = new JSONObject(result);
-
- if (credentialObject.has("credential_id") && credentialObject.getInt("vault_id") == vault.vault_id) {
- Credential currentCredential = Credential.fromJSON(credentialObject, vault);
- vault.addCredential(currentCredential);
- ((HashMap<String, Vault>) ton.getExtra(SettingValues.VAULTS.toString())).put(vault.guid, vault);
- Vault activeVault = (Vault) SingleTon.getTon().getExtra(SettingValues.ACTIVE_VAULT.toString());
- if (vault.guid.equals(activeVault.guid)) {
- ton.addExtra(SettingValues.ACTIVE_VAULT.toString(), vault);
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ if (statusCode == 200) {
+ try {
+ JSONObject credentialObject = new JSONObject(result);
+
+ if (credentialObject.has("credential_id") && credentialObject.getInt("vault_id") == vault.vault_id) {
+ Credential currentCredential = Credential.fromJSON(credentialObject, vault);
+ vault.addCredential(currentCredential);
+ ((HashMap<String, Vault>) ton.getExtra(SettingValues.VAULTS.toString())).put(vault.guid, vault);
+ Vault activeVault = (Vault) SingleTon.getTon().getExtra(SettingValues.ACTIVE_VAULT.toString());
+ if (vault.guid.equals(activeVault.guid)) {
+ ton.addExtra(SettingValues.ACTIVE_VAULT.toString(), vault);
+ }
+
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(baseContext);
+ Vault.updateAutofillVault(vault, settings);
+
+ Toast.makeText(applicationContext, applicationContext.getString(R.string.successfully_saved), Toast.LENGTH_SHORT).show();
+ Log.d(TAG, applicationContext.getString(R.string.successfully_saved));
+ return;
+ }
+ } catch (JSONException e) {
+ e.printStackTrace();
}
- SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(baseContext);
- Vault.updateAutofillVault(vault, settings);
-
- Toast.makeText(applicationContext, applicationContext.getString(R.string.successfully_saved), Toast.LENGTH_SHORT).show();
- Log.d(TAG, applicationContext.getString(R.string.successfully_saved));
- return;
+ Toast.makeText(applicationContext, applicationContext.getString(R.string.error_occurred), Toast.LENGTH_SHORT).show();
+ Log.d(TAG, "onSaveRequest(), failed to save: " + R.string.error_occurred);
}
- } catch (JSONException e) {
- e.printStackTrace();
}
-
- Toast.makeText(applicationContext, applicationContext.getString(R.string.error_occurred), Toast.LENGTH_SHORT).show();
- Log.d(TAG, "onSaveRequest(), failed to save: " + R.string.error_occurred);
- }
+ });
}
@Override
public void onFailure(int statusCode, cz.msebera.android.httpclient.Header[] headers, byte[] responseBody, Throwable error) {
- String response = "";
-
- if (responseBody != null && responseBody.length > 0) {
- response = new String(responseBody);
- }
-
- if (!response.equals("") && JSONUtils.isJSONObject(response)) {
- try {
- JSONObject o = new JSONObject(response);
- if (o.has("message") && o.getString("message").equals("Current user is not logged in")) {
+ String response = new String(responseBody);
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ if (!response.equals("") && JSONUtils.isJSONObject(response)) {
+ try {
+ JSONObject o = new JSONObject(response);
+ if (o.has("message") && o.getString("message").equals("Current user is not logged in")) {
+
+ Toast.makeText(applicationContext, o.getString("message"), Toast.LENGTH_LONG).show();
+ Log.d(TAG, "onSaveRequest(), failed to save: " + o.getString("message"));
+ return;
+ }
+ } catch (JSONException e1) {
+ e1.printStackTrace();
+ }
+ }
- Toast.makeText(applicationContext, o.getString("message"), Toast.LENGTH_LONG).show();
- Log.d(TAG, "onSaveRequest(), failed to save: " + o.getString("message"));
- return;
+ if (error != null && error.getMessage() != null && statusCode != 302) {
+ error.printStackTrace();
+ Log.e("async http response", new String(responseBody));
+ Toast.makeText(applicationContext, error.getMessage(), Toast.LENGTH_SHORT).show();
+ Log.d(TAG, error.getMessage());
+ } else {
+ Toast.makeText(applicationContext, applicationContext.getString(R.string.error_occurred), Toast.LENGTH_SHORT).show();
+ Log.d(TAG, applicationContext.getString(R.string.error_occurred));
}
- } catch (JSONException e1) {
- e1.printStackTrace();
}
- }
-
- if (error != null && error.getMessage() != null && statusCode != 302) {
- error.printStackTrace();
- Log.e("async http response", response);
- Toast.makeText(applicationContext, error.getMessage(), Toast.LENGTH_SHORT).show();
- Log.d(TAG, error.getMessage());
- } else {
- Toast.makeText(applicationContext, applicationContext.getString(R.string.error_occurred), Toast.LENGTH_SHORT).show();
- Log.d(TAG, applicationContext.getString(R.string.error_occurred));
- }
+ });
}
@Override
diff --git a/app/src/main/java/es/wolfi/app/ResponseHandlers/CoreAPIGETResponseHandler.java b/app/src/main/java/es/wolfi/app/ResponseHandlers/CoreAPIGETResponseHandler.java
index 31012ca..aa7e38f 100644
--- a/app/src/main/java/es/wolfi/app/ResponseHandlers/CoreAPIGETResponseHandler.java
+++ b/app/src/main/java/es/wolfi/app/ResponseHandlers/CoreAPIGETResponseHandler.java
@@ -22,6 +22,9 @@
package es.wolfi.app.ResponseHandlers;
+import android.os.Handler;
+import android.os.Looper;
+
import com.koushikdutta.async.future.FutureCallback;
import com.loopj.android.http.AsyncHttpResponseHandler;
@@ -43,34 +46,44 @@ public class CoreAPIGETResponseHandler extends AsyncHttpResponseHandler {
@Override
public void onSuccess(int statusCode, cz.msebera.android.httpclient.Header[] headers, byte[] responseBody) {
String result = new String(responseBody);
- if (statusCode == 200) {
- if (JSONUtils.isJSONObject(result)) {
- try {
- JSONObject o = new JSONObject(result);
- if (o.has("message") && o.getString("message").equals("Current user is not logged in")) {
- callback.onCompleted(new Exception("401"), null);
- return;
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ if (statusCode == 200) {
+ if (JSONUtils.isJSONObject(result)) {
+ try {
+ JSONObject o = new JSONObject(result);
+ if (o.has("message") && o.getString("message").equals("Current user is not logged in")) {
+ callback.onCompleted(new Exception("401"), null);
+ return;
+ }
+ } catch (JSONException e1) {
+ e1.printStackTrace();
+ }
}
- } catch (JSONException e1) {
- e1.printStackTrace();
}
+ callback.onCompleted(null, result);
}
- }
- callback.onCompleted(null, result);
+ });
}
@Override
public void onFailure(int statusCode, cz.msebera.android.httpclient.Header[] headers, byte[] responseBody, Throwable error) {
- String errorMessage = error.getMessage();
- if (errorMessage == null) {
- error.printStackTrace();
- errorMessage = "Unknown error";
- }
- if (statusCode == 401) {
- callback.onCompleted(new Exception("401"), null);
- } else {
- callback.onCompleted(new Exception(errorMessage), null);
- }
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ String errorMessage = error.getMessage();
+ if (errorMessage == null) {
+ error.printStackTrace();
+ errorMessage = "Unknown error";
+ }
+ if (statusCode == 401) {
+ callback.onCompleted(new Exception("401"), null);
+ } else {
+ callback.onCompleted(new Exception(errorMessage), null);
+ }
+ }
+ });
}
@Override
diff --git a/app/src/main/java/es/wolfi/app/ResponseHandlers/CredentialAddFileResponseHandler.java b/app/src/main/java/es/wolfi/app/ResponseHandlers/CredentialAddFileResponseHandler.java
index c32e0a9..708aafd 100644
--- a/app/src/main/java/es/wolfi/app/ResponseHandlers/CredentialAddFileResponseHandler.java
+++ b/app/src/main/java/es/wolfi/app/ResponseHandlers/CredentialAddFileResponseHandler.java
@@ -23,6 +23,8 @@
package es.wolfi.app.ResponseHandlers;
import android.app.ProgressDialog;
+import android.os.Handler;
+import android.os.Looper;
import android.view.View;
import android.widget.Toast;
@@ -30,9 +32,9 @@ import com.loopj.android.http.AsyncHttpResponseHandler;
import org.json.JSONObject;
+import es.wolfi.app.passman.R;
import es.wolfi.app.passman.adapters.CustomFieldEditAdapter;
import es.wolfi.app.passman.adapters.FileEditAdapter;
-import es.wolfi.app.passman.R;
import es.wolfi.passman.API.CustomField;
import es.wolfi.passman.API.File;
import es.wolfi.utils.FileUtils;
@@ -60,45 +62,55 @@ public class CredentialAddFileResponseHandler extends AsyncHttpResponseHandler {
@Override
public void onSuccess(int statusCode, cz.msebera.android.httpclient.Header[] headers, byte[] responseBody) {
String result = new String(responseBody);
- if (statusCode == 200) {
- try {
- JSONObject fileObject = new JSONObject(result);
- if (fileObject.has("message") && fileObject.getString("message").equals("Current user is not logged in")) {
- throw new Exception(fileObject.getString("message"));
- }
-
- fileObject.put("filename", fileName);
- File file = new File(fileObject);
- if (requestCode == FileUtils.activityRequestFileCode.credentialAddFile.ordinal()) {
- fed.addFile(file);
- fed.notifyDataSetChanged();
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ if (statusCode == 200) {
+ try {
+ JSONObject fileObject = new JSONObject(result);
+ if (fileObject.has("message") && fileObject.getString("message").equals("Current user is not logged in")) {
+ throw new Exception(fileObject.getString("message"));
+ }
+
+ fileObject.put("filename", fileName);
+ File file = new File(fileObject);
+
+ if (requestCode == FileUtils.activityRequestFileCode.credentialAddFile.ordinal()) {
+ fed.addFile(file);
+ fed.notifyDataSetChanged();
+ }
+ if (requestCode == FileUtils.activityRequestFileCode.credentialAddCustomFieldFile.ordinal()) {
+ CustomField cf = new CustomField();
+ cf.setLabel("newLabel" + cfed.getItemCount() + 1);
+ cf.setSecret(false);
+ cf.setFieldType("file");
+ cf.setJValue(file.getAsJSONObject());
+
+ cfed.addCustomField(cf);
+ cfed.notifyDataSetChanged();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ Toast.makeText(view.getContext(), e.getMessage() != null ? e.getMessage() : view.getContext().getString(R.string.error_occurred), Toast.LENGTH_LONG).show();
+ }
+ } else {
+ Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
}
- if (requestCode == FileUtils.activityRequestFileCode.credentialAddCustomFieldFile.ordinal()) {
- CustomField cf = new CustomField();
- cf.setLabel("newLabel" + cfed.getItemCount() + 1);
- cf.setSecret(false);
- cf.setFieldType("file");
- cf.setJValue(file.getAsJSONObject());
-
- cfed.addCustomField(cf);
- cfed.notifyDataSetChanged();
- }
- } catch (Exception e) {
- e.printStackTrace();
- Toast.makeText(view.getContext(), e.getMessage() != null ? e.getMessage() : view.getContext().getString(R.string.error_occurred), Toast.LENGTH_LONG).show();
+ progress.dismiss();
}
- } else {
- Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
- }
-
- progress.dismiss();
+ });
}
@Override
public void onFailure(int statusCode, cz.msebera.android.httpclient.Header[] headers, byte[] responseBody, Throwable error) {
error.printStackTrace();
- Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ }
+ });
progress.dismiss();
}
diff --git a/app/src/main/java/es/wolfi/app/ResponseHandlers/CredentialDeleteResponseHandler.java b/app/src/main/java/es/wolfi/app/ResponseHandlers/CredentialDeleteResponseHandler.java
index a00f1f7..673c666 100644
--- a/app/src/main/java/es/wolfi/app/ResponseHandlers/CredentialDeleteResponseHandler.java
+++ b/app/src/main/java/es/wolfi/app/ResponseHandlers/CredentialDeleteResponseHandler.java
@@ -37,10 +37,10 @@ import org.json.JSONObject;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
-import es.wolfi.app.passman.activities.PasswordListActivity;
import es.wolfi.app.passman.R;
import es.wolfi.app.passman.SettingValues;
import es.wolfi.app.passman.SingleTon;
+import es.wolfi.app.passman.activities.PasswordListActivity;
import es.wolfi.passman.API.Credential;
import es.wolfi.passman.API.Vault;
import es.wolfi.utils.JSONUtils;
@@ -73,7 +73,9 @@ public class CredentialDeleteResponseHandler extends AsyncHttpResponseHandler {
if (credentialObject.has("credential_id") && credentialObject.getInt("vault_id") == v.vault_id) {
Credential currentCredential = Credential.fromJSON(credentialObject, v);
- Toast.makeText(view.getContext(), R.string.successfully_deleted, Toast.LENGTH_LONG).show();
+ passwordListActivity.runOnUiThread(() -> {
+ Toast.makeText(view.getContext(), R.string.successfully_deleted, Toast.LENGTH_LONG).show();
+ });
Objects.requireNonNull(passwordListActivity).deleteCredentialInCurrentLocalVaultList(currentCredential);
Objects.requireNonNull(passwordListActivity).showLockVaultButton();
@@ -95,7 +97,9 @@ public class CredentialDeleteResponseHandler extends AsyncHttpResponseHandler {
alreadySaving.set(false);
progress.dismiss();
- Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ passwordListActivity.runOnUiThread(() -> {
+ Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ });
}
@Override
@@ -108,25 +112,28 @@ public class CredentialDeleteResponseHandler extends AsyncHttpResponseHandler {
response = new String(responseBody);
}
- if (!response.equals("") && JSONUtils.isJSONObject(response)) {
- try {
- JSONObject o = new JSONObject(response);
- if (o.has("message") && o.getString("message").equals("Current user is not logged in")) {
- Toast.makeText(view.getContext(), o.getString("message"), Toast.LENGTH_LONG).show();
- return;
+ final String finalResponse = response;
+ passwordListActivity.runOnUiThread(() -> {
+ if (!finalResponse.equals("") && JSONUtils.isJSONObject(finalResponse)) {
+ try {
+ JSONObject o = new JSONObject(finalResponse);
+ if (o.has("message") && o.getString("message").equals("Current user is not logged in")) {
+ Toast.makeText(view.getContext(), o.getString("message"), Toast.LENGTH_LONG).show();
+ return;
+ }
+ } catch (JSONException e1) {
+ e1.printStackTrace();
}
- } catch (JSONException e1) {
- e1.printStackTrace();
}
- }
- if (error != null && error.getMessage() != null && statusCode != 302) {
- error.printStackTrace();
- Log.e("async http response", response);
- Toast.makeText(view.getContext(), error.getMessage(), Toast.LENGTH_LONG).show();
- } else {
- Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
- }
+ if (error != null && error.getMessage() != null && statusCode != 302) {
+ error.printStackTrace();
+ Log.e("async http response", new String(responseBody));
+ Toast.makeText(view.getContext(), error.getMessage(), Toast.LENGTH_LONG).show();
+ } else {
+ Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ }
+ });
}
@Override
diff --git a/app/src/main/java/es/wolfi/app/ResponseHandlers/CredentialSaveForNewVaultResponseHandler.java b/app/src/main/java/es/wolfi/app/ResponseHandlers/CredentialSaveForNewVaultResponseHandler.java
index 71a6b3b..48d24c6 100644
--- a/app/src/main/java/es/wolfi/app/ResponseHandlers/CredentialSaveForNewVaultResponseHandler.java
+++ b/app/src/main/java/es/wolfi/app/ResponseHandlers/CredentialSaveForNewVaultResponseHandler.java
@@ -23,6 +23,8 @@
package es.wolfi.app.ResponseHandlers;
import android.app.ProgressDialog;
+import android.os.Handler;
+import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
@@ -68,45 +70,56 @@ public class CredentialSaveForNewVaultResponseHandler extends AsyncHttpResponseH
@Override
public void onSuccess(int statusCode, cz.msebera.android.httpclient.Header[] headers, byte[] responseBody) {
String result = new String(responseBody);
- if (statusCode == 200) {
- try {
- JSONObject credentialObject = new JSONObject(result);
- if (credentialObject.has("credential_id") && credentialObject.getInt("vault_id") == vault.vault_id) {
-
- AsyncHttpResponseHandler createInitialSharingKeysResponseHandler = new AsyncHttpResponseHandler() {
- @Override
- public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
- if (statusCode == 200) {
- Toast.makeText(view.getContext(), R.string.successfully_saved, Toast.LENGTH_LONG).show();
- Objects.requireNonNull(passwordListActivity).addVaultToCurrentLocalVaultList(vault);
- fragmentManager.popBackStack();
- } else {
- Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
- }
-
- alreadySaving.set(false);
- progress.dismiss();
- }
-
- @Override
- public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ if (statusCode == 200) {
+ try {
+ JSONObject credentialObject = new JSONObject(result);
+ if (credentialObject.has("credential_id") && credentialObject.getInt("vault_id") == vault.vault_id) {
+
+ AsyncHttpResponseHandler createInitialSharingKeysResponseHandler = new AsyncHttpResponseHandler() {
+ @Override
+ public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ if (statusCode == 200) {
+ Toast.makeText(view.getContext(), R.string.successfully_saved, Toast.LENGTH_LONG).show();
+ Objects.requireNonNull(passwordListActivity).addVaultToCurrentLocalVaultList(vault);
+ fragmentManager.popBackStack();
+ } else {
+ Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ }
+
+ alreadySaving.set(false);
+ progress.dismiss();
+ }
+ });
+ }
+
+ @Override
+ public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
+
+ }
+ };
+
+ //create initial sharing keys
+ vault.updateSharingKeys(keyStrength, view.getContext(), createInitialSharingKeysResponseHandler);
+
+ return;
}
- };
-
- //create initial sharing keys
- vault.updateSharingKeys(keyStrength, view.getContext(), createInitialSharingKeysResponseHandler);
-
- return;
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
}
- } catch (JSONException e) {
- e.printStackTrace();
- }
- }
- alreadySaving.set(false);
- progress.dismiss();
- Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ alreadySaving.set(false);
+ progress.dismiss();
+ Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ }
+ });
}
@Override
@@ -115,28 +128,33 @@ public class CredentialSaveForNewVaultResponseHandler extends AsyncHttpResponseH
progress.dismiss();
String response = new String(responseBody);
- if (!response.equals("") && JSONUtils.isJSONObject(response)) {
- try {
- JSONObject o = new JSONObject(response);
- if (o.has("message") && o.getString("message").equals("Current user is not logged in")) {
- Toast.makeText(view.getContext(), o.getString("message"), Toast.LENGTH_LONG).show();
- return;
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ if (!response.equals("") && JSONUtils.isJSONObject(response)) {
+ try {
+ JSONObject o = new JSONObject(response);
+ if (o.has("message") && o.getString("message").equals("Current user is not logged in")) {
+ Toast.makeText(view.getContext(), o.getString("message"), Toast.LENGTH_LONG).show();
+ return;
+ }
+ } catch (JSONException e1) {
+ e1.printStackTrace();
+ Toast.makeText(view.getContext(),
+ view.getContext().getString(R.string.error_occurred).concat(e1.getMessage() != null ? e1.getMessage() : ""),
+ Toast.LENGTH_LONG).show();
+ }
+ }
+
+ if (error != null && error.getMessage() != null && statusCode != 302) {
+ error.printStackTrace();
+ Log.e("async http response", new String(responseBody));
+ Toast.makeText(view.getContext(), view.getContext().getString(R.string.error_occurred).concat(error.getMessage()), Toast.LENGTH_LONG).show();
+ } else {
+ Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
}
- } catch (JSONException e1) {
- e1.printStackTrace();
- Toast.makeText(view.getContext(),
- view.getContext().getString(R.string.error_occurred).concat(e1.getMessage() != null ? e1.getMessage() : ""),
- Toast.LENGTH_LONG).show();
}
- }
-
- if (error != null && error.getMessage() != null && statusCode != 302) {
- error.printStackTrace();
- Log.e("async http response", new String(responseBody));
- Toast.makeText(view.getContext(), view.getContext().getString(R.string.error_occurred).concat(error.getMessage()), Toast.LENGTH_LONG).show();
- } else {
- Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
- }
+ });
}
@Override
diff --git a/app/src/main/java/es/wolfi/app/ResponseHandlers/CredentialSaveResponseHandler.java b/app/src/main/java/es/wolfi/app/ResponseHandlers/CredentialSaveResponseHandler.java
index b93acc5..7559005 100644
--- a/app/src/main/java/es/wolfi/app/ResponseHandlers/CredentialSaveResponseHandler.java
+++ b/app/src/main/java/es/wolfi/app/ResponseHandlers/CredentialSaveResponseHandler.java
@@ -37,10 +37,10 @@ import org.json.JSONObject;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
-import es.wolfi.app.passman.activities.PasswordListActivity;
import es.wolfi.app.passman.R;
import es.wolfi.app.passman.SettingValues;
import es.wolfi.app.passman.SingleTon;
+import es.wolfi.app.passman.activities.PasswordListActivity;
import es.wolfi.passman.API.Credential;
import es.wolfi.passman.API.Vault;
import es.wolfi.utils.JSONUtils;
@@ -75,7 +75,9 @@ public class CredentialSaveResponseHandler extends AsyncHttpResponseHandler {
if (credentialObject.has("credential_id") && credentialObject.getInt("vault_id") == v.vault_id) {
Credential currentCredential = Credential.fromJSON(credentialObject, v);
- Toast.makeText(view.getContext(), R.string.successfully_saved, Toast.LENGTH_LONG).show();
+ passwordListActivity.runOnUiThread(() -> {
+ Toast.makeText(view.getContext(), R.string.successfully_saved, Toast.LENGTH_LONG).show();
+ });
if (updateCredential) {
Objects.requireNonNull(passwordListActivity).editCredentialInCurrentLocalVaultList(currentCredential);
@@ -95,7 +97,9 @@ public class CredentialSaveResponseHandler extends AsyncHttpResponseHandler {
alreadySaving.set(false);
progress.dismiss();
- Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ passwordListActivity.runOnUiThread(() -> {
+ Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ });
}
@Override
@@ -108,25 +112,28 @@ public class CredentialSaveResponseHandler extends AsyncHttpResponseHandler {
response = new String(responseBody);
}
- if (!response.equals("") && JSONUtils.isJSONObject(response)) {
- try {
- JSONObject o = new JSONObject(response);
- if (o.has("message") && o.getString("message").equals("Current user is not logged in")) {
- Toast.makeText(view.getContext(), o.getString("message"), Toast.LENGTH_LONG).show();
- return;
+ final String finalResponse = response;
+ passwordListActivity.runOnUiThread(() -> {
+ if (!finalResponse.equals("") && JSONUtils.isJSONObject(finalResponse)) {
+ try {
+ JSONObject o = new JSONObject(finalResponse);
+ if (o.has("message") && o.getString("message").equals("Current user is not logged in")) {
+ Toast.makeText(view.getContext(), o.getString("message"), Toast.LENGTH_LONG).show();
+ return;
+ }
+ } catch (JSONException e1) {
+ e1.printStackTrace();
}
- } catch (JSONException e1) {
- e1.printStackTrace();
}
- }
- if (error != null && error.getMessage() != null && statusCode != 302) {
- error.printStackTrace();
- Log.e("async http response", response);
- Toast.makeText(view.getContext(), error.getMessage(), Toast.LENGTH_LONG).show();
- } else {
- Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
- }
+ if (error != null && error.getMessage() != null && statusCode != 302) {
+ error.printStackTrace();
+ Log.e("async http response", new String(responseBody));
+ Toast.makeText(view.getContext(), error.getMessage(), Toast.LENGTH_LONG).show();
+ } else {
+ Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ }
+ });
}
@Override
diff --git a/app/src/main/java/es/wolfi/app/ResponseHandlers/CustomFieldFileDeleteResponseHandler.java b/app/src/main/java/es/wolfi/app/ResponseHandlers/CustomFieldFileDeleteResponseHandler.java
index 72c34d6..638912b 100644
--- a/app/src/main/java/es/wolfi/app/ResponseHandlers/CustomFieldFileDeleteResponseHandler.java
+++ b/app/src/main/java/es/wolfi/app/ResponseHandlers/CustomFieldFileDeleteResponseHandler.java
@@ -23,6 +23,8 @@
package es.wolfi.app.ResponseHandlers;
import android.app.ProgressDialog;
+import android.os.Handler;
+import android.os.Looper;
import android.widget.Toast;
import com.loopj.android.http.AsyncHttpResponseHandler;
@@ -53,7 +55,12 @@ public class CustomFieldFileDeleteResponseHandler extends AsyncHttpResponseHandl
public void onSuccess(int statusCode, cz.msebera.android.httpclient.Header[] headers, byte[] responseBody) {
if (statusCode == 200) {
mValues.remove(holder.mItem);
- customFieldEditAdapter.notifyDataSetChanged();
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ customFieldEditAdapter.notifyDataSetChanged();
+ }
+ });
}
progress.dismiss();
}
@@ -61,7 +68,12 @@ public class CustomFieldFileDeleteResponseHandler extends AsyncHttpResponseHandl
@Override
public void onFailure(int statusCode, cz.msebera.android.httpclient.Header[] headers, byte[] responseBody, Throwable error) {
error.printStackTrace();
- Toast.makeText(progress.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(progress.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ }
+ });
progress.dismiss();
}
diff --git a/app/src/main/java/es/wolfi/app/ResponseHandlers/FileDeleteResponseHandler.java b/app/src/main/java/es/wolfi/app/ResponseHandlers/FileDeleteResponseHandler.java
index e562112..eabea2e 100644
--- a/app/src/main/java/es/wolfi/app/ResponseHandlers/FileDeleteResponseHandler.java
+++ b/app/src/main/java/es/wolfi/app/ResponseHandlers/FileDeleteResponseHandler.java
@@ -23,6 +23,8 @@
package es.wolfi.app.ResponseHandlers;
import android.app.ProgressDialog;
+import android.os.Handler;
+import android.os.Looper;
import android.view.View;
import android.widget.Toast;
@@ -55,8 +57,13 @@ public class FileDeleteResponseHandler extends AsyncHttpResponseHandler {
if (statusCode == 200) {
mValues.remove(holder.mItem);
- holder.mContentView.setTextColor(view.getResources().getColor(R.color.disabled));
- holder.deleteButton.setVisibility(View.INVISIBLE);
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ holder.mContentView.setTextColor(view.getResources().getColor(R.color.disabled));
+ holder.deleteButton.setVisibility(View.INVISIBLE);
+ }
+ });
}
progress.dismiss();
}
@@ -64,7 +71,12 @@ public class FileDeleteResponseHandler extends AsyncHttpResponseHandler {
@Override
public void onFailure(int statusCode, cz.msebera.android.httpclient.Header[] headers, byte[] responseBody, Throwable error) {
error.printStackTrace();
- Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ }
+ });
progress.dismiss();
}
diff --git a/app/src/main/java/es/wolfi/app/ResponseHandlers/VaultDeleteResponseHandler.java b/app/src/main/java/es/wolfi/app/ResponseHandlers/VaultDeleteResponseHandler.java
index 17d5ae7..384c849 100644
--- a/app/src/main/java/es/wolfi/app/ResponseHandlers/VaultDeleteResponseHandler.java
+++ b/app/src/main/java/es/wolfi/app/ResponseHandlers/VaultDeleteResponseHandler.java
@@ -23,6 +23,8 @@
package es.wolfi.app.ResponseHandlers;
import android.app.ProgressDialog;
+import android.os.Handler;
+import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
@@ -67,32 +69,37 @@ public class VaultDeleteResponseHandler extends AsyncHttpResponseHandler {
@Override
public void onSuccess(int statusCode, cz.msebera.android.httpclient.Header[] headers, byte[] responseBody) {
String result = new String(responseBody);
- if (statusCode == 200) {
- try {
- JSONObject responseObject = new JSONObject(result);
- if (responseObject.has("ok") && responseObject.getBoolean("ok")) {
- if (isDeleteVaultContentRequest) {
- final AsyncHttpResponseHandler responseHandler = new VaultDeleteResponseHandler(alreadySaving, vault, false, progress, view, passwordListActivity, fragmentManager);
- vault.delete(view.getContext(), responseHandler);
- } else {
- Toast.makeText(view.getContext(), R.string.successfully_deleted, Toast.LENGTH_LONG).show();
-
- Objects.requireNonNull(passwordListActivity).deleteVaultInCurrentLocalVaultList(vault);
-
- alreadySaving.set(false);
- progress.dismiss();
- fragmentManager.popBackStack();
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ if (statusCode == 200) {
+ try {
+ JSONObject responseObject = new JSONObject(result);
+ if (responseObject.has("ok") && responseObject.getBoolean("ok")) {
+ if (isDeleteVaultContentRequest) {
+ final AsyncHttpResponseHandler responseHandler = new VaultDeleteResponseHandler(alreadySaving, vault, false, progress, view, passwordListActivity, fragmentManager);
+ vault.delete(view.getContext(), responseHandler);
+ } else {
+ Toast.makeText(view.getContext(), R.string.successfully_deleted, Toast.LENGTH_LONG).show();
+
+ Objects.requireNonNull(passwordListActivity).deleteVaultInCurrentLocalVaultList(vault);
+
+ alreadySaving.set(false);
+ progress.dismiss();
+ fragmentManager.popBackStack();
+ }
+ return;
+ }
+ } catch (JSONException e) {
+ e.printStackTrace();
}
- return;
}
- } catch (JSONException e) {
- e.printStackTrace();
- }
- }
- alreadySaving.set(false);
- progress.dismiss();
- Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ alreadySaving.set(false);
+ progress.dismiss();
+ Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ }
+ });
}
@Override
@@ -105,25 +112,31 @@ public class VaultDeleteResponseHandler extends AsyncHttpResponseHandler {
response = new String(responseBody);
}
- if (!response.equals("") && JSONUtils.isJSONObject(response)) {
- try {
- JSONObject o = new JSONObject(response);
- if (o.has("message") && o.getString("message").equals("Current user is not logged in")) {
- Toast.makeText(view.getContext(), o.getString("message"), Toast.LENGTH_LONG).show();
- return;
+ final String finalResponse = response;
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ if (!finalResponse.equals("") && JSONUtils.isJSONObject(finalResponse)) {
+ try {
+ JSONObject o = new JSONObject(finalResponse);
+ if (o.has("message") && o.getString("message").equals("Current user is not logged in")) {
+ Toast.makeText(view.getContext(), o.getString("message"), Toast.LENGTH_LONG).show();
+ return;
+ }
+ } catch (JSONException e1) {
+ e1.printStackTrace();
+ }
}
- } catch (JSONException e1) {
- e1.printStackTrace();
- }
- }
- if (error != null && error.getMessage() != null && statusCode != 302) {
- error.printStackTrace();
- Log.e("async http response", response);
- Toast.makeText(view.getContext(), error.getMessage(), Toast.LENGTH_LONG).show();
- } else {
- Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
- }
+ if (error != null && error.getMessage() != null && statusCode != 302) {
+ error.printStackTrace();
+ Log.e("async http response", finalResponse);
+ Toast.makeText(view.getContext(), error.getMessage(), Toast.LENGTH_LONG).show();
+ } else {
+ Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ }
+ }
+ });
}
@Override
diff --git a/app/src/main/java/es/wolfi/app/ResponseHandlers/VaultSaveResponseHandler.java b/app/src/main/java/es/wolfi/app/ResponseHandlers/VaultSaveResponseHandler.java
index a59e4c4..7c8ee0d 100644
--- a/app/src/main/java/es/wolfi/app/ResponseHandlers/VaultSaveResponseHandler.java
+++ b/app/src/main/java/es/wolfi/app/ResponseHandlers/VaultSaveResponseHandler.java
@@ -23,6 +23,8 @@
package es.wolfi.app.ResponseHandlers;
import android.app.ProgressDialog;
+import android.os.Handler;
+import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
@@ -70,57 +72,62 @@ public class VaultSaveResponseHandler extends AsyncHttpResponseHandler {
@Override
public void onSuccess(int statusCode, cz.msebera.android.httpclient.Header[] headers, byte[] responseBody) {
String result = new String(responseBody);
- if (statusCode == 200) {
- try {
- if (updateVault) {
- Vault localVaultInstance = Vault.getVaultByGuid(vault.guid);
- if (localVaultInstance != null) {
- localVaultInstance.setName(vault.getName());
- }
- alreadySaving.set(false);
- progress.dismiss();
- fragmentManager.popBackStack();
- return;
- } else {
- JSONObject vaultObject = new JSONObject(result);
- Vault v = Vault.fromJSON(vaultObject);
- if (vaultObject.has("vault_id") && vaultObject.has("name") && vaultObject.getString("name").equals(vault.getName())) {
- v.setEncryptionKey(vault.getEncryptionKey());
-
- Toast.makeText(view.getContext(), "Vault created", Toast.LENGTH_LONG).show();
-
- //create test credential
- Credential testCred = new Credential();
- testCred.setVault(v);
-
- testCred.setLabel(labelPrefixForFirstVaultConsistencyCredential + v.getName());
- testCred.setPassword("lorem ipsum");
- testCred.setOtp("{}");
- testCred.setTags("");
- testCred.setFavicon("");
- testCred.setUsername("");
- testCred.setEmail("");
- testCred.setUrl("");
- testCred.setDescription("");
- testCred.setFiles("[]");
- testCred.setCustomFields("[]");
- testCred.setCompromised(false);
- testCred.setHidden(true);
-
- final AsyncHttpResponseHandler responseHandler = new CredentialSaveForNewVaultResponseHandler(alreadySaving, v, keyStrength, progress, view, passwordListActivity, fragmentManager);
- testCred.save(view.getContext(), responseHandler);
-
- return;
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ if (statusCode == 200) {
+ try {
+ if (updateVault) {
+ Vault localVaultInstance = Vault.getVaultByGuid(vault.guid);
+ if (localVaultInstance != null) {
+ localVaultInstance.setName(vault.getName());
+ }
+ alreadySaving.set(false);
+ progress.dismiss();
+ fragmentManager.popBackStack();
+ return;
+ } else {
+ JSONObject vaultObject = new JSONObject(result);
+ Vault v = Vault.fromJSON(vaultObject);
+ if (vaultObject.has("vault_id") && vaultObject.has("name") && vaultObject.getString("name").equals(vault.getName())) {
+ v.setEncryptionKey(vault.getEncryptionKey());
+
+ Toast.makeText(view.getContext(), "Vault created", Toast.LENGTH_LONG).show();
+
+ //create test credential
+ Credential testCred = new Credential();
+ testCred.setVault(v);
+
+ testCred.setLabel(labelPrefixForFirstVaultConsistencyCredential + v.getName());
+ testCred.setPassword("lorem ipsum");
+ testCred.setOtp("{}");
+ testCred.setTags("");
+ testCred.setFavicon("");
+ testCred.setUsername("");
+ testCred.setEmail("");
+ testCred.setUrl("");
+ testCred.setDescription("");
+ testCred.setFiles("[]");
+ testCred.setCustomFields("[]");
+ testCred.setCompromised(false);
+ testCred.setHidden(true);
+
+ final AsyncHttpResponseHandler responseHandler = new CredentialSaveForNewVaultResponseHandler(alreadySaving, v, keyStrength, progress, view, passwordListActivity, fragmentManager);
+ testCred.save(view.getContext(), responseHandler);
+
+ return;
+ }
+ }
+ } catch (JSONException e) {
+ e.printStackTrace();
}
}
- } catch (JSONException e) {
- e.printStackTrace();
- }
- }
- alreadySaving.set(false);
- progress.dismiss();
- Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ alreadySaving.set(false);
+ progress.dismiss();
+ Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ }
+ });
}
@Override
@@ -133,25 +140,31 @@ public class VaultSaveResponseHandler extends AsyncHttpResponseHandler {
response = new String(responseBody);
}
- if (!response.equals("") && JSONUtils.isJSONObject(response)) {
- try {
- JSONObject o = new JSONObject(response);
- if (o.has("message") && o.getString("message").equals("Current user is not logged in")) {
- Toast.makeText(view.getContext(), o.getString("message"), Toast.LENGTH_LONG).show();
- return;
+ final String finalResponse = response;
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ if (!finalResponse.equals("") && JSONUtils.isJSONObject(finalResponse)) {
+ try {
+ JSONObject o = new JSONObject(finalResponse);
+ if (o.has("message") && o.getString("message").equals("Current user is not logged in")) {
+ Toast.makeText(view.getContext(), o.getString("message"), Toast.LENGTH_LONG).show();
+ return;
+ }
+ } catch (JSONException e1) {
+ e1.printStackTrace();
+ }
}
- } catch (JSONException e1) {
- e1.printStackTrace();
- }
- }
- if (error != null && error.getMessage() != null && statusCode != 302) {
- error.printStackTrace();
- Log.e("async http response", response);
- Toast.makeText(view.getContext(), error.getMessage(), Toast.LENGTH_LONG).show();
- } else {
- Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
- }
+ if (error != null && error.getMessage() != null && statusCode != 302) {
+ error.printStackTrace();
+ Log.e("async http response", finalResponse);
+ Toast.makeText(view.getContext(), error.getMessage(), Toast.LENGTH_LONG).show();
+ } else {
+ Toast.makeText(view.getContext(), R.string.error_occurred, Toast.LENGTH_LONG).show();
+ }
+ }
+ });
}
@Override
diff --git a/app/src/main/java/es/wolfi/app/passman/activities/LoginActivity.java b/app/src/main/java/es/wolfi/app/passman/activities/LoginActivity.java
index c6f3ceb..e9a840a 100644
--- a/app/src/main/java/es/wolfi/app/passman/activities/LoginActivity.java
+++ b/app/src/main/java/es/wolfi/app/passman/activities/LoginActivity.java
@@ -20,12 +20,22 @@
*/
package es.wolfi.app.passman.activities;
+import android.content.Context;
+import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.preference.PreferenceManager;
import android.util.Log;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.Toast;
@@ -33,16 +43,27 @@ import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import com.koushikdutta.async.future.FutureCallback;
+import com.nextcloud.android.sso.AccountImporter;
+import com.nextcloud.android.sso.Constants;
+import com.nextcloud.android.sso.exceptions.AccountImportCancelledException;
+import com.nextcloud.android.sso.exceptions.AndroidGetAccountsPermissionNotGranted;
+import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException;
+import com.nextcloud.android.sso.exceptions.NextcloudFilesAppNotInstalledException;
+import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException;
+import com.nextcloud.android.sso.helper.SingleAccountHelper;
+import com.nextcloud.android.sso.model.SingleSignOnAccount;
+import com.nextcloud.android.sso.ui.UiExceptionManager;
import java.net.MalformedURLException;
import java.net.URL;
+import java.util.Arrays;
+import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import es.wolfi.app.passman.R;
import es.wolfi.app.passman.SettingValues;
-import es.wolfi.app.passman.SettingsCache;
import es.wolfi.app.passman.SingleTon;
import es.wolfi.passman.API.Core;
import es.wolfi.utils.KeyStoreUtils;
@@ -63,15 +84,42 @@ public class LoginActivity extends AppCompatActivity {
SharedPreferences settings;
SingleTon ton;
+ boolean isLegacyOnly = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+
+ Log.d("LoginActivity", "in onCreate");
+
setContentView(R.layout.activity_login);
ButterKnife.bind(this);
- new SettingsCache().loadSharedPreferences(getBaseContext());
- settings = SettingsCache.getSharedPreferences();
+ if (!isNextcloudFilesAppInstalled(this)) {
+ isLegacyOnly = true;
+ loadLegacyLogin();
+ }
+ }
+
+ @OnClick(R.id.load_legacy_login_button)
+ public void loadLegacyLogin() {
+ hideLoginOptions();
+
+ ImageView login_options_logo = findViewById(R.id.login_options_logo);
+ login_options_logo.setVisibility(View.INVISIBLE);
+
+ LinearLayout content_legacy_login = findViewById(R.id.content_legacy_login);
+ content_legacy_login.setVisibility(View.VISIBLE);
+
+ EditText hostForm = findViewById(R.id.host);
+ hostForm.requestFocus();
+
+ InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm != null) {
+ imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
+ }
+
+ settings = PreferenceManager.getDefaultSharedPreferences(this);
KeyStoreUtils.initialize(settings);
ton = SingleTon.getTon();
@@ -98,6 +146,58 @@ public class LoginActivity extends AppCompatActivity {
setSupportActionBar(toolbar);
}
+ @OnClick(R.id.load_sso_login_button)
+ public void loadSSOLogin() {
+ hideLoginOptions();
+
+ try {
+ AccountImporter.pickNewAccount(this);
+ Log.w("SSO@LoginActivity", "try AccountImporter was successful");
+ } catch (NextcloudFilesAppNotInstalledException e1) {
+ UiExceptionManager.showDialogForException(this, e1);
+ Log.w("SSO@LoginActivity", "Nextcloud app is not installed. Cannot choose account. Use legacy login method.");
+
+ loadLegacyLogin();
+ } catch (AndroidGetAccountsPermissionNotGranted e2) {
+ AccountImporter.requestAndroidAccountPermissionsAndPickAccount(this);
+ }
+ }
+
+ private void showLoginOptions() {
+ LinearLayout content_legacy_login = findViewById(R.id.content_legacy_login);
+ content_legacy_login.setVisibility(View.INVISIBLE);
+
+ LinearLayout login_options = findViewById(R.id.login_options);
+ login_options.setVisibility(View.VISIBLE);
+ ImageView login_options_logo = findViewById(R.id.login_options_logo);
+ login_options_logo.setVisibility(View.VISIBLE);
+ }
+
+ private void hideLoginOptions() {
+ LinearLayout login_options = findViewById(R.id.login_options);
+ login_options.setVisibility(View.INVISIBLE);
+ }
+
+ private static boolean isNextcloudFilesAppInstalled(Context context) {
+ List<String> APPS = Arrays.asList(Constants.PACKAGE_NAME_PROD, Constants.PACKAGE_NAME_DEV);
+
+ boolean returnValue = false;
+ PackageManager pm = context.getPackageManager();
+ for (String app : APPS) {
+ try {
+ PackageInfo pi = pm.getPackageInfo(app, PackageManager.GET_ACTIVITIES);
+ // check if Nextcloud Files App version with the required PATCH request fix is installed
+ if ((pi.versionCode >= 30180052 && pi.packageName.equals("com.nextcloud.client")) ||
+ pi.versionCode >= 20211027 && pi.packageName.equals("com.nextcloud.android.beta")) {
+ returnValue = true;
+ break;
+ }
+ } catch (PackageManager.NameNotFoundException ignored) {
+ }
+ }
+ return returnValue;
+ }
+
@OnClick(R.id.next)
public void onNextClick() {
Log.e("Login", "begin");
@@ -129,4 +229,78 @@ public class LoginActivity extends AppCompatActivity {
}
});
}
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
+ Context c = this;
+ try {
+ AccountImporter.onActivityResult(requestCode, resultCode, data, this, new AccountImporter.IAccountAccessGranted() {
+
+ @Override
+ public void accountAccessGranted(SingleSignOnAccount account) {
+ Context l_context = getApplicationContext();
+
+ // As this library supports multiple accounts we created some helper methods if you only want to use one.
+ // The following line stores the selected account as the "default" account which can be queried by using
+ // the SingleAccountHelper.getCurrentSingleSignOnAccount(context) method
+ SingleAccountHelper.setCurrentAccount(l_context, account.name);
+
+ // Get the "default" account
+ SingleSignOnAccount ssoAccount;
+ try {
+ ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(l_context);
+ } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) {
+ UiExceptionManager.showDialogForException(l_context, e);
+ return;
+ }
+
+ SingleTon ton = SingleTon.getTon();
+ ton.addString(SettingValues.HOST.toString(), ssoAccount.url);
+ ton.addString(SettingValues.USER.toString(), ssoAccount.userId);
+ ton.addString(SettingValues.PASSWORD.toString(), ssoAccount.token);
+
+ SingleSignOnAccount finalSsoAccount = ssoAccount;
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ Core.checkLogin(c, true, new FutureCallback<Boolean>() {
+ @Override
+ public void onCompleted(Exception e, Boolean result) {
+ if (result) {
+ settings.edit()
+ .putString(SettingValues.HOST.toString(), finalSsoAccount.url)
+ .putString(SettingValues.USER.toString(), finalSsoAccount.userId)
+ .putString(SettingValues.PASSWORD.toString(), finalSsoAccount.token)
+ .apply();
+
+ setResult(RESULT_OK);
+ LoginActivity.this.finish();
+ } else {
+ ton.removeString(SettingValues.HOST.toString());
+ ton.removeString(SettingValues.USER.toString());
+ ton.removeString(SettingValues.PASSWORD.toString());
+ }
+ }
+ });
+ }
+ });
+ }
+ });
+ } catch (AccountImportCancelledException e) {
+ showLoginOptions();
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ LinearLayout login_options = findViewById(R.id.login_options);
+ if (login_options.getVisibility() == View.INVISIBLE && !isLegacyOnly) {
+ showLoginOptions();
+ } else {
+ PasswordListActivity.running = false;
+ super.onBackPressed();
+ }
+ }
}
diff --git a/app/src/main/java/es/wolfi/app/passman/activities/PasswordListActivity.java b/app/src/main/java/es/wolfi/app/passman/activities/PasswordListActivity.java
index d74039b..61bad4b 100644
--- a/app/src/main/java/es/wolfi/app/passman/activities/PasswordListActivity.java
+++ b/app/src/main/java/es/wolfi/app/passman/activities/PasswordListActivity.java
@@ -262,29 +262,32 @@ public class PasswordListActivity extends AppCompatActivity implements
.commitAllowingStateLoss();
Log.d("PL", "committed transaction");
} else {
- final ProgressDialog progress = ProgressUtils.showLoadingSequence(this);
- progress.show();
- Vault.getVaults(this, (e, result) -> {
- progress.dismiss();
- if (e != null) {
- // Not logged in, restart activity
- if (Objects.equals(e.getMessage(), "401")) {
- recreate();
- }
+ this.runOnUiThread(() -> {
+ final ProgressDialog progress = ProgressUtils.showLoadingSequence(this);
+ progress.show();
- Toast.makeText(getApplicationContext(), getString(R.string.net_error), Toast.LENGTH_LONG).show();
-
- new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
- @Override
- public void run() {
- showVaults();
+ Vault.getVaults(this, (e, result) -> {
+ progress.dismiss();
+ if (e != null) {
+ // Not logged in, restart activity
+ if (Objects.equals(e.getMessage(), "401")) {
+ recreate();
}
- }, 30000);
- return;
- }
- ton.addExtra(SettingValues.VAULTS.toString(), result);
- showVaults();
+ Toast.makeText(getApplicationContext(), getString(R.string.net_error), Toast.LENGTH_LONG).show();
+
+ new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ showVaults();
+ }
+ }, 30000);
+ return;
+ }
+
+ ton.addExtra(SettingValues.VAULTS.toString(), result);
+ showVaults();
+ });
});
}
}
@@ -309,6 +312,7 @@ public class PasswordListActivity extends AppCompatActivity implements
}
progress.dismiss();
} else {
+ final AppCompatActivity self = this;
Vault.getVault(this, vault.guid, new FutureCallback<Vault>() {
@Override
public void onCompleted(Exception e, Vault result) {
@@ -338,9 +342,10 @@ public class PasswordListActivity extends AppCompatActivity implements
}
ton.addExtra(SettingValues.ACTIVE_VAULT.toString(), result);
- showActiveVault();
-
- Vault.updateAutofillVault(result, settings);
+ self.runOnUiThread(() -> {
+ showActiveVault();
+ Vault.updateAutofillVault(result, settings);
+ });
}
});
}
@@ -521,7 +526,12 @@ public class PasswordListActivity extends AppCompatActivity implements
CredentialItemFragment credentialItems = (CredentialItemFragment)
getSupportFragmentManager().findFragmentById(R.id.content_password_list);
assert credentialItems != null;
- credentialItems.loadCredentialList(findViewById(R.id.content_password_list));
+ new Handler(Looper.getMainLooper()).post(new Runnable() {
+ @Override
+ public void run() {
+ credentialItems.loadCredentialList(findViewById(R.id.content_password_list));
+ }
+ });
}
}
});
@@ -636,7 +646,7 @@ public class PasswordListActivity extends AppCompatActivity implements
}
public void showLockVaultButton() {
- this.VaultLockButton.setVisibility(View.VISIBLE);
+ this.runOnUiThread(() -> this.VaultLockButton.setVisibility(View.VISIBLE));
}
private void showNotImplementedMessage() {
@@ -700,7 +710,7 @@ public class PasswordListActivity extends AppCompatActivity implements
try {
JSONObject o = new JSONObject(result);
if (o.has("file_data")) {
- progress.setMessage(getString(R.string.wait_while_decrypting));
+ runOnUiThread(() -> progress.setMessage(getString(R.string.wait_while_decrypting)));
String[] decryptedSplitString = v.decryptString(o.getString("file_data")).split(",");
if (decryptedSplitString.length == 2) {
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
diff --git a/app/src/main/java/es/wolfi/app/passman/fragments/CredentialAddFragment.java b/app/src/main/java/es/wolfi/app/passman/fragments/CredentialAddFragment.java
index d97f69a..3fd454c 100644
--- a/app/src/main/java/es/wolfi/app/passman/fragments/CredentialAddFragment.java
+++ b/app/src/main/java/es/wolfi/app/passman/fragments/CredentialAddFragment.java
@@ -26,6 +26,7 @@ import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
diff --git a/app/src/main/java/es/wolfi/app/passman/fragments/SettingsFragment.java b/app/src/main/java/es/wolfi/app/passman/fragments/SettingsFragment.java
index 29fd1da..45bb1b6 100644
--- a/app/src/main/java/es/wolfi/app/passman/fragments/SettingsFragment.java
+++ b/app/src/main/java/es/wolfi/app/passman/fragments/SettingsFragment.java
@@ -3,6 +3,7 @@
*
* @copyright Copyright (c) 2016, Sander Brand (brantje@gmail.com)
* @copyright Copyright (c) 2016, Marcos Zuriaga Miguel (wolfi@wolfi.es)
+ * @copyright Copyright (c) 2022, Timo Triebensky (timo@binsky.org)
* @license GNU AGPL version 3 or any later version
* <p>
* This program is free software: you can redistribute it and/or modify
@@ -22,11 +23,14 @@
package es.wolfi.app.passman.fragments;
import android.annotation.SuppressLint;
+import android.app.AlertDialog;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
+import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -34,6 +38,7 @@ import android.view.ViewManager;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
+import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.TextView;
@@ -43,7 +48,14 @@ import androidx.fragment.app.Fragment;
import com.google.android.material.checkbox.MaterialCheckBox;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.koushikdutta.async.future.FutureCallback;
-
+import com.nextcloud.android.sso.AccountImporter;
+import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException;
+import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException;
+import com.nextcloud.android.sso.helper.SingleAccountHelper;
+import com.nextcloud.android.sso.model.SingleSignOnAccount;
+
+import java.net.MalformedURLException;
+import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
@@ -63,6 +75,11 @@ import es.wolfi.utils.PasswordGenerator;
public class SettingsFragment extends Fragment {
+ RelativeLayout manual_server_connection_settings;
+ RelativeLayout sso_settings;
+
+ TextView sso_user_server;
+
EditText settings_nextcloud_url;
EditText settings_nextcloud_user;
EditText settings_nextcloud_password;
@@ -90,6 +107,7 @@ public class SettingsFragment extends Fragment {
Button clear_offline_cache_button;
SharedPreferences settings;
+ SingleSignOnAccount ssoAccount;
PasswordGenerator passwordGenerator;
public SettingsFragment() {
@@ -102,9 +120,7 @@ public class SettingsFragment extends Fragment {
* @return A new instance of fragment SettingsFragment.
*/
public static SettingsFragment newInstance() {
- SettingsFragment fragment = new SettingsFragment();
-
- return fragment;
+ return new SettingsFragment();
}
@Override
@@ -113,9 +129,17 @@ public class SettingsFragment extends Fragment {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_settings, container, false);
+ manual_server_connection_settings = view.findViewById(R.id.manual_server_connection_settings);
+ sso_settings = view.findViewById(R.id.sso_settings);
+
FloatingActionButton settingsSaveButton = view.findViewById(R.id.settings_save_button);
settingsSaveButton.setOnClickListener(this.getSaveButtonListener());
+ Button sso_user_server_logout_button = view.findViewById(R.id.sso_user_server_logout_button);
+ sso_user_server_logout_button.setOnClickListener(this.getSSOLogoutButtonListener());
+
+ sso_user_server = view.findViewById(R.id.sso_user_server);
+
settings_nextcloud_url = view.findViewById(R.id.settings_nextcloud_url);
settings_nextcloud_user = view.findViewById(R.id.settings_nextcloud_user);
settings_nextcloud_password = view.findViewById(R.id.settings_nextcloud_password);
@@ -156,6 +180,24 @@ public class SettingsFragment extends Fragment {
super.onViewCreated(view, savedInstanceState);
ButterKnife.bind(this, view);
+ try {
+ ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(getContext());
+ manual_server_connection_settings.removeAllViews();
+ sso_settings.setVisibility(View.VISIBLE);
+
+ String hostname = "";
+ try {
+ URL uri = new URL(ssoAccount.url);
+ hostname = uri.getHost();
+ } catch (MalformedURLException e) {
+ Log.d("SettingsFragment", "Error parsing host from sso account");
+ }
+ sso_user_server.setText(String.format("%s@%s", ssoAccount.userId, hostname));
+ } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) {
+ manual_server_connection_settings.setVisibility(View.VISIBLE);
+ sso_settings.removeAllViews();
+ }
+
settings_nextcloud_url.setText(KeyStoreUtils.getString(SettingValues.HOST.toString(), null));
settings_nextcloud_user.setText(KeyStoreUtils.getString(SettingValues.USER.toString(), null));
settings_nextcloud_password.setText(KeyStoreUtils.getString(SettingValues.PASSWORD.toString(), null));
@@ -230,6 +272,33 @@ public class SettingsFragment extends Fragment {
super.onDetach();
}
+ public View.OnClickListener getSSOLogoutButtonListener() {
+ return new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ AlertDialog.Builder builder = new AlertDialog.Builder(view.getContext());
+ builder.setMessage(R.string.confirm_account_logout);
+ builder.setCancelable(false);
+ builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialogInterface, int i) {
+ AccountImporter.clearAllAuthTokens(getContext());
+ SingleAccountHelper.setCurrentAccount(getContext(), null);
+
+ settings.edit().remove(SettingValues.HOST.toString()).commit();
+ settings.edit().remove(SettingValues.USER.toString()).commit();
+ settings.edit().remove(SettingValues.PASSWORD.toString()).commit();
+
+ dialogInterface.dismiss();
+ PasswordListActivity.triggerRebirth(Objects.requireNonNull(((PasswordListActivity) getActivity())));
+ }
+ });
+ builder.setNegativeButton(R.string.cancel, null);
+ builder.show();
+ }
+ };
+ }
+
public View.OnClickListener getSaveButtonListener() {
return new View.OnClickListener() {
@SuppressLint("ApplySharedPref")
@@ -290,9 +359,9 @@ public class SettingsFragment extends Fragment {
}
SettingsCache.clear();
- if (!KeyStoreUtils.getString(SettingValues.HOST.toString(), null).equals(settings_nextcloud_url.getText().toString()) ||
+ if (ssoAccount == null && (!KeyStoreUtils.getString(SettingValues.HOST.toString(), null).equals(settings_nextcloud_url.getText().toString()) ||
!KeyStoreUtils.getString(SettingValues.USER.toString(), null).equals(settings_nextcloud_user.getText().toString()) ||
- !KeyStoreUtils.getString(SettingValues.PASSWORD.toString(), null).equals(settings_nextcloud_password.getText().toString())) {
+ !KeyStoreUtils.getString(SettingValues.PASSWORD.toString(), null).equals(settings_nextcloud_password.getText().toString()))) {
ton.removeString(SettingValues.HOST.toString());
ton.removeString(SettingValues.USER.toString());
ton.removeString(SettingValues.PASSWORD.toString());
diff --git a/app/src/main/java/es/wolfi/passman/API/Core.java b/app/src/main/java/es/wolfi/passman/API/Core.java
index 5acf020..01572e0 100644
--- a/app/src/main/java/es/wolfi/passman/API/Core.java
+++ b/app/src/main/java/es/wolfi/passman/API/Core.java
@@ -3,6 +3,7 @@
*
* @copyright Copyright (c) 2016, Sander Brand (brantje@gmail.com)
* @copyright Copyright (c) 2016, Marcos Zuriaga Miguel (wolfi@wolfi.es)
+ * @copyright Copyright (c) 2022, Timo Triebensky (timo@binsky.org)
* @license GNU AGPL version 3 or any later version
* <p>
* This program is free software: you can redistribute it and/or modify
@@ -23,21 +24,46 @@ package es.wolfi.passman.API;
import android.app.AlertDialog;
import android.content.Context;
+import android.net.Uri;
+import android.os.AsyncTask;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
+import androidx.annotation.NonNull;
+
+import com.google.gson.GsonBuilder;
import com.koushikdutta.async.future.FutureCallback;
import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
+import com.nextcloud.android.sso.aidl.NextcloudRequest;
+import com.nextcloud.android.sso.api.AidlNetworkRequest;
+import com.nextcloud.android.sso.api.NextcloudAPI;
+import com.nextcloud.android.sso.api.Response;
+import com.nextcloud.android.sso.exceptions.NextcloudFilesAppAccountNotFoundException;
+import com.nextcloud.android.sso.exceptions.NoCurrentAccountSelectedException;
+import com.nextcloud.android.sso.helper.SingleAccountHelper;
+import com.nextcloud.android.sso.model.SingleSignOnAccount;
import org.json.JSONException;
import org.json.JSONObject;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
-
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import cz.msebera.android.httpclient.Header;
+import cz.msebera.android.httpclient.HeaderElement;
+import cz.msebera.android.httpclient.ParseException;
import cz.msebera.android.httpclient.entity.StringEntity;
import es.wolfi.app.ResponseHandlers.CoreAPIGETResponseHandler;
import es.wolfi.app.passman.OfflineStorage;
@@ -52,18 +78,27 @@ public abstract class Core {
protected static final String LOG_TAG = "API_LIB";
protected static final String JSON_CONTENT_TYPE = "application/json";
+ protected static SingleSignOnAccount ssoAccount;
protected static String host;
protected static String host_internal;
protected static String username;
protected static String password;
protected static String version_name;
+ protected static String API_URL = "/index.php/apps/passman/api/v2/";
+ protected static String API_URL_INTERNAL = "/index.php/apps/passman/api/internal/";
protected static int version_number = 0;
- public static void setUpAPI(String host, String username, String password) {
+ public static void setUpAPI(Context c, String host, String username, String password) {
Core.setAPIHost(host);
Core.username = username;
Core.password = password;
+
+ try {
+ Core.ssoAccount = SingleAccountHelper.getCurrentSingleSignOnAccount(c);
+ } catch (NextcloudFilesAppAccountNotFoundException | NoCurrentAccountSelectedException e) {
+ e.printStackTrace();
+ }
}
public static String getAPIHost() {
@@ -71,8 +106,8 @@ public abstract class Core {
}
public static void setAPIHost(String host) {
- Core.host = host.concat("/index.php/apps/passman/api/v2/");
- Core.host_internal = host.concat("/index.php/apps/passman/api/internal/");
+ Core.host = host.concat(API_URL);
+ Core.host_internal = host.concat(API_URL_INTERNAL);
}
public static String getAPIUsername() {
@@ -105,64 +140,99 @@ public abstract class Core {
public static void requestInternalAPIGET(Context c, String endpoint, final FutureCallback<String> callback) {
final AsyncHttpResponseHandler responseHandler = new CoreAPIGETResponseHandler(callback);
- AsyncHttpClient client = new AsyncHttpClient();
- client.setBasicAuth(username, password);
- client.setConnectTimeout(getConnectTimeout(c));
- client.setResponseTimeout(getResponseTimeout(c));
- client.setMaxRetriesAndTimeout(getConnectRetries(c), getConnectTimeout(c));
- client.addHeader("Content-Type", JSON_CONTENT_TYPE);
-
- try {
- client.get(host_internal.concat(endpoint), responseHandler);
- } catch (Exception e) {
- responseHandler.onFailure(0, null, null, e);
+ if (ssoAccount != null) {
+ final Map<String, List<String>> header = new HashMap<>();
+ header.put("Content-Type", Collections.singletonList(JSON_CONTENT_TYPE));
+
+ NextcloudRequest nextcloudRequest = new NextcloudRequest.Builder()
+ .setMethod("GET")
+ .setUrl(Uri.encode(API_URL_INTERNAL.concat(endpoint), "/"))
+ .build();
+ new SyncedRequestTask(nextcloudRequest, ssoAccount, responseHandler, c).execute();
+ } else {
+ AsyncHttpClient client = new AsyncHttpClient();
+ client.setBasicAuth(username, password);
+ client.setConnectTimeout(getConnectTimeout(c));
+ client.setResponseTimeout(getResponseTimeout(c));
+ client.setMaxRetriesAndTimeout(getConnectRetries(c), getConnectTimeout(c));
+ client.addHeader("Content-Type", JSON_CONTENT_TYPE);
+
+ try {
+ client.get(host_internal.concat(endpoint), responseHandler);
+ } catch (Exception e) {
+ responseHandler.onFailure(0, null, null, e);
+ }
}
}
public static void requestAPIGET(Context c, String endpoint, final FutureCallback<String> callback) {
final AsyncHttpResponseHandler responseHandler = new CoreAPIGETResponseHandler(callback);
- AsyncHttpClient client = new AsyncHttpClient();
- client.setBasicAuth(username, password);
- client.setConnectTimeout(getConnectTimeout(c));
- client.setResponseTimeout(getResponseTimeout(c));
- client.setMaxRetriesAndTimeout(getConnectRetries(c), getConnectTimeout(c));
- client.addHeader("Content-Type", JSON_CONTENT_TYPE);
-
- try {
- client.get(host.concat(endpoint), responseHandler);
- } catch (Exception e) {
- responseHandler.onFailure(0, null, null, e);
+ if (ssoAccount != null) {
+ final Map<String, List<String>> header = new HashMap<>();
+ header.put("Content-Type", Collections.singletonList(JSON_CONTENT_TYPE));
+
+ NextcloudRequest nextcloudRequest = new NextcloudRequest.Builder()
+ .setMethod("GET")
+ .setUrl(Uri.encode(API_URL.concat(endpoint), "/"))
+ .build();
+ new SyncedRequestTask(nextcloudRequest, ssoAccount, responseHandler, c).execute();
+ } else {
+ AsyncHttpClient client = new AsyncHttpClient();
+ client.setBasicAuth(username, password);
+ client.setConnectTimeout(getConnectTimeout(c));
+ client.setResponseTimeout(getResponseTimeout(c));
+ client.setMaxRetriesAndTimeout(getConnectRetries(c), getConnectTimeout(c));
+ client.addHeader("Content-Type", JSON_CONTENT_TYPE);
+
+ try {
+ client.get(host.concat(endpoint), responseHandler);
+ } catch (Exception e) {
+ responseHandler.onFailure(0, null, null, e);
+ }
}
}
public static void requestAPI(Context c, String endpoint, JSONObject jsonPostData, String requestType, final AsyncHttpResponseHandler responseHandler)
throws MalformedURLException, UnsupportedEncodingException {
- URL url = new URL(host.concat(endpoint));
-
- AsyncHttpClient client = new AsyncHttpClient();
- client.setBasicAuth(username, password);
- client.setConnectTimeout(getConnectTimeout(c));
- client.setResponseTimeout(getResponseTimeout(c));
- client.setMaxRetriesAndTimeout(getConnectRetries(c), getConnectTimeout(c));
- client.addHeader("Accept", "application/json, text/plain, */*");
-
- StringEntity entity = new StringEntity(jsonPostData.toString());
-
- try {
- if (requestType.equals("POST")) {
- client.post(c, url.toString(), entity, JSON_CONTENT_TYPE, responseHandler);
- } else if (requestType.equals("PATCH")) {
- client.patch(c, url.toString(), entity, JSON_CONTENT_TYPE, responseHandler);
- } else if (requestType.equals("DELETE")) {
- client.delete(c, url.toString(), entity, JSON_CONTENT_TYPE, responseHandler);
+ if (ssoAccount != null) {
+ final Map<String, List<String>> header = new HashMap<>();
+ header.put("Accept", Collections.singletonList("application/json, text/plain, */*"));
+ //header.put("Content-Type", Collections.singletonList(JSON_CONTENT_TYPE));
+
+ NextcloudRequest nextcloudRequest = new NextcloudRequest.Builder()
+ .setMethod(requestType)
+ .setUrl(Uri.encode(API_URL.concat(endpoint), "/"))
+ .setRequestBody(jsonPostData.toString())
+ .setHeader(header)
+ .build();
+
+ new SyncedRequestTask(nextcloudRequest, ssoAccount, responseHandler, c).execute();
+ } else {
+ URL url = new URL(host.concat(endpoint));
+ AsyncHttpClient client = new AsyncHttpClient();
+ client.setBasicAuth(username, password);
+ client.setConnectTimeout(getConnectTimeout(c));
+ client.setResponseTimeout(getResponseTimeout(c));
+ client.setMaxRetriesAndTimeout(getConnectRetries(c), getConnectTimeout(c));
+ client.addHeader("Accept", "application/json, text/plain, */*");
+
+ StringEntity entity = new StringEntity(jsonPostData.toString());
+
+ try {
+ if (requestType.equals("POST")) {
+ client.post(c, url.toString(), entity, JSON_CONTENT_TYPE, responseHandler);
+ } else if (requestType.equals("PATCH")) {
+ client.patch(c, url.toString(), entity, JSON_CONTENT_TYPE, responseHandler);
+ } else if (requestType.equals("DELETE")) {
+ client.delete(c, url.toString(), entity, JSON_CONTENT_TYPE, responseHandler);
+ }
+ } catch (Exception e) {
+ responseHandler.onFailure(0, null, null, e);
}
- } catch (Exception e) {
- responseHandler.onFailure(0, null, null, e);
}
}
- // TODO Test this method once the server response works!
public static void getAPIVersion(final Context c, FutureCallback<String> cb) {
if (version_name != null) {
cb.onCompleted(null, version_name);
@@ -264,7 +334,7 @@ public abstract class Core {
//Log.d(LOG_TAG, "Pass: " + pass);
Log.d(LOG_TAG, "Pass: " + pass.replaceAll("(?s).", "*"));
- setUpAPI(host, user, pass);
+ setUpAPI(c, host, user, pass);
getAPIVersion(c, new FutureCallback<String>() {
@Override
public void onCompleted(Exception e, String result) {
@@ -294,4 +364,106 @@ public abstract class Core {
}
});
}
+
+ private static class NCHeader implements Header {
+ String name, value;
+
+ public NCHeader(String name, String value) {
+ this.name = name;
+ this.value = value;
+ }
+
+ public NCHeader(ArrayList<AidlNetworkRequest.PlainHeader> plainHeaders, cz.msebera.android.httpclient.Header[] headers) {
+ Iterator<AidlNetworkRequest.PlainHeader> it = plainHeaders.iterator();
+ for (int i = 0; it.hasNext(); i++) {
+ AidlNetworkRequest.PlainHeader plainHeader = it.next();
+ headers[i] = new NCHeader(plainHeader.getName(), plainHeader.getValue());
+ }
+ }
+
+ @Override
+ public HeaderElement[] getElements() throws ParseException {
+ return new HeaderElement[0];
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getValue() {
+ return value;
+ }
+ }
+
+ private static class SyncedRequestTask extends AsyncTask<Void, Void, Boolean> {
+ private final NextcloudRequest nextcloudRequest;
+ private final NextcloudAPI mNextcloudAPI;
+ private final AsyncHttpResponseHandler responseHandler;
+ private final FutureCallback<String> callback;
+
+ SyncedRequestTask(@NonNull NextcloudRequest nextcloudRequest, @NonNull SingleSignOnAccount ssoAccount, final AsyncHttpResponseHandler responseHandler, Context c) {
+ this.nextcloudRequest = nextcloudRequest;
+ this.mNextcloudAPI = new NextcloudAPI(c.getApplicationContext(), ssoAccount, new GsonBuilder().create(), apiCallback);
+ this.responseHandler = responseHandler;
+ this.callback = null;
+ Log.d("SyncedRequestTask", ssoAccount.name + " → " + nextcloudRequest.getMethod() + " " + nextcloudRequest.getUrl() + " ");
+ }
+
+ SyncedRequestTask(@NonNull NextcloudRequest nextcloudRequest, @NonNull SingleSignOnAccount ssoAccount, final FutureCallback<String> callback, Context c) {
+ this.nextcloudRequest = nextcloudRequest;
+ this.mNextcloudAPI = new NextcloudAPI(c.getApplicationContext(), ssoAccount, new GsonBuilder().create(), apiCallback);
+ this.responseHandler = null;
+ this.callback = callback;
+ Log.d("SyncedRequestTask", ssoAccount.name + " → " + nextcloudRequest.getMethod() + " " + nextcloudRequest.getUrl() + " ");
+ }
+
+ @Override
+ protected Boolean doInBackground(Void... voids) {
+ try {
+ Response response = mNextcloudAPI.performNetworkRequestV2(nextcloudRequest);
+
+ StringBuilder textBuilder = new StringBuilder();
+ final BufferedReader rd = new BufferedReader(new InputStreamReader(response.getBody()));
+ String line;
+ while ((line = rd.readLine()) != null) {
+ textBuilder.append(line);
+ }
+ response.getBody().close();
+
+ cz.msebera.android.httpclient.Header[] headers = new cz.msebera.android.httpclient.Header[response.getPlainHeaders().size()];
+ new NCHeader(response.getPlainHeaders(), headers);
+
+ if (responseHandler != null) {
+ responseHandler.onSuccess(200, headers, textBuilder.toString().getBytes(StandardCharsets.UTF_8));
+ } else if (callback != null) {
+ callback.onCompleted(null, textBuilder.toString());
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.e("error msg:", e.getMessage());
+ if (responseHandler != null) {
+ responseHandler.onFailure(400, null, "".getBytes(), e);
+ }
+ }
+
+ return true;
+ }
+
+ private NextcloudAPI.ApiConnectedListener apiCallback = new NextcloudAPI.ApiConnectedListener() {
+ @Override
+ public void onConnected() {
+ // ignore this one… see 5)
+ Log.i("NextcloudAPI", "SSO API connected");
+ }
+
+ @Override
+ public void onError(Exception ex) {
+ Log.i("NextcloudAPI", "SSO API ERROR");
+ ex.printStackTrace();
+ // TODO handle errors
+ }
+ };
+ }
}
diff --git a/app/src/main/java/es/wolfi/passman/API/Credential.java b/app/src/main/java/es/wolfi/passman/API/Credential.java
index 09db5fa..405498b 100644
--- a/app/src/main/java/es/wolfi/passman/API/Credential.java
+++ b/app/src/main/java/es/wolfi/passman/API/Credential.java
@@ -3,6 +3,7 @@
*
* @copyright Copyright (c) 2016, Sander Brand (brantje@gmail.com)
* @copyright Copyright (c) 2016, Marcos Zuriaga Miguel (wolfi@wolfi.es)
+ * @copyright Copyright (c) 2022, Timo Triebensky (timo@binsky.org)
* @license GNU AGPL version 3 or any later version
* <p>
* This program is free software: you can redistribute it and/or modify
diff --git a/app/src/main/res/drawable/ic_baseline_logout_24_grey.xml b/app/src/main/res/drawable/ic_baseline_logout_24_grey.xml
new file mode 100644
index 0000000..57d92cf
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_logout_24_grey.xml
@@ -0,0 +1,5 @@
+<vector android:autoMirrored="true" android:height="24dp"
+ android:tint="#757575" android:viewportHeight="24"
+ android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="@android:color/white" android:pathData="M17,7l-1.41,1.41L18.17,11H8v2h10.17l-2.58,2.58L17,17l5,-5zM4,5h8V3H4c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h8v-2H4V5z"/>
+</vector>
diff --git a/app/src/main/res/drawable/login_decision_button.xml b/app/src/main/res/drawable/login_decision_button.xml
new file mode 100644
index 0000000..2d78d94
--- /dev/null
+++ b/app/src/main/res/drawable/login_decision_button.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="false">
+ <shape android:shape="rectangle">
+ <corners android:radius="1000dp" />
+ <solid android:color="#FFFFFF" />
+ <padding
+ android:bottom="16dp"
+ android:left="16dp"
+ android:right="16dp"
+ android:top="16dp" />
+ </shape>
+ </item>
+ <item android:state_pressed="true">
+ <shape android:shape="rectangle">
+ <corners android:radius="1000dp" />
+ <solid android:color="@color/colorPrimary" />
+ <stroke
+ android:width="1dip"
+ android:color="#FFFFFF" />
+ <padding
+ android:bottom="16dp"
+ android:left="16dp"
+ android:right="16dp"
+ android:top="16dp" />
+ </shape>
+ </item>
+</selector>
diff --git a/app/src/main/res/drawable/nc_background.png b/app/src/main/res/drawable/nc_background.png
new file mode 100644
index 0000000..67e1a4b
--- /dev/null
+++ b/app/src/main/res/drawable/nc_background.png
Binary files differ
diff --git a/app/src/main/res/layout-land/content_login.xml b/app/src/main/res/layout-land/content_legacy_login.xml
index 31f46c9..31f46c9 100644
--- a/app/src/main/res/layout-land/content_login.xml
+++ b/app/src/main/res/layout-land/content_legacy_login.xml
diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml
index b79443b..202717c 100644
--- a/app/src/main/res/layout/activity_login.xml
+++ b/app/src/main/res/layout/activity_login.xml
@@ -1,5 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
+<?xml version="1.0" encoding="utf-8"?><!--
* Passman Android App
*
* @copyright Copyright (c) 2017, Andy Scherzinger
@@ -28,6 +27,48 @@
android:fitsSystemWindows="true"
tools:context="es.wolfi.app.passman.activities.LoginActivity">
- <include layout="@layout/content_login" />
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:scaleType="center"
+ android:src="@drawable/nc_background" />
+
+ <ImageView
+ android:id="@+id/login_options_logo"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="50dp"
+ android:layout_marginBottom="@dimen/activity_vertical_margin"
+ android:contentDescription="@string/app_name_release"
+ android:src="@drawable/logo_horizontal" />
+
+ <include layout="@layout/content_legacy_login" />
+
+ <LinearLayout
+ android:id="@+id/login_options"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:orientation="vertical">
+
+ <Button
+ android:id="@+id/load_sso_login_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="12dp"
+ android:background="@drawable/login_decision_button"
+ android:text="@string/nextcloud_single_sign_on"
+ android:textColor="@color/colorPrimary" />
+
+ <Button
+ android:id="@+id/load_legacy_login_button"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="12dp"
+ android:background="@drawable/login_decision_button"
+ android:text="@string/manual_login"
+ android:textColor="@color/colorPrimary" />
+
+ </LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
diff --git a/app/src/main/res/layout/content_login.xml b/app/src/main/res/layout/content_legacy_login.xml
index 5fb993a..34ba4f4 100644
--- a/app/src/main/res/layout/content_login.xml
+++ b/app/src/main/res/layout/content_legacy_login.xml
@@ -22,17 +22,17 @@
*
-->
<ScrollView android:id="@+id/scroll"
- 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:layout_gravity="center"
- android:fillViewport="true"
- android:orientation="vertical">
+ 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:layout_gravity="center"
+ android:fillViewport="true"
+ android:orientation="vertical">
<LinearLayout
- android:id="@+id/content_login"
+ android:id="@+id/content_legacy_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
@@ -40,13 +40,14 @@
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
+ android:visibility="invisible"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:context="es.wolfi.app.passman.activities.LoginActivity"
tools:showIn="@layout/activity_login"
android:orientation="vertical">
<ImageView
- android:id="@+id/logo"
+ android:id="@+id/legacy_login_logo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/activity_vertical_margin"
@@ -84,8 +85,6 @@
android:inputType="textPersonName"
android:textColor="@color/login_text_color"
android:textColorHint="@color/login_text_hint_color">
-
- <requestFocus />
</EditText>
</LinearLayout>
diff --git a/app/src/main/res/layout/content_manual_server_connection_settings.xml b/app/src/main/res/layout/content_manual_server_connection_settings.xml
new file mode 100644
index 0000000..9b1d4c4
--- /dev/null
+++ b/app/src/main/res/layout/content_manual_server_connection_settings.xml
@@ -0,0 +1,92 @@
+<!--
+ * Passman Android App
+ *
+ * @copyright Copyright (c) 2016, Sander Brand (brantje@gmail.com)
+ * @copyright Copyright (c) 2016, Marcos Zuriaga Miguel (wolfi@wolfi.es)
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@+id/linear_layout_settings"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ style="@style/TextAppearance.AppCompat.Medium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/nextcloud_connection_settings" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="2dp"
+ android:layout_marginTop="4dp"
+ android:background="#8A8A8A" />
+
+ <TextView
+ style="@style/Label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/URL" />
+
+ <EditText
+ android:id="@+id/settings_nextcloud_url"
+ style="@style/FormText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:autofillHints="@string/URL"
+ android:inputType="textUri"
+ tools:ignore="LabelFor" />
+
+ <TextView
+ style="@style/Label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/username" />
+
+ <EditText
+ android:id="@+id/settings_nextcloud_user"
+ style="@style/FormText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:autofillHints="@string/username"
+ android:inputType="textNoSuggestions"
+ tools:ignore="LabelFor" />
+
+ <TextView
+ style="@style/Label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/password" />
+
+ <EditText
+ android:id="@+id/settings_nextcloud_password"
+ style="@style/FormText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:autofillHints="@string/password"
+ android:inputType="textPassword"
+ tools:ignore="LabelFor" />
+
+ </LinearLayout>
+
+</RelativeLayout>
diff --git a/app/src/main/res/layout/content_sso_settings.xml b/app/src/main/res/layout/content_sso_settings.xml
new file mode 100644
index 0000000..4a48bdb
--- /dev/null
+++ b/app/src/main/res/layout/content_sso_settings.xml
@@ -0,0 +1,68 @@
+<!--
+ * Passman Android App
+ *
+ * @copyright Copyright (c) 2016, Sander Brand (brantje@gmail.com)
+ * @copyright Copyright (c) 2016, Marcos Zuriaga Miguel (wolfi@wolfi.es)
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:id="@+id/linear_layout_settings"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ style="@style/TextAppearance.AppCompat.Medium"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/nextcloud_single_sign_on" />
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="2dp"
+ android:layout_marginTop="4dp"
+ android:background="#8A8A8A" />
+
+ <RelativeLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="5dp">
+
+ <TextView
+ android:id="@+id/sso_user_server"
+ style="@style/TextAppearance.AppCompat.Small"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/login_decision_button" />
+
+ <Button
+ android:id="@+id/sso_user_server_logout_button"
+ android:layout_width="30dp"
+ android:layout_height="30dp"
+ android:layout_alignEnd="@id/sso_user_server"
+ android:layout_centerInParent="true"
+ android:layout_marginEnd="5dp"
+ android:background="@drawable/ic_baseline_logout_24_grey" />
+ </RelativeLayout>
+
+ </LinearLayout>
+
+</RelativeLayout>
diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml
index daada53..e63a722 100644
--- a/app/src/main/res/layout/fragment_settings.xml
+++ b/app/src/main/res/layout/fragment_settings.xml
@@ -37,67 +37,17 @@
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingBottom="@dimen/activity_vertical_margin"
- tools:context="es.wolfi.app.passman.fragments.CredentialDisplayFragment">
+ android:paddingBottom="@dimen/activity_vertical_margin">
<!--Nextcloud connection settings-->
- <TextView
- style="@style/TextAppearance.AppCompat.Medium"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/nextcloud_connection_settings" />
-
- <View
- android:layout_width="match_parent"
- android:layout_height="2dp"
- android:layout_marginTop="4dp"
- android:background="#8A8A8A" />
-
- <TextView
- style="@style/Label"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/URL" />
-
- <EditText
- android:id="@+id/settings_nextcloud_url"
- style="@style/FormText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:autofillHints="@string/URL"
- android:inputType="textUri"
- tools:ignore="LabelFor" />
+ <include
+ android:id="@+id/manual_server_connection_settings"
+ layout="@layout/content_manual_server_connection_settings" />
- <TextView
- style="@style/Label"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/username" />
-
- <EditText
- android:id="@+id/settings_nextcloud_user"
- style="@style/FormText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:autofillHints="@string/username"
- android:inputType="textNoSuggestions"
- tools:ignore="LabelFor" />
-
- <TextView
- style="@style/Label"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/password" />
-
- <EditText
- android:id="@+id/settings_nextcloud_password"
- style="@style/FormText"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:autofillHints="@string/password"
- android:inputType="textPassword"
- tools:ignore="LabelFor" />
+ <include
+ android:id="@+id/sso_settings"
+ layout="@layout/content_sso_settings" />
<!--App settings-->
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 38af446..bc0d6ed 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -84,4 +84,7 @@
<string name="offline_cache_description">Caches the encrypted vaults if they were loaded manually</string>
<string name="enable_offline_cache">Enable offline cache</string>
<string name="clear_offline_cache">Clear offline cache</string>
+ <string name="nextcloud_single_sign_on">Nextcloud Single Sign On</string>
+ <string name="manual_login">Manual Login</string>
+ <string name="confirm_account_logout">Are you sure you want to log out?</string>
</resources>
diff --git a/build.gradle b/build.gradle
index ad34db2..016e46a 100644
--- a/build.gradle
+++ b/build.gradle
@@ -6,6 +6,7 @@ buildscript {
mavenCentral()
}
dependencies {
+ apply plugin: 'maven-publish'
classpath 'com.android.tools.build:gradle:7.1.2'
// NOTE: Do not place your application dependencies here; they belong
@@ -16,6 +17,7 @@ buildscript {
allprojects {
repositories {
google()
+ maven { url "https://jitpack.io" }
mavenCentral()
}
}