diff options
author | WolFi <wolfi@wolfi.es> | 2021-09-26 23:20:59 +0300 |
---|---|---|
committer | WolFi <wolfi@wolfi.es> | 2021-09-26 23:20:59 +0300 |
commit | 6701537238beee6e72984ffd0ae66da9e4d3ab50 (patch) | |
tree | 7ca71e7b68d7142961c1fdb4d6c0a50409cd472e | |
parent | 996e1c45a43c533dc86718f12b367284f1035b5b (diff) | |
parent | ee2e47a74fc1375dec0f0580204597d19ad18939 (diff) |
Merge branch 'next'v1.2.0
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 |