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:
authorWolFi <wolfi@wolfi.es>2021-09-26 23:20:59 +0300
committerWolFi <wolfi@wolfi.es>2021-09-26 23:20:59 +0300
commit6701537238beee6e72984ffd0ae66da9e4d3ab50 (patch)
tree7ca71e7b68d7142961c1fdb4d6c0a50409cd472e
parent996e1c45a43c533dc86718f12b367284f1035b5b (diff)
parentee2e47a74fc1375dec0f0580204597d19ad18939 (diff)
Merge branch 'next'v1.2.0
-rw-r--r--app/build.gradle10
-rw-r--r--app/src/main/AndroidManifest.xml3
-rw-r--r--app/src/main/java/es/wolfi/app/passman/EditPasswordTextItem.java117
-rw-r--r--app/src/main/java/es/wolfi/app/passman/SettingValues.java4
-rw-r--r--app/src/main/java/es/wolfi/app/passman/activities/PasswordListActivity.java42
-rw-r--r--app/src/main/java/es/wolfi/app/passman/activities/ShortcutActivity.java63
-rw-r--r--app/src/main/java/es/wolfi/app/passman/adapters/CredentialViewAdapter.java4
-rw-r--r--app/src/main/java/es/wolfi/app/passman/fragments/CredentialAddFragment.java3
-rw-r--r--app/src/main/java/es/wolfi/app/passman/fragments/CredentialEditFragment.java3
-rw-r--r--app/src/main/java/es/wolfi/app/passman/fragments/CredentialItemFragment.java21
-rw-r--r--app/src/main/java/es/wolfi/app/passman/fragments/SettingsFragment.java88
-rw-r--r--app/src/main/java/es/wolfi/passman/API/Credential.java2
-rw-r--r--app/src/main/java/es/wolfi/utils/IconUtils.java4
-rw-r--r--app/src/main/java/es/wolfi/utils/PasswordGenerator.java206
-rw-r--r--app/src/main/res/drawable/ic_baseline_refresh_24.xml5
-rw-r--r--app/src/main/res/layout/fragment_credential_add.xml5
-rw-r--r--app/src/main/res/layout/fragment_credential_display.xml2
-rw-r--r--app/src/main/res/layout/fragment_credential_edit.xml5
-rw-r--r--app/src/main/res/layout/fragment_edit_password_text_item.xml52
-rw-r--r--app/src/main/res/layout/fragment_settings.xml95
-rw-r--r--app/src/main/res/values/strings.xml12
-rw-r--r--openssl.conf.example2
22 files changed, 702 insertions, 46 deletions
diff --git a/app/build.gradle b/app/build.gradle
index d066cf0..d9fa3f5 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -6,8 +6,8 @@ ext {
android {
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility JavaVersion.VERSION_11
+ targetCompatibility JavaVersion.VERSION_11
}
signingConfigs {
alpha {
@@ -32,8 +32,8 @@ android {
applicationId "es.wolfi.app.passman"
minSdkVersion 21
targetSdkVersion 30
- versionCode 11
- versionName "1.1.1"
+ versionCode 12
+ versionName "1.2.0"
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
externalNativeBuild {
cmake {
@@ -51,7 +51,7 @@ android {
signingConfig signingConfigs.release
}
googlePlayRelease {
- minifyEnabled true
+ minifyEnabled false
debuggable false
resValue "string", "app_name", "@string/app_name_release"
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d218f83..2dd1406 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -29,6 +29,9 @@
android:name=".activities.LoginActivity"
android:label="@string/title_activity_login"
android:theme="@style/AppTheme.NoActionBar.Login" />
+ <activity
+ android:name=".activities.ShortcutActivity"
+ android:theme="@android:style/Theme.NoDisplay" />
<service
android:name=".autofill.CredentialAutofillService"
diff --git a/app/src/main/java/es/wolfi/app/passman/EditPasswordTextItem.java b/app/src/main/java/es/wolfi/app/passman/EditPasswordTextItem.java
new file mode 100644
index 0000000..481aca6
--- /dev/null
+++ b/app/src/main/java/es/wolfi/app/passman/EditPasswordTextItem.java
@@ -0,0 +1,117 @@
+/**
+ * Passman Android App
+ *
+ * @copyright Copyright (c) 2017, Andy Scherzinger
+ * @copyright Copyright (c) 2017, Sander Brand (brantje@gmail.com)
+ * @copyright Copyright (c) 2017, Marcos Zuriaga Miguel (wolfi@wolfi.es)
+ * @license GNU AGPL version 3 or any later version
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * 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/>.
+ */
+package es.wolfi.app.passman;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.text.Editable;
+import android.text.InputType;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.OnClick;
+import es.wolfi.utils.PasswordGenerator;
+
+public class EditPasswordTextItem extends LinearLayout {
+
+ @BindView(R.id.password)
+ EditText password;
+ @BindView(R.id.toggle_password_visibility_btn)
+ ImageButton toggle_password_visibility_btn;
+ @BindView(R.id.generate_password_btn)
+ ImageButton generate_password_btn;
+
+ public EditPasswordTextItem(Context context) {
+ super(context);
+ initView();
+ }
+
+ public EditPasswordTextItem(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ initView();
+ }
+
+ public EditPasswordTextItem(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initView();
+ }
+
+ public EditPasswordTextItem(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ initView();
+ }
+
+ void initView() {
+ setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ setOrientation(HORIZONTAL);
+
+ LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ View v = inflater.inflate(R.layout.fragment_edit_password_text_item, this, true);
+
+ ButterKnife.bind(this, v);
+
+ password.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ generate_password_btn.setVisibility(View.VISIBLE);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ }
+
+ public void setText(String password) {
+ this.password.setText(password);
+ }
+
+ public Editable getText() {
+ return this.password.getText();
+ }
+
+ public void setEnabled(boolean enabled) {
+ password.setEnabled(enabled);
+ }
+
+ @OnClick(R.id.toggle_password_visibility_btn)
+ public void toggleVisibility() {
+ switch (password.getInputType()) {
+ case InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD:
+ password.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
+ toggle_password_visibility_btn.setImageDrawable(getResources().getDrawable(R.drawable.ic_eye_off_grey));
+ break;
+ case InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD:
+ password.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ toggle_password_visibility_btn.setImageDrawable(getResources().getDrawable(R.drawable.ic_eye_grey));
+ break;
+ }
+ }
+
+ @OnClick(R.id.generate_password_btn)
+ public void generatePassword() {
+ this.password.setText(new PasswordGenerator(getContext()).generateRandomPassword());
+ }
+}
diff --git a/app/src/main/java/es/wolfi/app/passman/SettingValues.java b/app/src/main/java/es/wolfi/app/passman/SettingValues.java
index 1613039..d3e95dd 100644
--- a/app/src/main/java/es/wolfi/app/passman/SettingValues.java
+++ b/app/src/main/java/es/wolfi/app/passman/SettingValues.java
@@ -33,7 +33,9 @@ public enum SettingValues {
ENABLE_CREDENTIAL_LIST_ICONS("enable_credential_list_icons"),
REQUEST_CONNECT_TIMEOUT("request_connect_timeout"),
REQUEST_RESPONSE_TIMEOUT("request_response_timeout"),
- CLEAR_CLIPBOARD_DELAY("clear_clipboard_delay");
+ CLEAR_CLIPBOARD_DELAY("clear_clipboard_delay"),
+ PASSWORD_GENERATOR_SETTINGS("password_generator_settings"),
+ ENABLE_PASSWORD_GENERATOR_SHORTCUT("enable_password_generator_shortcut");
private final String name;
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 1f9e5d0..c0a817c 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
@@ -32,6 +32,7 @@ import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.nfc.Tag;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -48,6 +49,9 @@ import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatImageButton;
import androidx.appcompat.widget.Toolbar;
+import androidx.core.content.pm.ShortcutInfoCompat;
+import androidx.core.content.pm.ShortcutManagerCompat;
+import androidx.core.graphics.drawable.IconCompat;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
@@ -60,6 +64,7 @@ import org.json.JSONObject;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Objects;
@@ -102,6 +107,7 @@ public class PasswordListActivity extends AppCompatActivity implements
private String intentFilecontent = "";
HashMap<String, Integer> visibleButtonsBeforeEnterSettings = new HashMap<String, Integer>();
private ClipboardManager.OnPrimaryClipChangedListener onPrimaryClipChangedListener;
+ private int lastCredentialListPosition = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -111,6 +117,8 @@ public class PasswordListActivity extends AppCompatActivity implements
settings = PreferenceManager.getDefaultSharedPreferences(this);
ton = SingleTon.getTon();
+ updateShortcuts();
+
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar ab = getSupportActionBar();
@@ -251,6 +259,25 @@ public class PasswordListActivity extends AppCompatActivity implements
return progress;
}
+ private void updateShortcuts() {
+ if (settings.getBoolean(SettingValues.ENABLE_PASSWORD_GENERATOR_SHORTCUT.toString(), true) &&
+ android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
+ Intent shortcutActivityIntent = new Intent(this, ShortcutActivity.class);
+ shortcutActivityIntent.setAction(ShortcutActivity.GENERATE_PASSWORD_INTENT_ACTION);
+
+ ShortcutInfoCompat shortcut = new ShortcutInfoCompat.Builder(this, ShortcutActivity.GENERATE_PASSWORD_ID)
+ .setShortLabel(getString(R.string.generate_password))
+ .setLongLabel(getString(R.string.generate_password_to_clipboard))
+ .setIcon(IconCompat.createWithResource(this, R.drawable.ic_baseline_refresh_24))
+ .setIntent(shortcutActivityIntent)
+ .build();
+ ShortcutManagerCompat.addDynamicShortcuts(this, Collections.singletonList(shortcut));
+ } else if (!settings.getBoolean(SettingValues.ENABLE_PASSWORD_GENERATOR_SHORTCUT.toString(), true) &&
+ android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
+ ShortcutManagerCompat.removeDynamicShortcuts(this, Collections.singletonList(ShortcutActivity.GENERATE_PASSWORD_ID));
+ }
+ }
+
public void showVaults() {
this.VaultLockButton.setVisibility(View.INVISIBLE);
Core.getAPIVersion(this, new FutureCallback<Integer>() {
@@ -445,9 +472,12 @@ public class PasswordListActivity extends AppCompatActivity implements
void refreshVault() {
final Vault vault = (Vault) ton.getExtra(SettingValues.ACTIVE_VAULT.toString());
+ ProgressDialog progress = getProgressDialog();
+ progress.show();
Vault.getVault(this, vault.guid, new FutureCallback<Vault>() {
@Override
public void onCompleted(Exception e, Vault result) {
+ progress.dismiss();
if (e != null) {
// Not logged in, restart activity
if (e.getMessage() != null && e.getMessage().equals("401")) {
@@ -522,6 +552,8 @@ public class PasswordListActivity extends AppCompatActivity implements
public void applyNewSettings(boolean doRebirth) {
Toast.makeText(this, R.string.successfully_saved, Toast.LENGTH_SHORT).show();
+ updateShortcuts();
+
if (doRebirth) {
triggerRebirth(this);
} else {
@@ -663,6 +695,16 @@ public class PasswordListActivity extends AppCompatActivity implements
}
@Override
+ public void setLastCredentialListPosition(int pos) {
+ lastCredentialListPosition = pos;
+ }
+
+ @Override
+ public int getLastCredentialListPosition() {
+ return lastCredentialListPosition;
+ }
+
+ @Override
public void onListFragmentInteraction(File item) {
Vault v = (Vault) ton.getExtra(SettingValues.ACTIVE_VAULT.toString());
diff --git a/app/src/main/java/es/wolfi/app/passman/activities/ShortcutActivity.java b/app/src/main/java/es/wolfi/app/passman/activities/ShortcutActivity.java
new file mode 100644
index 0000000..e23d2fc
--- /dev/null
+++ b/app/src/main/java/es/wolfi/app/passman/activities/ShortcutActivity.java
@@ -0,0 +1,63 @@
+/**
+ * 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
+ * <p>
+ * 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.
+ * <p>
+ * 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.
+ * <p>
+ * 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/>.
+ */
+package es.wolfi.app.passman.activities;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.os.Bundle;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AppCompatActivity;
+
+import es.wolfi.app.passman.R;
+import es.wolfi.utils.PasswordGenerator;
+
+public class ShortcutActivity extends AppCompatActivity {
+ public final static String LOG_TAG = "ShortcutActivity";
+ public final static String GENERATE_PASSWORD_ID = "es.wolfi.app.passman.generate_password";
+ public final static String GENERATE_PASSWORD_INTENT_ACTION = "custom.actions.intent.GENERATE_PASSWORD";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (getIntent().getAction().equals(GENERATE_PASSWORD_INTENT_ACTION)) {
+ generatePassword();
+ }
+
+ finish();
+ }
+
+ protected void generatePassword() {
+ String password = new PasswordGenerator(getApplicationContext()).generateRandomPassword();
+
+ if (password != null && password.length() > 0) {
+ ClipboardManager clipboard = (ClipboardManager) getApplicationContext().getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipData clip = ClipData.newPlainText("generated_password", password);
+ clipboard.setPrimaryClip(clip);
+
+ Toast.makeText(getApplicationContext(), R.string.copied_to_clipboard, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ Toast.makeText(getApplicationContext(), R.string.error_occurred, Toast.LENGTH_SHORT).show();
+ }
+}
diff --git a/app/src/main/java/es/wolfi/app/passman/adapters/CredentialViewAdapter.java b/app/src/main/java/es/wolfi/app/passman/adapters/CredentialViewAdapter.java
index 17a31ad..da927e4 100644
--- a/app/src/main/java/es/wolfi/app/passman/adapters/CredentialViewAdapter.java
+++ b/app/src/main/java/es/wolfi/app/passman/adapters/CredentialViewAdapter.java
@@ -68,6 +68,8 @@ public class CredentialViewAdapter extends RecyclerView.Adapter<CredentialViewAd
if (holder.mItem != null && holder.mItem.getCompromised() != null && holder.mItem.getCompromised().equals("true")) {
holder.contentLayout.setBackgroundColor(holder.mView.getResources().getColor(R.color.compromised));
+ } else {
+ holder.contentLayout.setBackgroundColor(0);
}
if (holder.mItem != null && settings.getBoolean(SettingValues.ENABLE_CREDENTIAL_LIST_ICONS.toString(), true)) {
@@ -80,6 +82,8 @@ public class CredentialViewAdapter extends RecyclerView.Adapter<CredentialViewAd
@Override
public void onClick(View v) {
if (null != mListener) {
+ mListener.setLastCredentialListPosition(holder.getBindingAdapterPosition());
+
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
mListener.onListFragmentInteraction(holder.mItem);
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 17008f6..d97f69a 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
@@ -49,6 +49,7 @@ import butterknife.BindView;
import butterknife.ButterKnife;
import es.wolfi.app.ResponseHandlers.CredentialAddFileResponseHandler;
import es.wolfi.app.ResponseHandlers.CredentialSaveResponseHandler;
+import es.wolfi.app.passman.EditPasswordTextItem;
import es.wolfi.app.passman.R;
import es.wolfi.app.passman.SettingValues;
import es.wolfi.app.passman.SingleTon;
@@ -77,7 +78,7 @@ public class CredentialAddFragment extends Fragment implements View.OnClickListe
@BindView(R.id.add_credential_user)
EditText user;
@BindView(R.id.add_credential_password)
- EditText password;
+ EditPasswordTextItem password;
@BindView(R.id.add_credential_email)
EditText email;
@BindView(R.id.add_credential_url)
diff --git a/app/src/main/java/es/wolfi/app/passman/fragments/CredentialEditFragment.java b/app/src/main/java/es/wolfi/app/passman/fragments/CredentialEditFragment.java
index 4a65b52..30ae52f 100644
--- a/app/src/main/java/es/wolfi/app/passman/fragments/CredentialEditFragment.java
+++ b/app/src/main/java/es/wolfi/app/passman/fragments/CredentialEditFragment.java
@@ -54,6 +54,7 @@ import butterknife.ButterKnife;
import es.wolfi.app.ResponseHandlers.CredentialAddFileResponseHandler;
import es.wolfi.app.ResponseHandlers.CredentialDeleteResponseHandler;
import es.wolfi.app.ResponseHandlers.CredentialSaveResponseHandler;
+import es.wolfi.app.passman.EditPasswordTextItem;
import es.wolfi.app.passman.R;
import es.wolfi.app.passman.SettingValues;
import es.wolfi.app.passman.SingleTon;
@@ -82,7 +83,7 @@ public class CredentialEditFragment extends Fragment implements View.OnClickList
@BindView(R.id.edit_credential_user)
EditText user;
@BindView(R.id.edit_credential_password)
- EditText password;
+ EditPasswordTextItem password;
@BindView(R.id.edit_credential_email)
EditText email;
@BindView(R.id.edit_credential_url)
diff --git a/app/src/main/java/es/wolfi/app/passman/fragments/CredentialItemFragment.java b/app/src/main/java/es/wolfi/app/passman/fragments/CredentialItemFragment.java
index 7ae8ca6..27c0119 100644
--- a/app/src/main/java/es/wolfi/app/passman/fragments/CredentialItemFragment.java
+++ b/app/src/main/java/es/wolfi/app/passman/fragments/CredentialItemFragment.java
@@ -21,7 +21,6 @@
package es.wolfi.app.passman.fragments;
import android.content.Context;
-import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
@@ -125,6 +124,7 @@ public class CredentialItemFragment extends Fragment {
});
v.sort(sortMethod);
recyclerView.setAdapter(new CredentialViewAdapter(v.getCredentials(), mListener, PreferenceManager.getDefaultSharedPreferences(getContext())));
+ scrollToLastPosition();
updateToggleSortButtonImage(toggleSortButton);
}
@@ -171,7 +171,6 @@ public class CredentialItemFragment extends Fragment {
return view;
}
-
@Override
public void onAttach(Context context) {
super.onAttach(context);
@@ -183,6 +182,20 @@ public class CredentialItemFragment extends Fragment {
}
}
+ public void scrollToLastPosition() {
+ if (recyclerView != null) {
+ recyclerView.post(new Runnable() {
+ @Override
+ public void run() {
+ final LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
+ if (layoutManager != null) {
+ layoutManager.scrollToPositionWithOffset(mListener.getLastCredentialListPosition(), 0);
+ }
+ }
+ });
+ }
+ }
+
@Override
public void onDetach() {
super.onDetach();
@@ -202,5 +215,9 @@ public class CredentialItemFragment extends Fragment {
public interface OnListFragmentInteractionListener {
// TODO: Update argument type and name
void onListFragmentInteraction(Credential item);
+
+ void setLastCredentialListPosition(int pos);
+
+ int getLastCredentialListPosition();
}
}
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 0a095f5..e91bd08 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
@@ -24,6 +24,7 @@ package es.wolfi.app.passman.fragments;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
+import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
@@ -33,12 +34,12 @@ import android.view.ViewManager;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.Spinner;
-import android.widget.Switch;
import android.widget.TextView;
import androidx.annotation.Nullable;
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;
@@ -47,44 +48,43 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import butterknife.BindView;
import butterknife.ButterKnife;
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.Vault;
+import es.wolfi.utils.PasswordGenerator;
public class SettingsFragment extends Fragment {
- @BindView(R.id.settings_nextcloud_url)
EditText settings_nextcloud_url;
- @BindView(R.id.settings_nextcloud_user)
EditText settings_nextcloud_user;
- @BindView(R.id.settings_nextcloud_password)
EditText settings_nextcloud_password;
- @SuppressLint("UseSwitchCompatOrMaterialCode")
- @BindView(R.id.settings_app_start_password_switch)
- Switch settings_app_start_password_switch;
- @SuppressLint("UseSwitchCompatOrMaterialCode")
- @BindView(R.id.enable_credential_list_icons_switch)
- Switch enable_credential_list_icons_switch;
+ MaterialCheckBox settings_app_start_password_switch;
+
+ MaterialCheckBox settings_password_generator_shortcut_switch;
+ MaterialCheckBox settings_password_generator_use_uppercase_switch;
+ MaterialCheckBox settings_password_generator_use_lowercase_switch;
+ MaterialCheckBox settings_password_generator_use_digits_switch;
+ MaterialCheckBox settings_password_generator_use_special_chars_switch;
+ MaterialCheckBox settings_password_generator_avoid_ambiguous_chars_switch;
+ MaterialCheckBox settings_password_generator_require_every_char_type_switch;
+ EditText settings_password_generator_length_value;
+
+ MaterialCheckBox enable_credential_list_icons_switch;
- @BindView(R.id.default_autofill_vault_title)
TextView default_autofill_vault_title;
- @BindView(R.id.default_autofill_vault)
Spinner default_autofill_vault;
- @BindView(R.id.clear_clipboard_delay_value)
EditText clear_clipboard_delay_value;
- @BindView(R.id.request_connect_timeout_value)
EditText request_connect_timeout_value;
- @BindView(R.id.request_response_timeout_value)
EditText request_response_timeout_value;
SharedPreferences settings;
+ PasswordGenerator passwordGenerator;
public SettingsFragment() {
// Required empty public constructor
@@ -107,9 +107,33 @@ public class SettingsFragment extends Fragment {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_settings, container, false);
- FloatingActionButton settingsSaveButton = (FloatingActionButton) view.findViewById(R.id.settings_save_button);
+ FloatingActionButton settingsSaveButton = view.findViewById(R.id.settings_save_button);
settingsSaveButton.setOnClickListener(this.getSaveButtonListener());
+ 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);
+
+ settings_app_start_password_switch = view.findViewById(R.id.settings_app_start_password_switch);
+
+ settings_password_generator_shortcut_switch = view.findViewById(R.id.settings_password_generator_shortcut_switch);
+ settings_password_generator_use_uppercase_switch = view.findViewById(R.id.settings_password_generator_use_uppercase_switch);
+ settings_password_generator_use_lowercase_switch = view.findViewById(R.id.settings_password_generator_use_lowercase_switch);
+ settings_password_generator_use_digits_switch = view.findViewById(R.id.settings_password_generator_use_digits_switch);
+ settings_password_generator_use_special_chars_switch = view.findViewById(R.id.settings_password_generator_use_special_chars_switch);
+ settings_password_generator_avoid_ambiguous_chars_switch = view.findViewById(R.id.settings_password_generator_avoid_ambiguous_chars_switch);
+ settings_password_generator_require_every_char_type_switch = view.findViewById(R.id.settings_password_generator_require_every_char_type_switch);
+ settings_password_generator_length_value = view.findViewById(R.id.settings_password_generator_length_value);
+
+ enable_credential_list_icons_switch = view.findViewById(R.id.enable_credential_list_icons_switch);
+
+ default_autofill_vault_title = view.findViewById(R.id.default_autofill_vault_title);
+ default_autofill_vault = view.findViewById(R.id.default_autofill_vault);
+ clear_clipboard_delay_value = view.findViewById(R.id.clear_clipboard_delay_value);
+
+ request_connect_timeout_value = view.findViewById(R.id.request_connect_timeout_value);
+ request_response_timeout_value = view.findViewById(R.id.request_response_timeout_value);
+
return view;
}
@@ -126,7 +150,24 @@ public class SettingsFragment extends Fragment {
settings_nextcloud_url.setText(settings.getString(SettingValues.HOST.toString(), null));
settings_nextcloud_user.setText(settings.getString(SettingValues.USER.toString(), null));
settings_nextcloud_password.setText(settings.getString(SettingValues.PASSWORD.toString(), null));
+
settings_app_start_password_switch.setChecked(settings.getBoolean(SettingValues.ENABLE_APP_START_DEVICE_PASSWORD.toString(), false));
+
+ passwordGenerator = new PasswordGenerator(getContext());
+
+ settings_password_generator_shortcut_switch.setChecked(settings.getBoolean(SettingValues.ENABLE_PASSWORD_GENERATOR_SHORTCUT.toString(), true));
+ settings_password_generator_use_uppercase_switch.setChecked(passwordGenerator.isUseUppercase());
+ settings_password_generator_use_lowercase_switch.setChecked(passwordGenerator.isUseLowercase());
+ settings_password_generator_use_digits_switch.setChecked(passwordGenerator.isUseDigits());
+ settings_password_generator_use_special_chars_switch.setChecked(passwordGenerator.isUseSpecialChars());
+ settings_password_generator_avoid_ambiguous_chars_switch.setChecked(passwordGenerator.isAvoidAmbiguousCharacters());
+ settings_password_generator_require_every_char_type_switch.setChecked(passwordGenerator.isRequireEveryCharType());
+ settings_password_generator_length_value.setText(String.valueOf(passwordGenerator.getLength()));
+
+ if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.N_MR1) {
+ ((ViewManager) settings_password_generator_shortcut_switch.getParent()).removeView(settings_password_generator_shortcut_switch);
+ }
+
enable_credential_list_icons_switch.setChecked(settings.getBoolean(SettingValues.ENABLE_CREDENTIAL_LIST_ICONS.toString(), true));
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
@@ -185,6 +226,19 @@ public class SettingsFragment extends Fragment {
SingleTon ton = SingleTon.getTon();
settings.edit().putBoolean(SettingValues.ENABLE_APP_START_DEVICE_PASSWORD.toString(), settings_app_start_password_switch.isChecked()).commit();
+
+ settings.edit().putBoolean(SettingValues.ENABLE_PASSWORD_GENERATOR_SHORTCUT.toString(), settings_password_generator_shortcut_switch.isChecked()).commit();
+
+ int length = Integer.parseInt(settings_password_generator_length_value.getText().toString());
+ passwordGenerator.setLength(length > 0 ? length : 12);
+ passwordGenerator.setUseUppercase(settings_password_generator_use_uppercase_switch.isChecked());
+ passwordGenerator.setUseLowercase(settings_password_generator_use_lowercase_switch.isChecked());
+ passwordGenerator.setUseDigits(settings_password_generator_use_digits_switch.isChecked());
+ passwordGenerator.setUseSpecialChars(settings_password_generator_use_special_chars_switch.isChecked());
+ passwordGenerator.setAvoidAmbiguousCharacters(settings_password_generator_avoid_ambiguous_chars_switch.isChecked());
+ passwordGenerator.setRequireEveryCharType(settings_password_generator_require_every_char_type_switch.isChecked());
+ passwordGenerator.applyChanges();
+
settings.edit().putBoolean(SettingValues.ENABLE_CREDENTIAL_LIST_ICONS.toString(), enable_credential_list_icons_switch.isChecked()).commit();
settings.edit().putInt(SettingValues.CLEAR_CLIPBOARD_DELAY.toString(), Integer.parseInt(clear_clipboard_delay_value.getText().toString())).commit();
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 4b72c99..57cc9ae 100644
--- a/app/src/main/java/es/wolfi/passman/API/Credential.java
+++ b/app/src/main/java/es/wolfi/passman/API/Credential.java
@@ -331,7 +331,7 @@ public class Credential extends Core implements Filterable {
}
private boolean isEncryptedWithSharedKey() {
- if (this.sharedKeyDecrypted == null && this.sharedKey.length() > 1 && !this.sharedKey.equals("null")) {
+ if (this.sharedKeyDecrypted == null && this.sharedKey != null && this.sharedKey.length() > 1 && !this.sharedKey.equals("null")) {
this.sharedKeyDecrypted = vault.decryptString(this.sharedKey);
}
return this.sharedKeyDecrypted != null && this.sharedKeyDecrypted.length() > 1 && !this.sharedKeyDecrypted.equals("null");
diff --git a/app/src/main/java/es/wolfi/utils/IconUtils.java b/app/src/main/java/es/wolfi/utils/IconUtils.java
index dc346b0..6ac5735 100644
--- a/app/src/main/java/es/wolfi/utils/IconUtils.java
+++ b/app/src/main/java/es/wolfi/utils/IconUtils.java
@@ -3,6 +3,7 @@ package es.wolfi.utils;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.PictureDrawable;
+import android.os.Build;
import android.util.Base64;
import android.widget.ImageView;
@@ -27,7 +28,8 @@ public class IconUtils {
Bitmap bitmapImageData = BitmapFactory.decodeByteArray(byteImageData, 0, byteImageData.length);
if (bitmapImageData != null) {
credentialIconImageView.setImageBitmap(bitmapImageData);
- } else if (icon.getString("type").equals("svg+xml")) {
+ } else if (icon.getString("type").equals("svg+xml") &&
+ android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
SVG svg = SVG.getFromInputStream(new ByteArrayInputStream(byteImageData));
credentialIconImageView.setImageDrawable(new PictureDrawable(svg.renderToPicture()));
}
diff --git a/app/src/main/java/es/wolfi/utils/PasswordGenerator.java b/app/src/main/java/es/wolfi/utils/PasswordGenerator.java
new file mode 100644
index 0000000..359804d
--- /dev/null
+++ b/app/src/main/java/es/wolfi/utils/PasswordGenerator.java
@@ -0,0 +1,206 @@
+package es.wolfi.utils;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.security.SecureRandom;
+
+import es.wolfi.app.passman.SettingValues;
+
+public class PasswordGenerator {
+
+ private final Context context;
+ private int length = 12;
+ private boolean useUppercase = true;
+ private boolean useLowercase = true;
+ private boolean useDigits = true;
+ private boolean useSpecialChars = true;
+ private boolean avoidAmbiguousCharacters = true;
+ private boolean requireEveryCharType = true;
+
+ public PasswordGenerator(Context context) {
+ this.context = context;
+
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
+
+ try {
+ String cs = settings.getString(SettingValues.PASSWORD_GENERATOR_SETTINGS.toString(), null);
+ if (cs != null) {
+ JSONObject customPasswordGeneratorSettings = new JSONObject(cs);
+
+ if (customPasswordGeneratorSettings.has("length")) {
+ setLength(customPasswordGeneratorSettings.getInt("length"));
+ }
+ if (customPasswordGeneratorSettings.has("useUppercase")) {
+ setUseUppercase(customPasswordGeneratorSettings.getBoolean("useUppercase"));
+ }
+ if (customPasswordGeneratorSettings.has("useLowercase")) {
+ setUseLowercase(customPasswordGeneratorSettings.getBoolean("useLowercase"));
+ }
+ if (customPasswordGeneratorSettings.has("useDigits")) {
+ setUseDigits(customPasswordGeneratorSettings.getBoolean("useDigits"));
+ }
+ if (customPasswordGeneratorSettings.has("useSpecialChars")) {
+ setUseSpecialChars(customPasswordGeneratorSettings.getBoolean("useSpecialChars"));
+ }
+ if (customPasswordGeneratorSettings.has("avoidAmbiguousCharacters")) {
+ setAvoidAmbiguousCharacters(customPasswordGeneratorSettings.getBoolean("avoidAmbiguousCharacters"));
+ }
+ if (customPasswordGeneratorSettings.has("requireEveryCharType")) {
+ setRequireEveryCharType(customPasswordGeneratorSettings.getBoolean("requireEveryCharType"));
+ }
+ }
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void applyChanges() {
+ SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this.context);
+ String passwordGeneratorSettingsString = null;
+
+ try {
+ JSONObject passwordGeneratorSettings = new JSONObject();
+
+ passwordGeneratorSettings.put("length", getLength());
+ passwordGeneratorSettings.put("useUppercase", isUseUppercase());
+ passwordGeneratorSettings.put("useLowercase", isUseLowercase());
+ passwordGeneratorSettings.put("useDigits", isUseDigits());
+ passwordGeneratorSettings.put("useSpecialChars", isUseSpecialChars());
+ passwordGeneratorSettings.put("avoidAmbiguousCharacters", isAvoidAmbiguousCharacters());
+ passwordGeneratorSettings.put("requireEveryCharType", isRequireEveryCharType());
+
+ passwordGeneratorSettingsString = passwordGeneratorSettings.toString();
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+
+ settings.edit().putString(SettingValues.PASSWORD_GENERATOR_SETTINGS.toString(), passwordGeneratorSettingsString).apply();
+ }
+
+ public int getLength() {
+ return length;
+ }
+
+ public void setLength(int length) {
+ this.length = length;
+ }
+
+ public boolean isUseUppercase() {
+ return useUppercase;
+ }
+
+ public void setUseUppercase(boolean useUppercase) {
+ this.useUppercase = useUppercase;
+ }
+
+ public boolean isUseLowercase() {
+ return useLowercase;
+ }
+
+ public void setUseLowercase(boolean useLowercase) {
+ this.useLowercase = useLowercase;
+ }
+
+ public boolean isUseDigits() {
+ return useDigits;
+ }
+
+ public void setUseDigits(boolean useDigits) {
+ this.useDigits = useDigits;
+ }
+
+ public boolean isUseSpecialChars() {
+ return useSpecialChars;
+ }
+
+ public void setUseSpecialChars(boolean useSpecialChars) {
+ this.useSpecialChars = useSpecialChars;
+ }
+
+ public boolean isAvoidAmbiguousCharacters() {
+ return avoidAmbiguousCharacters;
+ }
+
+ public void setAvoidAmbiguousCharacters(boolean avoidAmbiguousCharacters) {
+ this.avoidAmbiguousCharacters = avoidAmbiguousCharacters;
+ }
+
+ public boolean isRequireEveryCharType() {
+ return requireEveryCharType;
+ }
+
+ public void setRequireEveryCharType(boolean requireEveryCharType) {
+ this.requireEveryCharType = requireEveryCharType;
+ }
+
+ public String generateRandomPassword() {
+ StringBuilder generatedPassword = new StringBuilder();
+ SecureRandom rand = new SecureRandom();
+
+ String characterPool = "";
+ String lowercaseCharacters = "abcdefghjkmnpqrstuvwxyz";
+ String uppercaseCharacters = "ABCDEFGHJKMNPQRSTUVWXYZ";
+ String digits = "23456789";
+ String specialCharacters = ".!@#$%^&*";
+
+ if (!isAvoidAmbiguousCharacters()) {
+ lowercaseCharacters += "ilo";
+ uppercaseCharacters += "ILO";
+ digits += "10";
+ }
+ if (isUseLowercase()) {
+ characterPool += lowercaseCharacters;
+ }
+ if (isUseUppercase()) {
+ characterPool += uppercaseCharacters;
+ }
+ if (isUseDigits()) {
+ characterPool += digits;
+ }
+ if (isUseSpecialChars()) {
+ characterPool += specialCharacters;
+ }
+
+ for (int generatorPosition = 0; generatorPosition < getLength(); generatorPosition++) {
+ String customCharacterPool = characterPool;
+
+ if (isRequireEveryCharType()) {
+ customCharacterPool = "";
+ switch (generatorPosition) {
+ case 0:
+ customCharacterPool += lowercaseCharacters;
+ break;
+ case 1:
+ customCharacterPool += uppercaseCharacters;
+ break;
+ case 2:
+ customCharacterPool += digits;
+ break;
+ case 3:
+ customCharacterPool += specialCharacters;
+ break;
+ default:
+ customCharacterPool = characterPool;
+ break;
+ }
+ }
+
+ generatedPassword.append(customCharacterPool.charAt(rand.nextInt(customCharacterPool.length())));
+ }
+
+ StringBuilder generatedPasswordShuffled = new StringBuilder();
+ while (generatedPassword.length() != 0) {
+ int index = rand.nextInt(generatedPassword.length());
+ char c = generatedPassword.charAt(index);
+ generatedPasswordShuffled.append(c);
+ generatedPassword.deleteCharAt(index);
+ }
+
+ return generatedPasswordShuffled.toString();
+ }
+}
diff --git a/app/src/main/res/drawable/ic_baseline_refresh_24.xml b/app/src/main/res/drawable/ic_baseline_refresh_24.xml
new file mode 100644
index 0000000..45161d4
--- /dev/null
+++ b/app/src/main/res/drawable/ic_baseline_refresh_24.xml
@@ -0,0 +1,5 @@
+<vector 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.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
+</vector>
diff --git a/app/src/main/res/layout/fragment_credential_add.xml b/app/src/main/res/layout/fragment_credential_add.xml
index 84b198c..6e861b7 100644
--- a/app/src/main/res/layout/fragment_credential_add.xml
+++ b/app/src/main/res/layout/fragment_credential_add.xml
@@ -73,12 +73,11 @@
android:layout_height="wrap_content"
android:text="@string/password" />
- <EditText
+ <es.wolfi.app.passman.EditPasswordTextItem
android:id="@+id/add_credential_password"
- style="@style/FormText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- tools:text="@string/password" />
+ android:paddingRight="-10dp" />
<TextView
style="@style/Label"
diff --git a/app/src/main/res/layout/fragment_credential_display.xml b/app/src/main/res/layout/fragment_credential_display.xml
index 20cb9a9..b23dacd 100644
--- a/app/src/main/res/layout/fragment_credential_display.xml
+++ b/app/src/main/res/layout/fragment_credential_display.xml
@@ -64,7 +64,7 @@
android:id="@+id/credentialIcon"
android:layout_width="50dp"
android:layout_height="50dp"
- android:layout_marginStart="-45dp"
+ android:layout_marginStart="-50dp"
android:layout_column="2"
android:layout_gravity="center_vertical" />
diff --git a/app/src/main/res/layout/fragment_credential_edit.xml b/app/src/main/res/layout/fragment_credential_edit.xml
index 357ede9..d9a0d45 100644
--- a/app/src/main/res/layout/fragment_credential_edit.xml
+++ b/app/src/main/res/layout/fragment_credential_edit.xml
@@ -73,12 +73,11 @@
android:layout_height="wrap_content"
android:text="@string/password" />
- <EditText
+ <es.wolfi.app.passman.EditPasswordTextItem
android:id="@+id/edit_credential_password"
- style="@style/FormText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- tools:text="@string/password" />
+ android:paddingRight="-10dp" />
<TextView
style="@style/Label"
diff --git a/app/src/main/res/layout/fragment_edit_password_text_item.xml b/app/src/main/res/layout/fragment_edit_password_text_item.xml
new file mode 100644
index 0000000..cc04580
--- /dev/null
+++ b/app/src/main/res/layout/fragment_edit_password_text_item.xml
@@ -0,0 +1,52 @@
+<!--
+ * 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/>.
+ *
+-->
+<merge 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">
+
+ <EditText
+ android:id="@+id/password"
+ style="@style/FormText"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_weight="1"
+ android:ems="10"
+ tools:text="@string/password"/>
+
+ <ImageButton
+ android:id="@+id/toggle_password_visibility_btn"
+ style="@style/ImageButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ app:srcCompat="@drawable/ic_eye_grey"/>
+
+ <ImageButton
+ android:id="@+id/generate_password_btn"
+ style="@style/ImageButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ app:srcCompat="@drawable/ic_baseline_refresh_24"/>
+
+</merge>
diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml
index fb547e3..6972a32 100644
--- a/app/src/main/res/layout/fragment_settings.xml
+++ b/app/src/main/res/layout/fragment_settings.xml
@@ -40,6 +40,8 @@
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context="es.wolfi.app.passman.fragments.CredentialDisplayFragment">
+ <!--Nextcloud connection settings-->
+
<TextView
style="@style/TextAppearance.AppCompat.Medium"
android:layout_width="match_parent"
@@ -97,6 +99,8 @@
android:inputType="textPassword"
tools:ignore="LabelFor" />
+ <!--App settings-->
+
<TextView
style="@style/TextAppearance.AppCompat.Medium"
android:layout_width="match_parent"
@@ -116,15 +120,90 @@
android:layout_height="wrap_content"
android:text="@string/app_start_password" />
- <Switch
+ <com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/settings_app_start_password_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:checked="false"
- android:switchMinWidth="56dp"
- android:text="@string/app_start_password_android_auth_description"
- tools:ignore="UseSwitchCompatOrMaterialXml" />
+ android:text="@string/app_start_password_android_auth_description" />
+
+ <TextView
+ style="@style/Label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/generate_password" />
+
+ <com.google.android.material.checkbox.MaterialCheckBox
+ android:id="@+id/settings_password_generator_shortcut_switch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:checked="false"
+ android:text="@string/enable_password_generator_shortcut_description" />
+
+ <com.google.android.material.checkbox.MaterialCheckBox
+ android:id="@+id/settings_password_generator_use_uppercase_switch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:checked="false"
+ android:text="@string/password_generator_use_uppercase" />
+
+ <com.google.android.material.checkbox.MaterialCheckBox
+ android:id="@+id/settings_password_generator_use_lowercase_switch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:checked="false"
+ android:text="@string/password_generator_use_lowercase" />
+
+ <com.google.android.material.checkbox.MaterialCheckBox
+ android:id="@+id/settings_password_generator_use_digits_switch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:checked="false"
+ android:text="@string/password_generator_use_digits" />
+
+ <com.google.android.material.checkbox.MaterialCheckBox
+ android:id="@+id/settings_password_generator_use_special_chars_switch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:checked="false"
+ android:text="@string/password_generator_use_special_chars" />
+
+ <com.google.android.material.checkbox.MaterialCheckBox
+ android:id="@+id/settings_password_generator_avoid_ambiguous_chars_switch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:checked="false"
+ android:text="@string/password_generator_avoid_ambiguous_chars" />
+
+ <com.google.android.material.checkbox.MaterialCheckBox
+ android:id="@+id/settings_password_generator_require_every_char_type_switch"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:checked="false"
+ android:text="@string/password_generator_require_every_char_type" />
+
+ <TextView
+ style="@style/Label"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/password_length" />
+
+ <EditText
+ android:id="@+id/settings_password_generator_length_value"
+ style="@style/FormText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:digits="123456789"
+ android:inputType="number"
+ tools:ignore="LabelFor" />
<TextView
style="@style/Label"
@@ -132,15 +211,13 @@
android:layout_height="wrap_content"
android:text="@string/credential_icon" />
- <Switch
+ <com.google.android.material.checkbox.MaterialCheckBox
android:id="@+id/enable_credential_list_icons_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:checked="false"
- android:switchMinWidth="56dp"
- android:text="@string/enable_credential_list_icons"
- tools:ignore="UseSwitchCompatOrMaterialXml" />
+ android:text="@string/enable_credential_list_icons" />
<TextView
android:id="@+id/default_autofill_vault_title"
@@ -170,6 +247,8 @@
android:inputType="number"
tools:ignore="LabelFor" />
+ <!--Expert settings-->
+
<TextView
style="@style/TextAppearance.AppCompat.Medium"
android:layout_width="match_parent"
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index c8f26eb..6b74d80 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -45,7 +45,7 @@
<string name="unlock_passman">Unlock Passman</string>
<string name="unlock_passman_message_device_auth">Please authenticate to access Passman</string>
<string name="app_start_password">App start password</string>
- <string name="app_start_password_android_auth_description">Enable Android user authentication on app start?</string>
+ <string name="app_start_password_android_auth_description">Enable Android user authentication on app start</string>
<string name="wait_while_encrypting">Wait while encrypting …</string>
<string name="wait_while_decrypting">Wait while decrypting …</string>
<string name="wait_while_uploading">Wait while uploading …</string>
@@ -65,4 +65,14 @@
<string name="enable_credential_list_icons">Enable credential list icons</string>
<string name="credential_icon">Credential icon</string>
<string name="clear_clipboard_delay">Clear clipboard (after seconds)</string>
+ <string name="generate_password">Generate password</string>
+ <string name="generate_password_to_clipboard">Generate a random password and copy to clipboard</string>
+ <string name="enable_password_generator_shortcut_description">Enable password generator app shortcut</string>
+ <string name="password_generator_require_every_char_type">Require every char type</string>
+ <string name="password_generator_avoid_ambiguous_chars">Avoid ambiguous characters</string>
+ <string name="password_generator_use_special_chars">Use special characters</string>
+ <string name="password_generator_use_digits">Use digits</string>
+ <string name="password_generator_use_lowercase">Use lowercase characters</string>
+ <string name="password_generator_use_uppercase">Use uppercase characters</string>
+ <string name="password_length">Password length</string>
</resources>
diff --git a/openssl.conf.example b/openssl.conf.example
index c969361..69d933c 100644
--- a/openssl.conf.example
+++ b/openssl.conf.example
@@ -4,7 +4,7 @@ if [[ -z $ANDROID_HOME ]]; then
export ANDROID_HOME=$HOME/Android/Sdk
fi
-export ANDROID_NDK_HOME=$ANDROID_HOME/ndk/21.3.6528147
+export ANDROID_NDK_HOME=$ANDROID_HOME/ndk/21.4.7075529
export HOST_TAG=linux-x86_64 # darwin-x86_64 / linux-x86_64 / windows / windows-x86_64
export MIN_SDK_VERSION=21