Switch to a more flexible intro library

This commit is contained in:
Alexander Bakker 2017-08-13 19:51:54 +02:00
parent 1528aa5eaf
commit 302c4802b7
15 changed files with 263 additions and 385 deletions

View file

@ -2,12 +2,17 @@ package me.impy.aegis;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.text.Editable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import com.github.paolorotolo.appintro.ISlidePolicy;
import com.github.paolorotolo.appintro.ISlideSelectionListener;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
@ -17,80 +22,29 @@ import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import agency.tango.materialintroscreen.SlideFragment;
import me.impy.aegis.crypto.CryptoUtils;
import me.impy.aegis.crypto.slots.PasswordSlot;
import me.impy.aegis.crypto.slots.Slot;
public class CustomAuthenticatedSlide extends SlideFragment {
public class CustomAuthenticatedSlide extends Fragment implements ISlidePolicy, ISlideSelectionListener {
private int cryptType;
private EditText textPassword;
private EditText textPasswordConfirm;
private int bgColor;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_authenticated_slide, container, false);
final View view = inflater.inflate(R.layout.fragment_authenticated_slide, container, false);
textPassword = (EditText) view.findViewById(R.id.text_password);
textPasswordConfirm = (EditText) view.findViewById(R.id.text_password_confirm);
view.findViewById(R.id.main).setBackgroundColor(bgColor);
return view;
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
if (!isVisibleToUser) {
return;
}
Intent intent = getActivity().getIntent();
cryptType = intent.getIntExtra("cryptType", 1337);
switch(cryptType) {
case CustomAuthenticationSlide.CRYPT_TYPE_NONE:
break;
case CustomAuthenticationSlide.CRYPT_TYPE_PASS:
break;
case CustomAuthenticationSlide.CRYPT_TYPE_FINGER:
break;
default:
throw new RuntimeException();
}
}
@Override
public int backgroundColor() {
return R.color.colorHeaderSuccess;
}
@Override
/*@Override
public int buttonsColor() {
return R.color.colorAccent;
}
@Override
public boolean canMoveFurther() {
switch(cryptType) {
case CustomAuthenticationSlide.CRYPT_TYPE_NONE:
return true;
case CustomAuthenticationSlide.CRYPT_TYPE_PASS:
char[] password = getEditTextChars(textPassword);
char[] passwordConfirm = getEditTextChars(textPasswordConfirm);
boolean equal = password.length != 0 && Arrays.equals(password, passwordConfirm);
CryptoUtils.zero(password);
CryptoUtils.zero(passwordConfirm);
return equal;
case CustomAuthenticationSlide.CRYPT_TYPE_FINGER:
return false;
default:
throw new RuntimeException();
}
}
@Override
public String cantMoveFurtherErrorMessage() {
return "Passwords should be equal and non-empty";
}
}*/
public int getCryptType() {
return cryptType;
@ -121,4 +75,57 @@ public class CustomAuthenticatedSlide extends SlideFragment {
editable.getChars(0, editable.length(), chars, 0);
return chars;
}
@Override
public void onSlideSelected() {
Intent intent = getActivity().getIntent();
cryptType = intent.getIntExtra("cryptType", 1337);
switch(cryptType) {
case CustomAuthenticationSlide.CRYPT_TYPE_NONE:
break;
case CustomAuthenticationSlide.CRYPT_TYPE_PASS:
break;
case CustomAuthenticationSlide.CRYPT_TYPE_FINGER:
break;
default:
throw new RuntimeException();
}
}
public void setBgColor(int color) {
bgColor = color;
}
@Override
public void onSlideDeselected() {
}
@Override
public boolean isPolicyRespected() {
switch(cryptType) {
case CustomAuthenticationSlide.CRYPT_TYPE_NONE:
return true;
case CustomAuthenticationSlide.CRYPT_TYPE_PASS:
char[] password = getEditTextChars(textPassword);
char[] passwordConfirm = getEditTextChars(textPasswordConfirm);
boolean equal = password.length != 0 && Arrays.equals(password, passwordConfirm);
CryptoUtils.zero(password);
CryptoUtils.zero(passwordConfirm);
return equal;
case CustomAuthenticationSlide.CRYPT_TYPE_FINGER:
return false;
default:
throw new RuntimeException();
}
}
@Override
public void onUserIllegallyRequestedNextPage() {
View view = getView();
if (view != null) {
Snackbar snackbar = Snackbar.make(getView(), "Passwords should be equal and non-empty", Snackbar.LENGTH_LONG);
snackbar.show();
}
}
}

