2
0

Re-organize the project into a better hierarchy.

This commit is contained in:
Maarten Billemont
2017-03-06 13:37:05 -05:00
parent 67e18895ab
commit c6b285a9c0
1452 changed files with 43 additions and 350 deletions

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.lyndir.masterpassword">
<application
android:allowBackup="true"
android:icon="@drawable/icon"
android:label="@string/app_name">
<activity
android:name=".EmergencyActivity"
android:theme="@style/MPTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".TestActivity"
android:theme="@style/MPTheme" />
</application>
</manifest>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,435 @@
package com.lyndir.masterpassword;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import android.app.*;
import android.content.*;
import android.content.ClipboardManager;
import android.graphics.Paint;
import android.os.Build;
import android.os.Bundle;
import android.text.*;
import android.text.method.PasswordTransformationMethod;
import android.view.View;
import android.view.WindowManager;
import android.widget.*;
import butterknife.BindView;
import butterknife.ButterKnife;
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;
public class EmergencyActivity extends Activity {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( EmergencyActivity.class );
private static final ClipData EMPTY_CLIP = new ClipData( new ClipDescription( "", new String[0] ), new ClipData.Item( "" ) );
private static final int PASSWORD_NOTIFICATION = 0;
private final Preferences preferences = Preferences.get( this );
private final ListeningExecutorService executor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() );
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;
@BindView(R.id.progressView)
ProgressBar progressView;
@BindView(R.id.fullNameField)
EditText fullNameField;
@BindView(R.id.masterPasswordField)
EditText masterPasswordField;
@BindView(R.id.siteNameField)
EditText siteNameField;
@BindView(R.id.siteTypeButton)
Button siteTypeButton;
@BindView(R.id.counterField)
Button siteCounterButton;
@BindView(R.id.siteVersionButton)
Button siteVersionButton;
@BindView(R.id.sitePasswordField)
Button sitePasswordField;
@BindView(R.id.sitePasswordTip)
TextView sitePasswordTip;
@BindView(R.id.rememberFullNameField)
CheckBox rememberFullNameField;
@BindView(R.id.rememberPasswordField)
CheckBox forgetPasswordField;
@BindView(R.id.maskPasswordField)
CheckBox maskPasswordField;
private int id_userName;
private int id_masterPassword;
private int id_version;
private String sitePassword;
public static void start(Context context) {
context.startActivity( new Intent( context, EmergencyActivity.class ) );
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
Res.init( getResources() );
getWindow().setFlags( WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE );
setContentView( R.layout.activity_emergency );
ButterKnife.bind( this );
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() {
boolean noPassword = TextUtils.isEmpty( sitePasswordField.getText() );
sitePasswordTip.setVisibility( noPassword? View.INVISIBLE: View.VISIBLE );
if (noPassword)
sitePassword = null;
}
} );
fullNameField.setTypeface( Res.exo_Thin );
fullNameField.setPaintFlags( fullNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
masterPasswordField.setTypeface( Res.sourceCodePro_ExtraLight );
masterPasswordField.setPaintFlags( masterPasswordField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
siteNameField.setTypeface( Res.exo_Regular );
siteNameField.setPaintFlags( siteNameField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
sitePasswordField.setTypeface( Res.sourceCodePro_Black );
sitePasswordField.setPaintFlags( sitePasswordField.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG );
rememberFullNameField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
preferences.setRememberFullName( isChecked );
if (isChecked)
preferences.setFullName( fullNameField.getText().toString() );
else
preferences.setFullName( null );
}
} );
forgetPasswordField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
preferences.setForgetPassword( isChecked );
}
} );
maskPasswordField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
preferences.setMaskPassword( isChecked );
sitePasswordField.setTransformationMethod( isChecked? new PasswordTransformationMethod(): null );
}
} );
}
@Override
protected void onResume() {
super.onResume();
MasterKey.setAllowNativeByDefault( preferences.isAllowNativeKDF() );
fullNameField.setText( preferences.getFullName() );
rememberFullNameField.setChecked( preferences.isRememberFullName() );
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( fullNameField.getText() ))
fullNameField.requestFocus();
else if (TextUtils.isEmpty( masterPasswordField.getText() ))
masterPasswordField.requestFocus();
else
siteNameField.requestFocus();
}
@Override
protected void onPause() {
if (preferences.isForgetPassword()) {
synchronized (this) {
id_userName = id_masterPassword = 0;
if (masterKeyFuture != null) {
masterKeyFuture.cancel( true );
masterKeyFuture = null;
}
masterPasswordField.setText( "" );
}
}
siteNameField.setText( "" );
sitePasswordField.setText( "" );
progressView.setVisibility( View.INVISIBLE );
super.onPause();
}
private synchronized void updateMasterKey() {
final String fullName = fullNameField.getText().toString();
final char[] masterPassword = masterPasswordField.getText().toString().toCharArray();
final MasterKey.Version version = (MasterKey.Version) siteVersionButton.getTag();
if (fullName.hashCode() == id_userName && Arrays.hashCode( masterPassword ) == id_masterPassword &&
version.ordinal() == id_version && masterKeyFuture != null && !masterKeyFuture.isCancelled())
return;
id_userName = fullName.hashCode();
id_masterPassword = Arrays.hashCode( masterPassword );
id_version = version.ordinal();
if (preferences.isRememberFullName())
preferences.setFullName( fullName );
if (masterKeyFuture != null)
masterKeyFuture.cancel( true );
if (fullName.isEmpty() || masterPassword.length == 0) {
sitePasswordField.setText( "" );
progressView.setVisibility( View.INVISIBLE );
return;
}
sitePasswordField.setText( "" );
progressView.setVisibility( View.VISIBLE );
(masterKeyFuture = executor.submit( new Callable<MasterKey>() {
@Override
public MasterKey call()
throws Exception {
try {
return MasterKey.create( version, fullName, masterPassword );
}
catch (Exception 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 MPSiteType type = (MPSiteType) siteTypeButton.getTag();
final UnsignedInteger counter = UnsignedInteger.valueOf( siteCounterButton.getText().toString() );
if (masterKeyFuture == null || siteName.isEmpty() || type == null) {
sitePasswordField.setText( "" );
progressView.setVisibility( View.INVISIBLE );
if (masterKeyFuture == null)
updateMasterKey();
return;
}
sitePasswordField.setText( "" );
progressView.setVisibility( View.VISIBLE );
executor.submit( new Runnable() {
@Override
public void run() {
try {
sitePassword = masterKeyFuture.get().encode( siteName, type, counter, MPSiteVariant.Password, null );
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;
}
}
} );
}
public void integrityTests(View view) {
if (masterKeyFuture != null) {
masterKeyFuture.cancel( true );
masterKeyFuture = null;
}
TestActivity.startNoSkip( this );
}
public void copySitePassword(View view) {
final String currentSitePassword = this.sitePassword;
if (TextUtils.isEmpty( currentSitePassword ))
return;
final ClipboardManager clipboardManager = (ClipboardManager) getSystemService( CLIPBOARD_SERVICE );
final NotificationManager notificationManager = (NotificationManager) getSystemService( Context.NOTIFICATION_SERVICE );
String title = strf( "Password for %s", siteNameField.getText() );
ClipDescription description = new ClipDescription( title, new String[]{ ClipDescription.MIMETYPE_TEXT_PLAIN } );
clipboardManager.setPrimaryClip( new ClipData( description, new ClipData.Item( currentSitePassword ) ) );
Notification.Builder notificationBuilder = new Notification.Builder( this ).setContentTitle( title )
.setContentText( "Paste the password into your app." )
.setSmallIcon( R.drawable.icon )
.setAutoCancel( true );
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)
notificationBuilder.setVisibility( Notification.VISIBILITY_SECRET )
.setCategory( Notification.CATEGORY_RECOMMENDATION )
.setLocalOnly( true );
notificationManager.notify( PASSWORD_NOTIFICATION, notificationBuilder.build() );
final Timer timer = new Timer();
timer.schedule( new TimerTask() {
@Override
public void run() {
ClipData clip = clipboardManager.getPrimaryClip();
for (int i = 0; i < clip.getItemCount(); ++i)
if (currentSitePassword.equals( clip.getItemAt( i ).coerceToText( EmergencyActivity.this ) )) {
clipboardManager.setPrimaryClip( EMPTY_CLIP );
break;
}
notificationManager.cancel( PASSWORD_NOTIFICATION );
timer.cancel();
}
}, 20000 );
Intent startMain = new Intent( Intent.ACTION_MAIN );
startMain.addCategory( Intent.CATEGORY_HOME );
startMain.setFlags( Intent.FLAG_ACTIVITY_NEW_TASK );
startActivity( startMain );
}
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();
}
}
}

View File

@@ -0,0 +1,82 @@
package com.lyndir.masterpassword;
import android.os.Handler;
import android.os.Looper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Sets;
import java.util.*;
import java.util.concurrent.*;
/**
* @author lhunath, 2015-12-22
*/
public class MainThreadExecutor extends AbstractExecutorService {
private final Handler mHandler = new Handler( Looper.getMainLooper() );
private final Set<Runnable> commands = Sets.newLinkedHashSet();
private boolean shutdown;
@Override
public void execute(final Runnable command) {
if (shutdown)
throw new RejectedExecutionException( "This executor has been shut down" );
synchronized (commands) {
commands.add( command );
mHandler.post( new Runnable() {
@Override
public void run() {
synchronized (commands) {
if (!commands.remove( command ))
// Command was removed, not executing.
return;
}
command.run();
}
} );
}
}
@Override
public void shutdown() {
shutdown = true;
}
@Override
public List<Runnable> shutdownNow() {
shutdown = true;
mHandler.removeCallbacksAndMessages( null );
synchronized (commands) {
ImmutableList<Runnable> pendingTasks = ImmutableList.copyOf( commands );
commands.clear();
commands.notify();
return pendingTasks;
}
}
@Override
public boolean isShutdown() {
return shutdown;
}
@Override
public boolean isTerminated() {
synchronized (commands) {
return shutdown && commands.isEmpty();
}
}
@Override
public boolean awaitTermination(final long timeout, final TimeUnit unit)
throws InterruptedException {
if (isTerminated())
return true;
commands.wait( unit.toMillis( timeout ) );
return isTerminated();
}
}

View File

@@ -0,0 +1,148 @@
package com.lyndir.masterpassword;
import android.content.Context;
import android.content.SharedPreferences;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* @author lhunath, 2016-02-20
*/
public class Preferences {
private static final String PREF_TESTS_PASSED = "integrityTestsPassed";
private static final String PREF_NATIVE_KDF = "nativeKDF";
private static final String PREF_REMEMBER_FULL_NAME = "rememberFullName";
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 final String PREF_ALGORITHM_VERSION = "algorithmVersion";
private static Preferences instance;
private Context context;
@Nullable
private SharedPreferences prefs;
public static synchronized Preferences get(final Context context) {
if (instance == null)
instance = new Preferences( context );
return instance;
}
private Preferences(Context context) {
this.context = context;
}
@Nonnull
private SharedPreferences prefs() {
if (prefs == null)
prefs = (context = context.getApplicationContext()).getSharedPreferences( getClass().getCanonicalName(), Context.MODE_PRIVATE );
return prefs;
}
public boolean setNativeKDFEnabled(boolean enabled) {
if (isAllowNativeKDF() == enabled)
return false;
prefs().edit().putBoolean( PREF_NATIVE_KDF, enabled ).apply();
return true;
}
public boolean isAllowNativeKDF() {
return prefs().getBoolean( PREF_NATIVE_KDF, MasterKey.isAllowNativeByDefault() );
}
public boolean setTestsPassed(final Set<String> value) {
if (Sets.symmetricDifference( getTestsPassed(), value ).isEmpty())
return false;
prefs().edit().putStringSet( PREF_TESTS_PASSED, value ).apply();
return true;
}
public Set<String> getTestsPassed() {
return prefs().getStringSet( PREF_TESTS_PASSED, ImmutableSet.<String>of() );
}
public boolean setRememberFullName(boolean enabled) {
if (isRememberFullName() == enabled)
return false;
prefs().edit().putBoolean( PREF_REMEMBER_FULL_NAME, enabled ).apply();
return true;
}
public boolean isRememberFullName() {
return prefs().getBoolean( PREF_REMEMBER_FULL_NAME, false );
}
public boolean setForgetPassword(boolean enabled) {
if (isForgetPassword() == enabled)
return false;
prefs().edit().putBoolean( PREF_FORGET_PASSWORD, enabled ).apply();
return true;
}
public boolean isForgetPassword() {
return prefs().getBoolean( PREF_FORGET_PASSWORD, false );
}
public boolean setMaskPassword(boolean enabled) {
if (isMaskPassword() == enabled)
return false;
prefs().edit().putBoolean( PREF_MASK_PASSWORD, enabled ).apply();
return true;
}
public boolean isMaskPassword() {
return prefs().getBoolean( PREF_MASK_PASSWORD, false );
}
public boolean setFullName(@Nullable String value) {
if (getFullName().equals( value ))
return false;
prefs().edit().putString( PREF_FULL_NAME, value ).apply();
return true;
}
@Nonnull
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_ALGORITHM_VERSION, value.ordinal() ).apply();
return true;
}
@Nonnull
public MasterKey.Version getDefaultVersion() {
return MasterKey.Version.values()[prefs().getInt( PREF_ALGORITHM_VERSION, MasterKey.Version.CURRENT.ordinal() )];
}
}

