Merge branch '274-code-encapsulation' into 'master'

274 code encapsulation

The goal of this branch is to better compartmentalize, structure and simplify the code in the Android module. It does this via the following techniques:

* Extended use of Dagger by introducing a component for Activities that injects, among other things, SharedPreferences and Fragments. The latter makes it possible to mock fragments more easily and letting Dagger automatically handle its injections.
* Decouple Briar API code, from the activities and Fragments, and encapsulate in re-usable Controller classes
* Introduce a UI-thread App event bus that the Helper classes use to communicate with Activities & Fragments.
* Define the testing methods in the briar-android module per Robolectric and Mockito

NOTE! Don't be alarmed if you see the contact introduction message one more time, one of the changes was to purify UI shared preferences from the Briar shared preferences, which now only contain preferences related to the API and it's usage.

closes #274 



See merge request !130
This commit is contained in:
akwizgran
2016-05-11 10:54:07 +00:00
69 changed files with 1678 additions and 719 deletions

View File

@@ -29,7 +29,7 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:name=".android.BriarApplication"
android:name=".android.BriarApplicationImpl"
android:allowBackup="false"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"

View File

@@ -8,6 +8,7 @@ apply plugin: 'de.undercouch.download'
repositories {
jcenter()
maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
}
dependencies {
@@ -44,6 +45,18 @@ dependencies {
compile 'com.google.zxing:core:3.2.1'
apt 'com.google.dagger:dagger-compiler:2.0.2'
provided 'javax.annotation:jsr250-api:1.0'
testCompile 'junit:junit:4.12'
testCompile 'net.jodah:concurrentunit:0.4.2'
testApt 'com.google.dagger:dagger-compiler:2.0.2'
testCompile project(path: ':briar-tests')
testCompile 'org.robolectric:robolectric:3.0'
testCompile 'org.mockito:mockito-core:1.10.19'
androidTestCompile 'org.robolectric:robolectric:3.0'
androidTestCompile 'org.mockito:mockito-core:1.10.19'
androidTestApt 'com.google.dagger:dagger-compiler:2.0.2'
androidTestCompile 'junit:junit:4.12'
}
dependencyVerification {
@@ -80,8 +93,19 @@ android {
assets.srcDirs = ['assets']
}
// Move the tests to tests/java, tests/res, etc...
instrumentTest.setRoot('tests')
androidTest.setRoot('androidTest')
androidTest {
java.srcDirs = ['androidTest/java']
}
test.setRoot('test')
test {
java.srcDirs = ['test/java']
testOptions {
unitTests.returnDefaultValues = true
}
}
// Move the build types to build-types/<type>
// For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...

View File

@@ -3,6 +3,7 @@
-dontpreverify
-dontobfuscate
-verbose
-useuniqueclassmembernames
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
# For comfortability in case we do obfuscate
# -renamesourcefileattribute SourceFile

View File

@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/dashboard_background"
>
</RelativeLayout>

View File

@@ -0,0 +1,107 @@
package org.briarproject.android;
import android.app.Activity;
import org.briarproject.android.contact.ConversationActivity;
import org.briarproject.android.forum.AvailableForumsActivity;
import org.briarproject.android.forum.ContactSelectorFragment;
import org.briarproject.android.forum.CreateForumActivity;
import org.briarproject.android.forum.ForumActivity;
import org.briarproject.android.forum.ReadForumPostActivity;
import org.briarproject.android.forum.ShareForumActivity;
import org.briarproject.android.forum.ShareForumMessageFragment;
import org.briarproject.android.forum.WriteForumPostActivity;
import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.android.identity.CreateIdentityActivity;
import org.briarproject.android.introduction.IntroductionActivity;
import org.briarproject.android.introduction.IntroductionMessageFragment;
import org.briarproject.android.invitation.AddContactActivity;
import org.briarproject.android.keyagreement.KeyAgreementActivity;
import org.briarproject.android.panic.PanicPreferencesActivity;
import org.briarproject.android.panic.PanicResponderActivity;
import javax.inject.Named;
import dagger.Component;
@ActivityScope
@Component(modules = ActivityModule.class,
dependencies = AndroidComponent.class)
public interface ActivityComponent {
Activity activity();
void inject(SplashScreenActivity activity);
void inject(SetupActivity activity);
void inject(NavDrawerActivity activity);
void inject(PasswordActivity activity);
void inject(PanicResponderActivity activity);
void inject(PanicPreferencesActivity activity);
void inject(AddContactActivity activity);
void inject(KeyAgreementActivity activity);
void inject(ConversationActivity activity);
void inject(CreateIdentityActivity activity);
void inject(AvailableForumsActivity activity);
void inject(WriteForumPostActivity activity);
void inject(CreateForumActivity activity);
void inject(ShareForumActivity activity);
void inject(ReadForumPostActivity activity);
void inject(ForumActivity activity);
void inject(SettingsActivity activity);
/*
void inject(ContactListFragment fragment);
void inject(ForumListFragment fragment);
void inject(ShowQrCodeFragment fragment);
*/
void inject(IntroductionActivity activity);
/*
void inject(ContactChooserFragment fragment);
void inject(introductionmessagefragment fragment);
*/
@Named("ContactListFragment")
BaseFragment newContactListFragment();
@Named("ForumListFragment")
BaseFragment newForumListFragment();
@Named("ChooseIdentityFragment")
BaseFragment newChooseIdentityFragment();
@Named("ShowQrCodeFragment")
BaseFragment newShowQrCodeFragment();
@Named("ContactChooserFragment")
BaseFragment newContactChooserFragment();
@Named("ContactSelectorFragment")
ContactSelectorFragment newContactSelectorFragment();
@Named("ShareForumMessageFragment")
ShareForumMessageFragment newShareForumMessageFragment();
@Named("IntroductionMessageFragment")
IntroductionMessageFragment newIntroductionMessageFragment();
}

View File

@@ -0,0 +1,175 @@
package org.briarproject.android;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import org.briarproject.android.contact.ContactListFragment;
import org.briarproject.android.controller.BriarController;
import org.briarproject.android.controller.BriarControllerImpl;
import org.briarproject.android.controller.ConfigController;
import org.briarproject.android.controller.ConfigControllerImpl;
import org.briarproject.android.controller.NavDrawerController;
import org.briarproject.android.controller.NavDrawerControllerImpl;
import org.briarproject.android.controller.PasswordController;
import org.briarproject.android.controller.PasswordControllerImpl;
import org.briarproject.android.controller.SetupController;
import org.briarproject.android.controller.SetupControllerImpl;
import org.briarproject.android.controller.TransportStateListener;
import org.briarproject.android.forum.ContactSelectorFragment;
import org.briarproject.android.forum.ForumListFragment;
import org.briarproject.android.forum.ShareForumMessageFragment;
import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.android.introduction.ContactChooserFragment;
import org.briarproject.android.introduction.IntroductionMessageFragment;
import org.briarproject.android.keyagreement.ChooseIdentityFragment;
import org.briarproject.android.keyagreement.ShowQrCodeFragment;
import javax.inject.Named;
import dagger.Module;
import dagger.Provides;
import static org.briarproject.android.BriarService.BriarServiceConnection;
@Module
public class ActivityModule {
private final BaseActivity activity;
public ActivityModule(BaseActivity activity) {
this.activity = activity;
}
@ActivityScope
@Provides
BaseActivity providesBaseActivity() {
return activity;
}
@ActivityScope
@Provides
Activity providesActivity() {
return activity;
}
@ActivityScope
@Provides
protected SetupController provideSetupController(
SetupControllerImpl setupControllerImpl) {
return setupControllerImpl;
}
@ActivityScope
@Provides
protected ConfigController provideConfigController(
ConfigControllerImpl configControllerImpl) {
return configControllerImpl;
}
@ActivityScope
@Provides
protected SharedPreferences provideSharedPreferences(Activity activity) {
return activity.getSharedPreferences("db", Context.MODE_PRIVATE);
}
@ActivityScope
@Provides
protected PasswordController providePasswordController(
PasswordControllerImpl passwordControllerImp) {
return passwordControllerImp;
}
@ActivityScope
@Provides
protected BriarController provideBriarController(
BriarControllerImpl briarControllerImpl) {
activity.addLifecycleController(briarControllerImpl);
return briarControllerImpl;
}
@ActivityScope
@Provides
protected NavDrawerController provideNavDrawerController(
NavDrawerControllerImpl navDrawerControllerImp) {
activity.addLifecycleController(navDrawerControllerImp);
if (activity instanceof TransportStateListener) {
navDrawerControllerImp
.setTransportListener((TransportStateListener) activity);
}
return navDrawerControllerImp;
}
@ActivityScope
@Provides
protected BriarServiceConnection provideBriarServiceConnection() {
return new BriarServiceConnection();
}
@Provides
@Named("ForumListFragment")
BaseFragment provideForumListFragment(
ForumListFragment forumListFragment) {
forumListFragment.setArguments(new Bundle());
return forumListFragment;
}
@Provides
@Named("ContactListFragment")
BaseFragment provideContactListFragment(
ContactListFragment contactListFragment) {
contactListFragment.setArguments(new Bundle());
return contactListFragment;
}
@Provides
@Named("ChooseIdentityFragment")
BaseFragment provideChooseIdendityFragment() {
ChooseIdentityFragment fragment = new ChooseIdentityFragment();
fragment.setArguments(new Bundle());
return fragment;
}
@Provides
@Named("ShowQrCodeFragment")
BaseFragment provideShowQrCodeFragment() {
ShowQrCodeFragment fragment = new ShowQrCodeFragment();
fragment.setArguments(new Bundle());
return fragment;
}
@Provides
@Named("ContactChooserFragment")
BaseFragment provideContactChooserFragment() {
ContactChooserFragment fragment = new ContactChooserFragment();
fragment.setArguments(new Bundle());
return fragment;
}
@Provides
@Named("ContactSelectorFragment")
ContactSelectorFragment provideContactSelectorFragment() {
ContactSelectorFragment fragment = new ContactSelectorFragment();
fragment.setArguments(new Bundle());
return fragment;
}
@Provides
@Named("ShareForumMessageFragment")
ShareForumMessageFragment provideShareForumMessageFragment() {
ShareForumMessageFragment fragment = new ShareForumMessageFragment();
fragment.setArguments(new Bundle());
return fragment;
}
@Provides
@Named("IntroductionMessageFragment")
IntroductionMessageFragment provideIntroductionMessageFragment() {
IntroductionMessageFragment fragment =
new IntroductionMessageFragment();
fragment.setArguments(new Bundle());
return fragment;
}
}

View File

@@ -0,0 +1,11 @@
package org.briarproject.android;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import javax.inject.Scope;
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScope {
}

View File

@@ -2,31 +2,41 @@ package org.briarproject.android;
import org.briarproject.CoreEagerSingletons;
import org.briarproject.CoreModule;
import org.briarproject.android.contact.ContactListFragment;
import org.briarproject.android.contact.ConversationActivity;
import org.briarproject.android.forum.AvailableForumsActivity;
import org.briarproject.android.forum.ContactSelectorFragment;
import org.briarproject.android.forum.CreateForumActivity;
import org.briarproject.android.forum.ForumActivity;
import org.briarproject.android.forum.ForumListFragment;
import org.briarproject.android.forum.ReadForumPostActivity;
import org.briarproject.android.forum.ShareForumActivity;
import org.briarproject.android.forum.ShareForumMessageFragment;
import org.briarproject.android.forum.WriteForumPostActivity;
import org.briarproject.android.identity.CreateIdentityActivity;
import org.briarproject.android.introduction.ContactChooserFragment;
import org.briarproject.android.introduction.IntroductionActivity;
import org.briarproject.android.introduction.IntroductionMessageFragment;
import org.briarproject.android.invitation.AddContactActivity;
import org.briarproject.android.keyagreement.ChooseIdentityFragment;
import org.briarproject.android.keyagreement.KeyAgreementActivity;
import org.briarproject.android.keyagreement.ShowQrCodeFragment;
import org.briarproject.android.panic.PanicPreferencesActivity;
import org.briarproject.android.panic.PanicResponderActivity;
import org.briarproject.android.api.AndroidExecutor;
import org.briarproject.android.api.AndroidNotificationManager;
import org.briarproject.android.api.ReferenceManager;
import org.briarproject.android.util.BriarReportSender;
import org.briarproject.api.contact.ContactExchangeTask;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.CryptoExecutor;
import org.briarproject.api.crypto.PasswordStrengthEstimator;
import org.briarproject.api.db.DatabaseConfig;
import org.briarproject.api.db.DatabaseExecutor;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.forum.ForumPostFactory;
import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.introduction.IntroductionManager;
import org.briarproject.api.invitation.InvitationTaskFactory;
import org.briarproject.api.keyagreement.KeyAgreementTaskFactory;
import org.briarproject.api.keyagreement.PayloadEncoder;
import org.briarproject.api.keyagreement.PayloadParser;
import org.briarproject.api.lifecycle.IoExecutor;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.messaging.MessagingManager;
import org.briarproject.api.messaging.PrivateMessageFactory;
import org.briarproject.api.plugins.ConnectionRegistry;
import org.briarproject.api.plugins.PluginManager;
import org.briarproject.api.properties.TransportPropertyManager;
import org.briarproject.api.settings.SettingsManager;
import org.briarproject.plugins.AndroidPluginsModule;
import org.briarproject.system.AndroidSystemModule;
import java.util.concurrent.Executor;
import javax.inject.Singleton;
import dagger.Component;
@@ -39,63 +49,72 @@ import dagger.Component;
AndroidSystemModule.class
})
public interface AndroidComponent extends CoreEagerSingletons {
// Exposed objects
@CryptoExecutor
Executor cryptoExecutor();
PasswordStrengthEstimator passwordStrengthIndicator();
CryptoComponent cryptoComponent();
DatabaseConfig databaseConfig();
AuthorFactory authFactory();
ReferenceManager referenceMangager();
@DatabaseExecutor
Executor databaseExecutor();
LifecycleManager lifecycleManager();
IdentityManager identityManager();
PluginManager pluginManager();
EventBus eventBus();
InvitationTaskFactory invitationTaskFactory();
AndroidNotificationManager androidNotificationManager();
ConnectionRegistry connectionRegistry();
ContactManager contactManager();
MessagingManager messagingManager();
PrivateMessageFactory privateMessageFactory();
TransportPropertyManager transportPropertyManager();
ForumManager forumManager();
ForumSharingManager forumSharingManager();
ForumPostFactory forumPostFactory();
SettingsManager settingsManager();
ContactExchangeTask contactExchangeTask();
KeyAgreementTaskFactory keyAgreementTaskFactory();
PayloadEncoder payloadEncoder();
PayloadParser payloadParser();
IntroductionManager introductionManager();
AndroidExecutor androidExecutor();
@IoExecutor
Executor ioExecutor();
void inject(DevReportActivity devReportActivity);
void inject(SplashScreenActivity activity);
void inject(SetupActivity activity);
void inject(NavDrawerActivity activity);
void inject(PasswordActivity activity);
void inject(BriarService activity);
void inject(PanicResponderActivity activity);
void inject(PanicPreferencesActivity activity);
void inject(AddContactActivity activity);
void inject(KeyAgreementActivity activity);
void inject(ConversationActivity activity);
void inject(CreateIdentityActivity activity);
void inject(AvailableForumsActivity activity);
void inject(WriteForumPostActivity activity);
void inject(CreateForumActivity activity);
void inject(ShareForumActivity activity);
void inject(ContactSelectorFragment fragment);
void inject(ShareForumMessageFragment fragment);
void inject(ReadForumPostActivity activity);
void inject(ForumActivity activity);
void inject(SettingsActivity activity);
void inject(ContactListFragment fragment);
void inject(ForumListFragment fragment);
void inject(ChooseIdentityFragment fragment);
void inject(ShowQrCodeFragment fragment);
void inject(IntroductionActivity activity);
void inject(ContactChooserFragment fragment);
void inject(IntroductionMessageFragment fragment);
// Eager singleton load
void inject(AppModule.EagerSingletons init);

View File

@@ -1,21 +1,31 @@
package org.briarproject.android;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import org.briarproject.android.controller.ActivityLifecycleController;
import java.util.ArrayList;
import java.util.List;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
import static org.briarproject.android.TestingConstants.PREVENT_SCREENSHOTS;
public abstract class BaseActivity extends AppCompatActivity {
public final static String PREFS_NAME = "db";
public final static String PREF_DB_KEY = "key";
public final static String PREF_SEEN_WELCOME_MESSAGE = "welcome_message";
protected ActivityComponent activityComponent;
private List<ActivityLifecycleController> lifecycleControllers =
new ArrayList<ActivityLifecycleController>();
public void addLifecycleController(
ActivityLifecycleController lifecycleController) {
this.lifecycleControllers.add(lifecycleController);
}
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -23,32 +33,52 @@ public abstract class BaseActivity extends AppCompatActivity {
if (PREVENT_SCREENSHOTS) getWindow().addFlags(FLAG_SECURE);
BriarApplication application = (BriarApplication) getApplication();
injectActivity(application.getApplicationComponent());
AndroidComponent applicationComponent =
((BriarApplication) getApplication()).getApplicationComponent();
activityComponent = DaggerActivityComponent.builder()
.androidComponent(applicationComponent)
.activityModule(getActivityModule())
.build();
injectActivity(activityComponent);
for (ActivityLifecycleController alc : lifecycleControllers) {
alc.onActivityCreate();
}
}
public abstract void injectActivity(AndroidComponent component);
private SharedPreferences getSharedPrefs() {
return getSharedPreferences(PREFS_NAME, MODE_PRIVATE);
// This exists to make test overrides easier
protected ActivityModule getActivityModule() {
return new ActivityModule(this);
}
protected String getEncryptedDatabaseKey() {
return getSharedPrefs().getString(PREF_DB_KEY, null);
@Override
protected void onResume() {
super.onResume();
for (ActivityLifecycleController alc : lifecycleControllers) {
alc.onActivityResume();
}
}
protected void storeEncryptedDatabaseKey(final String hex) {
SharedPreferences.Editor editor = getSharedPrefs().edit();
editor.putString(PREF_DB_KEY, hex);
editor.apply();
@Override
protected void onPause() {
super.onPause();
for (ActivityLifecycleController alc : lifecycleControllers) {
alc.onActivityPause();
}
}
protected void clearSharedPrefs() {
SharedPreferences.Editor editor = getSharedPrefs().edit();
editor.clear();
editor.apply();
@Override
protected void onDestroy() {
super.onDestroy();
for (ActivityLifecycleController alc : lifecycleControllers) {
alc.onActivityDestroy();
}
}
public abstract void injectActivity(ActivityComponent component);
protected void showSoftKeyboard(View view) {
Object o = getSystemService(INPUT_METHOD_SERVICE);
((InputMethodManager) o).showSoftInput(view, SHOW_IMPLICIT);
@@ -59,4 +89,5 @@ public abstract class BaseActivity extends AppCompatActivity {
Object o = getSystemService(INPUT_METHOD_SERVICE);
((InputMethodManager) o).hideSoftInputFromWindow(token, 0);
}
}

View File

@@ -3,17 +3,11 @@ package org.briarproject.android;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import org.briarproject.android.BriarService.BriarBinder;
import org.briarproject.android.BriarService.BriarServiceConnection;
import org.briarproject.android.controller.BriarController;
import org.briarproject.android.controller.handler.UiResultHandler;
import org.briarproject.android.panic.ExitActivity;
import org.briarproject.api.db.DatabaseConfig;
import org.briarproject.api.db.DatabaseExecutor;
import org.briarproject.api.lifecycle.LifecycleManager;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.inject.Inject;
@@ -37,33 +31,14 @@ public abstract class BriarActivity extends BaseActivity {
private static final Logger LOG =
Logger.getLogger(BriarActivity.class.getName());
private final BriarServiceConnection serviceConnection =
new BriarServiceConnection();
@Inject
protected DatabaseConfig databaseConfig;
private boolean bound = false;
// Fields that are accessed from background threads must be volatile
@Inject
@DatabaseExecutor
protected volatile Executor dbExecutor;
@Inject
protected volatile LifecycleManager lifecycleManager;
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
if (databaseConfig.getEncryptionKey() != null) startAndBindService();
}
protected BriarController briarController;
@Override
protected void onActivityResult(int request, int result, Intent data) {
super.onActivityResult(request, result, data);
if (request == REQUEST_PASSWORD) {
if (result == RESULT_OK) startAndBindService();
if (result == RESULT_OK) briarController.startAndBindService();
else finish();
}
}
@@ -71,51 +46,22 @@ public abstract class BriarActivity extends BaseActivity {
@Override
public void onResume() {
super.onResume();
if (databaseConfig.getEncryptionKey() == null && !isFinishing()) {
if (!briarController.hasEncryptionKey() && !isFinishing()) {
Intent i = new Intent(this, PasswordActivity.class);
i.setFlags(FLAG_ACTIVITY_NO_ANIMATION | FLAG_ACTIVITY_SINGLE_TOP);
startActivityForResult(i, REQUEST_PASSWORD);
}
}
@Override
public void onDestroy() {
super.onDestroy();
unbindService();
}
private void startAndBindService() {
startService(new Intent(this, BriarService.class));
bound = bindService(new Intent(this, BriarService.class),
serviceConnection, 0);
}
private void unbindService() {
if (bound) unbindService(serviceConnection);
}
protected void signOut(final boolean removeFromRecentApps) {
// Use a new thread to avoid deadlock with executor tasks
new Thread() {
briarController.signOut(new UiResultHandler<Void>(this) {
@Override
public void run() {
try {
// Wait for the service to finish starting up
IBinder binder = serviceConnection.waitForBinder();
BriarService service = ((BriarBinder) binder).getService();
service.waitForStartup();
// Shut down the service and wait for it to shut down
LOG.info("Shutting down service");
service.shutdown();
service.waitForShutdown();
} catch (InterruptedException e) {
LOG.warning("Interrupted while waiting for service");
Thread.currentThread().interrupt();
}
public void onResultUi(Void result) {
if (removeFromRecentApps) startExitActivity();
else finishAndExit();
}
}.start();
});
}
protected void signOut() {
@@ -123,46 +69,29 @@ public abstract class BriarActivity extends BaseActivity {
}
private void startExitActivity() {
runOnUiThread(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(BriarActivity.this,
ExitActivity.class);
intent.addFlags(FLAG_ACTIVITY_NEW_TASK
| FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
| FLAG_ACTIVITY_NO_ANIMATION);
if (Build.VERSION.SDK_INT >= 11)
intent.addFlags(FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
});
Intent intent = new Intent(BriarActivity.this,
ExitActivity.class);
intent.addFlags(FLAG_ACTIVITY_NEW_TASK
| FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
| FLAG_ACTIVITY_NO_ANIMATION);
if (Build.VERSION.SDK_INT >= 11)
intent.addFlags(FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
}
private void finishAndExit() {
runOnUiThread(new Runnable() {
public void run() {
if (Build.VERSION.SDK_INT >= 21) finishAndRemoveTask();
else finish();
LOG.info("Exiting");
System.exit(0);
}
});
if (Build.VERSION.SDK_INT >= 21) finishAndRemoveTask();
else finish();
LOG.info("Exiting");
System.exit(0);
}
public void runOnDbThread(final Runnable task) {
dbExecutor.execute(new Runnable() {
public void run() {
try {
lifecycleManager.waitForDatabase();
task.run();
} catch (InterruptedException e) {
LOG.warning("Interrupted while waiting for database");
Thread.currentThread().interrupt();
}
}
});
@Deprecated
public void runOnDbThread(Runnable task) {
briarController.runOnDbThread(task);
}
@Deprecated
protected void finishOnUiThread() {
runOnUiThread(new Runnable() {
public void run() {

View File

@@ -1,56 +1,10 @@
package org.briarproject.android;
import android.app.Application;
import android.content.Context;
/**
* This exists so that the Application object will not necessarily be cast
* directly to the Briar application object.
*/
public interface BriarApplication {
import org.acra.ACRA;
import org.acra.ReportingInteractionMode;
import org.acra.annotation.ReportsCrashes;
import org.briarproject.CoreModule;
import org.briarproject.R;
import org.briarproject.android.util.BriarReportPrimer;
import org.briarproject.android.util.BriarReportSenderFactory;
import java.util.logging.Logger;
@ReportsCrashes(
reportPrimerClass = BriarReportPrimer.class,
logcatArguments = {"-d", "-v", "time", "*:I"},
reportSenderFactoryClasses = {BriarReportSenderFactory.class},
mode = ReportingInteractionMode.DIALOG,
reportDialogClass = DevReportActivity.class,
resDialogOkToast = R.string.dev_report_saved,
deleteOldUnsentReportsOnApplicationStart = false
)
public class BriarApplication extends Application {
private static final Logger LOG =
Logger.getLogger(BriarApplication.class.getName());
private AndroidComponent applicationComponent;
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
ACRA.init(this);
}
@Override
public void onCreate() {
super.onCreate();
LOG.info("Created");
applicationComponent = DaggerAndroidComponent.builder()
.appModule(new AppModule(this))
.build();
// We need to load the eager singletons directly after making the
// dependency graphs
CoreModule.initEagerSingletons(applicationComponent);
AndroidEagerSingletons.initEagerSingletons(applicationComponent);
}
public AndroidComponent getApplicationComponent() {
return applicationComponent;
}
AndroidComponent getApplicationComponent();
}

View File

@@ -0,0 +1,56 @@
package org.briarproject.android;
import android.app.Application;
import android.content.Context;
import org.acra.ACRA;
import org.acra.ReportingInteractionMode;
import org.acra.annotation.ReportsCrashes;
import org.briarproject.CoreModule;
import org.briarproject.R;
import org.briarproject.android.util.BriarReportPrimer;
import org.briarproject.android.util.BriarReportSenderFactory;
import java.util.logging.Logger;
@ReportsCrashes(
reportPrimerClass = BriarReportPrimer.class,
logcatArguments = {"-d", "-v", "time", "*:I"},
reportSenderFactoryClasses = {BriarReportSenderFactory.class},
mode = ReportingInteractionMode.DIALOG,
reportDialogClass = DevReportActivity.class,
resDialogOkToast = R.string.dev_report_saved,
deleteOldUnsentReportsOnApplicationStart = false
)
public class BriarApplicationImpl extends Application implements BriarApplication {
private static final Logger LOG =
Logger.getLogger(BriarApplicationImpl.class.getName());
private AndroidComponent applicationComponent;
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
ACRA.init(this);
}
@Override
public void onCreate() {
super.onCreate();
LOG.info("Created");
applicationComponent = DaggerAndroidComponent.builder()
.appModule(new AppModule(this))
.build();
// We need to load the eager singletons directly after making the
// dependency graphs
CoreModule.initEagerSingletons(applicationComponent);
AndroidEagerSingletons.initEagerSingletons(applicationComponent);
}
public AndroidComponent getApplicationComponent() {
return applicationComponent;
}
}

View File

@@ -10,7 +10,6 @@ import org.briarproject.R;
import org.briarproject.android.contact.ContactListFragment;
import org.briarproject.android.forum.ForumListFragment;
import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.android.fragment.DashboardFragment;
/**
* This class should be extended by classes that wish to utilise fragments in
@@ -23,9 +22,7 @@ public abstract class BriarFragmentActivity extends BriarActivity {
if (actionBar == null)
return;
if (fragmentTag.equals(DashboardFragment.TAG)) {
actionBar.setTitle(R.string.dashboard_toolbar_header);
} else if (fragmentTag.equals(ContactListFragment.TAG)) {
if (fragmentTag.equals(ContactListFragment.TAG)) {
actionBar.setTitle(R.string.contacts_toolbar_header);
} else if (fragmentTag.equals(ForumListFragment.TAG)) {
actionBar.setTitle(R.string.forums_toolbar_header);
@@ -52,7 +49,7 @@ public abstract class BriarFragmentActivity extends BriarActivity {
exiting. This models the typical Google navigation behaviour such
as in Gmail/Inbox.
*/
startFragment(ContactListFragment.newInstance());
startFragment(activityComponent.newContactListFragment());
} else {
super.onBackPressed();

View File

@@ -89,6 +89,7 @@ public class DevReportActivity extends BaseCrashReportDialog
((BriarApplication) getApplication()).getApplicationComponent()
.inject(this);
sharedPreferencesFactory =
new SharedPreferencesFactory(getApplicationContext(),
getConfig());

View File

@@ -1,5 +1,6 @@
package org.briarproject.android;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
@@ -13,28 +14,18 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;
import org.briarproject.R;
import org.briarproject.android.contact.ContactListFragment;
import org.briarproject.android.forum.ForumListFragment;
import org.briarproject.android.controller.NavDrawerController;
import org.briarproject.android.controller.TransportStateListener;
import org.briarproject.android.controller.handler.UiResultHandler;
import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.android.util.CustomAnimations;
import org.briarproject.api.TransportId;
import org.briarproject.android.api.ReferenceManager;
import org.briarproject.api.db.DbException;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.TransportDisabledEvent;
import org.briarproject.api.event.TransportEnabledEvent;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.plugins.PluginManager;
import java.util.ArrayList;
import java.util.List;
@@ -42,11 +33,10 @@ import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
public class NavDrawerActivity extends BriarFragmentActivity implements
BaseFragment.BaseFragmentListener, EventListener {
BaseFragment.BaseFragmentListener, TransportStateListener {
public final static String PREF_SEEN_WELCOME_MESSAGE = "welcome_message";
public static final String INTENT_CONTACTS = "intent_contacts";
public static final String INTENT_FORUMS = "intent_forums";
@@ -57,20 +47,10 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
private ActionBarDrawerToggle drawerToggle;
@Inject
protected ReferenceManager referenceManager;
// Fields that are accessed from background threads must be volatile
@Inject
protected volatile IdentityManager identityManager;
@Inject
protected PluginManager pluginManager;
@Inject
protected volatile EventBus eventBus;
protected NavDrawerController controller;
private Toolbar toolbar;
private DrawerLayout drawerLayout;
private Button contactButton;
private Button forumsButton;
private Button settingsButton;
private GridView transportsView;
private TextView progressTitle;
private ViewGroup progressViewGroup;
@@ -85,14 +65,14 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
checkAuthorHandle(intent);
clearBackStack();
if (intent.getBooleanExtra(INTENT_FORUMS, false))
startFragment(ForumListFragment.newInstance());
startFragment(activityComponent.newForumListFragment());
else if (intent.getBooleanExtra(INTENT_CONTACTS, false))
startFragment(ContactListFragment.newInstance());
startFragment(activityComponent.newContactListFragment());
}
}
@Override
public void injectActivity(AndroidComponent component) {
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@@ -106,14 +86,11 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
setContentView(R.layout.activity_nav_drawer);
toolbar = (Toolbar)findViewById(R.id.toolbar);
drawerLayout = (DrawerLayout)findViewById(R.id.drawer_layout);
contactButton = (Button)findViewById(R.id.nav_btn_contacts);
forumsButton = (Button)findViewById(R.id.nav_btn_forums);
settingsButton = (Button)findViewById(R.id.nav_btn_settings);
transportsView = (GridView)findViewById(R.id.transportsView);
progressTitle = (TextView)findViewById(R.id.title_progress_bar);
progressViewGroup = (ViewGroup)findViewById(R.id.container_progress);
toolbar = (Toolbar) findViewById(R.id.toolbar);
drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
transportsView = (GridView) findViewById(R.id.transportsView);
progressTitle = (TextView) findViewById(R.id.title_progress_bar);
progressViewGroup = (ViewGroup) findViewById(R.id.container_progress);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
@@ -133,7 +110,7 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
}
};
drawerLayout.setDrawerListener(drawerToggle);
if (state == null) startFragment(ContactListFragment.newInstance());
if (state == null) startFragment(activityComponent.newContactListFragment());
checkAuthorHandle(getIntent());
initializeTransports(getLayoutInflater());
@@ -143,8 +120,7 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
}
private void welcomeMessageCheck() {
SharedPreferences prefs = getSharedPreferences(PREFS_NAME,
MODE_PRIVATE);
SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
if (!prefs.getBoolean(PREF_SEEN_WELCOME_MESSAGE, false)) {
showMessageDialog(R.string.dialog_title_welcome,
R.string.dialog_welcome_message);
@@ -155,22 +131,14 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
@Override
public void onResume() {
super.onResume();
eventBus.addListener(this);
updateTransports();
}
@Override
protected void onPause() {
super.onPause();
eventBus.removeListener(this);
}
private void checkAuthorHandle(Intent intent) {
long handle = intent.getLongExtra(KEY_LOCAL_AUTHOR_HANDLE, -1);
if (handle != -1) {
LocalAuthor a = controller.removeAuthorHandle(handle);
// The activity was launched from the setup wizard
LocalAuthor a = referenceManager.removeReference(handle,
LocalAuthor.class);
if (a != null) {
showLoadingScreen(true, R.string.progress_title_please_wait);
storeLocalAuthor(a);
@@ -189,27 +157,13 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
}
private void storeLocalAuthor(final LocalAuthor a) {
runOnDbThread(new Runnable() {
public void run() {
try {
long now = System.currentTimeMillis();
identityManager.addLocalAuthor(a);
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Storing author took " + duration + " ms");
runOnUiThread(new Runnable() {
public void run() {
hideLoadingScreen();
}
});
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
controller.storeLocalAuthor(a,
new UiResultHandler<Void>(this) {
@Override
public void onResultUi(Void result) {
hideLoadingScreen();
}
});
}
public void onNavigationClick(View view) {
@@ -217,10 +171,10 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
clearBackStack();
switch (view.getId()) {
case R.id.nav_btn_contacts:
startFragment(ContactListFragment.newInstance());
startFragment(activityComponent.newContactListFragment());
break;
case R.id.nav_btn_forums:
startFragment(ForumListFragment.newInstance());
startFragment(activityComponent.newForumListFragment());
break;
case R.id.nav_btn_settings:
startActivity(new Intent(this, SettingsActivity.class));
@@ -243,7 +197,7 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
public void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
drawerToggle.syncState();
}
@@ -285,24 +239,21 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
Transport tor = new Transport();
tor.id = new TransportId("tor");
Plugin torPlugin = pluginManager.getPlugin(tor.id);
tor.enabled = torPlugin != null && torPlugin.isRunning();
tor.enabled = controller.isTransportRunning(tor.id);
tor.iconId = R.drawable.transport_tor;
tor.textId = R.string.transport_tor;
transports.add(tor);
Transport bt = new Transport();
bt.id = new TransportId("bt");
Plugin btPlugin = pluginManager.getPlugin(bt.id);
bt.enabled = btPlugin != null && btPlugin.isRunning();
bt.enabled = controller.isTransportRunning(bt.id);
bt.iconId = R.drawable.transport_bt;
bt.textId = R.string.transport_bt;
transports.add(bt);
Transport lan = new Transport();
lan.id = new TransportId("lan");
Plugin lanPlugin = pluginManager.getPlugin(lan.id);
lan.enabled = lanPlugin != null && lanPlugin.isRunning();
lan.enabled = controller.isTransportRunning(lan.id);
lan.iconId = R.drawable.transport_lan;
lan.textId = R.string.transport_lan;
transports.add(lan);
@@ -369,27 +320,14 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
private void updateTransports() {
if (transports == null || transportsAdapter == null) return;
for (Transport t : transports) {
Plugin plugin = pluginManager.getPlugin(t.id);
t.enabled = plugin != null && plugin.isRunning();
t.enabled = controller.isTransportRunning(t.id);
}
transportsAdapter.notifyDataSetChanged();
}
@Override
public void eventOccurred(Event e) {
if (e instanceof TransportEnabledEvent) {
TransportId id = ((TransportEnabledEvent) e).getTransportId();
if (LOG.isLoggable(INFO)) {
LOG.info("TransportEnabledEvent: " + id.getString());
}
setTransport(id, true);
} else if (e instanceof TransportDisabledEvent) {
TransportId id = ((TransportDisabledEvent) e).getTransportId();
if (LOG.isLoggable(INFO)) {
LOG.info("TransportDisabledEvent: " + id.getString());
}
setTransport(id, false);
}
public void stateUpdate(TransportId id, boolean enabled) {
setTransport(id, enabled);
}
private static class Transport {

View File

@@ -3,6 +3,7 @@ package org.briarproject.android;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.TextInputLayout;
import android.support.v7.app.AlertDialog;
import android.text.Editable;
@@ -16,14 +17,9 @@ import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import org.briarproject.R;
import org.briarproject.android.controller.PasswordController;
import org.briarproject.android.controller.handler.UiResultHandler;
import org.briarproject.android.util.AndroidUtils;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.CryptoExecutor;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.db.DatabaseConfig;
import org.briarproject.util.StringUtils;
import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -34,28 +30,22 @@ import static android.view.View.VISIBLE;
public class PasswordActivity extends BaseActivity {
@Inject @CryptoExecutor protected Executor cryptoExecutor;
private Button signInButton;
private ProgressBar progress;
private TextInputLayout input;
private EditText password;
private byte[] encrypted;
// Fields that are accessed from background threads must be volatile
@Inject protected volatile CryptoComponent crypto;
@Inject protected volatile DatabaseConfig databaseConfig;
@Inject
PasswordController passwordController;
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
String hex = getEncryptedDatabaseKey();
if (hex == null || !databaseConfig.databaseExists()) {
if (!passwordController.initialized()) {
clearSharedPrefsAndDeleteEverything();
return;
}
encrypted = StringUtils.fromHexString(hex);
setContentView(R.layout.activity_password);
signInButton = (Button) findViewById(R.id.btn_sign_in);
@@ -66,8 +56,7 @@ public class PasswordActivity extends BaseActivity {
@Override
public boolean onEditorAction(TextView v, int actionId,
KeyEvent event) {
hideSoftKeyboard(password);
validatePassword(encrypted, password.getText());
validatePassword();
return true;
}
});
@@ -90,7 +79,7 @@ public class PasswordActivity extends BaseActivity {
}
@Override
public void injectActivity(AndroidComponent component) {
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@@ -103,7 +92,7 @@ public class PasswordActivity extends BaseActivity {
}
private void clearSharedPrefsAndDeleteEverything() {
clearSharedPrefs();
passwordController.clearPrefs();
AndroidUtils.deleteAppData(this);
setResult(RESULT_CANCELED);
startActivity(new Intent(this, SetupActivity.class));
@@ -111,7 +100,7 @@ public class PasswordActivity extends BaseActivity {
}
public void onSignInClick(View v) {
validatePassword(encrypted, password.getText());
validatePassword();
}
public void onForgottenPasswordClick(View v) {
@@ -132,47 +121,33 @@ public class PasswordActivity extends BaseActivity {
dialog.show();
}
private void validatePassword(final byte[] encrypted, Editable e) {
private void validatePassword() {
hideSoftKeyboard(password);
// Replace the button with a progress bar
signInButton.setVisibility(INVISIBLE);
progress.setVisibility(VISIBLE);
// Decrypt the database key in a background thread
final String password = e.toString();
cryptoExecutor.execute(new Runnable() {
public void run() {
byte[] key = crypto.decryptWithPassword(encrypted, password);
if (key == null) {
tryAgain();
} else {
databaseConfig.setEncryptionKey(new SecretKey(key));
setResultAndFinish();
}
}
});
passwordController.validatePassword(password.getText().toString(),
new UiResultHandler<Boolean>(this) {
@Override
public void onResultUi(@NonNull Boolean result) {
if (result) {
setResult(RESULT_OK);
finish();
} else {
tryAgain();
}
}
});
}
private void tryAgain() {
runOnUiThread(new Runnable() {
public void run() {
AndroidUtils.setError(input, getString(R.string.try_again),
true);
signInButton.setVisibility(VISIBLE);
progress.setVisibility(INVISIBLE);
password.setText("");
AndroidUtils.setError(input, getString(R.string.try_again),
true);
signInButton.setVisibility(VISIBLE);
progress.setVisibility(INVISIBLE);
password.setText("");
// show the keyboard again
showSoftKeyboard(password);
}
});
// show the keyboard again
showSoftKeyboard(password);
}
private void setResultAndFinish() {
runOnUiThread(new Runnable() {
public void run() {
setResult(RESULT_OK);
finish();
}
});
}
}

View File

@@ -34,7 +34,7 @@ public class SettingsActivity extends BriarActivity {
}
@Override
public void injectActivity(AndroidComponent component) {
public void injectActivity(ActivityComponent component) {
component.inject(this);
}

View File

@@ -2,6 +2,7 @@ package org.briarproject.android;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.TextInputLayout;
import android.text.Editable;
import android.text.TextWatcher;
@@ -15,29 +16,18 @@ import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import org.briarproject.R;
import org.briarproject.android.controller.SetupController;
import org.briarproject.android.controller.handler.UiResultHandler;
import org.briarproject.android.util.AndroidUtils;
import org.briarproject.android.util.StrengthMeter;
import org.briarproject.android.api.ReferenceManager;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.CryptoExecutor;
import org.briarproject.api.crypto.KeyPair;
import org.briarproject.api.crypto.PasswordStrengthEstimator;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.db.DatabaseConfig;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.util.StringUtils;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.inject.Inject;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static java.util.logging.Level.INFO;
import static org.briarproject.android.TestingConstants.PREVENT_SCREENSHOTS;
import static org.briarproject.api.crypto.PasswordStrengthEstimator.WEAK;
import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
@@ -45,17 +35,8 @@ import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENG
public class SetupActivity extends BaseActivity implements OnClickListener,
OnEditorActionListener {
private static final Logger LOG =
Logger.getLogger(SetupActivity.class.getName());
@Inject @CryptoExecutor protected Executor cryptoExecutor;
@Inject protected PasswordStrengthEstimator strengthEstimator;
// Fields that are accessed from background threads must be volatile
@Inject protected volatile CryptoComponent crypto;
@Inject protected volatile DatabaseConfig databaseConfig;
@Inject protected volatile AuthorFactory authorFactory;
@Inject protected volatile ReferenceManager referenceManager;
@Inject
protected SetupController setupController;
TextInputLayout nicknameEntryWrapper;
TextInputLayout passwordEntryWrapper;
@@ -72,15 +53,18 @@ public class SetupActivity extends BaseActivity implements OnClickListener,
super.onCreate(state);
setContentView(R.layout.activity_setup);
nicknameEntryWrapper = (TextInputLayout)findViewById(R.id.nickname_entry_wrapper);
passwordEntryWrapper = (TextInputLayout)findViewById(R.id.password_entry_wrapper);
passwordConfirmationWrapper = (TextInputLayout)findViewById(R.id.password_confirm_wrapper);
nicknameEntry = (EditText)findViewById(R.id.nickname_entry);
passwordEntry = (EditText)findViewById(R.id.password_entry);
passwordConfirmation = (EditText)findViewById(R.id.password_confirm);
strengthMeter = (StrengthMeter)findViewById(R.id.strength_meter);
createAccountButton = (Button)findViewById(R.id.create_account);
progress = (ProgressBar)findViewById(R.id.progress_wheel);
nicknameEntryWrapper =
(TextInputLayout) findViewById(R.id.nickname_entry_wrapper);
passwordEntryWrapper =
(TextInputLayout) findViewById(R.id.password_entry_wrapper);
passwordConfirmationWrapper =
(TextInputLayout) findViewById(R.id.password_confirm_wrapper);
nicknameEntry = (EditText) findViewById(R.id.nickname_entry);
passwordEntry = (EditText) findViewById(R.id.password_entry);
passwordConfirmation = (EditText) findViewById(R.id.password_confirm);
strengthMeter = (StrengthMeter) findViewById(R.id.strength_meter);
createAccountButton = (Button) findViewById(R.id.create_account);
progress = (ProgressBar) findViewById(R.id.progress_wheel);
if (PREVENT_SCREENSHOTS) getWindow().addFlags(FLAG_SECURE);
@@ -109,7 +93,7 @@ public class SetupActivity extends BaseActivity implements OnClickListener,
}
@Override
public void injectActivity(AndroidComponent component) {
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@@ -123,7 +107,7 @@ public class SetupActivity extends BaseActivity implements OnClickListener,
String firstPassword = passwordEntry.getText().toString();
String secondPassword = passwordConfirmation.getText().toString();
boolean passwordsMatch = firstPassword.equals(secondPassword);
float strength = strengthEstimator.estimateStrength(firstPassword);
float strength = setupController.estimatePasswordStrength(firstPassword);
strengthMeter.setStrength(strength);
AndroidUtils.setError(nicknameEntryWrapper,
getString(R.string.name_too_long),
@@ -141,6 +125,7 @@ public class SetupActivity extends BaseActivity implements OnClickListener,
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
hideSoftKeyboard(v);
return true;
}
@@ -150,52 +135,21 @@ public class SetupActivity extends BaseActivity implements OnClickListener,
progress.setVisibility(VISIBLE);
final String nickname = nicknameEntry.getText().toString();
final String password = passwordEntry.getText().toString();
// Store the DB key and create the identity in a background thread
cryptoExecutor.execute(new Runnable() {
public void run() {
SecretKey key = crypto.generateSecretKey();
databaseConfig.setEncryptionKey(key);
String hex = encryptDatabaseKey(key, password);
storeEncryptedDatabaseKey(hex);
LocalAuthor localAuthor = createLocalAuthor(nickname);
showDashboard(referenceManager.putReference(localAuthor,
LocalAuthor.class));
}
});
setupController.createIdentity(nickname, password,
new UiResultHandler<Long>(this) {
@Override
public void onResultUi(@NonNull Long result) {
showMain(result);
}
});
}
private String encryptDatabaseKey(SecretKey key, String password) {
long now = System.currentTimeMillis();
byte[] encrypted = crypto.encryptWithPassword(key.getBytes(), password);
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Key derivation took " + duration + " ms");
return StringUtils.toHexString(encrypted);
}
private LocalAuthor createLocalAuthor(String nickname) {
long now = System.currentTimeMillis();
KeyPair keyPair = crypto.generateSignatureKeyPair();
byte[] publicKey = keyPair.getPublic().getEncoded();
byte[] privateKey = keyPair.getPrivate().getEncoded();
LocalAuthor localAuthor = authorFactory.createLocalAuthor(nickname,
publicKey, privateKey);
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Identity creation took " + duration + " ms");
return localAuthor;
}
private void showDashboard(final long handle) {
runOnUiThread(new Runnable() {
public void run() {
Intent i = new Intent(SetupActivity.this,
NavDrawerActivity.class);
i.putExtra(BriarActivity.KEY_LOCAL_AUTHOR_HANDLE, handle);
i.setFlags(FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
finish();
}
});
private void showMain(final long handle) {
Intent i = new Intent(SetupActivity.this,
NavDrawerActivity.class);
i.putExtra(BriarActivity.KEY_LOCAL_AUTHOR_HANDLE, handle);
i.setFlags(FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
finish();
}
}

View File

@@ -10,8 +10,8 @@ import android.support.v7.preference.PreferenceManager;
import org.briarproject.R;
import org.briarproject.android.api.AndroidExecutor;
import org.briarproject.android.controller.ConfigController;
import org.briarproject.android.util.AndroidUtils;
import org.briarproject.api.db.DatabaseConfig;
import java.util.logging.Logger;
@@ -29,7 +29,7 @@ public class SplashScreenActivity extends BaseActivity {
private static final long EXPIRY_DATE = 1464735600 * 1000L;
@Inject
protected DatabaseConfig dbConfig;
ConfigController configController;
@Inject
protected AndroidExecutor androidExecutor;
@@ -56,7 +56,7 @@ public class SplashScreenActivity extends BaseActivity {
}
@Override
public void injectActivity(AndroidComponent component) {
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@@ -65,11 +65,11 @@ public class SplashScreenActivity extends BaseActivity {
LOG.info("Expired");
startActivity(new Intent(this, ExpiredActivity.class));
} else {
String hex = getEncryptedDatabaseKey();
if (hex != null && dbConfig.databaseExists()) {
if (configController.initialized()) {
startActivity(new Intent(this, NavDrawerActivity.class));
} else {
clearSharedPrefs();
configController.clearPrefs();
// TODO replace this static call with a controller method
AndroidUtils.deleteAppData(this);
startActivity(new Intent(this, SetupActivity.class));
}

View File

@@ -20,7 +20,7 @@ public class StartupFailureActivity extends BaseActivity {
}
@Override
public void injectActivity(AndroidComponent component) {
public void injectActivity(ActivityComponent component) {
}

View File

@@ -12,8 +12,7 @@ import android.view.View;
import android.view.ViewGroup;
import org.briarproject.R;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.fragment.BaseEventFragment;
import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.android.keyagreement.KeyAgreementActivity;
import org.briarproject.android.util.BriarRecyclerView;
import org.briarproject.api.contact.Contact;
@@ -28,6 +27,7 @@ import org.briarproject.api.event.ContactRemovedEvent;
import org.briarproject.api.event.ContactStatusChangedEvent;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.MessageValidatedEvent;
import org.briarproject.api.forum.ForumInvitationMessage;
import org.briarproject.api.forum.ForumSharingManager;
@@ -52,22 +52,13 @@ import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.android.BriarActivity.GROUP_ID;
public class ContactListFragment extends BaseEventFragment {
public class ContactListFragment extends BaseFragment implements EventListener {
private static final Logger LOG =
Logger.getLogger(ContactListFragment.class.getName());
public final static String TAG = "ContactListFragment";
public static ContactListFragment newInstance() {
Bundle args = new Bundle();
ContactListFragment fragment = new ContactListFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public String getUniqueTag() {
return TAG;
@@ -92,9 +83,10 @@ public class ContactListFragment extends BaseEventFragment {
@Inject
protected volatile EventBus eventBus;
@Override
public void injectActivity(AndroidComponent component) {
component.inject(this);
@Inject
public ContactListFragment() {
}
@Nullable
@@ -155,7 +147,7 @@ public class ContactListFragment extends BaseEventFragment {
@Override
public void onResume() {
super.onResume();
eventBus.addListener(this);
loadContacts();
}
@@ -163,6 +155,7 @@ public class ContactListFragment extends BaseEventFragment {
public void onPause() {
super.onPause();
adapter.clear();
eventBus.removeListener(this);
}
private void loadContacts() {

View File

@@ -23,6 +23,7 @@ import android.widget.TextView;
import android.widget.Toast;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.BriarActivity;
import org.briarproject.android.api.AndroidNotificationManager;
@@ -157,7 +158,7 @@ public class ConversationActivity extends BriarActivity
}
@Override
public void injectActivity(AndroidComponent component) {
public void injectActivity(ActivityComponent component) {
component.inject(this);
}

View File

@@ -0,0 +1,11 @@
package org.briarproject.android.controller;
public interface ActivityLifecycleController {
void onActivityCreate();
void onActivityResume();
void onActivityPause();
void onActivityDestroy();
}

View File

@@ -0,0 +1,14 @@
package org.briarproject.android.controller;
import org.briarproject.android.controller.handler.ResultHandler;
public interface BriarController extends ActivityLifecycleController {
void runOnDbThread(final Runnable task);
void startAndBindService();
boolean hasEncryptionKey();
void signOut(ResultHandler<Void> eventHandler);
}

View File

@@ -0,0 +1,118 @@
package org.briarproject.android.controller;
import android.app.Activity;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.CallSuper;
import org.briarproject.android.BriarService;
import org.briarproject.android.BriarService.BriarServiceConnection;
import org.briarproject.android.controller.handler.ResultHandler;
import org.briarproject.api.db.DatabaseConfig;
import org.briarproject.api.db.DatabaseExecutor;
import org.briarproject.api.lifecycle.LifecycleManager;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.inject.Inject;
public class BriarControllerImpl implements BriarController {
private static final Logger LOG =
Logger.getLogger(BriarControllerImpl.class.getName());
@Inject
protected BriarServiceConnection serviceConnection;
@Inject
protected DatabaseConfig databaseConfig;
// Fields that are accessed from background threads must be volatile
@Inject
@DatabaseExecutor
protected volatile Executor dbExecutor;
@Inject
protected volatile LifecycleManager lifecycleManager;
@Inject
protected Activity activity;
private boolean bound = false;
@Inject
public BriarControllerImpl() {
}
@Override
@CallSuper
public void onActivityCreate() {
if (databaseConfig.getEncryptionKey() != null) startAndBindService();
}
@Override
@CallSuper
public void onActivityResume() {
}
@Override
@CallSuper
public void onActivityPause() {
}
@Override
@CallSuper
public void onActivityDestroy() {
unbindService();
}
public void startAndBindService() {
activity.startService(new Intent(activity, BriarService.class));
bound = activity.bindService(new Intent(activity, BriarService.class),
serviceConnection, 0);
}
@Override
public boolean hasEncryptionKey() {
return databaseConfig.getEncryptionKey() != null;
}
@Override
public void signOut(final ResultHandler<Void> eventHandler) {
new Thread() {
@Override
public void run() {
try {
// Wait for the service to finish starting up
IBinder binder = serviceConnection.waitForBinder();
BriarService service = ((BriarService.BriarBinder) binder).getService();
service.waitForStartup();
// Shut down the service and wait for it to shut down
LOG.info("Shutting down service");
service.shutdown();
service.waitForShutdown();
} catch (InterruptedException e) {
LOG.warning("Interrupted while waiting for service");
}
eventHandler.onResult(null);
}
}.start();
}
private void unbindService() {
if (bound) activity.unbindService(serviceConnection);
}
public void runOnDbThread(final Runnable task) {
dbExecutor.execute(new Runnable() {
public void run() {
try {
lifecycleManager.waitForDatabase();
task.run();
} catch (InterruptedException e) {
LOG.warning("Interrupted while waiting for database");
Thread.currentThread().interrupt();
}
}
});
}
}

View File

@@ -0,0 +1,9 @@
package org.briarproject.android.controller;
public interface ConfigController {
String getEncryptedDatabaseKey();
void clearPrefs();
boolean initialized();
}

View File

@@ -0,0 +1,41 @@
package org.briarproject.android.controller;
import android.content.SharedPreferences;
import org.briarproject.api.db.DatabaseConfig;
import javax.inject.Inject;
public class ConfigControllerImpl implements ConfigController {
private final static String PREF_DB_KEY = "key";
@Inject
protected SharedPreferences briarPrefs;
@Inject
protected volatile DatabaseConfig databaseConfig;
@Inject
public ConfigControllerImpl() {
}
public String getEncryptedDatabaseKey() {
return briarPrefs.getString(PREF_DB_KEY, null);
}
public void clearPrefs() {
SharedPreferences.Editor editor = briarPrefs.edit();
editor.clear();
editor.apply();
}
@Override
public boolean initialized() {
String hex = getEncryptedDatabaseKey();
if (hex != null && databaseConfig.databaseExists()) {
return true;
}
return false;
}
}

View File

@@ -0,0 +1,16 @@
package org.briarproject.android.controller;
import org.briarproject.android.controller.handler.UiResultHandler;
import org.briarproject.api.TransportId;
import org.briarproject.api.identity.LocalAuthor;
public interface NavDrawerController extends BriarController {
void setTransportListener(TransportStateListener transportListener);
boolean isTransportRunning(TransportId transportId);
void storeLocalAuthor(LocalAuthor author,
UiResultHandler<Void> resultHandler);
LocalAuthor removeAuthorHandle(long handle);
}

View File

@@ -0,0 +1,136 @@
package org.briarproject.android.controller;
import android.app.Activity;
import org.briarproject.android.api.ReferenceManager;
import org.briarproject.android.controller.handler.UiResultHandler;
import org.briarproject.api.TransportId;
import org.briarproject.api.db.DbException;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.TransportDisabledEvent;
import org.briarproject.api.event.TransportEnabledEvent;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.plugins.PluginManager;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
public class NavDrawerControllerImpl extends BriarControllerImpl
implements NavDrawerController, EventListener {
private static final Logger LOG =
Logger.getLogger(NavDrawerControllerImpl.class.getName());
@Inject
protected ReferenceManager referenceManager;
@Inject
protected volatile IdentityManager identityManager;
@Inject
protected PluginManager pluginManager;
@Inject
protected EventBus eventBus;
@Inject
protected Activity activity;
private List<Plugin> transports = new ArrayList<Plugin>();
private TransportStateListener transportStateListener;
@Inject
public NavDrawerControllerImpl() {
}
@Override
public void onActivityCreate() {
super.onActivityCreate();
}
@Override
public void onActivityResume() {
super.onActivityResume();
eventBus.addListener(this);
}
@Override
public void onActivityPause() {
super.onActivityPause();
eventBus.removeListener(this);
}
@Override
public void eventOccurred(Event e) {
if (e instanceof TransportEnabledEvent) {
TransportId id = ((TransportEnabledEvent) e).getTransportId();
if (LOG.isLoggable(INFO)) {
LOG.info("TransportEnabledEvent: " + id.getString());
}
transportStateUpdate(id, true);
} else if (e instanceof TransportDisabledEvent) {
TransportId id = ((TransportDisabledEvent) e).getTransportId();
if (LOG.isLoggable(INFO)) {
LOG.info("TransportDisabledEvent: " + id.getString());
}
transportStateUpdate(id, false);
}
}
private void transportStateUpdate(final TransportId id,
final boolean enabled) {
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
if (transportStateListener != null) {
transportStateListener.stateUpdate(id, enabled);
}
}
});
}
@Override
public void setTransportListener(TransportStateListener transportListener) {
this.transportStateListener = transportListener;
}
@Override
public boolean isTransportRunning(TransportId transportId) {
Plugin plugin = pluginManager.getPlugin(transportId);
return plugin != null && plugin.isRunning();
}
@Override
public void storeLocalAuthor(final LocalAuthor author,
final UiResultHandler<Void> resultHandler) {
runOnDbThread(new Runnable() {
public void run() {
try {
long now = System.currentTimeMillis();
identityManager.addLocalAuthor(author);
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Storing author took " + duration + " ms");
resultHandler.onResult(null);
} catch (final DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
@Override
public LocalAuthor removeAuthorHandle(long handle) {
return referenceManager.removeReference(handle,
LocalAuthor.class);
}
}

View File

@@ -0,0 +1,8 @@
package org.briarproject.android.controller;
import org.briarproject.android.controller.handler.ResultHandler;
public interface PasswordController extends ConfigController {
void validatePassword(String password,
ResultHandler<Boolean> resultHandler);
}

View File

@@ -0,0 +1,54 @@
package org.briarproject.android.controller;
import android.app.Activity;
import org.briarproject.android.controller.handler.ResultHandler;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.CryptoExecutor;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.util.StringUtils;
import java.util.concurrent.Executor;
import javax.inject.Inject;
public class PasswordControllerImpl extends ConfigControllerImpl
implements PasswordController {
@Inject
@CryptoExecutor
protected Executor cryptoExecutor;
@Inject
protected CryptoComponent crypto;
@Inject
protected Activity activity;
@Inject
public PasswordControllerImpl() {
}
@Override
public void validatePassword(final String password,
final ResultHandler<Boolean> resultHandler) {
final byte[] encrypted = getEncryptedKey();
cryptoExecutor.execute(new Runnable() {
public void run() {
byte[] key = crypto.decryptWithPassword(encrypted, password);
if (key == null) {
resultHandler.onResult(false);
} else {
databaseConfig.setEncryptionKey(new SecretKey(key));
resultHandler.onResult(true);
}
}
});
}
private byte[] getEncryptedKey() {
String hex = getEncryptedDatabaseKey();
if (hex == null)
throw new IllegalStateException("Encrypted database key is null.");
return StringUtils.fromHexString(hex);
}
}

View File

@@ -0,0 +1,10 @@
package org.briarproject.android.controller;
import org.briarproject.android.controller.handler.ResultHandler;
public interface SetupController {
float estimatePasswordStrength(String password);
void createIdentity(String nickname, String password,
ResultHandler<Long> resultHandler);
}

View File

@@ -0,0 +1,107 @@
package org.briarproject.android.controller;
import android.app.Activity;
import android.content.SharedPreferences;
import org.briarproject.android.api.ReferenceManager;
import org.briarproject.android.controller.handler.ResultHandler;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.CryptoExecutor;
import org.briarproject.api.crypto.KeyPair;
import org.briarproject.api.crypto.PasswordStrengthEstimator;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.db.DatabaseConfig;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.util.StringUtils;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
public class SetupControllerImpl implements SetupController {
private static final Logger LOG =
Logger.getLogger(SetupControllerImpl.class.getName());
private final static String PREF_DB_KEY = "key";
@Inject
@CryptoExecutor
protected Executor cryptoExecutor;
@Inject
protected PasswordStrengthEstimator strengthEstimator;
// Fields that are accessed from background threads must be volatile
@Inject
protected volatile CryptoComponent crypto;
@Inject
protected volatile DatabaseConfig databaseConfig;
@Inject
protected volatile AuthorFactory authorFactory;
@Inject
protected volatile ReferenceManager referenceManager;
@Inject
protected Activity activity;
@Inject
protected SharedPreferences briarPrefs;
@Inject
public SetupControllerImpl() {
}
private String encryptDatabaseKey(SecretKey key, String password) {
long now = System.currentTimeMillis();
byte[] encrypted = crypto.encryptWithPassword(key.getBytes(), password);
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Key derivation took " + duration + " ms");
return StringUtils.toHexString(encrypted);
}
private LocalAuthor createLocalAuthor(String nickname) {
long now = System.currentTimeMillis();
KeyPair keyPair = crypto.generateSignatureKeyPair();
byte[] publicKey = keyPair.getPublic().getEncoded();
byte[] privateKey = keyPair.getPrivate().getEncoded();
LocalAuthor localAuthor = authorFactory.createLocalAuthor(nickname,
publicKey, privateKey);
long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO))
LOG.info("Identity creation took " + duration + " ms");
return localAuthor;
}
@Override
public float estimatePasswordStrength(String password) {
return strengthEstimator.estimateStrength(password);
}
@Override
public void createIdentity(final String nickname, final String password,
final ResultHandler<Long> resultHandler) {
cryptoExecutor.execute(new Runnable() {
public void run() {
SecretKey key = crypto.generateSecretKey();
databaseConfig.setEncryptionKey(key);
String hex = encryptDatabaseKey(key, password);
storeEncryptedDatabaseKey(hex);
LocalAuthor localAuthor = createLocalAuthor(nickname);
long handle = referenceManager.putReference(localAuthor,
LocalAuthor.class);
resultHandler.onResult(handle);
}
});
}
private void storeEncryptedDatabaseKey(final String hex) {
SharedPreferences.Editor editor = briarPrefs.edit();
editor.putString(PREF_DB_KEY, hex);
editor.apply();
}
}

View File

@@ -0,0 +1,7 @@
package org.briarproject.android.controller;
import org.briarproject.api.TransportId;
public interface TransportStateListener {
void stateUpdate(TransportId id, boolean enabled);
}

View File

@@ -0,0 +1,6 @@
package org.briarproject.android.controller.handler;
public interface ResultExceptionHandler<R, E extends Exception> {
void onResult(R result);
void onException(E exception);
}

View File

@@ -0,0 +1,5 @@
package org.briarproject.android.controller.handler;
public interface ResultHandler<R> {
void onResult(R result);
}

View File

@@ -0,0 +1,33 @@
package org.briarproject.android.controller.handler;
import android.app.Activity;
public abstract class UiResultExceptionHandler<R, E extends Exception>
implements ResultExceptionHandler<R, E> {
private final Activity activity;
public UiResultExceptionHandler(Activity activity) {
this.activity = activity;
}
public void onResult(final R result) {
activity.runOnUiThread(new Runnable() {
public void run() {
onResultUi(result);
}
});
}
public void onException(final E exception) {
activity.runOnUiThread(new Runnable() {
public void run() {
onExceptionUi(exception);
}
});
}
public abstract void onResultUi(R result);
public abstract void onExceptionUi(E exception);
}

View File

@@ -0,0 +1,22 @@
package org.briarproject.android.controller.handler;
import android.app.Activity;
public abstract class UiResultHandler<R> implements ResultHandler<R> {
private final Activity activity;
public UiResultHandler(Activity activity) {
this.activity = activity;
}
public void onResult(final R result) {
activity.runOnUiThread(new Runnable() {
public void run() {
onResultUi(result);
}
});
}
public abstract void onResultUi(R result);
}

View File

@@ -5,6 +5,7 @@ import android.support.v7.widget.LinearLayoutManager;
import android.widget.Toast;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.BriarActivity;
import org.briarproject.android.util.BriarRecyclerView;
@@ -61,7 +62,7 @@ public class AvailableForumsActivity extends BriarActivity
}
@Override
public void injectActivity(AndroidComponent component) {
public void injectActivity(ActivityComponent component) {
component.inject(this);
}

View File

@@ -13,7 +13,6 @@ import android.view.View;
import android.view.ViewGroup;
import org.briarproject.R;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.contact.BaseContactListAdapter;
import org.briarproject.android.contact.ContactListItem;
import org.briarproject.android.fragment.BaseFragment;
@@ -63,13 +62,15 @@ public class ContactSelectorFragment extends BaseFragment implements
@Inject
protected volatile ForumSharingManager forumSharingManager;
public static ContactSelectorFragment newInstance(GroupId groupId) {
Bundle args = new Bundle();
args.putByteArray(GROUP_ID, groupId.getBytes());
public void initBundle(GroupId groupId) {
Bundle bundle = new Bundle();
bundle.putByteArray(GROUP_ID, groupId.getBytes());
setArguments(bundle);
}
@Inject
public ContactSelectorFragment() {
ContactSelectorFragment fragment = new ContactSelectorFragment();
fragment.setArguments(args);
return fragment;
}
@Override
@@ -83,11 +84,6 @@ public class ContactSelectorFragment extends BaseFragment implements
}
}
@Override
public void injectActivity(AndroidComponent component) {
component.inject(this);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

View File

@@ -15,7 +15,7 @@ import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;
import org.briarproject.R;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.BriarActivity;
import org.briarproject.api.db.DbException;
import org.briarproject.api.forum.Forum;
@@ -86,7 +86,7 @@ public class CreateForumActivity extends BriarActivity
}
@Override
public void injectActivity(AndroidComponent component) {
public void injectActivity(ActivityComponent component) {
component.inject(this);
}

View File

@@ -19,6 +19,7 @@ import android.widget.TextView;
import android.widget.Toast;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.BriarActivity;
import org.briarproject.android.api.AndroidNotificationManager;
@@ -127,7 +128,7 @@ public class ForumActivity extends BriarActivity implements EventListener,
}
@Override
public void injectActivity(AndroidComponent component) {
public void injectActivity(ActivityComponent component) {
component.inject(this);
}

View File

@@ -14,6 +14,7 @@ import android.view.View;
import android.view.ViewGroup;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.fragment.BaseEventFragment;
import org.briarproject.android.util.BriarRecyclerView;
@@ -50,14 +51,6 @@ public class ForumListFragment extends BaseEventFragment implements
private static final Logger LOG =
Logger.getLogger(ForumListFragment.class.getName());
public static ForumListFragment newInstance() {
Bundle args = new Bundle();
ForumListFragment fragment = new ForumListFragment();
fragment.setArguments(args);
return fragment;
}
private BriarRecyclerView list;
private ForumListAdapter adapter;
@@ -67,6 +60,11 @@ public class ForumListFragment extends BaseEventFragment implements
@Inject protected volatile ForumManager forumManager;
@Inject protected volatile ForumSharingManager forumSharingManager;
@Inject
public ForumListFragment() {
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
@@ -99,11 +97,6 @@ public class ForumListFragment extends BaseEventFragment implements
return TAG;
}
@Override
public void injectActivity(AndroidComponent component) {
component.inject(this);
}
@Override
public void onResume() {
super.onResume();

View File

@@ -12,6 +12,7 @@ import android.widget.ScrollView;
import android.widget.TextView;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.BriarActivity;
import org.briarproject.android.util.AuthorView;
@@ -168,7 +169,7 @@ implements OnClickListener {
}
@Override
public void injectActivity(AndroidComponent component) {
public void injectActivity(ActivityComponent component) {
component.inject(this);
}

View File

@@ -5,7 +5,7 @@ import android.os.Bundle;
import android.view.View;
import org.briarproject.R;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.BriarActivity;
import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.api.contact.ContactId;
@@ -33,7 +33,8 @@ public class ShareForumActivity extends BriarActivity implements
if (savedInstanceState == null) {
ContactSelectorFragment contactSelectorFragment =
ContactSelectorFragment.newInstance(groupId);
activityComponent.newContactSelectorFragment();
contactSelectorFragment.initBundle(groupId);
getSupportFragmentManager().beginTransaction()
.add(R.id.shareForumContainer, contactSelectorFragment)
.commit();
@@ -41,7 +42,7 @@ public class ShareForumActivity extends BriarActivity implements
}
@Override
public void injectActivity(AndroidComponent component) {
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@@ -49,7 +50,8 @@ public class ShareForumActivity extends BriarActivity implements
Collection<ContactId> contacts) {
ShareForumMessageFragment messageFragment =
ShareForumMessageFragment.newInstance(groupId, contacts);
activityComponent.newShareForumMessageFragment();
messageFragment.initBundle(groupId, contacts);
getSupportFragmentManager().beginTransaction()
.setCustomAnimations(android.R.anim.fade_in,

View File

@@ -13,7 +13,6 @@ import android.widget.TextView;
import android.widget.Toast;
import org.briarproject.R;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DbException;
@@ -45,17 +44,16 @@ public class ShareForumMessageFragment extends BaseFragment {
private volatile GroupId groupId;
private volatile Collection<ContactId> contacts;
public static ShareForumMessageFragment newInstance(GroupId groupId,
Collection<ContactId> contacts) {
public void initBundle(GroupId groupId, Collection<ContactId> contacts) {
Bundle bundle = new Bundle();
bundle.putByteArray(GROUP_ID, groupId.getBytes());
bundle.putIntegerArrayList(CONTACTS, getContactsFromIds(contacts));
setArguments(bundle);
}
ShareForumMessageFragment f = new ShareForumMessageFragment();
@Inject
public ShareForumMessageFragment() {
Bundle args = new Bundle();
args.putByteArray(GROUP_ID, groupId.getBytes());
args.putIntegerArrayList(CONTACTS, getContactsFromIds(contacts));
f.setArguments(args);
return f;
}
@Override
@@ -69,11 +67,6 @@ public class ShareForumMessageFragment extends BaseFragment {
}
}
@Override
public void injectActivity(AndroidComponent component) {
component.inject(this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {

View File

@@ -16,6 +16,7 @@ import android.widget.TextView;
import android.widget.Toast;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.BriarActivity;
import org.briarproject.android.identity.CreateIdentityActivity;
@@ -161,7 +162,7 @@ implements OnItemSelectedListener, OnClickListener {
}
@Override
public void injectActivity(AndroidComponent component) {
public void injectActivity(ActivityComponent component) {
component.inject(this);
}

View File

@@ -5,9 +5,6 @@ import org.briarproject.api.event.EventListener;
import javax.inject.Inject;
/**
* Created by Ernir Erlingsson (ernir@ymirmobile.com) on 8.1.2016.
*/
public abstract class BaseEventFragment extends BaseFragment implements
EventListener {

View File

@@ -4,9 +4,6 @@ import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.BriarApplication;
public abstract class BaseFragment extends Fragment {
public abstract String getUniqueTag();
@@ -27,16 +24,9 @@ public abstract class BaseFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BriarApplication application =
(BriarApplication) getActivity().getApplication();
injectActivity(application.getApplicationComponent());
}
public abstract void injectActivity(AndroidComponent component);
public interface BaseFragmentListener {
void showLoadingScreen(boolean isBlocking, int stringId);
void hideLoadingScreen();
@@ -45,4 +35,5 @@ public abstract class BaseFragment extends Fragment {
void runOnDbThread(Runnable runnable);
}
}

View File

@@ -1,67 +0,0 @@
package org.briarproject.android.fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.GridView;
import org.briarproject.R;
import org.briarproject.android.AndroidComponent;
import org.briarproject.api.event.Event;
import org.briarproject.api.plugins.PluginManager;
import java.util.logging.Logger;
import javax.inject.Inject;
public class DashboardFragment extends BaseEventFragment {
public final static String TAG = "DashboardFragment";
private static final Logger LOG =
Logger.getLogger(DashboardFragment.class.getName());
@Inject
protected PluginManager pluginManager;
public static DashboardFragment newInstance() {
Bundle args = new Bundle();
DashboardFragment fragment = new DashboardFragment();
fragment.setArguments(args);
return fragment;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View contentView =
inflater.inflate(R.layout.fragment_dashboard, container, false);
return contentView;
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
@Override
public String getUniqueTag() {
return TAG;
}
@Override
public void injectActivity(AndroidComponent component) {
}
@Override
public void eventOccurred(Event e) {
}
}

View File

@@ -14,6 +14,7 @@ import android.widget.TextView.OnEditorActionListener;
import android.widget.Toast;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.BriarActivity;
import org.briarproject.android.util.LayoutUtils;
@@ -114,7 +115,7 @@ implements OnEditorActionListener, OnClickListener {
}
@Override
public void injectActivity(AndroidComponent component) {
public void injectActivity(ActivityComponent component) {
component.inject(this);
}

View File

@@ -12,7 +12,6 @@ import android.view.View;
import android.view.ViewGroup;
import org.briarproject.R;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.contact.ContactListAdapter;
import org.briarproject.android.contact.ContactListItem;
import org.briarproject.android.contact.ConversationItem;
@@ -66,6 +65,11 @@ public class ContactChooserFragment extends BaseFragment {
@Inject
protected volatile ConnectionRegistry connectionRegistry;
@Inject
public ContactChooserFragment() {
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
@@ -77,11 +81,6 @@ public class ContactChooserFragment extends BaseFragment {
}
}
@Override
public void injectActivity(AndroidComponent component) {
component.inject(this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {

View File

@@ -3,6 +3,7 @@ package org.briarproject.android.introduction;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.transition.ChangeBounds;
import android.transition.Fade;
@@ -10,7 +11,7 @@ import android.view.MenuItem;
import android.view.View;
import org.briarproject.R;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.BriarActivity;
import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.api.contact.Contact;
@@ -33,15 +34,15 @@ public class IntroductionActivity extends BriarActivity implements
setContentView(R.layout.activity_introduction);
if (savedInstanceState == null) {
ContactChooserFragment chooserFragment =
new ContactChooserFragment();
getSupportFragmentManager().beginTransaction()
.add(R.id.introductionContainer, chooserFragment).commit();
.add(R.id.introductionContainer,
activityComponent.newContactChooserFragment())
.commit();
}
}
@Override
public void injectActivity(AndroidComponent component) {
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@@ -85,13 +86,14 @@ public class IntroductionActivity extends BriarActivity implements
final Contact c2) {
IntroductionMessageFragment messageFragment =
IntroductionMessageFragment
.newInstance(c1.getId().getInt(), c2.getId().getInt());
activityComponent.newIntroductionMessageFragment();
messageFragment.initBundle(c1.getId().getInt(), c2.getId().getInt());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
messageFragment.setSharedElementEnterTransition(new ChangeBounds());
messageFragment.setEnterTransition(new Fade());
messageFragment.setSharedElementReturnTransition(new ChangeBounds());
messageFragment
.setSharedElementReturnTransition(new ChangeBounds());
}
getSupportFragmentManager().beginTransaction()

View File

@@ -13,7 +13,6 @@ import android.widget.TextView;
import android.widget.Toast;
import org.briarproject.R;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.api.FormatException;
import org.briarproject.api.contact.Contact;
@@ -49,18 +48,19 @@ public class IntroductionMessageFragment extends BaseFragment {
@Inject
protected volatile IntroductionManager introductionManager;
public static IntroductionMessageFragment newInstance(int contactId1,
int contactId2) {
IntroductionMessageFragment f = new IntroductionMessageFragment();
public void initBundle(int contactId1, int contactId2) {
Bundle args = new Bundle();
args.putInt(CONTACT_ID_1, contactId1);
args.putInt(CONTACT_ID_2, contactId2);
f.setArguments(args);
return f;
setArguments(args);
}
@Inject
public IntroductionMessageFragment() {
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
@@ -72,11 +72,6 @@ public class IntroductionMessageFragment extends BaseFragment {
}
}
@Override
public void injectActivity(AndroidComponent component) {
component.inject(this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
@@ -183,7 +178,8 @@ public class IntroductionMessageFragment extends BaseFragment {
// actually make the introduction
try {
long timestamp = System.currentTimeMillis();
introductionManager.makeIntroduction(c1, c2, msg, timestamp);
introductionManager
.makeIntroduction(c1, c2, msg, timestamp);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);

View File

@@ -5,7 +5,7 @@ import android.os.Bundle;
import android.widget.Toast;
import org.briarproject.R;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.BriarActivity;
import org.briarproject.android.api.ReferenceManager;
import org.briarproject.api.crypto.CryptoComponent;
@@ -145,7 +145,7 @@ implements InvitationListener {
}
@Override
public void injectActivity(AndroidComponent component) {
public void injectActivity(ActivityComponent component) {
component.inject(this);
}

View File

@@ -13,6 +13,7 @@ import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Spinner;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.android.identity.CreateIdentityActivity;
@@ -59,11 +60,9 @@ public class ChooseIdentityFragment extends BaseFragment
@Inject
protected volatile IdentityManager identityManager;
public static ChooseIdentityFragment newInstance() {
Bundle args = new Bundle();
ChooseIdentityFragment fragment = new ChooseIdentityFragment();
fragment.setArguments(args);
return fragment;
@Inject
public ChooseIdentityFragment() {
}
@Override
@@ -82,11 +81,6 @@ public class ChooseIdentityFragment extends BaseFragment
return TAG;
}
@Override
public void injectActivity(AndroidComponent component) {
component.inject(this);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,

View File

@@ -7,6 +7,7 @@ import android.widget.TextView;
import android.widget.Toast;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.BriarFragmentActivity;
import org.briarproject.android.fragment.BaseFragment;
@@ -63,7 +64,7 @@ public class KeyAgreementActivity extends BriarFragmentActivity implements
protected volatile IdentityManager identityManager;
@Override
public void injectActivity(AndroidComponent component) {
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@@ -96,11 +97,11 @@ public class KeyAgreementActivity extends BriarFragmentActivity implements
STEPS));
switch (step) {
case STEP_QR:
startFragment(ShowQrCodeFragment.newInstance());
startFragment(activityComponent.newShowQrCodeFragment());
break;
case STEP_ID:
default:
startFragment(ChooseIdentityFragment.newInstance());
startFragment(activityComponent.newChooseIdentityFragment());
break;
}
}

View File

@@ -86,11 +86,9 @@ public class ShowQrCodeFragment extends BaseEventFragment
private volatile KeyAgreementTask task;
private volatile boolean waitingForBluetooth;
public static ShowQrCodeFragment newInstance() {
Bundle args = new Bundle();
ShowQrCodeFragment fragment = new ShowQrCodeFragment();
fragment.setArguments(args);
return fragment;
@Inject
public ShowQrCodeFragment() {
}
@Override
@@ -98,11 +96,6 @@ public class ShowQrCodeFragment extends BaseEventFragment
return TAG;
}
@Override
public void injectActivity(AndroidComponent component) {
component.inject(this);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,

View File

@@ -3,6 +3,7 @@ package org.briarproject.android.panic;
import android.os.Build;
import android.os.Bundle;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.BaseActivity;
@@ -23,7 +24,7 @@ public class ExitActivity extends BaseActivity {
}
@Override
public void injectActivity(AndroidComponent component) {
public void injectActivity(ActivityComponent component) {
}
}

View File

@@ -5,6 +5,7 @@ import android.support.v7.app.ActionBar;
import android.view.MenuItem;
import org.briarproject.R;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.BriarActivity;
@@ -24,7 +25,7 @@ public class PanicPreferencesActivity extends BriarActivity {
}
@Override
public void injectActivity(AndroidComponent component) {
public void injectActivity(ActivityComponent component) {
component.inject(this);
}

View File

@@ -7,11 +7,11 @@ import android.os.Build;
import android.os.Bundle;
import android.support.v7.preference.PreferenceManager;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.ActivityComponent;
import org.briarproject.android.BriarActivity;
import org.briarproject.android.api.AndroidExecutor;
import org.briarproject.android.controller.ConfigController;
import org.briarproject.android.util.AndroidUtils;
import org.briarproject.api.db.DatabaseConfig;
import org.iilab.IilabEngineeringRSA2048Pin;
import java.util.logging.Logger;
@@ -32,9 +32,7 @@ public class PanicResponderActivity extends BriarActivity {
private static final Logger LOG =
Logger.getLogger(PanicResponderActivity.class.getName());
@Inject
protected DatabaseConfig databaseConfig;
@Inject protected ConfigController configController;
@Inject
protected AndroidExecutor androidExecutor;
@@ -105,15 +103,16 @@ public class PanicResponderActivity extends BriarActivity {
}
@Override
public void injectActivity(AndroidComponent component) {
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
private void deleteAllData() {
androidExecutor.execute(new Runnable() {
public void run() {
clearSharedPrefs();
configController.clearPrefs();
// TODO somehow delete/shred the database more thoroughly
// TODO replace this static call with a controller method
AndroidUtils.deleteAppData(PanicResponderActivity.this);
PanicResponder.deleteAllAppData(PanicResponderActivity.this);

View File

@@ -23,11 +23,11 @@ import static org.briarproject.api.crypto.PasswordStrengthEstimator.WEAK;
public class StrengthMeter extends ProgressBar {
private static final int MAX = 100;
private static final int RED = Color.rgb(255, 0, 0);
private static final int ORANGE = Color.rgb(255, 160, 0);
private static final int YELLOW = Color.rgb(255, 255, 0);
private static final int LIME = Color.rgb(180, 255, 0);
private static final int GREEN = Color.rgb(0, 255, 0);
public static final int RED = Color.rgb(255, 0, 0);
public static final int ORANGE = Color.rgb(255, 160, 0);
public static final int YELLOW = Color.rgb(255, 255, 0);
public static final int LIME = Color.rgb(180, 255, 0);
public static final int GREEN = Color.rgb(0, 255, 0);
private final ShapeDrawable bar;
@@ -57,6 +57,10 @@ public class StrengthMeter extends ProgressBar {
return MAX;
}
public int getColor() {
return bar.getPaint().getColor();
}
public void setStrength(float strength) {
if (strength < 0 || strength > 1) throw new IllegalArgumentException();
int colour;

View File

@@ -8,6 +8,7 @@ import android.provider.Settings;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.logging.Logger;
import javax.inject.Inject;
@@ -15,6 +16,9 @@ import static android.provider.Settings.Secure.ANDROID_ID;
class AndroidSeedProvider extends LinuxSeedProvider {
private static final Logger LOG =
Logger.getLogger(LinuxSeedProvider.class.getName());
private final Context appContext;
@Inject
@@ -30,7 +34,10 @@ class AndroidSeedProvider extends LinuxSeedProvider {
if (Build.FINGERPRINT != null) out.writeUTF(Build.FINGERPRINT);
if (Build.SERIAL != null) out.writeUTF(Build.SERIAL);
ContentResolver contentResolver = appContext.getContentResolver();
out.writeUTF(Settings.Secure.getString(contentResolver, ANDROID_ID));
String str = Settings.Secure.getString(contentResolver, ANDROID_ID);
if (str != null) {
out.writeUTF(str);
}
super.writeToEntropyPool(out);
}
}

View File

@@ -0,0 +1,7 @@
package android.net.http;
// This class is here to fix an issue with Robolectric.
// https://github.com/robolectric/robolectric/issues/1862
// TODO Check if this class can be removed on next Robolectric update
public class AndroidHttpClient {
}

View File

@@ -0,0 +1,229 @@
package briarproject.activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.support.design.widget.TextInputLayout;
import android.widget.Button;
import android.widget.EditText;
import com.google.common.base.Strings;
import org.briarproject.BuildConfig;
import org.briarproject.R;
import org.briarproject.android.NavDrawerActivity;
import org.briarproject.android.controller.SetupController;
import org.briarproject.android.controller.handler.ResultHandler;
import org.briarproject.android.util.StrengthMeter;
import org.briarproject.api.identity.AuthorConstants;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowActivity;
import static junit.framework.Assert.assertEquals;
import static org.briarproject.api.crypto.PasswordStrengthEstimator.NONE;
import static org.briarproject.api.crypto.PasswordStrengthEstimator.QUITE_STRONG;
import static org.briarproject.api.crypto.PasswordStrengthEstimator.QUITE_WEAK;
import static org.briarproject.api.crypto.PasswordStrengthEstimator.STRONG;
import static org.briarproject.api.crypto.PasswordStrengthEstimator.WEAK;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
@RunWith(RobolectricGradleTestRunner.class)
@Config(constants = BuildConfig.class, sdk = 21,
application = TestBriarApplicationImp.class)
public class SetupActivityTest {
TestSetupActivity setupActivity;
TextInputLayout nicknameEntryWrapper;
TextInputLayout passwordEntryWrapper;
TextInputLayout passwordConfirmationWrapper;
EditText nicknameEntry;
EditText passwordEntry;
EditText passwordConfirmation;
StrengthMeter strengthMeter;
Button createAccountButton;
@Mock
SetupController setupController;
@Captor
ArgumentCaptor<ResultHandler<Long>> resultCaptor;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
setupActivity = Robolectric.setupActivity(TestSetupActivity.class);
nicknameEntryWrapper = (TextInputLayout) setupActivity
.findViewById(R.id.nickname_entry_wrapper);
passwordEntryWrapper = (TextInputLayout) setupActivity
.findViewById(R.id.password_entry_wrapper);
passwordConfirmationWrapper = (TextInputLayout) setupActivity
.findViewById(R.id.password_confirm_wrapper);
nicknameEntry =
(EditText) setupActivity.findViewById(R.id.nickname_entry);
passwordEntry =
(EditText) setupActivity.findViewById(R.id.password_entry);
passwordConfirmation =
(EditText) setupActivity.findViewById(R.id.password_confirm);
strengthMeter =
(StrengthMeter) setupActivity.findViewById(R.id.strength_meter);
createAccountButton =
(Button) setupActivity.findViewById(R.id.create_account);
}
private void testStrengthMeter(String pass, float strength, int color) {
passwordEntry.setText(pass);
assertEquals(strengthMeter.getProgress(),
(int) (strengthMeter.getMax() * strength));
assertEquals(color, strengthMeter.getColor());
}
@Test
public void testPasswordMatchUI() {
// Password mismatch
passwordEntry.setText("really.safe.password");
passwordConfirmation.setText("really.safe.pass");
assertEquals(createAccountButton.isEnabled(), false);
assertEquals(passwordConfirmationWrapper.getError(),
setupActivity.getString(R.string.passwords_do_not_match));
// Button enabled
passwordEntry.setText("really.safe.pass");
passwordConfirmation.setText("really.safe.pass");
// Confirm that the password mismatch error message is not visible
Assert.assertNotEquals(passwordConfirmationWrapper.getError(),
setupActivity.getString(R.string.passwords_do_not_match));
// Nick has not been set, expect the button to be disabled
assertEquals(createAccountButton.isEnabled(), false);
}
@Test
public void testCreateAccountUI() {
SetupController mockedController = this.setupController;
setupActivity.setController(mockedController);
// Mock strong password strength answer
when(mockedController.estimatePasswordStrength(anyString())).thenReturn(
STRONG);
String safePass = "really.safe.password";
String nick = "nick.nickerton";
passwordEntry.setText(safePass);
passwordConfirmation.setText(safePass);
nicknameEntry.setText(nick);
// Confirm that the create account button is clickable
assertEquals(createAccountButton.isEnabled(), true);
createAccountButton.performClick();
// Verify that the controller's method was called with the correct
// params and get the callback
verify(mockedController, times(1))
.createIdentity(eq(nick), eq(safePass), resultCaptor.capture());
// execute the callback
resultCaptor.getValue().onResult(1L);
assertEquals(setupActivity.isFinishing(), true);
// Confirm that the correct Activity has been started
ShadowActivity shadowActivity = shadowOf(setupActivity);
Intent intent = shadowActivity.peekNextStartedActivity();
assertEquals(intent.getComponent().getClassName(),
NavDrawerActivity.class.getName());
}
@Test
public void testNicknameUI() {
Assert.assertNotNull(setupActivity);
String longNick =
Strings.padEnd("*", AuthorConstants.MAX_AUTHOR_NAME_LENGTH + 1,
'*');
nicknameEntry.setText(longNick);
// Nickname should be too long
assertEquals(nicknameEntryWrapper.getError(),
setupActivity.getString(R.string.name_too_long));
}
@Test
public void testAccountCreation() {
SetupController controller = setupActivity.getController();
// mock a resulthandler
ResultHandler<Long> resultHandler =
(ResultHandler<Long>) mock(ResultHandler.class);
controller
.createIdentity("nick", "some.strong.pass", resultHandler);
// blocking verification call with timeout that waits until the mocked
// result gets called with handle 0L, the expected value
verify(resultHandler, timeout(2000).times(1)).onResult(0L);
SharedPreferences prefs =
setupActivity.getSharedPreferences("db", Context.MODE_PRIVATE);
// Confirm database key
assertTrue(prefs.contains("key"));
// Note that Robolectric uses its own persistant storage that it
// wipes clean after each test run, no need to clean up manually.
}
@Test
public void testStrengthMeter() {
SetupController controller = setupActivity.getController();
String strongPass = "very.strong.password.123";
String weakPass = "we";
String quiteStrongPass = "quite.strong";
float val = controller.estimatePasswordStrength(strongPass);
assertTrue(val == STRONG);
val = controller.estimatePasswordStrength(weakPass);
assertTrue(val < WEAK && val > NONE);
val = controller.estimatePasswordStrength(quiteStrongPass);
assertTrue(val < STRONG && val > QUITE_WEAK);
}
@Test
public void testStrengthMeterUI() {
Assert.assertNotNull(setupActivity);
// replace the setup controller with our mocked copy
SetupController mockedController = this.setupController;
setupActivity.setController(mockedController);
// Mock answers for UI testing only
when(mockedController.estimatePasswordStrength("strong")).thenReturn(
STRONG);
when(mockedController.estimatePasswordStrength("qstring")).thenReturn(
QUITE_STRONG);
when(mockedController.estimatePasswordStrength("qweak")).thenReturn(
QUITE_WEAK);
when(mockedController.estimatePasswordStrength("weak")).thenReturn(
WEAK);
when(mockedController.estimatePasswordStrength("empty")).thenReturn(
NONE);
// Test the meters progress and color for several values
testStrengthMeter("strong", STRONG, StrengthMeter.GREEN);
Mockito.verify(mockedController, Mockito.times(1))
.estimatePasswordStrength(eq("strong"));
testStrengthMeter("qstring", QUITE_STRONG, StrengthMeter.LIME);
Mockito.verify(mockedController, Mockito.times(1))
.estimatePasswordStrength(eq("qstring"));
testStrengthMeter("qweak", QUITE_WEAK, StrengthMeter.YELLOW);
Mockito.verify(mockedController, Mockito.times(1))
.estimatePasswordStrength(eq("qweak"));
testStrengthMeter("weak", WEAK, StrengthMeter.ORANGE);
Mockito.verify(mockedController, Mockito.times(1))
.estimatePasswordStrength(eq("weak"));
// Not sure this should be the correct behaviour on an empty input ?
testStrengthMeter("empty", NONE, StrengthMeter.RED);
Mockito.verify(mockedController, Mockito.times(1))
.estimatePasswordStrength(eq("empty"));
}
}

View File

@@ -0,0 +1,43 @@
package briarproject.activity;
import android.app.Application;
import org.briarproject.CoreModule;
import org.briarproject.android.AndroidComponent;
import org.briarproject.android.AndroidEagerSingletons;
import org.briarproject.android.AppModule;
import org.briarproject.android.BriarApplication;
import org.briarproject.android.DaggerAndroidComponent;
import java.util.logging.Logger;
/**
* This Class only exists to get around ACRA
*/
public class TestBriarApplicationImp extends Application implements
BriarApplication{
private static final Logger LOG =
Logger.getLogger(TestBriarApplicationImp.class.getName());
private AndroidComponent applicationComponent;
@Override
public void onCreate() {
super.onCreate();
LOG.info("Created");
applicationComponent = DaggerAndroidComponent.builder()
.appModule(new AppModule(this))
.build();
// We need to load the eager singletons directly after making the
// dependency graphs
CoreModule.initEagerSingletons(applicationComponent);
AndroidEagerSingletons.initEagerSingletons(applicationComponent);
}
public AndroidComponent getApplicationComponent() {
return applicationComponent;
}
}

View File

@@ -0,0 +1,20 @@
package briarproject.activity;
import org.briarproject.android.SetupActivity;
import org.briarproject.android.controller.SetupController;
/**
* This class exposes the SetupController and offers the possibility to
* override it.
*/
public class TestSetupActivity extends SetupActivity {
public SetupController getController() {
return this.setupController;
}
public void setController(SetupController setupController) {
this.setupController = setupController;
}
}

View File

@@ -26,7 +26,7 @@ public class IntroductionModule {
@Provides
@Singleton
MessageValidator getValidator(MessageQueueManager messageQueueManager,
MessageValidator provideValidator(MessageQueueManager messageQueueManager,
IntroductionManager introductionManager,
MetadataEncoder metadataEncoder, ClientHelper clientHelper,
Clock clock) {
@@ -43,7 +43,7 @@ public class IntroductionModule {
@Provides
@Singleton
IntroductionManager getIntroductionManager(
IntroductionManager provideIntroductionManager(
LifecycleManager lifecycleManager,
ContactManager contactManager,
MessageQueueManager messageQueueManager,

View File

@@ -1,6 +1,6 @@
package org.briarproject.system;
import static java.util.logging.Level.WARNING;
import org.briarproject.api.system.SeedProvider;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -13,7 +13,7 @@ import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import org.briarproject.api.system.SeedProvider;
import static java.util.logging.Level.WARNING;
class LinuxSeedProvider implements SeedProvider {