View file

@ -7,24 +7,28 @@ import android.content.pm.PackageManager;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.IdRes;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;
import agency.tango.materialintroscreen.SlideFragment;
import com.github.paolorotolo.appintro.ISlidePolicy;
public class CustomAuthenticationSlide extends SlideFragment {
public static final int CRYPT_TYPE_NONE = 0;
public static final int CRYPT_TYPE_PASS = 1;
public static final int CRYPT_TYPE_FINGER = 2;
public class CustomAuthenticationSlide extends Fragment implements ISlidePolicy {
public static final int CRYPT_TYPE_INVALID = 0;
public static final int CRYPT_TYPE_NONE = 1;
public static final int CRYPT_TYPE_PASS = 2;
public static final int CRYPT_TYPE_FINGER = 3;
private RadioGroup buttonGroup;
private int bgColor;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@ -34,7 +38,7 @@ public class CustomAuthenticationSlide extends SlideFragment {
buttonGroup = (RadioGroup) view.findViewById(R.id.rg_authenticationMethod);
buttonGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, @IdRes int checkedId) {
public void onCheckedChanged(RadioGroup group, int checkedId) {
if (checkedId == -1) {
return;
}
@ -72,26 +76,27 @@ public class CustomAuthenticationSlide extends SlideFragment {
}
}
view.findViewById(R.id.main).setBackgroundColor(bgColor);
return view;
}
@Override
public int backgroundColor() {
return R.color.colorHeaderSuccess;
public void setBgColor(int color) {
bgColor = color;
}
@Override
public int buttonsColor() {
return R.color.colorAccent;
}
@Override
public boolean canMoveFurther() {
public boolean isPolicyRespected() {
return buttonGroup.getCheckedRadioButtonId() != -1;
}
@Override
public String cantMoveFurtherErrorMessage() {
return "Please select an authentication method";
public void onUserIllegallyRequestedNextPage() {
Snackbar snackbar = Snackbar.make(getView(), "Please select an authentication method", Snackbar.LENGTH_LONG);
snackbar.show();
}
/*@Override
public int buttonsColor() {
return R.color.colorAccent;
}*/
}

View file

@ -1,9 +1,5 @@
package me.impy.aegis;
import android.app.Dialog;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.BottomSheetDialogFragment;
@ -11,7 +7,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.Toast;
public class EditProfileBottomSheetdialog extends BottomSheetDialogFragment {
LinearLayout copyLayout;

View file

@ -5,17 +5,14 @@ import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.View;
import android.support.v4.app.Fragment;
import java.lang.reflect.UndeclaredThrowableException;
import java.security.NoSuchAlgorithmException;
import com.github.paolorotolo.appintro.AppIntro;
import com.github.paolorotolo.appintro.AppIntroFragment;
import com.github.paolorotolo.appintro.model.SliderPage;
import javax.crypto.Cipher;
import agency.tango.materialintroscreen.MaterialIntroActivity;
import agency.tango.materialintroscreen.MessageButtonBehaviour;
import agency.tango.materialintroscreen.SlideFragmentBuilder;
import me.impy.aegis.crypto.CryptResult;
import me.impy.aegis.crypto.MasterKey;
import me.impy.aegis.crypto.slots.PasswordSlot;
@ -23,63 +20,77 @@ import me.impy.aegis.crypto.slots.SlotCollection;
import me.impy.aegis.db.Database;
import me.impy.aegis.db.DatabaseFile;
public class IntroActivity extends MaterialIntroActivity {
public class IntroActivity extends AppIntro {
public static final int RESULT_OK = 0;
public static final int RESULT_EXCEPTION = 1;
private CustomAuthenticatedSlide authenticatedSlide;
private CustomAuthenticationSlide authenticationSlide;
private Fragment endSlide;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
hideBackButton();
showSkipButton(false);
//showPagerIndicator(false);
setGoBackLock(true);
// TODO: remove this once github.com/apl-devs/AppIntro/issues/347 is fixed
setSwipeLock(true);
addSlide(new SlideFragmentBuilder()
.backgroundColor(R.color.colorPrimary)
.buttonsColor(R.color.colorAccent)
.image(R.drawable.intro_shield)
.title("Welcome")
.description("Aegis is a brand new open source(!) authenticator app which generates tokens for your accounts.")
.build());
SliderPage homeSliderPage = new SliderPage();
homeSliderPage.setTitle("Welcome");
homeSliderPage.setDescription("Aegis is a secure, free and open source 2FA app");
homeSliderPage.setImageDrawable(R.drawable.intro_shield);
homeSliderPage.setBgColor(getResources().getColor(R.color.colorPrimary));
addSlide(AppIntroFragment.newInstance(homeSliderPage));
addSlide(new SlideFragmentBuilder()
.backgroundColor(R.color.colorAccent)
.buttonsColor(R.color.colorPrimary)
.neededPermissions(new String[]{Manifest.permission.CAMERA})
.image(R.drawable.intro_scanner)
.title("Permissions")
.description("Aegis needs permission to your camera in order to function properly. This is needed to scan QR codes.")
.build(),
new MessageButtonBehaviour(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
}, "Permission granted"));
addSlide(new CustomAuthenticationSlide());
SliderPage permSliderPage = new SliderPage();
permSliderPage.setTitle("Permissions");
permSliderPage.setDescription("Aegis needs permission to use your camera in order to scan QR codes.");
permSliderPage.setImageDrawable(R.drawable.intro_scanner);
permSliderPage.setBgColor(getResources().getColor(R.color.colorAccent));
addSlide(AppIntroFragment.newInstance(permSliderPage));
askForPermissions(new String[]{Manifest.permission.CAMERA}, 2);
authenticationSlide = new CustomAuthenticationSlide();
authenticationSlide.setBgColor(getResources().getColor(R.color.colorHeaderSuccess));
addSlide(authenticationSlide);
authenticatedSlide = new CustomAuthenticatedSlide();
authenticatedSlide.setBgColor(getResources().getColor(R.color.colorPrimary));
addSlide(authenticatedSlide);
addSlide(new SlideFragmentBuilder()
.backgroundColor(R.color.colorPrimary)
.buttonsColor(R.color.colorAccent)
.image(R.drawable.intro_shield)
.title("All done!")
.description("Aegis has been set up and is ready to go.")
.build());
SliderPage endSliderPage = new SliderPage();
endSliderPage.setTitle("All done!");
endSliderPage.setDescription("Aegis has been set up and is ready to go.");
endSliderPage.setImageDrawable(R.drawable.intro_shield);
endSliderPage.setBgColor(getResources().getColor(R.color.colorPrimary));
endSlide = AppIntroFragment.newInstance(endSliderPage);
addSlide(endSlide);
}
private void setException(Exception e) {
Intent result = new Intent();
result.putExtra("exception", e);
setResult(RESULT_EXCEPTION, result);
finish();
}
@Override
public void onFinish() {
super.onFinish();
public void onSlideChanged(Fragment oldFragment, Fragment newFragment) {
// skip to the last slide if no encryption will be used
if (oldFragment == authenticationSlide && newFragment != endSlide) {
Intent intent = getIntent();
int cryptType = intent.getIntExtra("cryptType", CustomAuthenticationSlide.CRYPT_TYPE_INVALID);
if (cryptType == CustomAuthenticationSlide.CRYPT_TYPE_NONE) {
// TODO: no magic indices
getPager().setCurrentItem(5);
}
}
}
@Override
public void onDonePressed(Fragment currentFragment) {
super.onDonePressed(currentFragment);
// create the database and database file
Database database = new Database();
@ -142,5 +153,6 @@ public class IntroActivity extends MaterialIntroActivity {
// TODO: show the intro if we can't find any database files
SharedPreferences prefs = this.getSharedPreferences("me.impy.aegis", Context.MODE_PRIVATE);
prefs.edit().putBoolean("passedIntro", true).apply();
finish();
}
}

View file

@ -409,9 +409,4 @@ public class MainActivity extends AppCompatActivity {
e.printStackTrace();
}
}
private boolean causeIsKeyUserNotAuthenticated(Exception e) {
// TODO: is there a way to catch "Key user not authenticated" specifically aside from checking the exception message?
return e.getCause().getMessage().equals("Key user not authenticated");
}
}

View file

@ -1,13 +0,0 @@
package me.impy.aegis;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class SetPasswordActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_set_password);
}
}