diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml index 10b50aa4d7fb22e251f1d3a2b0ea5a02744518e1..6901075a358d1da14b42e48c0276d8ce70e0eb9a 100644 --- a/briar-android/AndroidManifest.xml +++ b/briar-android/AndroidManifest.xml @@ -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" diff --git a/briar-android/build.gradle b/briar-android/build.gradle index 6ff778d86d62aa63fe035f7a0f434455f2dea65f..7b8deefc3722575fa5c6c914cb181a7fd84843d3 100644 --- a/briar-android/build.gradle +++ b/briar-android/build.gradle @@ -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, ... diff --git a/briar-android/proguard-rules.txt b/briar-android/proguard-rules.txt index 48d785a430ba139f85a88a0a71396a4b5b5671fc..3f11de5e3bc4635ddd3fbc97833267f45188b5b9 100644 --- a/briar-android/proguard-rules.txt +++ b/briar-android/proguard-rules.txt @@ -3,6 +3,7 @@ -dontpreverify -dontobfuscate -verbose +-useuniqueclassmembernames -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* # For comfortability in case we do obfuscate # -renamesourcefileattribute SourceFile diff --git a/briar-android/res/layout/fragment_dashboard.xml b/briar-android/res/layout/fragment_dashboard.xml deleted file mode 100644 index 1d73431f59b46c0699d6cef6895bdd8d54bafff1..0000000000000000000000000000000000000000 --- a/briar-android/res/layout/fragment_dashboard.xml +++ /dev/null @@ -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> \ No newline at end of file diff --git a/briar-android/src/org/briarproject/android/ActivityComponent.java b/briar-android/src/org/briarproject/android/ActivityComponent.java new file mode 100644 index 0000000000000000000000000000000000000000..529bbf7ba2a69379d6e56580d2ef8206467e0493 --- /dev/null +++ b/briar-android/src/org/briarproject/android/ActivityComponent.java @@ -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(); +} diff --git a/briar-android/src/org/briarproject/android/ActivityModule.java b/briar-android/src/org/briarproject/android/ActivityModule.java new file mode 100644 index 0000000000000000000000000000000000000000..1ed65e9beca59f3dd4a666910c5a44b0c45316c7 --- /dev/null +++ b/briar-android/src/org/briarproject/android/ActivityModule.java @@ -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; + } + +} diff --git a/briar-android/src/org/briarproject/android/ActivityScope.java b/briar-android/src/org/briarproject/android/ActivityScope.java new file mode 100644 index 0000000000000000000000000000000000000000..a6ceb30ab7d9896edcf523aa2d6d22ed377b2fde --- /dev/null +++ b/briar-android/src/org/briarproject/android/ActivityScope.java @@ -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 { +} diff --git a/briar-android/src/org/briarproject/android/AndroidComponent.java b/briar-android/src/org/briarproject/android/AndroidComponent.java index 8b2ac951ab65f8b7ed6e5565f850a054d06719bf..ba2578addca7d1cd26378440b28add5ba3b20420 100644 --- a/briar-android/src/org/briarproject/android/AndroidComponent.java +++ b/briar-android/src/org/briarproject/android/AndroidComponent.java @@ -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,62 +49,71 @@ import dagger.Component; AndroidSystemModule.class }) public interface AndroidComponent extends CoreEagerSingletons { + // Exposed objects + @CryptoExecutor + Executor cryptoExecutor(); - void inject(DevReportActivity devReportActivity); + PasswordStrengthEstimator passwordStrengthIndicator(); - void inject(SplashScreenActivity activity); + CryptoComponent cryptoComponent(); - void inject(SetupActivity activity); + DatabaseConfig databaseConfig(); - void inject(NavDrawerActivity activity); + AuthorFactory authFactory(); - void inject(PasswordActivity activity); + ReferenceManager referenceMangager(); - void inject(BriarService activity); + @DatabaseExecutor + Executor databaseExecutor(); + + LifecycleManager lifecycleManager(); - void inject(PanicResponderActivity activity); + IdentityManager identityManager(); - void inject(PanicPreferencesActivity activity); + PluginManager pluginManager(); - void inject(AddContactActivity activity); + EventBus eventBus(); - void inject(KeyAgreementActivity activity); + InvitationTaskFactory invitationTaskFactory(); - void inject(ConversationActivity activity); + AndroidNotificationManager androidNotificationManager(); - void inject(CreateIdentityActivity activity); + ConnectionRegistry connectionRegistry(); - void inject(AvailableForumsActivity activity); + ContactManager contactManager(); - void inject(WriteForumPostActivity activity); + MessagingManager messagingManager(); - void inject(CreateForumActivity activity); + PrivateMessageFactory privateMessageFactory(); - void inject(ShareForumActivity activity); + TransportPropertyManager transportPropertyManager(); - void inject(ContactSelectorFragment fragment); + ForumManager forumManager(); - void inject(ShareForumMessageFragment fragment); + ForumSharingManager forumSharingManager(); - void inject(ReadForumPostActivity activity); + ForumPostFactory forumPostFactory(); - void inject(ForumActivity activity); + SettingsManager settingsManager(); - void inject(SettingsActivity activity); + ContactExchangeTask contactExchangeTask(); - void inject(ContactListFragment fragment); + KeyAgreementTaskFactory keyAgreementTaskFactory(); - void inject(ForumListFragment fragment); + PayloadEncoder payloadEncoder(); - void inject(ChooseIdentityFragment fragment); + PayloadParser payloadParser(); - void inject(ShowQrCodeFragment fragment); + IntroductionManager introductionManager(); - void inject(IntroductionActivity activity); + AndroidExecutor androidExecutor(); - void inject(ContactChooserFragment fragment); + @IoExecutor + Executor ioExecutor(); - void inject(IntroductionMessageFragment fragment); + void inject(DevReportActivity devReportActivity); + + void inject(BriarService activity); // Eager singleton load void inject(AppModule.EagerSingletons init); diff --git a/briar-android/src/org/briarproject/android/BaseActivity.java b/briar-android/src/org/briarproject/android/BaseActivity.java index 1a93a006ec6b200e2522e8431c4168eefc1efdbc..fa2e68d6b8e57d60d3f7d693a5b14fb0e7a72275 100644 --- a/briar-android/src/org/briarproject/android/BaseActivity.java +++ b/briar-android/src/org/briarproject/android/BaseActivity.java @@ -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(); - public abstract void injectActivity(AndroidComponent component); + injectActivity(activityComponent); - private SharedPreferences getSharedPrefs() { - return getSharedPreferences(PREFS_NAME, MODE_PRIVATE); + for (ActivityLifecycleController alc : lifecycleControllers) { + alc.onActivityCreate(); + } } - protected String getEncryptedDatabaseKey() { - return getSharedPrefs().getString(PREF_DB_KEY, null); + // This exists to make test overrides easier + protected ActivityModule getActivityModule() { + return new ActivityModule(this); } - protected void storeEncryptedDatabaseKey(final String hex) { - SharedPreferences.Editor editor = getSharedPrefs().edit(); - editor.putString(PREF_DB_KEY, hex); - editor.apply(); + @Override + protected void onResume() { + super.onResume(); + for (ActivityLifecycleController alc : lifecycleControllers) { + alc.onActivityResume(); + } } - protected void clearSharedPrefs() { - SharedPreferences.Editor editor = getSharedPrefs().edit(); - editor.clear(); - editor.apply(); + @Override + protected void onPause() { + super.onPause(); + for (ActivityLifecycleController alc : lifecycleControllers) { + alc.onActivityPause(); + } } + @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); } + } diff --git a/briar-android/src/org/briarproject/android/BriarActivity.java b/briar-android/src/org/briarproject/android/BriarActivity.java index 8043b1a14038cb473963917a06bfe2e427b97edc..0da52cf2be6bfd4689d2d42afbbf5f9acdefea63 100644 --- a/briar-android/src/org/briarproject/android/BriarActivity.java +++ b/briar-android/src/org/briarproject/android/BriarActivity.java @@ -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() { diff --git a/briar-android/src/org/briarproject/android/BriarApplication.java b/briar-android/src/org/briarproject/android/BriarApplication.java index eec480309deb5f4ae863345868662f0a6d4d670c..cd28990adcaefb0cd28629747175e1b596629f13 100644 --- a/briar-android/src/org/briarproject/android/BriarApplication.java +++ b/briar-android/src/org/briarproject/android/BriarApplication.java @@ -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(); } diff --git a/briar-android/src/org/briarproject/android/BriarApplicationImpl.java b/briar-android/src/org/briarproject/android/BriarApplicationImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..5abe5dff2d542cec09ffd8be44e930ea24e6d800 --- /dev/null +++ b/briar-android/src/org/briarproject/android/BriarApplicationImpl.java @@ -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; + } +} diff --git a/briar-android/src/org/briarproject/android/BriarFragmentActivity.java b/briar-android/src/org/briarproject/android/BriarFragmentActivity.java index 766a11e33044711faa87e978d22682d9ea4b2fe8..00fe0f39acd18b60e4339ef8deaca47ebb305097 100644 --- a/briar-android/src/org/briarproject/android/BriarFragmentActivity.java +++ b/briar-android/src/org/briarproject/android/BriarFragmentActivity.java @@ -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(); diff --git a/briar-android/src/org/briarproject/android/DevReportActivity.java b/briar-android/src/org/briarproject/android/DevReportActivity.java index 2ea4e07e370b558cef1e433bb3a2febb0fb1295a..8f94b5a07624c4e9cc6f4e42d9ad8e7e01ebeb0c 100644 --- a/briar-android/src/org/briarproject/android/DevReportActivity.java +++ b/briar-android/src/org/briarproject/android/DevReportActivity.java @@ -89,6 +89,7 @@ public class DevReportActivity extends BaseCrashReportDialog ((BriarApplication) getApplication()).getApplicationComponent() .inject(this); + sharedPreferencesFactory = new SharedPreferencesFactory(getApplicationContext(), getConfig()); diff --git a/briar-android/src/org/briarproject/android/NavDrawerActivity.java b/briar-android/src/org/briarproject/android/NavDrawerActivity.java index 367bb12ba16daf71f903ce4e1218e610850dad15..4d18cd6ec588ebcb0834e503a72923ac3425fb9b 100644 --- a/briar-android/src/org/briarproject/android/NavDrawerActivity.java +++ b/briar-android/src/org/briarproject/android/NavDrawerActivity.java @@ -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 { diff --git a/briar-android/src/org/briarproject/android/PasswordActivity.java b/briar-android/src/org/briarproject/android/PasswordActivity.java index 33f14f206b1b2c7ef29c640d061b3d1595dcd916..346b2f2cd96f5e69b1db0de6038d744c27b63913 100644 --- a/briar-android/src/org/briarproject/android/PasswordActivity.java +++ b/briar-android/src/org/briarproject/android/PasswordActivity.java @@ -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(""); - - // show the keyboard again - showSoftKeyboard(password); - } - }); + AndroidUtils.setError(input, getString(R.string.try_again), + true); + signInButton.setVisibility(VISIBLE); + progress.setVisibility(INVISIBLE); + password.setText(""); + + // show the keyboard again + showSoftKeyboard(password); } - private void setResultAndFinish() { - runOnUiThread(new Runnable() { - public void run() { - setResult(RESULT_OK); - finish(); - } - }); - } } diff --git a/briar-android/src/org/briarproject/android/SettingsActivity.java b/briar-android/src/org/briarproject/android/SettingsActivity.java index e270578a74065285f7ee43ee4f7dabc0ca8cffa2..bb783a06517004ff316d1d36763daafb61c8a0be 100644 --- a/briar-android/src/org/briarproject/android/SettingsActivity.java +++ b/briar-android/src/org/briarproject/android/SettingsActivity.java @@ -34,7 +34,7 @@ public class SettingsActivity extends BriarActivity { } @Override - public void injectActivity(AndroidComponent component) { + public void injectActivity(ActivityComponent component) { component.inject(this); } diff --git a/briar-android/src/org/briarproject/android/SetupActivity.java b/briar-android/src/org/briarproject/android/SetupActivity.java index 6caf20e5acbaf686aed94603143eb980750689df..13bb49f4b2410e1e222bfb285cbe79e6e6d03fef 100644 --- a/briar-android/src/org/briarproject/android/SetupActivity.java +++ b/briar-android/src/org/briarproject/android/SetupActivity.java @@ -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(); } } diff --git a/briar-android/src/org/briarproject/android/SplashScreenActivity.java b/briar-android/src/org/briarproject/android/SplashScreenActivity.java index 0ba0d47b9479aa1b8e59432ce4f7993cf199c0f4..e4ff0b9418a3397ab1e893dc267f05a1616a2b4e 100644 --- a/briar-android/src/org/briarproject/android/SplashScreenActivity.java +++ b/briar-android/src/org/briarproject/android/SplashScreenActivity.java @@ -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)); } diff --git a/briar-android/src/org/briarproject/android/StartupFailureActivity.java b/briar-android/src/org/briarproject/android/StartupFailureActivity.java index 5180c33c27fe72a5b9fe4b1009f8597bfe7560bf..b258369391ff701b735b130d1ee901c46d22ccb7 100644 --- a/briar-android/src/org/briarproject/android/StartupFailureActivity.java +++ b/briar-android/src/org/briarproject/android/StartupFailureActivity.java @@ -20,7 +20,7 @@ public class StartupFailureActivity extends BaseActivity { } @Override - public void injectActivity(AndroidComponent component) { + public void injectActivity(ActivityComponent component) { } diff --git a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java index 82137d92ad531ebbcb49cca43c635d7d39abb961..5e7e017cd6139f37769bfc4e0bee392947aa7be4 100644 --- a/briar-android/src/org/briarproject/android/contact/ContactListFragment.java +++ b/briar-android/src/org/briarproject/android/contact/ContactListFragment.java @@ -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() { diff --git a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java index 5a62bef1f3c378449c2ce8e8ff1178d9aba76fa9..045c58e7f165e4b72ba16e08fa8ed05093624ac5 100644 --- a/briar-android/src/org/briarproject/android/contact/ConversationActivity.java +++ b/briar-android/src/org/briarproject/android/contact/ConversationActivity.java @@ -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); } diff --git a/briar-android/src/org/briarproject/android/controller/ActivityLifecycleController.java b/briar-android/src/org/briarproject/android/controller/ActivityLifecycleController.java new file mode 100644 index 0000000000000000000000000000000000000000..68b15592ce12a740f3ffbd4c357501269136d57e --- /dev/null +++ b/briar-android/src/org/briarproject/android/controller/ActivityLifecycleController.java @@ -0,0 +1,11 @@ +package org.briarproject.android.controller; + +public interface ActivityLifecycleController { + void onActivityCreate(); + + void onActivityResume(); + + void onActivityPause(); + + void onActivityDestroy(); +} diff --git a/briar-android/src/org/briarproject/android/controller/BriarController.java b/briar-android/src/org/briarproject/android/controller/BriarController.java new file mode 100644 index 0000000000000000000000000000000000000000..4be89b1085be3057c68b3d6b1b54ce0f078798ae --- /dev/null +++ b/briar-android/src/org/briarproject/android/controller/BriarController.java @@ -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); +} diff --git a/briar-android/src/org/briarproject/android/controller/BriarControllerImpl.java b/briar-android/src/org/briarproject/android/controller/BriarControllerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..fd24291c78233b44cdc4c14cb3e61cddf654055a --- /dev/null +++ b/briar-android/src/org/briarproject/android/controller/BriarControllerImpl.java @@ -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(); + } + } + }); + } + +} diff --git a/briar-android/src/org/briarproject/android/controller/ConfigController.java b/briar-android/src/org/briarproject/android/controller/ConfigController.java new file mode 100644 index 0000000000000000000000000000000000000000..cddbc1bc688e98e21cc7e562175e91d073c52698 --- /dev/null +++ b/briar-android/src/org/briarproject/android/controller/ConfigController.java @@ -0,0 +1,9 @@ +package org.briarproject.android.controller; + +public interface ConfigController { + String getEncryptedDatabaseKey(); + + void clearPrefs(); + + boolean initialized(); +} diff --git a/briar-android/src/org/briarproject/android/controller/ConfigControllerImpl.java b/briar-android/src/org/briarproject/android/controller/ConfigControllerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..b823b98e2c879478517211cfedaeceeaa30efa90 --- /dev/null +++ b/briar-android/src/org/briarproject/android/controller/ConfigControllerImpl.java @@ -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; + } +} diff --git a/briar-android/src/org/briarproject/android/controller/NavDrawerController.java b/briar-android/src/org/briarproject/android/controller/NavDrawerController.java new file mode 100644 index 0000000000000000000000000000000000000000..56d48b1ca17feddac9c6cf0aa1363864681984dc --- /dev/null +++ b/briar-android/src/org/briarproject/android/controller/NavDrawerController.java @@ -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); +} diff --git a/briar-android/src/org/briarproject/android/controller/NavDrawerControllerImpl.java b/briar-android/src/org/briarproject/android/controller/NavDrawerControllerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..4be03f94999432a82e25051b354597eefbdc46ab --- /dev/null +++ b/briar-android/src/org/briarproject/android/controller/NavDrawerControllerImpl.java @@ -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); + } +} diff --git a/briar-android/src/org/briarproject/android/controller/PasswordController.java b/briar-android/src/org/briarproject/android/controller/PasswordController.java new file mode 100644 index 0000000000000000000000000000000000000000..bf1c9b230ec1349c2fda9f076d6880c7dac1fc6e --- /dev/null +++ b/briar-android/src/org/briarproject/android/controller/PasswordController.java @@ -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); +} diff --git a/briar-android/src/org/briarproject/android/controller/PasswordControllerImpl.java b/briar-android/src/org/briarproject/android/controller/PasswordControllerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..12f7e424072860a6853beee0d70542e76b0cb201 --- /dev/null +++ b/briar-android/src/org/briarproject/android/controller/PasswordControllerImpl.java @@ -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); + } +} diff --git a/briar-android/src/org/briarproject/android/controller/SetupController.java b/briar-android/src/org/briarproject/android/controller/SetupController.java new file mode 100644 index 0000000000000000000000000000000000000000..476db0f5e8cefddf5fa661e5ad9249281a768730 --- /dev/null +++ b/briar-android/src/org/briarproject/android/controller/SetupController.java @@ -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); +} diff --git a/briar-android/src/org/briarproject/android/controller/SetupControllerImpl.java b/briar-android/src/org/briarproject/android/controller/SetupControllerImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..29bed6d1940dbe3863666fc9ccd143e60b88b979 --- /dev/null +++ b/briar-android/src/org/briarproject/android/controller/SetupControllerImpl.java @@ -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(); + } + +} diff --git a/briar-android/src/org/briarproject/android/controller/TransportStateListener.java b/briar-android/src/org/briarproject/android/controller/TransportStateListener.java new file mode 100644 index 0000000000000000000000000000000000000000..0a00c0f7dc0c73d846e3222884b7d37311fde601 --- /dev/null +++ b/briar-android/src/org/briarproject/android/controller/TransportStateListener.java @@ -0,0 +1,7 @@ +package org.briarproject.android.controller; + +import org.briarproject.api.TransportId; + +public interface TransportStateListener { + void stateUpdate(TransportId id, boolean enabled); +} diff --git a/briar-android/src/org/briarproject/android/controller/handler/ResultExceptionHandler.java b/briar-android/src/org/briarproject/android/controller/handler/ResultExceptionHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..5d3cb030109f11e730e54c6ff3ecffe2e8667d60 --- /dev/null +++ b/briar-android/src/org/briarproject/android/controller/handler/ResultExceptionHandler.java @@ -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); +} diff --git a/briar-android/src/org/briarproject/android/controller/handler/ResultHandler.java b/briar-android/src/org/briarproject/android/controller/handler/ResultHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..55516f9422162e458633532d60043989dfb6c58b --- /dev/null +++ b/briar-android/src/org/briarproject/android/controller/handler/ResultHandler.java @@ -0,0 +1,5 @@ +package org.briarproject.android.controller.handler; + +public interface ResultHandler<R> { + void onResult(R result); +} diff --git a/briar-android/src/org/briarproject/android/controller/handler/UiResultExceptionHandler.java b/briar-android/src/org/briarproject/android/controller/handler/UiResultExceptionHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..7d9c08f4fb2b1ef3ad67f0806186b9b965b292e1 --- /dev/null +++ b/briar-android/src/org/briarproject/android/controller/handler/UiResultExceptionHandler.java @@ -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); +} diff --git a/briar-android/src/org/briarproject/android/controller/handler/UiResultHandler.java b/briar-android/src/org/briarproject/android/controller/handler/UiResultHandler.java new file mode 100644 index 0000000000000000000000000000000000000000..1998060a5c9ff4b736f0e4102e3237d19122f4b1 --- /dev/null +++ b/briar-android/src/org/briarproject/android/controller/handler/UiResultHandler.java @@ -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); +} diff --git a/briar-android/src/org/briarproject/android/forum/AvailableForumsActivity.java b/briar-android/src/org/briarproject/android/forum/AvailableForumsActivity.java index dd7a722e7518cdbf206f410a65b8e8e40f278c95..f1e1a7b9efb6cdee845c4338a0359ef6b122b487 100644 --- a/briar-android/src/org/briarproject/android/forum/AvailableForumsActivity.java +++ b/briar-android/src/org/briarproject/android/forum/AvailableForumsActivity.java @@ -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); } diff --git a/briar-android/src/org/briarproject/android/forum/ContactSelectorFragment.java b/briar-android/src/org/briarproject/android/forum/ContactSelectorFragment.java index 73de55ec1817a8f7dff2dde0a87576a1a5b0afca..cd5ff9bcffa3353f5abc0cf94c4b7765e6e2ad82 100644 --- a/briar-android/src/org/briarproject/android/forum/ContactSelectorFragment.java +++ b/briar-android/src/org/briarproject/android/forum/ContactSelectorFragment.java @@ -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); diff --git a/briar-android/src/org/briarproject/android/forum/CreateForumActivity.java b/briar-android/src/org/briarproject/android/forum/CreateForumActivity.java index a35834c93aead75bb04298bae7c94ac1efbfbaee..fbb10650e834a08d2f288279aebd3febb0063253 100644 --- a/briar-android/src/org/briarproject/android/forum/CreateForumActivity.java +++ b/briar-android/src/org/briarproject/android/forum/CreateForumActivity.java @@ -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); } diff --git a/briar-android/src/org/briarproject/android/forum/ForumActivity.java b/briar-android/src/org/briarproject/android/forum/ForumActivity.java index 9a2ec711d9233f87d2a60ad3d643433511de4419..1ea92c3c08b6c73d5c66def586d8796908042c0e 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumActivity.java +++ b/briar-android/src/org/briarproject/android/forum/ForumActivity.java @@ -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); } diff --git a/briar-android/src/org/briarproject/android/forum/ForumListFragment.java b/briar-android/src/org/briarproject/android/forum/ForumListFragment.java index 4aa853bd7e42dc5d04db6037ac689543c41566fd..0129990936a525b30ee901014cec39cb06faa345 100644 --- a/briar-android/src/org/briarproject/android/forum/ForumListFragment.java +++ b/briar-android/src/org/briarproject/android/forum/ForumListFragment.java @@ -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(); diff --git a/briar-android/src/org/briarproject/android/forum/ReadForumPostActivity.java b/briar-android/src/org/briarproject/android/forum/ReadForumPostActivity.java index f320ed6efd14a776d2f94c210846d53d82817438..cfc80ace2a4a4363327a4c88b3115bdc6cecbf64 100644 --- a/briar-android/src/org/briarproject/android/forum/ReadForumPostActivity.java +++ b/briar-android/src/org/briarproject/android/forum/ReadForumPostActivity.java @@ -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); } diff --git a/briar-android/src/org/briarproject/android/forum/ShareForumActivity.java b/briar-android/src/org/briarproject/android/forum/ShareForumActivity.java index 820e25666fdf0a26d0644a2ffa25cb0031aeb0dc..81bcdf8cd7025b179a4a4ba66ab36768926db478 100644 --- a/briar-android/src/org/briarproject/android/forum/ShareForumActivity.java +++ b/briar-android/src/org/briarproject/android/forum/ShareForumActivity.java @@ -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, diff --git a/briar-android/src/org/briarproject/android/forum/ShareForumMessageFragment.java b/briar-android/src/org/briarproject/android/forum/ShareForumMessageFragment.java index 7ed79d67cde563f0669adb6afde95095fbed7b51..ab2586666e9df61356badaec563bac9c78c5e23f 100644 --- a/briar-android/src/org/briarproject/android/forum/ShareForumMessageFragment.java +++ b/briar-android/src/org/briarproject/android/forum/ShareForumMessageFragment.java @@ -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) { - - ShareForumMessageFragment f = new ShareForumMessageFragment(); + 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); + } - Bundle args = new Bundle(); - args.putByteArray(GROUP_ID, groupId.getBytes()); - args.putIntegerArrayList(CONTACTS, getContactsFromIds(contacts)); - f.setArguments(args); + @Inject + public ShareForumMessageFragment() { - 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) { diff --git a/briar-android/src/org/briarproject/android/forum/WriteForumPostActivity.java b/briar-android/src/org/briarproject/android/forum/WriteForumPostActivity.java index 4087115e036c5598709903df94dd337e5959e5e2..d2185aa4630df3d1dcd024fd322a6c4bcdd154dd 100644 --- a/briar-android/src/org/briarproject/android/forum/WriteForumPostActivity.java +++ b/briar-android/src/org/briarproject/android/forum/WriteForumPostActivity.java @@ -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); } diff --git a/briar-android/src/org/briarproject/android/fragment/BaseEventFragment.java b/briar-android/src/org/briarproject/android/fragment/BaseEventFragment.java index f353b6e08dddc6c4862fc21a531b0b591f14dcf1..69ceab43a6281a5a7ae1f0ad901697a6b2651d82 100644 --- a/briar-android/src/org/briarproject/android/fragment/BaseEventFragment.java +++ b/briar-android/src/org/briarproject/android/fragment/BaseEventFragment.java @@ -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 { diff --git a/briar-android/src/org/briarproject/android/fragment/BaseFragment.java b/briar-android/src/org/briarproject/android/fragment/BaseFragment.java index 494f93c2dbfb2a74f2b4188a15ba96c8a1c7ef83..96efaad58c2a7201c795daefaf66d3dd326d2ba1 100644 --- a/briar-android/src/org/briarproject/android/fragment/BaseFragment.java +++ b/briar-android/src/org/briarproject/android/fragment/BaseFragment.java @@ -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); } + } diff --git a/briar-android/src/org/briarproject/android/fragment/DashboardFragment.java b/briar-android/src/org/briarproject/android/fragment/DashboardFragment.java deleted file mode 100644 index f98c16ab08336ebfc98b50e94fca9fa2dca5ef51..0000000000000000000000000000000000000000 --- a/briar-android/src/org/briarproject/android/fragment/DashboardFragment.java +++ /dev/null @@ -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) { - - } -} diff --git a/briar-android/src/org/briarproject/android/identity/CreateIdentityActivity.java b/briar-android/src/org/briarproject/android/identity/CreateIdentityActivity.java index d1d37f9facfcf74e757fba257fb2692755ec410d..b95ca581fcb5879bc198e345f0f25c07c2d8e53a 100644 --- a/briar-android/src/org/briarproject/android/identity/CreateIdentityActivity.java +++ b/briar-android/src/org/briarproject/android/identity/CreateIdentityActivity.java @@ -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); } diff --git a/briar-android/src/org/briarproject/android/introduction/ContactChooserFragment.java b/briar-android/src/org/briarproject/android/introduction/ContactChooserFragment.java index 11e30d6356ab9bfe2b27dffae8a251dc28a88d6d..7b1bcbb5cb90c6465841adf142e066ee8119e3e3 100644 --- a/briar-android/src/org/briarproject/android/introduction/ContactChooserFragment.java +++ b/briar-android/src/org/briarproject/android/introduction/ContactChooserFragment.java @@ -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) { diff --git a/briar-android/src/org/briarproject/android/introduction/IntroductionActivity.java b/briar-android/src/org/briarproject/android/introduction/IntroductionActivity.java index 2faee1a8ab22b30d7757d91702bca976a796eb6a..79d511291f0065a8db188f480b819e7a373116e3 100644 --- a/briar-android/src/org/briarproject/android/introduction/IntroductionActivity.java +++ b/briar-android/src/org/briarproject/android/introduction/IntroductionActivity.java @@ -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() diff --git a/briar-android/src/org/briarproject/android/introduction/IntroductionMessageFragment.java b/briar-android/src/org/briarproject/android/introduction/IntroductionMessageFragment.java index ed0547da4e0b9eb8b69e7c8d6ea7433ba392e0b6..48f0726c086d49de467bacbb17a9ff3ec483ea6e 100644 --- a/briar-android/src/org/briarproject/android/introduction/IntroductionMessageFragment.java +++ b/briar-android/src/org/briarproject/android/introduction/IntroductionMessageFragment.java @@ -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); + setArguments(args); + } + + @Inject + public IntroductionMessageFragment() { - return f; } + @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); diff --git a/briar-android/src/org/briarproject/android/invitation/AddContactActivity.java b/briar-android/src/org/briarproject/android/invitation/AddContactActivity.java index 2faf5f6193f074e43bc0c43d56794b42fb26365e..acd8d36a14b4259ab09b09ad201ba00b62c98301 100644 --- a/briar-android/src/org/briarproject/android/invitation/AddContactActivity.java +++ b/briar-android/src/org/briarproject/android/invitation/AddContactActivity.java @@ -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); } diff --git a/briar-android/src/org/briarproject/android/keyagreement/ChooseIdentityFragment.java b/briar-android/src/org/briarproject/android/keyagreement/ChooseIdentityFragment.java index 16818d36a83d84a1159a651eee0cbc5a582c2c82..40206766ed660e72b846596dd34cc57c8493aa55 100644 --- a/briar-android/src/org/briarproject/android/keyagreement/ChooseIdentityFragment.java +++ b/briar-android/src/org/briarproject/android/keyagreement/ChooseIdentityFragment.java @@ -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, diff --git a/briar-android/src/org/briarproject/android/keyagreement/KeyAgreementActivity.java b/briar-android/src/org/briarproject/android/keyagreement/KeyAgreementActivity.java index 185d3c0b1efc3d4f14daca2d1b312d417a79aadb..b2395eaa2fe985529914fe17384c434361c6d711 100644 --- a/briar-android/src/org/briarproject/android/keyagreement/KeyAgreementActivity.java +++ b/briar-android/src/org/briarproject/android/keyagreement/KeyAgreementActivity.java @@ -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; } } diff --git a/briar-android/src/org/briarproject/android/keyagreement/ShowQrCodeFragment.java b/briar-android/src/org/briarproject/android/keyagreement/ShowQrCodeFragment.java index cbe175d8e124c968002e17b3e2184a287485bb88..8a997168d94403c77ad8395fef40d11b1af9f1d4 100644 --- a/briar-android/src/org/briarproject/android/keyagreement/ShowQrCodeFragment.java +++ b/briar-android/src/org/briarproject/android/keyagreement/ShowQrCodeFragment.java @@ -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, diff --git a/briar-android/src/org/briarproject/android/panic/ExitActivity.java b/briar-android/src/org/briarproject/android/panic/ExitActivity.java index 4417c1fe8f8500ea9c85fa46f748c984979dcc7d..7e85402018ee18a2c7bb901439cea1dfa7a5337f 100644 --- a/briar-android/src/org/briarproject/android/panic/ExitActivity.java +++ b/briar-android/src/org/briarproject/android/panic/ExitActivity.java @@ -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) { } } \ No newline at end of file diff --git a/briar-android/src/org/briarproject/android/panic/PanicPreferencesActivity.java b/briar-android/src/org/briarproject/android/panic/PanicPreferencesActivity.java index c25461c347476556401f09c57da3264b724112ec..f0645f3c2ed495066ab7d0b67cb536fc8f2c4e3e 100644 --- a/briar-android/src/org/briarproject/android/panic/PanicPreferencesActivity.java +++ b/briar-android/src/org/briarproject/android/panic/PanicPreferencesActivity.java @@ -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); } diff --git a/briar-android/src/org/briarproject/android/panic/PanicResponderActivity.java b/briar-android/src/org/briarproject/android/panic/PanicResponderActivity.java index 87ba49e7023eada4ff2051e63baed477500d75a4..fbe549e6ee0cd1c17eca51d0da70966ec2373bf5 100644 --- a/briar-android/src/org/briarproject/android/panic/PanicResponderActivity.java +++ b/briar-android/src/org/briarproject/android/panic/PanicResponderActivity.java @@ -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); diff --git a/briar-android/src/org/briarproject/android/util/StrengthMeter.java b/briar-android/src/org/briarproject/android/util/StrengthMeter.java index eee1e547396ed6d466c9afce4764c3145ede77c1..83fb20b8f1221916ca97f4b95d633376507b891e 100644 --- a/briar-android/src/org/briarproject/android/util/StrengthMeter.java +++ b/briar-android/src/org/briarproject/android/util/StrengthMeter.java @@ -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; diff --git a/briar-android/src/org/briarproject/system/AndroidSeedProvider.java b/briar-android/src/org/briarproject/system/AndroidSeedProvider.java index c15e0b411a1efdb7e5805ea1e85acee4a4168592..59c7a6e3a5bc8f25d52d909c5a979af320f61409 100644 --- a/briar-android/src/org/briarproject/system/AndroidSeedProvider.java +++ b/briar-android/src/org/briarproject/system/AndroidSeedProvider.java @@ -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); } } diff --git a/briar-android/test/java/android/net/http/AndroidHttpClient.java b/briar-android/test/java/android/net/http/AndroidHttpClient.java new file mode 100644 index 0000000000000000000000000000000000000000..f2cf7a34632bbf4e3a5ef2220ac8a9d01e0da1da --- /dev/null +++ b/briar-android/test/java/android/net/http/AndroidHttpClient.java @@ -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 { +} \ No newline at end of file diff --git a/briar-android/test/java/briarproject/activity/SetupActivityTest.java b/briar-android/test/java/briarproject/activity/SetupActivityTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b58d033b25b53394602b52d00dd4836242a10717 --- /dev/null +++ b/briar-android/test/java/briarproject/activity/SetupActivityTest.java @@ -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")); + } + +} diff --git a/briar-android/test/java/briarproject/activity/TestBriarApplicationImp.java b/briar-android/test/java/briarproject/activity/TestBriarApplicationImp.java new file mode 100644 index 0000000000000000000000000000000000000000..8e29d9efd238946a641c836aec944196f7ddd97c --- /dev/null +++ b/briar-android/test/java/briarproject/activity/TestBriarApplicationImp.java @@ -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; + } +} diff --git a/briar-android/test/java/briarproject/activity/TestSetupActivity.java b/briar-android/test/java/briarproject/activity/TestSetupActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..923372a494b72a011757be7602cd457750b1d18a --- /dev/null +++ b/briar-android/test/java/briarproject/activity/TestSetupActivity.java @@ -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; + } + +} diff --git a/briar-core/src/org/briarproject/introduction/IntroductionModule.java b/briar-core/src/org/briarproject/introduction/IntroductionModule.java index 9e51aca734a7bec5c1317e413593cf2285861b45..4ea0ac6c3b5a9251d6b41db231f01f02cc3409fe 100644 --- a/briar-core/src/org/briarproject/introduction/IntroductionModule.java +++ b/briar-core/src/org/briarproject/introduction/IntroductionModule.java @@ -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, diff --git a/briar-core/src/org/briarproject/system/LinuxSeedProvider.java b/briar-core/src/org/briarproject/system/LinuxSeedProvider.java index 58b22d32adc29c43285a7c27aa6ce395b2e8af5d..6481b4a5f2f3d8eec0d2e8357898eeab7dc45713 100644 --- a/briar-core/src/org/briarproject/system/LinuxSeedProvider.java +++ b/briar-core/src/org/briarproject/system/LinuxSeedProvider.java @@ -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 {