commit
cb442d95d8
15 changed files with 677 additions and 115 deletions
|
@ -128,6 +128,7 @@ So make sure you have a **current backup** before switching!
|
||||||
* [Apache Commons Codec](https://commons.apache.org/proper/commons-codec/)
|
* [Apache Commons Codec](https://commons.apache.org/proper/commons-codec/)
|
||||||
* [Expandable Layout](https://github.com/AAkira/ExpandableLayout)
|
* [Expandable Layout](https://github.com/AAkira/ExpandableLayout)
|
||||||
* [LicensesDialog](https://github.com/PSDev/LicensesDialog)
|
* [LicensesDialog](https://github.com/PSDev/LicensesDialog)
|
||||||
|
* [material-intro](https://github.com/heinrichreimer/material-intro)
|
||||||
* [MaterialProgressBar](https://github.com/DreaminginCodeZH/MaterialProgressBar)
|
* [MaterialProgressBar](https://github.com/DreaminginCodeZH/MaterialProgressBar)
|
||||||
* [OpenPGP API library](https://github.com/open-keychain/openpgp-api)
|
* [OpenPGP API library](https://github.com/open-keychain/openpgp-api)
|
||||||
* [VNTNumberPickerPreference](https://github.com/vanniktech/VNTNumberPickerPreference)
|
* [VNTNumberPickerPreference](https://github.com/vanniktech/VNTNumberPickerPreference)
|
||||||
|
|
|
@ -25,6 +25,9 @@ android {
|
||||||
lintOptions {
|
lintOptions {
|
||||||
abortOnError false
|
abortOnError false
|
||||||
}
|
}
|
||||||
|
dataBinding {
|
||||||
|
enabled = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
ext {
|
||||||
|
@ -41,6 +44,7 @@ dependencies {
|
||||||
compile "com.android.support:recyclerview-v7:$supportLibVersion"
|
compile "com.android.support:recyclerview-v7:$supportLibVersion"
|
||||||
compile "com.android.support.constraint:constraint-layout:1.0.2"
|
compile "com.android.support.constraint:constraint-layout:1.0.2"
|
||||||
compile "com.github.aakira:expandable-layout:1.6.0"
|
compile "com.github.aakira:expandable-layout:1.6.0"
|
||||||
|
compile "com.heinrichreimersoftware:material-intro:1.6.2"
|
||||||
compile "com.journeyapps:zxing-android-embedded:3.5.0"
|
compile "com.journeyapps:zxing-android-embedded:3.5.0"
|
||||||
compile "com.vanniktech:vntnumberpickerpreference:1.0.0"
|
compile "com.vanniktech:vntnumberpickerpreference:1.0.0"
|
||||||
compile "de.psdev.licensesdialog:licensesdialog:1.8.3"
|
compile "de.psdev.licensesdialog:licensesdialog:1.8.3"
|
||||||
|
|
|
@ -35,6 +35,10 @@
|
||||||
android:name=".Activities.BackupActivity"
|
android:name=".Activities.BackupActivity"
|
||||||
android:parentActivityName=".Activities.MainActivity"
|
android:parentActivityName=".Activities.MainActivity"
|
||||||
android:theme="@style/AppTheme.NoActionBar" />
|
android:theme="@style/AppTheme.NoActionBar" />
|
||||||
|
<activity
|
||||||
|
android:name=".Activities.IntroScreenActivity"
|
||||||
|
android:parentActivityName=".Activities.MainActivity"
|
||||||
|
android:theme="@style/Theme.Intro" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".Activities.SettingsActivity"
|
android:name=".Activities.SettingsActivity"
|
||||||
android:parentActivityName=".Activities.MainActivity"
|
android:parentActivityName=".Activities.MainActivity"
|
||||||
|
|
|
@ -0,0 +1,481 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2018 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.animation.ArgbEvaluator;
|
||||||
|
import android.animation.ObjectAnimator;
|
||||||
|
import android.app.KeyguardManager;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.design.widget.TextInputEditText;
|
||||||
|
import android.support.design.widget.TextInputLayout;
|
||||||
|
import android.support.v4.view.ViewPager;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.InputType;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.text.method.PasswordTransformationMethod;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.EditText;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.heinrichreimersoftware.materialintro.app.IntroActivity;
|
||||||
|
import com.heinrichreimersoftware.materialintro.app.OnNavigationBlockedListener;
|
||||||
|
import com.heinrichreimersoftware.materialintro.app.SlideFragment;
|
||||||
|
import com.heinrichreimersoftware.materialintro.slide.FragmentSlide;
|
||||||
|
import com.heinrichreimersoftware.materialintro.slide.SimpleSlide;
|
||||||
|
|
||||||
|
import org.shadowice.flocke.andotp.R;
|
||||||
|
import org.shadowice.flocke.andotp.Utilities.Constants;
|
||||||
|
import org.shadowice.flocke.andotp.Utilities.Settings;
|
||||||
|
import org.shadowice.flocke.andotp.Utilities.UIHelper;
|
||||||
|
|
||||||
|
public class IntroScreenActivity extends IntroActivity {
|
||||||
|
private Settings settings;
|
||||||
|
|
||||||
|
private EncryptionFragment encryptionFragment;
|
||||||
|
private AuthenticationFragment authenticationFragment;
|
||||||
|
|
||||||
|
private void saveSettings() {
|
||||||
|
Constants.EncryptionType encryptionType = encryptionFragment.getEncryptionType();
|
||||||
|
Constants.AuthMethod authMethod = authenticationFragment.getAuthMethod();
|
||||||
|
|
||||||
|
settings.setEncryption(encryptionType);
|
||||||
|
settings.setAuthMethod(authMethod);
|
||||||
|
|
||||||
|
if (authMethod == Constants.AuthMethod.PASSWORD || authMethod == Constants.AuthMethod.PIN) {
|
||||||
|
String password = authenticationFragment.getPassword();
|
||||||
|
settings.setAuthCredentials(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
settings.setFirstTimeWarningShown(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override protected void onCreate(Bundle savedInstanceState){
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
settings = new Settings(this);
|
||||||
|
|
||||||
|
encryptionFragment = new EncryptionFragment();
|
||||||
|
authenticationFragment = new AuthenticationFragment();
|
||||||
|
|
||||||
|
encryptionFragment.setEncryptionChangedCallback(new EncryptionFragment.EncryptionChangedCallback() {
|
||||||
|
@Override
|
||||||
|
public void onEncryptionChanged(Constants.EncryptionType newEncryptionType) {
|
||||||
|
authenticationFragment.updateEncryptionType(newEncryptionType);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setButtonBackFunction(BUTTON_BACK_FUNCTION_BACK);
|
||||||
|
|
||||||
|
addSlide(new SimpleSlide.Builder()
|
||||||
|
.title(R.string.intro_slide1_title)
|
||||||
|
.description(R.string.intro_slide1_desc)
|
||||||
|
.background(R.color.colorPrimary)
|
||||||
|
.backgroundDark(R.color.colorPrimaryDark)
|
||||||
|
.canGoBackward(false)
|
||||||
|
.scrollable(false)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
addSlide(new FragmentSlide.Builder()
|
||||||
|
.background(R.color.colorPrimary)
|
||||||
|
.backgroundDark(R.color.colorPrimaryDark)
|
||||||
|
.fragment(encryptionFragment)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
// Tell the fragment where it is located
|
||||||
|
authenticationFragment.setSlidePos(getSlides().size());
|
||||||
|
|
||||||
|
addSlide(new FragmentSlide.Builder()
|
||||||
|
.background(R.color.colorPrimary)
|
||||||
|
.backgroundDark(R.color.colorPrimaryDark)
|
||||||
|
.fragment(authenticationFragment)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
addSlide(new SimpleSlide.Builder()
|
||||||
|
.title(R.string.intro_slide4_title)
|
||||||
|
.description(R.string.intro_slide4_desc)
|
||||||
|
.background(R.color.colorPrimary)
|
||||||
|
.backgroundDark(R.color.colorPrimaryDark)
|
||||||
|
.scrollable(false)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
addOnNavigationBlockedListener(new OnNavigationBlockedListener() {
|
||||||
|
@Override
|
||||||
|
public void onNavigationBlocked(int position, int direction) {
|
||||||
|
if (position == 2)
|
||||||
|
authenticationFragment.flashWarning();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void onPageSelected(int position) {
|
||||||
|
if (position == 3)
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageScrollStateChanged(int state) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBackPressed() {
|
||||||
|
// We don't want users to quit the intro screen and end up in an uninitialized state
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class EncryptionFragment extends SlideFragment {
|
||||||
|
private EncryptionChangedCallback encryptionChangedCallback = null;
|
||||||
|
|
||||||
|
private Spinner selection;
|
||||||
|
private TextView desc;
|
||||||
|
|
||||||
|
private SparseArray<Constants.EncryptionType> selectionMapping;
|
||||||
|
|
||||||
|
public EncryptionFragment() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEncryptionChangedCallback(EncryptionChangedCallback cb) {
|
||||||
|
encryptionChangedCallback = cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateSelectionMapping() {
|
||||||
|
String[] encValues = getResources().getStringArray(R.array.settings_values_encryption);
|
||||||
|
|
||||||
|
selectionMapping = new SparseArray<>();
|
||||||
|
for (int i = 0; i < encValues.length; i++)
|
||||||
|
selectionMapping.put(i, Constants.EncryptionType.valueOf(encValues[i].toUpperCase()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Constants.EncryptionType getEncryptionType() {
|
||||||
|
return selectionMapping.get(selection.getSelectedItemPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
View root = inflater.inflate(R.layout.component_intro_encryption, container, false);
|
||||||
|
|
||||||
|
selection = root.findViewById(R.id.introEncryptionSelection);
|
||||||
|
desc = root.findViewById(R.id.introEncryptionDesc);
|
||||||
|
|
||||||
|
generateSelectionMapping();
|
||||||
|
|
||||||
|
selection.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||||
|
Constants.EncryptionType encryptionType = selectionMapping.get(i);
|
||||||
|
|
||||||
|
if (encryptionType == Constants.EncryptionType.PASSWORD)
|
||||||
|
desc.setText(R.string.intro_slide2_desc_password);
|
||||||
|
else if (encryptionType == Constants.EncryptionType.KEYSTORE)
|
||||||
|
desc.setText(R.string.intro_slide2_desc_keystore);
|
||||||
|
|
||||||
|
if (encryptionChangedCallback != null)
|
||||||
|
encryptionChangedCallback.onEncryptionChanged(encryptionType);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface EncryptionChangedCallback {
|
||||||
|
void onEncryptionChanged(Constants.EncryptionType newEncryptionType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class AuthenticationFragment extends SlideFragment {
|
||||||
|
private Constants.EncryptionType encryptionType = Constants.EncryptionType.KEYSTORE;
|
||||||
|
|
||||||
|
private int slidePos = -1;
|
||||||
|
|
||||||
|
private int minLength = Constants.AUTH_MIN_PASSWORD_LENGTH;
|
||||||
|
private String lengthWarning = "";
|
||||||
|
private String noPasswordWarning = "";
|
||||||
|
private String confirmPasswordWarning = "";
|
||||||
|
|
||||||
|
private TextView desc = null;
|
||||||
|
private Spinner selection = null;
|
||||||
|
private TextView authWarnings = null;
|
||||||
|
private LinearLayout credentialsLayout = null;
|
||||||
|
private TextInputLayout passwordLayout = null;
|
||||||
|
private TextInputEditText passwordInput = null;
|
||||||
|
private EditText passwordConfirm = null;
|
||||||
|
|
||||||
|
private SparseArray<Constants.AuthMethod> selectionMapping;
|
||||||
|
|
||||||
|
public AuthenticationFragment() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSlidePos(int pos) {
|
||||||
|
slidePos = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateEncryptionType(Constants.EncryptionType encryptionType) {
|
||||||
|
this.encryptionType = encryptionType;
|
||||||
|
|
||||||
|
if (desc != null) {
|
||||||
|
if (encryptionType == Constants.EncryptionType.KEYSTORE) {
|
||||||
|
desc.setText(R.string.intro_slide3_desc_keystore);
|
||||||
|
} else if (encryptionType == Constants.EncryptionType.PASSWORD) {
|
||||||
|
desc.setText(R.string.intro_slide3_desc_password);
|
||||||
|
|
||||||
|
Constants.AuthMethod selectedMethod = selectionMapping.get(selection.getSelectedItemPosition());
|
||||||
|
if (selectedMethod != Constants.AuthMethod.PASSWORD && selectedMethod != Constants.AuthMethod.PIN )
|
||||||
|
selection.setSelection(selectionMapping.indexOfValue(Constants.AuthMethod.PASSWORD));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateSelectionMapping() {
|
||||||
|
Constants.AuthMethod[] authValues = Constants.AuthMethod.values();
|
||||||
|
|
||||||
|
selectionMapping = new SparseArray<>();
|
||||||
|
for (int i = 0; i < authValues.length; i++)
|
||||||
|
selectionMapping.put(i, authValues[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void displayWarning(int resId) {
|
||||||
|
displayWarning(getString(resId));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void displayWarning(String warning) {
|
||||||
|
authWarnings.setVisibility(View.VISIBLE);
|
||||||
|
authWarnings.setText(warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void hideWarning() {
|
||||||
|
authWarnings.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void flashWarning() {
|
||||||
|
if (authWarnings.getVisibility() == View.VISIBLE) {
|
||||||
|
ObjectAnimator animator = ObjectAnimator.ofInt(authWarnings, "backgroundColor",
|
||||||
|
Color.TRANSPARENT, getResources().getColor(R.color.colorAccent), Color.TRANSPARENT);
|
||||||
|
animator.setDuration(500);
|
||||||
|
animator.setRepeatCount(0);
|
||||||
|
animator.setInterpolator(new AccelerateDecelerateInterpolator());
|
||||||
|
animator.setEvaluator(new ArgbEvaluator());
|
||||||
|
animator.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Constants.AuthMethod getAuthMethod() {
|
||||||
|
return selectionMapping.get(selection.getSelectedItemPosition());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPassword() {
|
||||||
|
return passwordInput.getText().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||||
|
Bundle savedInstanceState) {
|
||||||
|
final View root = inflater.inflate(R.layout.component_intro_authentication, container, false);
|
||||||
|
|
||||||
|
desc = root.findViewById(R.id.introAuthDesc);
|
||||||
|
selection = root.findViewById(R.id.introAuthSelection);
|
||||||
|
authWarnings = root.findViewById(R.id.introAuthWarnings);
|
||||||
|
credentialsLayout = root.findViewById(R.id.introCredentialsLayout);
|
||||||
|
passwordLayout = root.findViewById(R.id.introPasswordLayout);
|
||||||
|
passwordInput = root.findViewById(R.id.introPasswordEdit);
|
||||||
|
passwordConfirm = root.findViewById(R.id.introPasswordConfirm);
|
||||||
|
|
||||||
|
generateSelectionMapping();
|
||||||
|
|
||||||
|
final String[] authEntries = getResources().getStringArray(R.array.settings_entries_auth);
|
||||||
|
ArrayAdapter<String> spinnerArrayAdapter = new ArrayAdapter<String>(getIntroActivity(), android.R.layout.simple_spinner_item, authEntries) {
|
||||||
|
@Override
|
||||||
|
public boolean isEnabled(int position){
|
||||||
|
return encryptionType != Constants.EncryptionType.PASSWORD ||
|
||||||
|
position == selectionMapping.indexOfValue(Constants.AuthMethod.PASSWORD) ||
|
||||||
|
position == selectionMapping.indexOfValue(Constants.AuthMethod.PIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getDropDownView(int position, View convertView, @NonNull ViewGroup parent) {
|
||||||
|
View view = super.getDropDownView(position, convertView, parent);
|
||||||
|
TextView tv = (TextView) view;
|
||||||
|
|
||||||
|
tv.setEnabled(encryptionType != Constants.EncryptionType.PASSWORD ||
|
||||||
|
position == selectionMapping.indexOfValue(Constants.AuthMethod.PASSWORD) ||
|
||||||
|
position == selectionMapping.indexOfValue(Constants.AuthMethod.PIN));
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
selection.setAdapter(spinnerArrayAdapter);
|
||||||
|
|
||||||
|
selection.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||||
|
@Override
|
||||||
|
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
|
||||||
|
Constants.AuthMethod authMethod = selectionMapping.get(i);
|
||||||
|
|
||||||
|
if (authMethod == Constants.AuthMethod.PASSWORD) {
|
||||||
|
credentialsLayout.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
passwordLayout.setHint(getString(R.string.settings_hint_password));
|
||||||
|
passwordConfirm.setHint(R.string.settings_hint_password_confirm);
|
||||||
|
|
||||||
|
passwordInput.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());
|
||||||
|
|
||||||
|
minLength = Constants.AUTH_MIN_PASSWORD_LENGTH;
|
||||||
|
lengthWarning = getString(R.string.settings_label_short_password, minLength);
|
||||||
|
noPasswordWarning = getString(R.string.intro_slide3_warn_no_password);
|
||||||
|
confirmPasswordWarning = getString(R.string.intro_slide3_warn_confirm_password);
|
||||||
|
|
||||||
|
if (getIntroActivity().getCurrentSlidePosition() == slidePos) {
|
||||||
|
passwordInput.requestFocus();
|
||||||
|
UIHelper.showKeyboard(getContext(), passwordInput);
|
||||||
|
}
|
||||||
|
} else if (authMethod == Constants.AuthMethod.PIN) {
|
||||||
|
credentialsLayout.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
passwordLayout.setHint(getString(R.string.settings_hint_pin));
|
||||||
|
passwordConfirm.setHint(R.string.settings_hint_pin_confirm);
|
||||||
|
|
||||||
|
passwordInput.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());
|
||||||
|
|
||||||
|
minLength = Constants.AUTH_MIN_PIN_LENGTH;
|
||||||
|
lengthWarning = getString(R.string.settings_label_short_pin, minLength);
|
||||||
|
noPasswordWarning = getString(R.string.intro_slide3_warn_no_pin);
|
||||||
|
confirmPasswordWarning = getString(R.string.intro_slide3_warn_confirm_pin);
|
||||||
|
|
||||||
|
if (getIntroActivity().getCurrentSlidePosition() == slidePos) {
|
||||||
|
passwordInput.requestFocus();
|
||||||
|
UIHelper.showKeyboard(getContext(), passwordInput);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
credentialsLayout.setVisibility(View.INVISIBLE);
|
||||||
|
|
||||||
|
UIHelper.hideKeyboard(getIntroActivity(), root);
|
||||||
|
}
|
||||||
|
|
||||||
|
updateNavigation();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> adapterView) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
TextWatcher textWatcher = new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||||
|
updateNavigation();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable editable) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
passwordInput.addTextChangedListener(textWatcher);
|
||||||
|
passwordConfirm.addTextChangedListener(textWatcher);
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canGoForward() {
|
||||||
|
Constants.AuthMethod authMethod = selectionMapping.get(selection.getSelectedItemPosition());
|
||||||
|
|
||||||
|
if (authMethod == Constants.AuthMethod.PIN || authMethod == Constants.AuthMethod.PASSWORD) {
|
||||||
|
String password = passwordInput.getText().toString();
|
||||||
|
String confirm = passwordConfirm.getText().toString();
|
||||||
|
|
||||||
|
if (! password.isEmpty()) {
|
||||||
|
if (password.length() < minLength) {
|
||||||
|
displayWarning(lengthWarning);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (! confirm.isEmpty() && confirm.equals(password)) {
|
||||||
|
hideWarning();
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
displayWarning(confirmPasswordWarning);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
displayWarning(noPasswordWarning);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (authMethod == Constants.AuthMethod.DEVICE) {
|
||||||
|
KeyguardManager km = (KeyguardManager) getContext().getSystemService(KEYGUARD_SERVICE);
|
||||||
|
|
||||||
|
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
displayWarning(R.string.settings_toast_auth_device_pre_lollipop);
|
||||||
|
return false;
|
||||||
|
} else if (! km.isKeyguardSecure()) {
|
||||||
|
displayWarning(R.string.settings_toast_auth_device_not_secure);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
hideWarning();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
hideWarning();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,7 +44,6 @@ import android.support.v7.widget.helper.ItemTouchHelper;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.view.animation.LinearInterpolator;
|
import android.view.animation.LinearInterpolator;
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
|
@ -104,37 +103,8 @@ public class MainActivity extends BaseActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showFirstTimeWarning() {
|
private void showFirstTimeWarning() {
|
||||||
ViewGroup container = findViewById(R.id.main_content);
|
Intent introIntent = new Intent(this, IntroScreenActivity.class);
|
||||||
View msgView = getLayoutInflater().inflate(R.layout.dialog_database_encryption, container, false);
|
startActivityForResult(introIntent, Constants.INTENT_MAIN_INTRO);
|
||||||
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
|
||||||
builder.setTitle(R.string.dialog_title_encryption)
|
|
||||||
.setView(msgView)
|
|
||||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
|
||||||
settings.setFirstTimeWarningShown(true);
|
|
||||||
updateEncryption(null);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setNegativeButton(R.string.button_settings, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialogInterface, int i) {
|
|
||||||
settings.setFirstTimeWarningShown(true);
|
|
||||||
|
|
||||||
Intent settingsIntent = new Intent(getBaseContext(), SettingsActivity.class);
|
|
||||||
startActivityForResult(settingsIntent, Constants.INTENT_MAIN_SETTINGS);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.setOnCancelListener(new DialogInterface.OnCancelListener() {
|
|
||||||
@Override
|
|
||||||
public void onCancel(DialogInterface dialogInterface) {
|
|
||||||
settings.setFirstTimeWarningShown(true);
|
|
||||||
updateEncryption(null);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.create()
|
|
||||||
.show();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void authenticate(int messageId) {
|
public void authenticate(int messageId) {
|
||||||
|
|
|
@ -52,6 +52,7 @@ public class Constants {
|
||||||
public final static int INTENT_MAIN_AUTHENTICATE = 100;
|
public final static int INTENT_MAIN_AUTHENTICATE = 100;
|
||||||
public final static int INTENT_MAIN_SETTINGS = 101;
|
public final static int INTENT_MAIN_SETTINGS = 101;
|
||||||
public final static int INTENT_MAIN_BACKUP = 102;
|
public final static int INTENT_MAIN_BACKUP = 102;
|
||||||
|
public final static int INTENT_MAIN_INTRO = 103;
|
||||||
|
|
||||||
public final static int INTENT_BACKUP_OPEN_DOCUMENT_PLAIN = 200;
|
public final static int INTENT_BACKUP_OPEN_DOCUMENT_PLAIN = 200;
|
||||||
public final static int INTENT_BACKUP_SAVE_DOCUMENT_PLAIN = 201;
|
public final static int INTENT_BACKUP_SAVE_DOCUMENT_PLAIN = 201;
|
||||||
|
|
|
@ -220,6 +220,10 @@ public class Settings {
|
||||||
return AuthMethod.valueOf(authString.toUpperCase());
|
return AuthMethod.valueOf(authString.toUpperCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAuthMethod(AuthMethod authMethod) {
|
||||||
|
setString(R.string.settings_key_auth, authMethod.name().toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
public void removeAuthPasswordHash() {
|
public void removeAuthPasswordHash() {
|
||||||
remove(R.string.settings_key_auth_password_hash);
|
remove(R.string.settings_key_auth_password_hash);
|
||||||
}
|
}
|
||||||
|
@ -290,6 +294,10 @@ public class Settings {
|
||||||
return EncryptionType.valueOf(encType.toUpperCase());
|
return EncryptionType.valueOf(encType.toUpperCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setEncryption(EncryptionType encryptionType) {
|
||||||
|
setEncryption(encryptionType.name().toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
public void setEncryption(String encryption) {
|
public void setEncryption(String encryption) {
|
||||||
setString(R.string.settings_key_encryption, encryption);
|
setString(R.string.settings_key_encryption, encryption);
|
||||||
}
|
}
|
||||||
|
|
87
app/src/main/res/layout/component_intro_authentication.xml
Normal file
87
app/src/main/res/layout/component_intro_authentication.xml
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<android.support.v4.widget.NestedScrollView
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:overScrollMode="never">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="@dimen/activity_margin_large">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/settings_title_auth" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/introAuthDesc"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/activity_margin"
|
||||||
|
android:text="@string/intro_slide3_desc_keystore"/>
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/introAuthSelection"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/activity_margin_large"
|
||||||
|
android:layout_marginStart="@dimen/activity_margin"
|
||||||
|
android:layout_marginEnd="@dimen/activity_margin"
|
||||||
|
android:entries="@array/settings_entries_auth" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/introAuthWarnings"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/activity_margin"
|
||||||
|
android:padding="@dimen/activity_margin_small"
|
||||||
|
android:visibility="gone"
|
||||||
|
android:textColor="@color/warning_red"
|
||||||
|
android:textAlignment="center"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/introCredentialsLayout"
|
||||||
|
android:visibility="invisible"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginTop="@dimen/activity_margin_large"
|
||||||
|
android:paddingStart="@dimen/activity_margin"
|
||||||
|
android:paddingEnd="@dimen/activity_margin">
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:id="@+id/introPasswordLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/settings_hint_password"
|
||||||
|
app:passwordToggleEnabled="true" >
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputEditText
|
||||||
|
android:id="@+id/introPasswordEdit"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:inputType="textPassword" />
|
||||||
|
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/introPasswordConfirm"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/settings_hint_password_confirm"
|
||||||
|
android:inputType="textPassword" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</android.support.v4.widget.NestedScrollView>
|
40
app/src/main/res/layout/component_intro_encryption.xml
Normal file
40
app/src/main/res/layout/component_intro_encryption.xml
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:gravity="center"
|
||||||
|
android:padding="@dimen/activity_margin_large" >
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
|
||||||
|
android:textStyle="bold"
|
||||||
|
android:text="@string/settings_title_encryption" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/activity_margin"
|
||||||
|
android:text="@string/intro_slide2_desc"/>
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/introEncryptionSelection"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/activity_margin_large"
|
||||||
|
android:layout_marginStart="@dimen/activity_margin"
|
||||||
|
android:layout_marginEnd="@dimen/activity_margin"
|
||||||
|
android:entries="@array/settings_entries_encryption" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/introEncryptionDesc"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginTop="@dimen/activity_margin_large"
|
||||||
|
android:text="@string/intro_slide2_desc_keystore"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -1,60 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:paddingTop="@dimen/activity_margin"
|
|
||||||
android:paddingBottom="@dimen/activity_margin_small">
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_marginStart="@dimen/activity_margin_medium"
|
|
||||||
android:layout_marginEnd="@dimen/activity_margin_medium">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
|
||||||
android:text="@string/dialog_msg_security_first" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/activity_margin_small"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:text="@string/dialog_title_security_keystore"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/activity_margin_small"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
|
||||||
android:text="@string/dialog_msg_security_keystore" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/activity_margin_small"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:text="@string/dialog_title_security_password"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginStart="@dimen/activity_margin_small"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
|
||||||
android:text="@string/dialog_msg_security_password" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/activity_margin_small"
|
|
||||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
|
||||||
android:text="@string/dialog_msg_security_default" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</ScrollView>
|
|
|
@ -12,6 +12,12 @@
|
||||||
<copyright>Copyright (C) 2015 A.Akira</copyright>
|
<copyright>Copyright (C) 2015 A.Akira</copyright>
|
||||||
<license>Apache Software License 2.0</license>
|
<license>Apache Software License 2.0</license>
|
||||||
</notice>
|
</notice>
|
||||||
|
<notice>
|
||||||
|
<name>material-intro</name>
|
||||||
|
<url>https://github.com/heinrichreimer/material-intro</url>
|
||||||
|
<copyright>Copyright (c) 2017 Jan Heinrich Reimer</copyright>
|
||||||
|
<license>MIT License</license>
|
||||||
|
</notice>
|
||||||
<notice>
|
<notice>
|
||||||
<name>MaterialProgressBar</name>
|
<name>MaterialProgressBar</name>
|
||||||
<url>https://github.com/DreaminginCodeZH/MaterialProgressBar</url>
|
<url>https://github.com/DreaminginCodeZH/MaterialProgressBar</url>
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
<color name="paypal_dark">#003087</color>
|
<color name="paypal_dark">#003087</color>
|
||||||
<color name="paypal_light">#009cde</color>
|
<color name="paypal_light">#009cde</color>
|
||||||
|
|
||||||
|
<color name="warning_red">#b71c1c</color> <!-- material_red_900 -->
|
||||||
|
|
||||||
<color name="dark_thumbnail_background">#ffe0e0e0</color> <!-- material_grey_300 -->
|
<color name="dark_thumbnail_background">#ffe0e0e0</color> <!-- material_grey_300 -->
|
||||||
|
|
||||||
<color name="fab_small_label_background">#212121</color>
|
<color name="fab_small_label_background">#212121</color>
|
||||||
|
|
39
app/src/main/res/values/strings_intro.xml
Normal file
39
app/src/main/res/values/strings_intro.xml
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<string name="intro_slide1_title">Let\'s get started</string>
|
||||||
|
<string name="intro_slide1_desc">Welcome to <b>andOTP</b>, this wizard will guide you through
|
||||||
|
the initial setup. Please pay attention and read carefully otherwise you could lose data!
|
||||||
|
\n\nDon\'t worry, you can still change any of the options later in the <b>Settings</b>.</string>
|
||||||
|
|
||||||
|
<string name="intro_slide2_desc">To ensure the security of your accounts <b>andOTP</b> only
|
||||||
|
stores them in encrypted data files. Here you can choose which method of encryption will be
|
||||||
|
used.</string>
|
||||||
|
<string name="intro_slide2_desc_keystore">The KeyStore is a system component of Android for
|
||||||
|
securely storing cryptographic keys. The advantage of this method is that the keys are
|
||||||
|
stored separated from the data files and can be backed by hardware cryptography (if your
|
||||||
|
device supports it). However, as the keys are not stored with the apps data, <b>this method
|
||||||
|
prevents external backup solutions (like Titanium) from working</b>. If you choose this
|
||||||
|
method you will have to rely on the internal backup functions provided by <b>andOTP</b>.
|
||||||
|
\n\n<b>Warning</b>: The KeyStore is known to cause problems on some custom ROMs (and a few
|
||||||
|
stock ones as well). If you don\'t mind entering a password / PIN every time you start
|
||||||
|
<b>andOTP</b> it is highly recommended to use the password-based encryption instead.</string>
|
||||||
|
<string name="intro_slide2_desc_password">This method will encrypt your data with a key
|
||||||
|
generated from a password or PIN. The main advantage here is that this will work with
|
||||||
|
external backup solutions (like Titanium) and is much less failure-prone than the KeyStore.
|
||||||
|
However, you will have to enter your credentials every time you start <b>andOTP</b>.</string>
|
||||||
|
|
||||||
|
<string name="intro_slide3_desc_keystore">Here you can setup an authentication to lock
|
||||||
|
<b>andOTP</b>. Since you have chosen <b>Android KeyStore</b> as encryption method this is
|
||||||
|
optional.</string>
|
||||||
|
<string name="intro_slide3_desc_password">Here you can setup an authentication to lock
|
||||||
|
<b>andOTP</b>. Since you have chosen <b>Password / PIN</b> as encryption method you need to
|
||||||
|
set either a password or a PIN to proceed.</string>
|
||||||
|
<string name="intro_slide3_warn_no_password">Please set a password to continue!</string>
|
||||||
|
<string name="intro_slide3_warn_no_pin">Please set a PIN to continue!</string>
|
||||||
|
<string name="intro_slide3_warn_confirm_password">Please confirm your password to continue!</string>
|
||||||
|
<string name="intro_slide3_warn_confirm_pin">Please confirm your PIN to continue!</string>
|
||||||
|
|
||||||
|
<string name="intro_slide4_title">Finished</string>
|
||||||
|
<string name="intro_slide4_desc">Your settings have been saved, you are all set to use
|
||||||
|
<b>andOTP</b> now!</string>
|
||||||
|
</resources>
|
|
@ -65,30 +65,10 @@
|
||||||
<string name="dialog_title_rename">Rename</string>
|
<string name="dialog_title_rename">Rename</string>
|
||||||
<string name="dialog_title_last_used">Last used</string>
|
<string name="dialog_title_last_used">Last used</string>
|
||||||
<string name="dialog_title_keystore_error">KeyStore error</string>
|
<string name="dialog_title_keystore_error">KeyStore error</string>
|
||||||
<string name="dialog_title_encryption">Database encryption</string>
|
|
||||||
|
|
||||||
<string name="dialog_msg_auth">Please 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 the account \"%1$s\"?</string>
|
<string name="dialog_msg_confirm_delete">Are you sure you want do remove the account \"%1$s\"?</string>
|
||||||
|
|
||||||
<string name="dialog_title_security_keystore">1. Android KeyStore</string>
|
|
||||||
<string name="dialog_title_security_password">2. Password / PIN</string>
|
|
||||||
|
|
||||||
<string name="dialog_msg_security_first">To ensure the security of your accounts this app
|
|
||||||
only stores them in encrypted data files using one of the following two methods:</string>
|
|
||||||
<string name="dialog_msg_security_keystore">The KeyStore is a system component of Android for
|
|
||||||
securely storing cryptographic keys. The advantage of this approach is that the keys are
|
|
||||||
stored separated from the data files and can be backed by hardware cryptography (if the
|
|
||||||
hardware supports it). However as the keys are not stored with the apps data this method
|
|
||||||
prevents external backup solutions (like Titanium) from working. If you choose this method
|
|
||||||
you will have to rely on the internal backup functions provided by andOTP.</string>
|
|
||||||
<string name="dialog_msg_security_password">This method will encrypt your data with a key
|
|
||||||
generated from a password or PIN. The main advantage here is that this will work with
|
|
||||||
external backup solutions (like Titanium). However you will have to enter your credentials
|
|
||||||
every time you start andOTP.</string>
|
|
||||||
<string name="dialog_msg_security_default">By default the Android KeyStore will be used, however
|
|
||||||
this is known to cause problems on certain custom ROMs (and a few stock ones as well). You
|
|
||||||
can change the encryption in the <b>Settings</b> by clicking on the button below.</string>
|
|
||||||
|
|
||||||
<string name="dialog_msg_last_used">In order for andOTP to recognize which token was used last
|
<string name="dialog_msg_last_used">In order for andOTP to recognize which token was used last
|
||||||
you have to have \"tap to reveal\" enabled or use the copy button.\n\nThis message will not
|
you have to have \"tap to reveal\" enabled or use the copy button.\n\nThis message will not
|
||||||
be shown again.</string>
|
be shown again.</string>
|
||||||
|
|
|
@ -18,9 +18,8 @@ buildscript {
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
maven {
|
maven { url "https://maven.google.com" }
|
||||||
url "https://maven.google.com"
|
maven { url "https://jitpack.io" }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue