diff --git a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPElementType.java b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPElementType.java index 25b576d4..213cf704 100644 --- a/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPElementType.java +++ b/MasterPassword/Java/masterpassword-algorithm/src/main/java/com/lyndir/masterpassword/MPElementType.java @@ -1,5 +1,6 @@ package com.lyndir.masterpassword; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.lyndir.lhunath.opal.system.logging.Logger; import java.util.Set; @@ -93,14 +94,12 @@ public enum MPElementType { * * @return All types that support the given class. */ - public static ImmutableSet forClass(final MPElementTypeClass typeClass) { + public static ImmutableList forClass(final MPElementTypeClass typeClass) { - ImmutableSet.Builder types = ImmutableSet.builder(); - for (final MPElementType type : values()) { - if (type.getTypeClass() == typeClass) { + ImmutableList.Builder types = ImmutableList.builder(); + for (final MPElementType type : values()) + if (type.getTypeClass() == typeClass) types.add( type ); - } - } return types.build(); } diff --git a/MasterPassword/Java/masterpassword-android/AndroidManifest.xml b/MasterPassword/Java/masterpassword-android/AndroidManifest.xml index fecf60d4..e844b6f8 100644 --- a/MasterPassword/Java/masterpassword-android/AndroidManifest.xml +++ b/MasterPassword/Java/masterpassword-android/AndroidManifest.xml @@ -5,14 +5,14 @@ android:versionName="GIT-SNAPSHOT"> - + diff --git a/MasterPassword/Java/masterpassword-android/assets/Exo2.0-Bold.otf b/MasterPassword/Java/masterpassword-android/assets/Exo2.0-Bold.otf new file mode 100644 index 00000000..1e7072dd Binary files /dev/null and b/MasterPassword/Java/masterpassword-android/assets/Exo2.0-Bold.otf differ diff --git a/MasterPassword/Java/masterpassword-android/assets/Exo2.0-ExtraBold.otf b/MasterPassword/Java/masterpassword-android/assets/Exo2.0-ExtraBold.otf new file mode 100644 index 00000000..32dadb3f Binary files /dev/null and b/MasterPassword/Java/masterpassword-android/assets/Exo2.0-ExtraBold.otf differ diff --git a/MasterPassword/Java/masterpassword-android/assets/Exo2.0-Regular.otf b/MasterPassword/Java/masterpassword-android/assets/Exo2.0-Regular.otf new file mode 100644 index 00000000..4bd82f98 Binary files /dev/null and b/MasterPassword/Java/masterpassword-android/assets/Exo2.0-Regular.otf differ diff --git a/MasterPassword/Java/masterpassword-android/assets/Exo2.0-Thin.otf b/MasterPassword/Java/masterpassword-android/assets/Exo2.0-Thin.otf new file mode 100644 index 00000000..768f03f3 Binary files /dev/null and b/MasterPassword/Java/masterpassword-android/assets/Exo2.0-Thin.otf differ diff --git a/MasterPassword/Java/masterpassword-android/assets/SourceCodePro-Black.otf b/MasterPassword/Java/masterpassword-android/assets/SourceCodePro-Black.otf new file mode 100644 index 00000000..ba47f3db Binary files /dev/null and b/MasterPassword/Java/masterpassword-android/assets/SourceCodePro-Black.otf differ diff --git a/MasterPassword/Java/masterpassword-android/assets/SourceCodePro-ExtraLight.otf b/MasterPassword/Java/masterpassword-android/assets/SourceCodePro-ExtraLight.otf new file mode 100644 index 00000000..5cb8e5fb Binary files /dev/null and b/MasterPassword/Java/masterpassword-android/assets/SourceCodePro-ExtraLight.otf differ diff --git a/MasterPassword/Java/masterpassword-android/pom.xml b/MasterPassword/Java/masterpassword-android/pom.xml index f2bab397..7a784064 100644 --- a/MasterPassword/Java/masterpassword-android/pom.xml +++ b/MasterPassword/Java/masterpassword-android/pom.xml @@ -46,6 +46,12 @@ butterknife + + org.slf4j + slf4j-android + 1.7.7 + + diff --git a/MasterPassword/Java/masterpassword-android/res/drawable/double_.png b/MasterPassword/Java/masterpassword-android/res/drawable/double_.png new file mode 100644 index 00000000..842e9bec Binary files /dev/null and b/MasterPassword/Java/masterpassword-android/res/drawable/double_.png differ diff --git a/MasterPassword/Java/masterpassword-android/res/layout/activity_emergency.xml b/MasterPassword/Java/masterpassword-android/res/layout/activity_emergency.xml new file mode 100644 index 00000000..132a7cc3 --- /dev/null +++ b/MasterPassword/Java/masterpassword-android/res/layout/activity_emergency.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MasterPassword/Java/masterpassword-android/res/layout/type_item.xml b/MasterPassword/Java/masterpassword-android/res/layout/type_item.xml new file mode 100644 index 00000000..d4121535 --- /dev/null +++ b/MasterPassword/Java/masterpassword-android/res/layout/type_item.xml @@ -0,0 +1,5 @@ + + + diff --git a/MasterPassword/Java/masterpassword-android/res/values/strings.xml b/MasterPassword/Java/masterpassword-android/res/values/strings.xml index 3eb0d582..99ce70fa 100644 --- a/MasterPassword/Java/masterpassword-android/res/values/strings.xml +++ b/MasterPassword/Java/masterpassword-android/res/values/strings.xml @@ -2,4 +2,8 @@ Master Password User Avatar + Site Name + Your Name + Your Master Password + diff --git a/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java b/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java new file mode 100644 index 00000000..ed8ab9f3 --- /dev/null +++ b/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/EmergencyActivity.java @@ -0,0 +1,255 @@ +package com.lyndir.masterpassword; + +import android.app.Activity; +import android.content.SharedPreferences; +import android.graphics.Paint; +import android.os.Bundle; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.View; +import android.widget.*; +import butterknife.ButterKnife; +import butterknife.InjectView; +import com.google.common.base.Throwables; +import com.google.common.util.concurrent.*; +import com.lyndir.lhunath.opal.system.logging.Logger; +import java.util.concurrent.*; +import java.util.prefs.Preferences; + + +public class EmergencyActivity extends Activity { + + @SuppressWarnings("UnusedDeclaration") + private static final Logger logger = Logger.get( EmergencyActivity.class ); + + private final ListeningExecutorService executor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() ); + private final ValueChangedListener updateMasterKey = new ValueChangedListener() { + @Override + void update() { + updateMasterKey(); + } + }; + private final ValueChangedListener updateSitePassword = new ValueChangedListener() { + @Override + void update() { + updateSitePassword(); + } + }; + + private ListenableFuture masterKeyFuture; + + @InjectView(R.id.progressView) + ProgressBar progressView; + + @InjectView(R.id.userNameField) + EditText userNameField; + + @InjectView(R.id.masterPasswordField) + EditText masterPasswordField; + + @InjectView(R.id.siteNameField) + EditText siteNameField; + + @InjectView(R.id.typeField) + Spinner typeField; + + @InjectView(R.id.counterField) + NumberPicker counterField; + + @InjectView(R.id.sitePasswordField) + TextView sitePasswordField; + + private int hc_userName; + private int hc_masterPassword; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate( savedInstanceState ); + Res.init( getResources() ); + + setContentView( R.layout.activity_emergency ); + ButterKnife.inject( this ); + + userNameField.setOnFocusChangeListener( updateMasterKey ); + masterPasswordField.setOnFocusChangeListener( updateMasterKey ); + siteNameField.addTextChangedListener( updateSitePassword ); + typeField.setOnItemSelectedListener( updateSitePassword ); + counterField.setOnValueChangedListener( updateSitePassword ); + + userNameField.setTypeface( Res.exo_Thin ); + userNameField.setPaintFlags( userNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG ); + masterPasswordField.setTypeface( Res.sourceCodePro_ExtraLight ); + masterPasswordField.setPaintFlags( userNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG ); + siteNameField.setTypeface( Res.exo_Regular ); + siteNameField.setPaintFlags( userNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG ); + sitePasswordField.setTypeface( Res.sourceCodePro_Black ); + sitePasswordField.setPaintFlags( userNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG ); + + typeField.setAdapter( new ArrayAdapter<>( this, R.layout.type_item, MPElementType.forClass( MPElementTypeClass.Generated ) ) ); + typeField.setSelection( MPElementType.GeneratedLong.ordinal() ); + + counterField.setMinValue( 1 ); + counterField.setMaxValue( Integer.MAX_VALUE ); + counterField.setWrapSelectorWheel( false ); + } + + @Override + protected void onResume() { + super.onResume(); + + userNameField.setText( getPreferences( MODE_PRIVATE ).getString( "userName", "" ) ); + masterPasswordField.requestFocus(); + } + + @Override + protected void onPause() { + synchronized (this) { + hc_userName = hc_masterPassword = 0; + if (masterKeyFuture != null) { + masterKeyFuture.cancel( true ); + masterKeyFuture = null; + } + } + + sitePasswordField.setText( "" ); + progressView.setVisibility( View.INVISIBLE ); + + super.onPause(); + } + + private synchronized void updateMasterKey() { + final String userName = userNameField.getText().toString(); + final String masterPassword = masterPasswordField.getText().toString(); + if (userName.hashCode() == hc_userName && masterPassword.hashCode() == hc_masterPassword) + return; + hc_userName = userName.hashCode(); + hc_masterPassword = masterPassword.hashCode(); + + SharedPreferences.Editor pref = getPreferences( MODE_PRIVATE ).edit(); + pref.putString( "userName", userName ); + pref.commit(); + + if (masterKeyFuture != null) + masterKeyFuture.cancel( true ); + + if (userName.isEmpty() || masterPassword.isEmpty()) { + sitePasswordField.setText( "" ); + progressView.setVisibility( View.INVISIBLE ); + return; + } + + progressView.setVisibility( View.VISIBLE ); + (masterKeyFuture = executor.submit( new Callable() { + @Override + public byte[] call() + throws Exception { + try { + return MasterPassword.keyForPassword( masterPassword, userName ); + } + catch (RuntimeException e) { + sitePasswordField.setText( "" ); + progressView.setVisibility( View.INVISIBLE ); + logger.err( e, "While generating master key." ); + throw e; + } + } + } )).addListener( new Runnable() { + @Override + public void run() { + runOnUiThread( new Runnable() { + @Override + public void run() { + updateSitePassword(); + } + } ); + } + }, executor ); + } + + private void updateSitePassword() { + final String siteName = siteNameField.getText().toString(); + final MPElementType type = (MPElementType) typeField.getSelectedItem(); + final int counter = counterField.getValue(); + + if (masterKeyFuture == null || siteName.isEmpty() || type == null) { + sitePasswordField.setText( "" ); + progressView.setVisibility( View.INVISIBLE ); + return; + } + + progressView.setVisibility( View.VISIBLE ); + executor.submit( new Runnable() { + @Override + public void run() { + try { + final String sitePassword = MasterPassword.generateContent( type, siteName, masterKeyFuture.get(), counter ); + + runOnUiThread( new Runnable() { + @Override + public void run() { + sitePasswordField.setText( sitePassword ); + progressView.setVisibility( View.INVISIBLE ); + } + } ); + } + catch (InterruptedException ignored) { + sitePasswordField.setText( "" ); + progressView.setVisibility( View.INVISIBLE ); + } + catch (ExecutionException e) { + sitePasswordField.setText( "" ); + progressView.setVisibility( View.INVISIBLE ); + logger.err( e, "While generating site password." ); + throw Throwables.propagate( e ); + } + catch (RuntimeException e) { + sitePasswordField.setText( "" ); + progressView.setVisibility( View.INVISIBLE ); + logger.err( e, "While generating site password." ); + throw e; + } + } + } ); + } + + private abstract class ValueChangedListener + implements TextWatcher, NumberPicker.OnValueChangeListener, AdapterView.OnItemSelectedListener, View.OnFocusChangeListener { + + abstract void update(); + + @Override + public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) { + } + + @Override + public void onTextChanged(final CharSequence s, final int start, final int before, final int count) { + } + + @Override + public void afterTextChanged(final Editable s) { + update(); + } + + @Override + public void onValueChange(final NumberPicker picker, final int oldVal, final int newVal) { + update(); + } + + @Override + public void onItemSelected(final AdapterView parent, final View view, final int position, final long id) { + update(); + } + + @Override + public void onNothingSelected(final AdapterView parent) { + update(); + } + + @Override + public void onFocusChange(final View v, final boolean hasFocus) { + if (!hasFocus) + update(); + } + } +} + diff --git a/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/Res.java b/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/Res.java new file mode 100644 index 00000000..0f8824e7 --- /dev/null +++ b/MasterPassword/Java/masterpassword-android/src/main/java/com/lyndir/masterpassword/Res.java @@ -0,0 +1,35 @@ +package com.lyndir.masterpassword; + +import android.content.res.Resources; +import android.graphics.Paint; +import android.graphics.Typeface; + + +/** + * @author lhunath, 2014-08-25 + */ +public class Res { + + public static Typeface sourceCodePro_Black; + public static Typeface sourceCodePro_ExtraLight; + public static Typeface exo_Bold; + public static Typeface exo_ExtraBold; + public static Typeface exo_Regular; + public static Typeface exo_Thin; + + private static boolean initialized; + + public static void init(Resources resources) { + + if (initialized) + return; + initialized = true; + + sourceCodePro_Black = Typeface.createFromAsset( resources.getAssets(), "SourceCodePro-Black.otf" ); + sourceCodePro_ExtraLight = Typeface.createFromAsset( resources.getAssets(), "SourceCodePro-ExtraLight.otf" ); + exo_Bold = Typeface.createFromAsset( resources.getAssets(), "Exo2.0-Bold.otf" ); + exo_ExtraBold = Typeface.createFromAsset( resources.getAssets(), "Exo2.0-ExtraBold.otf" ); + exo_Regular = Typeface.createFromAsset( resources.getAssets(), "Exo2.0-Regular.otf" ); + exo_Thin = Typeface.createFromAsset( resources.getAssets(), "Exo2.0-Thin.otf" ); + } +}