View File

@@ -0,0 +1,34 @@
package com.lyndir.masterpassword;
import android.content.res.Resources;
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" );
}
}

View File

@@ -0,0 +1,177 @@
package com.lyndir.masterpassword;
import static com.lyndir.lhunath.opal.system.util.StringUtils.strf;
import android.app.*;
import android.content.Context;
import android.content.Intent;
import android.os.*;
import android.view.View;
import android.widget.*;
import butterknife.BindView;
import butterknife.ButterKnife;
import com.google.common.base.*;
import com.google.common.collect.*;
import com.google.common.util.concurrent.*;
import com.lyndir.lhunath.opal.system.logging.Logger;
import java.util.concurrent.*;
import javax.annotation.Nullable;
public class TestActivity extends Activity implements MPTestSuite.Listener {
@SuppressWarnings("UnusedDeclaration")
private static final Logger logger = Logger.get( TestActivity.class );
private final Preferences preferences = Preferences.get( this );
private final ListeningExecutorService backgroundExecutor = MoreExecutors.listeningDecorator( Executors.newSingleThreadExecutor() );
private final ListeningExecutorService mainExecutor = MoreExecutors.listeningDecorator( new MainThreadExecutor() );
@BindView(R.id.progressView)
ProgressBar progressView;
@BindView(R.id.statusView)
TextView statusView;
@BindView(R.id.logView)
TextView logView;
@BindView(R.id.actionButton)
Button actionButton;
@BindView(R.id.nativeKDFField)
CheckBox nativeKDFField;
private MPTestSuite testSuite;
private ListenableFuture<Boolean> testFuture;
private Runnable action;
private ImmutableSet<String> testNames;
public static void startNoSkip(Context context) {
context.startActivity( new Intent( context, TestActivity.class ) );
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate( savedInstanceState );
Res.init( getResources() );
setContentView( R.layout.activity_test );
ButterKnife.bind( this );
nativeKDFField.setOnCheckedChangeListener( new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(final CompoundButton buttonView, final boolean isChecked) {
preferences.setNativeKDFEnabled( isChecked );
MasterKey.setAllowNativeByDefault( isChecked );
}
} );
try {
setStatus( 0, 0, null );
testSuite = new MPTestSuite();
testSuite.setListener( this );
testNames = FluentIterable.from( testSuite.getTests().getCases() ).transform(
new Function<MPTests.Case, String>() {
@Nullable
@Override
public String apply(@Nullable final MPTests.Case input) {
return input == null? null: input.identifier;
}
} ).filter( Predicates.notNull() ).toSet();
}
catch (MPTestSuite.UnavailableException e) {
logger.err( e, "While loading test suite" );
setStatus( R.string.tests_unavailable, R.string.tests_btn_unavailable, new Runnable() {
@Override
public void run() {
finish();
}
} );
}
}
@Override
protected void onResume() {
super.onResume();
nativeKDFField.setChecked( preferences.isAllowNativeKDF() );
if (testFuture == null)
startTestSuite();
}
private void startTestSuite() {
if (testFuture != null)
testFuture.cancel( true );
MasterKey.setAllowNativeByDefault( preferences.isAllowNativeKDF() );
setStatus( R.string.tests_testing, R.string.tests_btn_testing, null );
Futures.addCallback( testFuture = backgroundExecutor.submit( testSuite ), new FutureCallback<Boolean>() {
@Override
public void onSuccess(@Nullable final Boolean result) {
if (result != null && result)
setStatus( R.string.tests_passed, R.string.tests_btn_passed, new Runnable() {
@Override
public void run() {
preferences.setTestsPassed( testNames );
finish();
}
} );
else
setStatus( R.string.tests_failed, R.string.tests_btn_failed, new Runnable() {
@Override
public void run() {
startTestSuite();
}
} );
}
@Override
public void onFailure(final Throwable t) {
logger.err( t, "While running test suite" );
setStatus( R.string.tests_failed, R.string.tests_btn_failed, new Runnable() {
@Override
public void run() {
finish();
}
} );
}
}, mainExecutor );
}
public void onAction(View v) {
if (action != null)
action.run();
}
private void setStatus(int statusId, int buttonId, @Nullable Runnable action) {
this.action = action;
if (statusId == 0)
statusView.setText( null );
else
statusView.setText( statusId );
if (buttonId == 0)
actionButton.setText( null );
else
actionButton.setText( buttonId );
actionButton.setEnabled( action != null );
}
@Override
public void progress(final int current, final int max, final String messageFormat, final Object... args) {
runOnUiThread( new Runnable() {
@Override
public void run() {
logView.append( strf( '\n' + messageFormat, args ) );
progressView.setMax( max );
progressView.setProgress( current );
}
} );
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 697 B

View File

@@ -0,0 +1,273 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:background="@drawable/background">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:orientation="vertical"
android:gravity="center">
<View
android:layout_width="1dp"
android:layout_height="0dp"
android:layout_weight="1" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:importantForAccessibility="no"
android:src="@drawable/img_identity" />
<EditText
android:id="@+id/fullNameField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nextFocusForward="@+id/masterPasswordField"
android:inputType="text|textCapWords|textPersonName"
android:hint="@string/fullName_hint"
android:gravity="center"
android:textColor="#FFFFFF"
android:textSize="16sp" />
<CheckBox
android:id="@+id/rememberFullNameField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nextFocusForward="@+id/rememberPasswordField"
android:textSize="12sp"
android:textColor="@android:color/tertiary_text_dark"
android:text="@string/remember" />
<EditText
android:id="@id/masterPasswordField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nextFocusForward="@+id/siteNameField"
android:inputType="text|textPassword"
android:hint="@string/masterPassword_hint"
android:gravity="center"
android:textColor="#FFFFFF"
android:textSize="16sp" />
<CheckBox
android:id="@id/rememberPasswordField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="12sp"
android:textColor="@android:color/tertiary_text_dark"
android:text="@string/forgetOnClose" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginBottom="8dp"
android:importantForAccessibility="no"
android:src="@drawable/img_key" />
<EditText
android:id="@id/siteNameField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nextFocusForward="@+id/sitePasswordField"
android:inputType="text|textNoSuggestions|textUri"
android:hint="@string/siteName_hint"
android:gravity="center"
android:textColor="#FFFFFF"
android:textSize="16sp" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ProgressBar
android:id="@+id/progressView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="invisible"
android:indeterminate="true"
tools:visibility="visible" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical">
<Button
android:id="@id/sitePasswordField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nextFocusForward="@+id/siteTypeButton"
android:gravity="center"
android:background="@android:color/transparent"
android:textColor="#FFFFFF"
android:textSize="28sp"
tools:text="LuxdZozvDuma4["
android:onClick="copySitePassword" />
<TextView
android:id="@+id/sitePasswordTip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:labelFor="@id/sitePasswordField"
android:gravity="center"
android:background="@android:color/transparent"
android:textSize="12sp"
android:textColor="@android:color/tertiary_text_dark"
android:text="@string/sitePassword_hint" />
</LinearLayout>
</FrameLayout>
<CheckBox
android:id="@+id/maskPasswordField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="12sp"
android:textColor="@android:color/tertiary_text_dark"
android:text="@string/maskPassword" />
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:layout_marginBottom="8dp"
android:importantForAccessibility="no"
android:src="@drawable/divider" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="?android:buttonBarStyle"
android:orientation="horizontal"
android:gravity="center">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center">
<Button
android:id="@id/siteTypeButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
style="?android:buttonBarButtonStyle"
android:nextFocusForward="@+id/counterField"
android:gravity="center"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:drawableStart="@drawable/icon_key"
android:drawablePadding="8dp"
android:background="@android:color/transparent"
tools:text="Long" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@id/siteTypeButton"
android:gravity="center"
android:background="@android:color/transparent"
android:textSize="12sp"
android:textColor="@android:color/tertiary_text_dark"
android:text="@string/siteType_hint" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center">
<Button
android:id="@id/counterField"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
style="?android:buttonBarButtonStyle"
android:nextFocusForward="@+id/siteVersionButton"
android:gravity="center"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:drawableStart="@drawable/icon_plus"
android:drawablePadding="8dp"
android:background="@android:color/transparent"
tools:text="1" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@id/siteVersionButton"
android:gravity="center"
android:background="@android:color/transparent"
android:textSize="12sp"
android:textColor="@android:color/tertiary_text_dark"
android:text="@string/siteCounter_hint" />
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="center">
<Button
android:id="@id/siteVersionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
style="?android:buttonBarButtonStyle"
android:nextFocusForward="@+id/rememberFullNameField"
android:gravity="center"
android:textColor="#FFFFFF"
android:textSize="16sp"
android:drawableStart="@drawable/icon_gears"
android:drawablePadding="8dp"
android:background="@android:color/transparent"
tools:text="3" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:labelFor="@id/siteVersionButton"
android:gravity="center"
android:background="@android:color/transparent"
android:textSize="12sp"
android:textColor="@android:color/tertiary_text_dark"
android:text="@string/siteVersion_hint" />
</LinearLayout>
</LinearLayout>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:textSize="16sp"
android:text="@string/btn_tests"
android:onClick="integrityTests"
android:background="@android:color/transparent" />
<View
android:layout_width="1dp"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:background="@drawable/background">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="20dp"
android:orientation="vertical"
android:gravity="center">
<View
android:layout_width="1dp"
android:layout_height="0dp"
android:layout_weight="1" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:importantForAccessibility="no"
android:src="@drawable/img_stats" />
<ProgressBar
android:id="@+id/progressView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="8dp"
tools:max="100"
tools:progress="80"
style="?android:progressBarStyleHorizontal" />
<TextView
android:id="@+id/statusView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:labelFor="@id/sitePasswordField"
android:gravity="center"
android:background="@android:color/transparent"
android:textSize="12sp"
android:textColor="@android:color/tertiary_text_dark"
android:text="@string/tests_testing" />
<TextView
android:id="@+id/logView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:layout_marginTop="20dp"
android:gravity="bottom"
android:background="@android:color/transparent"
android:textSize="9sp"
android:textColor="@android:color/tertiary_text_dark" />
<Button
android:id="@+id/actionButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:enabled="false"
android:text="@string/tests_btn_testing"
android:onClick="onAction" />
<CheckBox
android:id="@+id/nativeKDFField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="12sp"
android:textColor="@android:color/tertiary_text_dark"
android:text="@string/nativeKDF" />
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Master Password</string>
<string name="remember">Remember</string>
<string name="forgetOnClose">Forget on close</string>
<string name="maskPassword">Hide password</string>
<string name="fullName_hint">Your full name</string>
<string name="masterPassword_hint">Your master password</string>
<string name="siteName_hint">eg. google.com</string>
<string name="sitePassword_hint">Tap to copy</string>
<string name="siteType_hint">Type</string>
<string name="siteCounter_hint">Counter</string>
<string name="siteVersion_hint">Algorithm</string>
<string name="empty" />
<string name="btn_tests">Integrity Tests </string>
<string name="tests_unavailable">Test suite unavailable.</string>
<string name="tests_btn_unavailable">Retest</string>
<string name="tests_testing">Testing device\'s password generation integrity…</string>
<string name="tests_btn_testing">Please Stand By…</string>
<string name="tests_failed">Incompatible device or OS.</string>
<string name="tests_btn_failed">Retest</string>
<string name="tests_passed">Integrity checks passed!</string>
<string name="tests_btn_passed">Close</string>
<string name="nativeKDF">Use native key derivation</string>
</resources>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="MPTheme" parent="android:Theme.Holo.Dialog.MinWidth">
</style>
</resources>