2
0

Android improvements.

[UPDATED]   Opal API
[ADDED]     Scrypt native binaries for more archs.
[IMPROVED]  Android activity secure.
[FIXED]     White background on buttons for some devices.
[IMPROVED]  Android layout.
[WIP]       Remember password on Android.
This commit is contained in:
Maarten Billemont 2015-01-31 10:55:08 -05:00
parent ca5d83d40c
commit a6e7a749bf
7 changed files with 93 additions and 74 deletions

View File

@ -24,12 +24,12 @@
<dependency> <dependency>
<groupId>com.lyndir.lhunath.opal</groupId> <groupId>com.lyndir.lhunath.opal</groupId>
<artifactId>opal-system</artifactId> <artifactId>opal-system</artifactId>
<version>1.6-p6</version> <version>1.6-p7</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.lyndir.lhunath.opal</groupId> <groupId>com.lyndir.lhunath.opal</groupId>
<artifactId>opal-crypto</artifactId> <artifactId>opal-crypto</artifactId>
<version>1.6-p6</version> <version>1.6-p7</version>
</dependency> </dependency>
<!-- EXTERNAL DEPENDENCIES --> <!-- EXTERNAL DEPENDENCIES -->

View File

@ -77,15 +77,6 @@ public class MasterKey {
return idForBytes( masterKey ); return idForBytes( masterKey );
} }
private byte[] getSubKey(final int subkeyLength) {
Preconditions.checkState( valid );
byte[] subkey = new byte[Math.min( subkeyLength, masterKey.length )];
System.arraycopy( masterKey, 0, subkey, 0, subkey.length );
return subkey;
}
public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant, public String encode(final String siteName, final MPSiteType siteType, int siteCounter, final MPSiteVariant siteVariant,
@Nullable final String siteContext) { @Nullable final String siteContext) {
Preconditions.checkState( valid ); Preconditions.checkState( valid );

View File

@ -114,11 +114,10 @@
<dependency> <dependency>
<groupId>com.lambdaworks</groupId> <groupId>com.lambdaworks</groupId>
<artifactId>libscrypt</artifactId> <artifactId>scrypt</artifactId>
<version>1.4.0</version> <version>1.4.0-android</version>
<type>so</type> <type>jar</type>
<classifier>android</classifier> <classifier>native</classifier>
<scope>runtime</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View File

@ -12,64 +12,61 @@
android:gravity="center"> android:gravity="center">
<View <View
android:layout_width="0dp" android:layout_width="1dp"
android:layout_height="0dp" android:layout_height="0dp"
android:layout_weight="1" /> android:layout_weight="1" />
<ProgressBar <ProgressBar
android:id="@+id/progressView" android:id="@+id/progressView"
android:layout_width="wrap_content" android:layout_width="300dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="20dp"
android:indeterminate="true" /> android:indeterminate="true" />
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<EditText <EditText
android:id="@+id/userNameField" android:id="@+id/userNameField"
android:layout_width="wrap_content" android:layout_width="300dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="text|textCapWords|textPersonName" android:inputType="text|textCapWords|textPersonName"
android:hint="@string/userName.hint" android:hint="@string/userName_hint"
android:gravity="center" android:gravity="center"
android:textColor="#FFFFFF" android:textColor="#FFFFFF"
android:textSize="26sp" /> android:textSize="26sp" />
<EditText <EditText
android:id="@+id/masterPasswordField" android:id="@+id/masterPasswordField"
android:layout_width="wrap_content" android:layout_width="300dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="text|textPassword" android:inputType="text|textPassword"
android:hint="@string/masterPassword.hint" android:hint="@string/masterPassword_hint"
android:password="true"
android:gravity="center" android:gravity="center"
android:textColor="#FFFFFF" android:textColor="#FFFFFF"
android:textSize="18sp" /> android:textSize="18sp" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/double_"
android:contentDescription="@string/empty" />
<EditText <EditText
android:id="@+id/siteNameField" android:id="@+id/siteNameField"
android:layout_width="wrap_content" android:layout_width="300dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="text|textNoSuggestions|textUri" android:inputType="text|textNoSuggestions|textUri"
android:hint="@string/siteName.hint" android:hint="@string/siteName_hint"
android:gravity="center" android:gravity="center"
android:textColor="#FFFFFF" android:textColor="#FFFFFF"
android:textSize="26sp" /> android:textSize="26sp" />
<Button <ImageView
android:layout_width="300dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginBottom="30dp"
android:src="@drawable/double_"
android:contentDescription="@string/empty" />
<TextView
android:id="@+id/sitePasswordField" android:id="@+id/sitePasswordField"
android:layout_width="wrap_content" android:layout_width="300dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center" android:gravity="center"
android:background="@null" android:background="@android:color/transparent"
android:textColor="#FFFFFF" android:textColor="#FFFFFF"
android:textSize="32sp" android:textSize="32sp"
android:text="LuxdZozvDuma4[" android:text="LuxdZozvDuma4["
@ -77,13 +74,30 @@
<Spinner <Spinner
android:id="@+id/typeField" android:id="@+id/typeField"
android:layout_width="wrap_content" android:layout_width="300dp"
android:layout_height="wrap_content" /> android:layout_height="wrap_content"
android:gravity="center" />
<NumberPicker <EditText
android:id="@+id/counterField" android:id="@+id/counterField"
android:layout_width="wrap_content" android:layout_width="300dp"
android:layout_height="wrap_content" /> android:layout_height="wrap_content"
android:gravity="center"
android:inputType="text|textNoSuggestions"
android:textColor="#FFFFFF"
android:textSize="26sp"
android:text="1" />
<View
android:layout_width="1dp"
android:layout_height="0dp"
android:layout_weight="1" />
<CheckBox
android:id="@+id/rememberPasswordField"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:text="@string/remember" />
</LinearLayout> </LinearLayout>

View File

@ -2,8 +2,9 @@
<resources> <resources>
<string name="app_name">Master Password</string> <string name="app_name">Master Password</string>
<string name="avatar">User Avatar</string> <string name="avatar">User Avatar</string>
<string name="siteName.hint">Site Name</string> <string name="remember">Remember Password</string>
<string name="userName.hint">Your Name</string> <string name="siteName_hint">Site Name</string>
<string name="masterPassword.hint">Your Master Password</string> <string name="userName_hint">Your Name</string>
<string name="masterPassword_hint">Your Master Password</string>
<string name="empty" /> <string name="empty" />
</resources> </resources>

View File

@ -9,12 +9,14 @@ import android.os.Bundle;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.view.View; import android.view.View;
import android.view.WindowManager;
import android.widget.*; import android.widget.*;
import butterknife.ButterKnife; import butterknife.ButterKnife;
import butterknife.InjectView; import butterknife.InjectView;
import com.google.common.base.Throwables; import com.google.common.base.Throwables;
import com.google.common.util.concurrent.*; import com.google.common.util.concurrent.*;
import com.lyndir.lhunath.opal.system.logging.Logger; import com.lyndir.lhunath.opal.system.logging.Logger;
import com.lyndir.lhunath.opal.system.util.ConversionUtils;
import java.util.concurrent.*; import java.util.concurrent.*;
@ -55,11 +57,14 @@ public class EmergencyActivity extends Activity {
Spinner typeField; Spinner typeField;
@InjectView(R.id.counterField) @InjectView(R.id.counterField)
NumberPicker counterField; EditText counterField;
@InjectView(R.id.sitePasswordField) @InjectView(R.id.sitePasswordField)
TextView sitePasswordField; TextView sitePasswordField;
@InjectView(R.id.rememberPasswordField)
CheckBox rememberPasswordField;
private int hc_userName; private int hc_userName;
private int hc_masterPassword; private int hc_masterPassword;
@ -68,6 +73,7 @@ public class EmergencyActivity extends Activity {
super.onCreate( savedInstanceState ); super.onCreate( savedInstanceState );
Res.init( getResources() ); Res.init( getResources() );
getWindow().setFlags( WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE );
setContentView( R.layout.activity_emergency ); setContentView( R.layout.activity_emergency );
ButterKnife.inject( this ); ButterKnife.inject( this );
@ -75,23 +81,26 @@ public class EmergencyActivity extends Activity {
masterPasswordField.setOnFocusChangeListener( updateMasterKey ); masterPasswordField.setOnFocusChangeListener( updateMasterKey );
siteNameField.addTextChangedListener( updateSitePassword ); siteNameField.addTextChangedListener( updateSitePassword );
typeField.setOnItemSelectedListener( updateSitePassword ); typeField.setOnItemSelectedListener( updateSitePassword );
counterField.setOnValueChangedListener( updateSitePassword ); counterField.addTextChangedListener( updateSitePassword );
userNameField.setTypeface( Res.exo_Thin ); userNameField.setTypeface( Res.exo_Thin );
userNameField.setPaintFlags( userNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG ); userNameField.setPaintFlags( userNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
masterPasswordField.setTypeface( Res.sourceCodePro_ExtraLight ); masterPasswordField.setTypeface( Res.sourceCodePro_ExtraLight );
masterPasswordField.setPaintFlags( userNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG ); masterPasswordField.setPaintFlags( masterPasswordField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
siteNameField.setTypeface( Res.exo_Regular ); siteNameField.setTypeface( Res.exo_Regular );
siteNameField.setPaintFlags( userNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG ); siteNameField.setPaintFlags( siteNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
sitePasswordField.setTypeface( Res.sourceCodePro_Black ); sitePasswordField.setTypeface( Res.sourceCodePro_Black );
sitePasswordField.setPaintFlags( userNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG ); sitePasswordField.setPaintFlags( sitePasswordField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
typeField.setAdapter( new ArrayAdapter<>( this, R.layout.type_item, MPSiteType.forClass( MPSiteTypeClass.Generated ) ) ); typeField.setAdapter( new ArrayAdapter<>( this, R.layout.type_item, MPSiteType.forClass( MPSiteTypeClass.Generated ) ) );
typeField.setSelection( MPSiteType.GeneratedLong.ordinal() ); typeField.setSelection( MPSiteType.GeneratedLong.ordinal() );
counterField.setMinValue( 1 ); rememberPasswordField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
counterField.setMaxValue( Integer.MAX_VALUE ); @Override
counterField.setWrapSelectorWheel( false ); public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
getPreferences( MODE_PRIVATE ).edit().putBoolean( "rememberPassword", isChecked ).apply();
}
} );
} }
@Override @Override
@ -99,25 +108,32 @@ public class EmergencyActivity extends Activity {
super.onResume(); super.onResume();
userNameField.setText( getPreferences( MODE_PRIVATE ).getString( "userName", "" ) ); userNameField.setText( getPreferences( MODE_PRIVATE ).getString( "userName", "" ) );
rememberPasswordField.setSelected( isRememberPasswordEnabled() );
masterPasswordField.requestFocus(); masterPasswordField.requestFocus();
} }
@Override @Override
protected void onPause() { protected void onPause() {
synchronized (this) { if (!isRememberPasswordEnabled()) {
hc_userName = hc_masterPassword = 0; synchronized (this) {
if (masterKeyFuture != null) { hc_userName = hc_masterPassword = 0;
masterKeyFuture.cancel( true ); if (masterKeyFuture != null) {
masterKeyFuture = null; masterKeyFuture.cancel( true );
masterKeyFuture = null;
}
sitePasswordField.setText( "" );
progressView.setVisibility( View.INVISIBLE );
} }
} }
sitePasswordField.setText( "" );
progressView.setVisibility( View.INVISIBLE );
super.onPause(); super.onPause();
} }
private boolean isRememberPasswordEnabled() {
return getPreferences( MODE_PRIVATE ).getBoolean( "rememberPassword", false );
}
private synchronized void updateMasterKey() { private synchronized void updateMasterKey() {
final String userName = userNameField.getText().toString(); final String userName = userNameField.getText().toString();
final String masterPassword = masterPasswordField.getText().toString(); final String masterPassword = masterPasswordField.getText().toString();
@ -126,9 +142,7 @@ public class EmergencyActivity extends Activity {
hc_userName = userName.hashCode(); hc_userName = userName.hashCode();
hc_masterPassword = masterPassword.hashCode(); hc_masterPassword = masterPassword.hashCode();
SharedPreferences.Editor pref = getPreferences( MODE_PRIVATE ).edit(); getPreferences( MODE_PRIVATE ).edit().putString( "userName", userName ).apply();
pref.putString( "userName", userName );
pref.apply();
if (masterKeyFuture != null) if (masterKeyFuture != null)
masterKeyFuture.cancel( true ); masterKeyFuture.cancel( true );
@ -170,7 +184,7 @@ public class EmergencyActivity extends Activity {
private void updateSitePassword() { private void updateSitePassword() {
final String siteName = siteNameField.getText().toString(); final String siteName = siteNameField.getText().toString();
final MPSiteType type = (MPSiteType) typeField.getSelectedItem(); final MPSiteType type = (MPSiteType) typeField.getSelectedItem();
final int counter = counterField.getValue(); final int counter = ConversionUtils.toIntegerNN( counterField.getText() );
if (masterKeyFuture == null || siteName.isEmpty() || type == null) { if (masterKeyFuture == null || siteName.isEmpty() || type == null) {
sitePasswordField.setText( "" ); sitePasswordField.setText( "" );
@ -220,13 +234,13 @@ public class EmergencyActivity extends Activity {
ClipDescription description = new ClipDescription( strf( "Password for %s", siteNameField.getText() ), ClipDescription description = new ClipDescription( strf( "Password for %s", siteNameField.getText() ),
new String[]{ ClipDescription.MIMETYPE_TEXT_PLAIN } ); new String[]{ ClipDescription.MIMETYPE_TEXT_PLAIN } );
((ClipboardManager) getSystemService( CLIPBOARD_SERVICE )).setPrimaryClip( ClipData clipData = new ClipData( description, new ClipData.Item( sitePassword ) );
new ClipData( description, new ClipData.Item( sitePassword ) ) ); ((ClipboardManager) getSystemService( CLIPBOARD_SERVICE )).setPrimaryClip( clipData );
Intent startMain = new Intent(Intent.ACTION_MAIN); Intent startMain = new Intent( Intent.ACTION_MAIN );
startMain.addCategory(Intent.CATEGORY_HOME); startMain.addCategory( Intent.CATEGORY_HOME );
startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startMain.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK );
startActivity(startMain); startActivity( startMain );
} }
private abstract class ValueChangedListener private abstract class ValueChangedListener

View File

@ -7,7 +7,7 @@
<parent> <parent>
<groupId>com.lyndir.lhunath</groupId> <groupId>com.lyndir.lhunath</groupId>
<artifactId>lyndir</artifactId> <artifactId>lyndir</artifactId>
<version>1.18</version> <version>1.20</version>
</parent> </parent>
<name>Master Password</name> <name>Master Password</name>