2
0

Finish up Android UI improvements.

This commit is contained in:
Maarten Billemont 2016-02-20 21:27:59 -05:00
parent 060ec0b5cd
commit b346b3be65
5 changed files with 143 additions and 69 deletions

View File

@ -16,12 +16,12 @@ import org.jetbrains.annotations.Contract;
*/
public enum MPSiteType {
GeneratedMaximum( "20 characters, contains symbols.", //
GeneratedMaximum( "Max", "20 characters, contains symbols.", //
ImmutableList.of( "x", "max", "maximum" ), //
ImmutableList.of( new MPTemplate( "anoxxxxxxxxxxxxxxxxx" ), new MPTemplate( "axxxxxxxxxxxxxxxxxno" ) ), //
MPSiteTypeClass.Generated, 0x0 ),
GeneratedLong( "Copy-friendly, 14 characters, contains symbols.", //
GeneratedLong( "Long", "Copy-friendly, 14 characters, contains symbols.", //
ImmutableList.of( "l", "long" ), //
ImmutableList.of( new MPTemplate( "CvcvnoCvcvCvcv" ), new MPTemplate( "CvcvCvcvnoCvcv" ),
new MPTemplate( "CvcvCvcvCvcvno" ), new MPTemplate( "CvccnoCvcvCvcv" ),
@ -36,49 +36,50 @@ public enum MPSiteType {
new MPTemplate( "CvccCvcvCvccno" ) ), //
MPSiteTypeClass.Generated, 0x1 ),
GeneratedMedium( "Copy-friendly, 8 characters, contains symbols.", //
GeneratedMedium( "Medium", "Copy-friendly, 8 characters, contains symbols.", //
ImmutableList.of( "m", "med", "medium" ), //
ImmutableList.of( new MPTemplate( "CvcnoCvc" ), new MPTemplate( "CvcCvcno" ) ), //
MPSiteTypeClass.Generated, 0x2 ),
GeneratedBasic( "8 characters, no symbols.", //
GeneratedBasic( "Basic", "8 characters, no symbols.", //
ImmutableList.of( "b", "basic" ), //
ImmutableList.of( new MPTemplate( "aaanaaan" ), new MPTemplate( "aannaaan" ), new MPTemplate( "aaannaaa" ) ), //
MPSiteTypeClass.Generated, 0x3 ),
GeneratedShort( "Copy-friendly, 4 characters, no symbols.", //
GeneratedShort( "Short", "Copy-friendly, 4 characters, no symbols.", //
ImmutableList.of( "s", "short" ), //
ImmutableList.of( new MPTemplate( "Cvcn" ) ), //
MPSiteTypeClass.Generated, 0x4 ),
GeneratedPIN( "4 numbers.", //
GeneratedPIN( "PIN", "4 numbers.", //
ImmutableList.of( "i", "pin" ), //
ImmutableList.of( new MPTemplate( "nnnn" ) ), //
MPSiteTypeClass.Generated, 0x5 ),
GeneratedName( "9 letter name.", //
GeneratedName( "Name", "9 letter name.", //
ImmutableList.of( "n", "name" ), //
ImmutableList.of( new MPTemplate( "cvccvcvcv" ) ), //
MPSiteTypeClass.Generated, 0xE ),
GeneratedPhrase( "20 character sentence.", //
GeneratedPhrase( "Phrase", "20 character sentence.", //
ImmutableList.of( "p", "phrase" ), //
ImmutableList.of( new MPTemplate( "cvcc cvc cvccvcv cvc" ), new MPTemplate( "cvc cvccvcvcv cvcv" ),
new MPTemplate( "cv cvccv cvc cvcvccv" ) ), //
MPSiteTypeClass.Generated, 0xF ),
StoredPersonal( "AES-encrypted, exportable.", //
StoredPersonal( "Personal", "AES-encrypted, exportable.", //
ImmutableList.of( "personal" ), //
ImmutableList.<MPTemplate>of(), //
MPSiteTypeClass.Stored, 0x0, MPSiteFeature.ExportContent ),
StoredDevicePrivate( "AES-encrypted, not exported.", //
StoredDevicePrivate( "Device", "AES-encrypted, not exported.", //
ImmutableList.of( "device" ), //
ImmutableList.<MPTemplate>of(), //
MPSiteTypeClass.Stored, 0x1, MPSiteFeature.DevicePrivate );
static final Logger logger = Logger.get( MPSiteType.class );
private final String shortName;
private final String description;
private final List<String> options;
private final List<MPTemplate> templates;
@ -86,9 +87,10 @@ public enum MPSiteType {
private final int typeIndex;
private final Set<MPSiteFeature> typeFeatures;
MPSiteType(final String description, final List<String> options, final List<MPTemplate> templates, final MPSiteTypeClass typeClass,
final int typeIndex, final MPSiteFeature... typeFeatures) {
MPSiteType(final String shortName, final String description, final List<String> options, final List<MPTemplate> templates,
final MPSiteTypeClass typeClass, final int typeIndex, final MPSiteFeature... typeFeatures) {
this.shortName = shortName;
this.description = description;
this.options = options;
this.templates = templates;
@ -102,6 +104,10 @@ public enum MPSiteType {
this.typeFeatures = typeFeaturesBuilder.build();
}
public String getShortName() {
return shortName;
}
public String getDescription() {
return description;

View File

@ -11,7 +11,8 @@
<application
android:icon="@drawable/icon"
android:label="@string/app_name"
android:allowBackup="true">
android:allowBackup="true"
android:debuggable="true">
<activity android:name=".EmergencyActivity" android:theme="@style/MPTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View File

@ -92,7 +92,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true" />
android:visibility="invisible"
android:indeterminate="true"
tools:visibility="visible" />
<LinearLayout
android:layout_width="match_parent"
@ -104,7 +106,7 @@
android:id="@id/sitePasswordField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nextFocusForward="@+id/siteTypeField"
android:nextFocusForward="@+id/siteTypeButton"
android:gravity="center"
android:background="@android:color/transparent"
android:textColor="#FFFFFF"
@ -156,7 +158,7 @@
android:gravity="center">
<Button
android:id="@id/siteTypeField"
android:id="@id/siteTypeButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
@ -174,7 +176,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@id/siteTypeField"
android:labelFor="@id/siteTypeButton"
android:gravity="center"
android:background="@android:color/transparent"
android:textSize="12sp"
@ -196,7 +198,7 @@
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
style="?android:buttonBarButtonStyle"
android:nextFocusForward="@+id/siteVersionField"
android:nextFocusForward="@+id/siteVersionButton"
android:gravity="center"
android:textColor="#FFFFFF"
android:textSize="16sp"
@ -208,7 +210,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@id/siteVersionField"
android:labelFor="@id/siteVersionButton"
android:gravity="center"
android:background="@android:color/transparent"
android:textSize="12sp"
@ -224,7 +226,7 @@
android:gravity="center">
<Button
android:id="@id/siteVersionField"
android:id="@id/siteVersionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
@ -242,7 +244,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@id/siteVersionField"
android:labelFor="@id/siteVersionButton"
android:gravity="center"
android:background="@android:color/transparent"
android:textSize="12sp"

View File

@ -1,6 +1,5 @@
package com.lyndir.masterpassword;
import static com.lyndir.lhunath.opal.system.util.ObjectUtils.ifNotNullElse;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import android.app.*;
@ -17,9 +16,11 @@ import android.widget.*;
import butterknife.ButterKnife;
import butterknife.InjectView;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableList;
import com.google.common.primitives.UnsignedInteger;
import com.google.common.util.concurrent.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.text.MessageFormat;
import java.util.*;
import java.util.concurrent.*;
import javax.annotation.Nullable;
@ -34,18 +35,8 @@ public class EmergencyActivity extends Activity {
private final Preferences preferences = Preferences.get( this );
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 final ImmutableList<MPSiteType> allSiteTypes = ImmutableList.copyOf( MPSiteType.forClass( MPSiteTypeClass.Generated ) );
private final ImmutableList<MasterKey.Version> allVersions = ImmutableList.copyOf( MasterKey.Version.values() );
private ListenableFuture<MasterKey> masterKeyFuture;
@ -61,14 +52,14 @@ public class EmergencyActivity extends Activity {
@InjectView(R.id.siteNameField)
EditText siteNameField;
@InjectView(R.id.siteTypeField)
Button siteTypeField;
@InjectView(R.id.siteTypeButton)
Button siteTypeButton;
@InjectView(R.id.counterField)
Button counterField;
Button siteCounterButton;
@InjectView(R.id.siteVersionField)
Button siteVersionField;
@InjectView(R.id.siteVersionButton)
Button siteVersionButton;
@InjectView(R.id.sitePasswordField)
Button sitePasswordField;
@ -102,12 +93,58 @@ public class EmergencyActivity extends Activity {
setContentView( R.layout.activity_emergency );
ButterKnife.inject( this );
fullNameField.setOnFocusChangeListener( updateMasterKey );
masterPasswordField.setOnFocusChangeListener( updateMasterKey );
siteNameField.addTextChangedListener( updateSitePassword );
// siteTypeField.setOnItemSelectedListener( updateSitePassword );
counterField.addTextChangedListener( updateSitePassword );
// siteVersionField.setOnItemSelectedListener( updateMasterKey );
fullNameField.setOnFocusChangeListener( new ValueChangedListener() {
@Override
void update() {
updateMasterKey();
}
} );
masterPasswordField.setOnFocusChangeListener( new ValueChangedListener() {
@Override
void update() {
updateMasterKey();
}
} );
siteNameField.addTextChangedListener( new ValueChangedListener() {
@Override
void update() {
siteCounterButton.setText( MessageFormat.format( "{0}", 1 ) );
updateSitePassword();
}
} );
siteTypeButton.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(final View v) {
@SuppressWarnings("SuspiciousMethodCalls")
MPSiteType siteType =
allSiteTypes.get( (allSiteTypes.indexOf( siteTypeButton.getTag() ) + 1) % allSiteTypes.size() );
preferences.setDefaultSiteType( siteType );
siteTypeButton.setTag( siteType );
siteTypeButton.setText( siteType.getShortName() );
updateSitePassword();
}
} );
siteCounterButton.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(final View v) {
UnsignedInteger counter =
UnsignedInteger.valueOf( siteCounterButton.getText().toString() ).plus( UnsignedInteger.ONE );
siteCounterButton.setText( MessageFormat.format( "{0}", counter ) );
updateSitePassword();
}
} );
siteVersionButton.setOnClickListener( new View.OnClickListener() {
@Override
public void onClick(final View v) {
@SuppressWarnings("SuspiciousMethodCalls")
MasterKey.Version siteVersion =
allVersions.get( (allVersions.indexOf( siteVersionButton.getTag() ) + 1) % allVersions.size() );
preferences.setDefaultVersion( siteVersion );
siteVersionButton.setTag( siteVersion );
siteVersionButton.setText( siteVersion.name() );
updateMasterKey();
}
} );
sitePasswordField.addTextChangedListener( new ValueChangedListener() {
@Override
void update() {
@ -128,12 +165,6 @@ public class EmergencyActivity extends Activity {
sitePasswordField.setTypeface( Res.sourceCodePro_Black );
sitePasswordField.setPaintFlags( sitePasswordField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
// siteTypeField.setAdapter( new ArrayAdapter<>( this, R.layout.spinner_item, MPSiteType.forClass( MPSiteTypeClass.Generated ) ) );
// siteTypeField.setSelection( MPSiteType.GeneratedLong.ordinal() );
// siteVersionField.setAdapter( new ArrayAdapter<>( this, R.layout.spinner_item, MasterKey.Version.values() ) );
// siteVersionField.setSelection( MasterKey.Version.CURRENT.ordinal() );
rememberFullNameField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
@ -170,8 +201,17 @@ public class EmergencyActivity extends Activity {
forgetPasswordField.setChecked( preferences.isForgetPassword() );
maskPasswordField.setChecked( preferences.isMaskPassword() );
sitePasswordField.setTransformationMethod( preferences.isMaskPassword()? new PasswordTransformationMethod(): null );
MPSiteType defaultSiteType = preferences.getDefaultSiteType();
siteTypeButton.setTag( defaultSiteType );
siteTypeButton.setText( defaultSiteType.getShortName() );
MasterKey.Version defaultVersion = preferences.getDefaultVersion();
siteVersionButton.setTag( defaultVersion );
siteVersionButton.setText( defaultVersion.name() );
siteCounterButton.setText( MessageFormat.format( "{0}", 1 ) );
if (TextUtils.isEmpty( masterPasswordField.getText() ))
if (TextUtils.isEmpty( fullNameField.getText() ))
fullNameField.requestFocus();
else if (TextUtils.isEmpty( masterPasswordField.getText() ))
masterPasswordField.requestFocus();
else
siteNameField.requestFocus();
@ -201,7 +241,7 @@ public class EmergencyActivity extends Activity {
private synchronized void updateMasterKey() {
final String fullName = fullNameField.getText().toString();
final char[] masterPassword = masterPasswordField.getText().toString().toCharArray();
final MasterKey.Version version = MasterKey.Version.CURRENT;//( MasterKey.Version) siteVersionField.getSelectedItem();
final MasterKey.Version version = (MasterKey.Version) siteVersionButton.getTag();
try {
if (fullName.hashCode() == hc_userName && Arrays.hashCode( masterPassword ) == hc_masterPassword &&
masterKeyFuture != null && masterKeyFuture.get().getAlgorithmVersion() == version)
@ -256,10 +296,8 @@ public class EmergencyActivity extends Activity {
private void updateSitePassword() {
final String siteName = siteNameField.getText().toString();
final MPSiteType type = MPSiteType.GeneratedLong;//(MPSiteType) siteTypeField.getSelectedItem();
CharSequence counterText = counterField.getText();
final UnsignedInteger counter =
TextUtils.isEmpty( counterText )? UnsignedInteger.valueOf( 1L ): UnsignedInteger.valueOf( counterText.toString() );
final MPSiteType type = (MPSiteType) siteTypeButton.getTag();
final UnsignedInteger counter = UnsignedInteger.valueOf( siteCounterButton.getText().toString() );
if (masterKeyFuture == null || siteName.isEmpty() || type == null) {
sitePasswordField.setText( "" );

View File

@ -20,9 +20,10 @@ public class Preferences {
private static final String PREF_FORGET_PASSWORD = "forgetPassword";
private static final String PREF_MASK_PASSWORD = "maskPassword";
private static final String PREF_FULL_NAME = "fullName";
private static final String PREF_SITE_TYPE = "siteType";
private static Preferences instance;
private final Context context;
private Context context;
@Nullable
private SharedPreferences prefs;
@ -34,13 +35,13 @@ public class Preferences {
}
private Preferences(Context context) {
this.context = context.getApplicationContext();
this.context = context;
}
@Nonnull
private SharedPreferences prefs() {
if (prefs == null)
prefs = context.getSharedPreferences( getClass().getCanonicalName(), Context.MODE_PRIVATE );
prefs = (context = context.getApplicationContext()).getSharedPreferences( getClass().getCanonicalName(), Context.MODE_PRIVATE );
return prefs;
}
@ -57,11 +58,11 @@ public class Preferences {
return prefs().getBoolean( PREF_NATIVE_KDF, MasterKey.isAllowNativeByDefault() );
}
public boolean setTestsPassed(final Set<String> testsPassed) {
if (Sets.symmetricDifference( getTestsPassed(), testsPassed ).isEmpty())
public boolean setTestsPassed(final Set<String> value) {
if (Sets.symmetricDifference( getTestsPassed(), value ).isEmpty())
return false;
prefs().edit().putStringSet( PREF_TESTS_PASSED, testsPassed ).apply();
prefs().edit().putStringSet( PREF_TESTS_PASSED, value ).apply();
return true;
}
@ -105,11 +106,11 @@ public class Preferences {
return prefs().getBoolean( PREF_MASK_PASSWORD, false );
}
public boolean setFullName(@Nullable String fullName) {
if (getFullName().equals( fullName ))
public boolean setFullName(@Nullable String value) {
if (getFullName().equals( value ))
return false;
prefs().edit().putString( PREF_FULL_NAME, fullName ).apply();
prefs().edit().putString( PREF_FULL_NAME, value ).apply();
return true;
}
@ -117,4 +118,30 @@ public class Preferences {
public String getFullName() {
return prefs().getString( PREF_FULL_NAME, "" );
}
public boolean setDefaultSiteType(@Nonnull MPSiteType value) {
if (getDefaultSiteType().equals( value ))
return false;
prefs().edit().putInt( PREF_SITE_TYPE, value.ordinal() ).apply();
return true;
}
@Nonnull
public MPSiteType getDefaultSiteType() {
return MPSiteType.values()[prefs().getInt( PREF_SITE_TYPE, MPSiteType.GeneratedLong.ordinal() )];
}
public boolean setDefaultVersion(@Nonnull MasterKey.Version value) {
if (getDefaultVersion().equals( value ))
return false;
prefs().edit().putInt( PREF_SITE_TYPE, value.ordinal() ).apply();
return true;
}
@Nonnull
public MasterKey.Version getDefaultVersion() {
return MasterKey.Version.values()[prefs().getInt( PREF_SITE_TYPE, MasterKey.Version.CURRENT.ordinal() )];
}
}