Support PIN and Password authentication [2/2]
This adds the authentication activity used to check the PIN/password Closes #23
This commit is contained in:
parent
8ef25888db
commit
8690eb1181
9 changed files with 215 additions and 29 deletions
|
@ -3,6 +3,7 @@
|
||||||
#### v0.2.6
|
#### v0.2.6
|
||||||
|
|
||||||
* New feature: custom password preference with confirmation (Issue #26)
|
* New feature: custom password preference with confirmation (Issue #26)
|
||||||
|
* New feature: use an individual password or PIN to lock the app (Issue #23)
|
||||||
* New feature: support for Panic Trigger (PR #27 by carmebar)
|
* New feature: support for Panic Trigger (PR #27 by carmebar)
|
||||||
* Bug fix: OpenPGP with security token (Issue #20, PR #25 by carmebar)
|
* Bug fix: OpenPGP with security token (Issue #20, PR #25 by carmebar)
|
||||||
* Style/UI: add Contributors, Translators and Translate to About
|
* Style/UI: add Contributors, Translators and Translate to About
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
android:theme="@style/AppTheme.NoActionBar">
|
android:theme="@style/AppTheme.NoActionBar">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
@ -27,6 +26,10 @@
|
||||||
android:name=".Activities.AboutActivity"
|
android:name=".Activities.AboutActivity"
|
||||||
android:parentActivityName=".Activities.MainActivity"
|
android:parentActivityName=".Activities.MainActivity"
|
||||||
android:theme="@style/AppTheme.NoActionBar" />
|
android:theme="@style/AppTheme.NoActionBar" />
|
||||||
|
<activity
|
||||||
|
android:name=".Activities.AuthenticateActivity"
|
||||||
|
android:parentActivityName=".Activities.MainActivity"
|
||||||
|
android:theme="@style/AppTheme.NoActionBar" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".Activities.BackupActivity"
|
android:name=".Activities.BackupActivity"
|
||||||
android:parentActivityName=".Activities.MainActivity"
|
android:parentActivityName=".Activities.MainActivity"
|
||||||
|
|
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2017 Jakob Nixdorf
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.shadowice.flocke.andotp.Activities;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.support.design.widget.TextInputEditText;
|
||||||
|
import android.support.design.widget.TextInputLayout;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
|
import android.text.InputType;
|
||||||
|
import android.text.method.PasswordTransformationMethod;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewStub;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.view.inputmethod.EditorInfo;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import org.shadowice.flocke.andotp.R;
|
||||||
|
|
||||||
|
public class AuthenticateActivity extends BaseActivity
|
||||||
|
implements EditText.OnEditorActionListener {
|
||||||
|
|
||||||
|
private SharedPreferences settings;
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
setTitle(R.string.auth_activity_title);
|
||||||
|
setContentView(R.layout.activity_container);
|
||||||
|
|
||||||
|
Toolbar toolbar = (Toolbar) findViewById(R.id.container_toolbar);
|
||||||
|
toolbar.setNavigationIcon(null);
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
|
ViewStub stub = (ViewStub) findViewById(R.id.container_stub);
|
||||||
|
stub.setLayoutResource(R.layout.content_authenticate);
|
||||||
|
View v = stub.inflate();
|
||||||
|
|
||||||
|
TextView passwordLabel = (TextView) v.findViewById(R.id.passwordLabel);
|
||||||
|
TextInputLayout passwordLayout = (TextInputLayout) v.findViewById(R.id.passwordLayout);
|
||||||
|
TextInputEditText passwordInput = (TextInputEditText) v.findViewById(R.id.passwordEdit);
|
||||||
|
|
||||||
|
settings = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
|
String authMethod = settings.getString(getString(R.string.settings_key_auth), getString(R.string.settings_default_auth));
|
||||||
|
|
||||||
|
if (authMethod.equals("password")) {
|
||||||
|
password = settings.getString(getString(R.string.settings_key_auth_password), "");
|
||||||
|
|
||||||
|
if (password.isEmpty()) {
|
||||||
|
Toast.makeText(this, R.string.auth_toast_password_missing, Toast.LENGTH_LONG).show();
|
||||||
|
finishWithResult(true);
|
||||||
|
} else {
|
||||||
|
passwordLabel.setText(R.string.auth_msg_password);
|
||||||
|
passwordLayout.setHint(getString(R.string.auth_hint_password));
|
||||||
|
passwordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||||
|
}
|
||||||
|
} else if (authMethod.equals("pin")) {
|
||||||
|
password = settings.getString(getString(R.string.settings_key_auth_pin), "");
|
||||||
|
|
||||||
|
if (password.isEmpty()) {
|
||||||
|
Toast.makeText(this, R.string.auth_toast_pin_missing, Toast.LENGTH_LONG).show();
|
||||||
|
finishWithResult(true);
|
||||||
|
} else {
|
||||||
|
passwordLabel.setText(R.string.auth_msg_pin);
|
||||||
|
passwordLayout.setHint(getString(R.string.auth_hint_pin));
|
||||||
|
passwordInput.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
finishWithResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
passwordInput.setTransformationMethod(new PasswordTransformationMethod());
|
||||||
|
passwordInput.setOnEditorActionListener(this);
|
||||||
|
|
||||||
|
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||||
|
if (actionId == EditorInfo.IME_ACTION_DONE) {
|
||||||
|
String input = v.getText().toString();
|
||||||
|
|
||||||
|
if (input.equals(password)) {
|
||||||
|
finishWithResult(true);
|
||||||
|
} else {
|
||||||
|
finishWithResult(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// End with a result
|
||||||
|
public void finishWithResult(boolean success) {
|
||||||
|
Intent data = new Intent();
|
||||||
|
|
||||||
|
if (success)
|
||||||
|
setResult(RESULT_OK, data);
|
||||||
|
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go back to the main activity
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
finishWithResult(false);
|
||||||
|
super.onBackPressed();
|
||||||
|
}
|
||||||
|
}
|
|
@ -171,30 +171,16 @@ public class MainActivity extends BaseActivity
|
||||||
public void authenticate() {
|
public void authenticate() {
|
||||||
String authMethod = sharedPref.getString(getString(R.string.settings_key_auth), getString(R.string.settings_default_auth));
|
String authMethod = sharedPref.getString(getString(R.string.settings_key_auth), getString(R.string.settings_default_auth));
|
||||||
|
|
||||||
switch (authMethod) {
|
if (authMethod.equals("device")) {
|
||||||
case "device":
|
KeyguardManager km = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
|
||||||
KeyguardManager km = (KeyguardManager) getSystemService(KEYGUARD_SERVICE);
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP && km.isKeyguardSecure()) {
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP && km.isKeyguardSecure()) {
|
Intent authIntent = km.createConfirmDeviceCredentialIntent(getString(R.string.dialog_title_auth), getString(R.string.dialog_msg_auth));
|
||||||
Intent authIntent = km.createConfirmDeviceCredentialIntent(getString(R.string.dialog_title_auth), getString(R.string.dialog_msg_auth));
|
startActivityForResult(authIntent, INTENT_AUTHENTICATE);
|
||||||
startActivityForResult(authIntent, INTENT_AUTHENTICATE);
|
}
|
||||||
}
|
} else if (authMethod.equals("password") || authMethod.equals("pin")) {
|
||||||
|
Intent authIntent = new Intent(this, AuthenticateActivity.class);
|
||||||
break;
|
startActivityForResult(authIntent, INTENT_AUTHENTICATE);
|
||||||
|
|
||||||
case "password":
|
|
||||||
Toast.makeText(this, "TODO: Password auth", Toast.LENGTH_LONG).show();
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "pin":
|
|
||||||
Toast.makeText(this, "TODO: PIN auth", Toast.LENGTH_LONG).show();
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the main application
|
// Initialize the main application
|
||||||
|
@ -357,8 +343,12 @@ public class MainActivity extends BaseActivity
|
||||||
if (intent.getBooleanExtra("reload", false))
|
if (intent.getBooleanExtra("reload", false))
|
||||||
adapter.loadEntries();
|
adapter.loadEntries();
|
||||||
} else if (requestCode == INTENT_AUTHENTICATE && resultCode != RESULT_OK) {
|
} else if (requestCode == INTENT_AUTHENTICATE && resultCode != RESULT_OK) {
|
||||||
|
Toast.makeText(getBaseContext(), R.string.toast_auth_failed, Toast.LENGTH_LONG).show();
|
||||||
|
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
|
||||||
finishAndRemoveTask();
|
finishAndRemoveTask();
|
||||||
|
} else {
|
||||||
|
finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,18 +98,17 @@ public class PasswordPreference extends DialogPreference
|
||||||
|
|
||||||
passwordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
passwordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||||
passwordConfirm.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
passwordConfirm.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
|
||||||
passwordInput.setTransformationMethod(new PasswordTransformationMethod());
|
|
||||||
passwordConfirm.setTransformationMethod(new PasswordTransformationMethod());
|
|
||||||
} else if (mode == Mode.PIN) {
|
} else if (mode == Mode.PIN) {
|
||||||
passwordLayout.setHint(getContext().getString(R.string.settings_hint_pin));
|
passwordLayout.setHint(getContext().getString(R.string.settings_hint_pin));
|
||||||
passwordConfirm.setHint(R.string.settings_hint_pin_confirm);
|
passwordConfirm.setHint(R.string.settings_hint_pin_confirm);
|
||||||
|
|
||||||
passwordInput.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
|
passwordInput.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
|
||||||
passwordConfirm.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
|
passwordConfirm.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_PASSWORD);
|
||||||
passwordInput.setTransformationMethod(new PasswordTransformationMethod());
|
|
||||||
passwordConfirm.setTransformationMethod(new PasswordTransformationMethod());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
passwordInput.setTransformationMethod(new PasswordTransformationMethod());
|
||||||
|
passwordConfirm.setTransformationMethod(new PasswordTransformationMethod());
|
||||||
|
|
||||||
passwordConfirm.addTextChangedListener(this);
|
passwordConfirm.addTextChangedListener(this);
|
||||||
passwordInput.addTextChangedListener(this);
|
passwordInput.addTextChangedListener(this);
|
||||||
|
|
||||||
|
|
36
app/src/main/res/layout/content_authenticate.xml
Normal file
36
app/src/main/res/layout/content_authenticate.xml
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="@dimen/activity_margin_xlarge">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/passwordLabel"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingBottom="@dimen/activity_margin_xlarge"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/auth_msg_password"/>
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:id="@+id/passwordLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/auth_hint_password"
|
||||||
|
app:passwordToggleEnabled="true" >
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputEditText
|
||||||
|
android:id="@+id/passwordEdit"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:imeOptions="actionDone"
|
||||||
|
android:inputType="textPassword" />
|
||||||
|
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -4,6 +4,8 @@
|
||||||
<dimen name="activity_margin">16dp</dimen>
|
<dimen name="activity_margin">16dp</dimen>
|
||||||
<dimen name="activity_margin_medium">24dp</dimen>
|
<dimen name="activity_margin_medium">24dp</dimen>
|
||||||
<dimen name="activity_margin_large">32dp</dimen>
|
<dimen name="activity_margin_large">32dp</dimen>
|
||||||
|
<dimen name="activity_margin_xlarge">40dp</dimen>
|
||||||
|
|
||||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||||
|
|
||||||
<dimen name="card_corner_radius">0dp</dimen>
|
<dimen name="card_corner_radius">0dp</dimen>
|
||||||
|
|
16
app/src/main/res/values/strings_auth.xml
Normal file
16
app/src/main/res/values/strings_auth.xml
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="auth_activity_title">Authenticate</string>
|
||||||
|
|
||||||
|
<!-- Hints -->
|
||||||
|
<string name="auth_hint_password">Password</string>
|
||||||
|
<string name="auth_hint_pin">PIN</string>
|
||||||
|
|
||||||
|
<!-- Messages -->
|
||||||
|
<string name="auth_msg_password">Please enter your password to start andOTP.</string>
|
||||||
|
<string name="auth_msg_pin">Please enter your PIN to start andOTP.</string>
|
||||||
|
|
||||||
|
<!-- Toast messages -->
|
||||||
|
<string name="auth_toast_password_missing">Please set a password in the settings!</string>
|
||||||
|
<string name="auth_toast_pin_missing">Please set a PIN in the settings!</string>
|
||||||
|
</resources>
|
|
@ -38,6 +38,7 @@
|
||||||
<string name="menu_popup_remove">Remove</string>
|
<string name="menu_popup_remove">Remove</string>
|
||||||
|
|
||||||
<!-- Toast messages -->
|
<!-- Toast messages -->
|
||||||
|
<string name="toast_auth_failed">Authentication failed, closing andOTP!</string>
|
||||||
<string name="toast_copied_to_clipboard">Copied to clipboard</string>
|
<string name="toast_copied_to_clipboard">Copied to clipboard</string>
|
||||||
<string name="toast_invalid_qr_code">Invalid QR Code</string>
|
<string name="toast_invalid_qr_code">Invalid QR Code</string>
|
||||||
|
|
||||||
|
@ -48,7 +49,7 @@
|
||||||
<string name="dialog_title_rename">Rename</string>
|
<string name="dialog_title_rename">Rename</string>
|
||||||
<string name="dialog_title_security_backup">Security and Backups</string>
|
<string name="dialog_title_security_backup">Security and Backups</string>
|
||||||
|
|
||||||
<string name="dialog_msg_auth">Enter your device credentials to start andOTP.</string>
|
<string name="dialog_msg_auth">Please enter your device credentials to start andOTP.</string>
|
||||||
<string name="dialog_msg_confirm_delete">Are you sure you want do remove this account?</string>
|
<string name="dialog_msg_confirm_delete">Are you sure you want do remove this account?</string>
|
||||||
|
|
||||||
<string name="dialog_msg_security_backup_desc">To keep your account information secure this app
|
<string name="dialog_msg_security_backup_desc">To keep your account information secure this app
|
||||||
|
|
Loading…
Reference in a new issue