Merge branch '1482-startup-activity' into 'master'

Combine Password and OpenDatabase Activity into StartupActivity

Closes #1482

See merge request briar/briar!1087
This commit is contained in:
akwizgran
2019-05-15 09:57:01 +00:00
44 changed files with 726 additions and 487 deletions

View File

@@ -73,13 +73,12 @@
</activity> </activity>
<activity <activity
android:name="org.briarproject.briar.android.login.PasswordActivity" android:name="org.briarproject.briar.android.login.StartupActivity"
android:label="@string/app_name" android:label="@string/app_name">
android:windowSoftInputMode="stateVisible">
</activity> </activity>
<activity <activity
android:name="org.briarproject.briar.android.login.SetupActivity" android:name="org.briarproject.briar.android.account.SetupActivity"
android:label="@string/setup_title" android:label="@string/setup_title"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
</activity> </activity>
@@ -94,11 +93,6 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name=".android.login.OpenDatabaseActivity"
android:label="@string/app_name"
android:launchMode="singleTop"/>
<activity <activity
android:name="org.briarproject.briar.android.navdrawer.NavDrawerActivity" android:name="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar" android:theme="@style/BriarTheme.NoActionBar"
@@ -420,7 +414,7 @@
</activity> </activity>
<activity <activity
android:name=".android.login.UnlockActivity" android:name=".android.account.UnlockActivity"
android:label="@string/lock_unlock" android:label="@string/lock_unlock"
android:launchMode="singleTask" android:launchMode="singleTask"
android:theme="@style/BriarTheme.NoActionBar"/> android:theme="@style/BriarTheme.NoActionBar"/>

View File

@@ -1,7 +1,10 @@
package org.briarproject.briar.android; package org.briarproject.briar.android;
import android.app.Activity;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import java.util.Collection; import java.util.Collection;
import java.util.logging.LogRecord; import java.util.logging.LogRecord;
@@ -11,6 +14,8 @@ import java.util.logging.LogRecord;
*/ */
public interface BriarApplication { public interface BriarApplication {
Class<? extends Activity> ENTRY_ACTIVITY = NavDrawerActivity.class;
Collection<LogRecord> getRecentLogRecords(); Collection<LogRecord> getRecentLogRecords();
AndroidComponent getApplicationComponent(); AndroidComponent getApplicationComponent();

View File

@@ -22,7 +22,6 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.logout.HideUiActivity; import org.briarproject.briar.android.logout.HideUiActivity;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.android.LockManager; import org.briarproject.briar.api.android.LockManager;
@@ -48,6 +47,7 @@ import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS;
import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY;
import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_CHANNEL_ID; import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_CHANNEL_ID;
import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_NOTIFICATION_ID; import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_NOTIFICATION_ID;
import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_CHANNEL_ID; import static org.briarproject.briar.api.android.AndroidNotificationManager.ONGOING_CHANNEL_ID;
@@ -182,7 +182,7 @@ public class BriarService extends Service {
NotificationManager nm = (NotificationManager) o; NotificationManager nm = (NotificationManager) o;
nm.notify(FAILURE_NOTIFICATION_ID, b.build()); nm.notify(FAILURE_NOTIFICATION_ID, b.build());
// Bring the dashboard to the front to clear the back stack // Bring the dashboard to the front to clear the back stack
i = new Intent(BriarService.this, NavDrawerActivity.class); i = new Intent(BriarService.this, ENTRY_ACTIVITY);
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP); i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
i.putExtra(EXTRA_STARTUP_FAILED, true); i.putExtra(EXTRA_STARTUP_FAILED, true);
startActivity(i); startActivity(i);

View File

@@ -74,7 +74,7 @@ public class StartupFailureActivity extends BaseActivity implements
@Override @Override
public void runOnDbThread(@NonNull Runnable runnable) { public void runOnDbThread(@NonNull Runnable runnable) {
throw new AssertionError("Deprecated and should not be used"); throw new UnsupportedOperationException();
} }
} }

View File

@@ -1,4 +1,4 @@
package org.briarproject.briar.android.login; package org.briarproject.briar.android.account;
import android.os.Bundle; import android.os.Bundle;
import android.support.design.widget.TextInputEditText; import android.support.design.widget.TextInputEditText;
@@ -21,6 +21,7 @@ import static android.view.inputmethod.EditorInfo.IME_ACTION_NONE;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.briar.android.util.UiUtils.setError; import static org.briarproject.briar.android.util.UiUtils.setError;
import static org.briarproject.briar.android.util.UiUtils.showSoftKeyboard;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -63,6 +64,12 @@ public class AuthorNameFragment extends SetupFragment {
return TAG; return TAG;
} }
@Override
public void onResume() {
super.onResume();
showSoftKeyboard(authorNameInput);
}
@Override @Override
protected String getHelpText() { protected String getHelpText() {
return getString(R.string.setup_name_explanation); return getString(R.string.setup_name_explanation);

View File

@@ -1,4 +1,4 @@
package org.briarproject.briar.android.login; package org.briarproject.briar.android.account;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Intent; import android.content.Intent;
@@ -13,8 +13,8 @@ import android.widget.ProgressBar;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.account.PowerView.OnCheckedChangedListener;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.login.PowerView.OnCheckedChangedListener;
import org.briarproject.briar.android.util.UiUtils; import org.briarproject.briar.android.util.UiUtils;
import static android.view.View.INVISIBLE; import static android.view.View.INVISIBLE;

View File

@@ -1,4 +1,4 @@
package org.briarproject.briar.android.login; package org.briarproject.briar.android.account;
import android.content.Context; import android.content.Context;

View File

@@ -1,4 +1,4 @@
package org.briarproject.briar.android.login; package org.briarproject.briar.android.account;
import android.content.Context; import android.content.Context;

View File

@@ -1,4 +1,4 @@
package org.briarproject.briar.android.login; package org.briarproject.briar.android.account;
import android.content.Context; import android.content.Context;
import android.os.Parcel; import android.os.Parcel;

View File

@@ -0,0 +1,131 @@
package org.briarproject.briar.android.account;
import android.os.Bundle;
import android.os.IBinder;
import android.support.design.widget.TextInputEditText;
import android.support.design.widget.TextInputLayout;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.ProgressBar;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.login.StrengthMeter;
import javax.annotation.Nullable;
import static android.content.Context.INPUT_METHOD_SERVICE;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
import static java.util.Objects.requireNonNull;
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK;
import static org.briarproject.briar.android.util.UiUtils.setError;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class SetPasswordFragment extends SetupFragment {
private final static String TAG = SetPasswordFragment.class.getName();
private TextInputLayout passwordEntryWrapper;
private TextInputLayout passwordConfirmationWrapper;
private TextInputEditText passwordEntry;
private TextInputEditText passwordConfirmation;
private StrengthMeter strengthMeter;
private Button nextButton;
private ProgressBar progressBar;
public static SetPasswordFragment newInstance() {
return new SetPasswordFragment();
}
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
}
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
requireNonNull(getActivity()).setTitle(getString(R.string.setup_password_intro));
View v = inflater.inflate(R.layout.fragment_setup_password, container,
false);
strengthMeter = v.findViewById(R.id.strength_meter);
passwordEntryWrapper = v.findViewById(R.id.password_entry_wrapper);
passwordEntry = v.findViewById(R.id.password_entry);
passwordEntry.requestFocus();
passwordConfirmationWrapper =
v.findViewById(R.id.password_confirm_wrapper);
passwordConfirmation = v.findViewById(R.id.password_confirm);
nextButton = v.findViewById(R.id.next);
progressBar = v.findViewById(R.id.progress);
passwordEntry.addTextChangedListener(this);
passwordConfirmation.addTextChangedListener(this);
nextButton.setOnClickListener(this);
if (!setupController.needToShowDozeFragment()) {
nextButton.setText(R.string.create_account_button);
passwordConfirmation.setImeOptions(IME_ACTION_DONE);
}
return v;
}
@Override
public String getUniqueTag() {
return TAG;
}
@Override
protected String getHelpText() {
return getString(R.string.setup_password_explanation);
}
@Override
public void onTextChanged(CharSequence authorName, int i, int i1, int i2) {
String password1 = passwordEntry.getText().toString();
String password2 = passwordConfirmation.getText().toString();
boolean passwordsMatch = password1.equals(password2);
strengthMeter
.setVisibility(password1.length() > 0 ? VISIBLE : INVISIBLE);
float strength = setupController.estimatePasswordStrength(password1);
strengthMeter.setStrength(strength);
boolean strongEnough = strength >= QUITE_WEAK;
setError(passwordEntryWrapper, getString(R.string.password_too_weak),
password1.length() > 0 && !strongEnough);
setError(passwordConfirmationWrapper,
getString(R.string.passwords_do_not_match),
password2.length() > 0 && !passwordsMatch);
boolean enabled = passwordsMatch && strongEnough;
nextButton.setEnabled(enabled);
passwordConfirmation.setOnEditorActionListener(enabled ? this : null);
}
@Override
public void onClick(View view) {
IBinder token = passwordEntry.getWindowToken();
Object o = getContext().getSystemService(INPUT_METHOD_SERVICE);
((InputMethodManager) o).hideSoftInputFromWindow(token, 0);
setupController.setPassword(passwordEntry.getText().toString());
if (setupController.needToShowDozeFragment()) {
setupController.showDozeFragment();
} else {
nextButton.setVisibility(INVISIBLE);
progressBar.setVisibility(VISIBLE);
setupController.createAccount();
}
}
}

View File

@@ -1,4 +1,4 @@
package org.briarproject.briar.android.login; package org.briarproject.briar.android.account;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.Intent; import android.content.Intent;
@@ -15,8 +15,11 @@ import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -86,7 +89,7 @@ public class SetupActivity extends BaseActivity
void showPasswordFragment() { void showPasswordFragment() {
if (authorName == null) throw new IllegalStateException(); if (authorName == null) throw new IllegalStateException();
showNextFragment(PasswordFragment.newInstance()); showNextFragment(SetPasswordFragment.newInstance());
} }
@TargetApi(23) @TargetApi(23)
@@ -97,8 +100,9 @@ public class SetupActivity extends BaseActivity
} }
void showApp() { void showApp() {
Intent i = new Intent(this, OpenDatabaseActivity.class); Intent i = new Intent(this, ENTRY_ACTIVITY);
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME); i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME |
FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i); startActivity(i);
supportFinishAfterTransition(); supportFinishAfterTransition();
overridePendingTransition(R.anim.screen_new_in, R.anim.screen_old_out); overridePendingTransition(R.anim.screen_new_in, R.anim.screen_old_out);

View File

@@ -1,9 +1,9 @@
package org.briarproject.briar.android.login; package org.briarproject.briar.android.account;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault @NotNullByDefault
public interface SetupController extends PasswordController { public interface SetupController {
void setSetupActivity(SetupActivity setupActivity); void setSetupActivity(SetupActivity setupActivity);
@@ -11,6 +11,8 @@ public interface SetupController extends PasswordController {
void setAuthorName(String authorName); void setAuthorName(String authorName);
float estimatePasswordStrength(String password);
void setPassword(String password); void setPassword(String password);
/** /**

View File

@@ -1,4 +1,4 @@
package org.briarproject.briar.android.login; package org.briarproject.briar.android.account;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
@@ -15,12 +15,15 @@ import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
@NotNullByDefault @NotNullByDefault
public class SetupControllerImpl extends PasswordControllerImpl public class SetupControllerImpl implements SetupController {
implements SetupController {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(SetupControllerImpl.class.getName()); Logger.getLogger(SetupControllerImpl.class.getName());
private final AccountManager accountManager;
private final PasswordStrengthEstimator strengthEstimator;
@IoExecutor
private final Executor ioExecutor;
@Nullable @Nullable
private volatile SetupActivity setupActivity; private volatile SetupActivity setupActivity;
@@ -28,7 +31,9 @@ public class SetupControllerImpl extends PasswordControllerImpl
SetupControllerImpl(AccountManager accountManager, SetupControllerImpl(AccountManager accountManager,
@IoExecutor Executor ioExecutor, @IoExecutor Executor ioExecutor,
PasswordStrengthEstimator strengthEstimator) { PasswordStrengthEstimator strengthEstimator) {
super(accountManager, ioExecutor, strengthEstimator); this.accountManager = accountManager;
this.strengthEstimator = strengthEstimator;
this.ioExecutor = ioExecutor;
} }
@Override @Override
@@ -51,6 +56,11 @@ public class SetupControllerImpl extends PasswordControllerImpl
setupActivity.setAuthorName(authorName); setupActivity.setAuthorName(authorName);
} }
@Override
public float estimatePasswordStrength(String password) {
return strengthEstimator.estimateStrength(password);
}
@Override @Override
public void setPassword(String password) { public void setPassword(String password) {
SetupActivity setupActivity = this.setupActivity; SetupActivity setupActivity = this.setupActivity;

View File

@@ -1,4 +1,4 @@
package org.briarproject.briar.android.login; package org.briarproject.briar.android.account;
import android.support.annotation.Nullable; import android.support.annotation.Nullable;
import android.text.Editable; import android.text.Editable;

View File

@@ -1,4 +1,4 @@
package org.briarproject.briar.android.login; package org.briarproject.briar.android.account;
import android.app.KeyguardManager; import android.app.KeyguardManager;
import android.content.Intent; import android.content.Intent;

View File

@@ -4,6 +4,11 @@ import android.app.Activity;
import org.briarproject.briar.android.AndroidComponent; import org.briarproject.briar.android.AndroidComponent;
import org.briarproject.briar.android.StartupFailureActivity; import org.briarproject.briar.android.StartupFailureActivity;
import org.briarproject.briar.android.account.AuthorNameFragment;
import org.briarproject.briar.android.account.DozeFragment;
import org.briarproject.briar.android.account.SetPasswordFragment;
import org.briarproject.briar.android.account.SetupActivity;
import org.briarproject.briar.android.account.UnlockActivity;
import org.briarproject.briar.android.blog.BlogActivity; import org.briarproject.briar.android.blog.BlogActivity;
import org.briarproject.briar.android.blog.BlogFragment; import org.briarproject.briar.android.blog.BlogFragment;
import org.briarproject.briar.android.blog.BlogModule; import org.briarproject.briar.android.blog.BlogModule;
@@ -37,14 +42,10 @@ import org.briarproject.briar.android.keyagreement.ContactExchangeActivity;
import org.briarproject.briar.android.keyagreement.ContactExchangeErrorFragment; import org.briarproject.briar.android.keyagreement.ContactExchangeErrorFragment;
import org.briarproject.briar.android.keyagreement.KeyAgreementActivity; import org.briarproject.briar.android.keyagreement.KeyAgreementActivity;
import org.briarproject.briar.android.keyagreement.KeyAgreementFragment; import org.briarproject.briar.android.keyagreement.KeyAgreementFragment;
import org.briarproject.briar.android.login.AuthorNameFragment;
import org.briarproject.briar.android.login.ChangePasswordActivity; import org.briarproject.briar.android.login.ChangePasswordActivity;
import org.briarproject.briar.android.login.DozeFragment; import org.briarproject.briar.android.login.OpenDatabaseFragment;
import org.briarproject.briar.android.login.OpenDatabaseActivity;
import org.briarproject.briar.android.login.PasswordActivity;
import org.briarproject.briar.android.login.PasswordFragment; import org.briarproject.briar.android.login.PasswordFragment;
import org.briarproject.briar.android.login.SetupActivity; import org.briarproject.briar.android.login.StartupActivity;
import org.briarproject.briar.android.login.UnlockActivity;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity; import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import org.briarproject.briar.android.panic.PanicPreferencesActivity; import org.briarproject.briar.android.panic.PanicPreferencesActivity;
import org.briarproject.briar.android.panic.PanicResponderActivity; import org.briarproject.briar.android.panic.PanicResponderActivity;
@@ -94,14 +95,12 @@ public interface ActivityComponent {
void inject(SplashScreenActivity activity); void inject(SplashScreenActivity activity);
void inject(StartupActivity activity);
void inject(SetupActivity activity); void inject(SetupActivity activity);
void inject(OpenDatabaseActivity activity);
void inject(NavDrawerActivity activity); void inject(NavDrawerActivity activity);
void inject(PasswordActivity activity);
void inject(PanicResponderActivity activity); void inject(PanicResponderActivity activity);
void inject(PanicPreferencesActivity activity); void inject(PanicPreferencesActivity activity);
@@ -177,12 +176,17 @@ public interface ActivityComponent {
void inject(PendingContactListActivity activity); void inject(PendingContactListActivity activity);
// Fragments // Fragments
void inject(AuthorNameFragment fragment); void inject(AuthorNameFragment fragment);
void inject(PasswordFragment fragment); void inject(SetPasswordFragment fragment);
void inject(DozeFragment fragment); void inject(DozeFragment fragment);
void inject(PasswordFragment imageFragment);
void inject(OpenDatabaseFragment activity);
void inject(ContactListFragment fragment); void inject(ContactListFragment fragment);
void inject(CreateGroupFragment fragment); void inject(CreateGroupFragment fragment);

View File

@@ -6,10 +6,10 @@ import org.briarproject.briar.android.controller.BriarController;
import org.briarproject.briar.android.controller.BriarControllerImpl; import org.briarproject.briar.android.controller.BriarControllerImpl;
import org.briarproject.briar.android.controller.DbController; import org.briarproject.briar.android.controller.DbController;
import org.briarproject.briar.android.controller.DbControllerImpl; import org.briarproject.briar.android.controller.DbControllerImpl;
import org.briarproject.briar.android.login.PasswordController; import org.briarproject.briar.android.login.ChangePasswordController;
import org.briarproject.briar.android.login.PasswordControllerImpl; import org.briarproject.briar.android.login.ChangePasswordControllerImpl;
import org.briarproject.briar.android.login.SetupController; import org.briarproject.briar.android.account.SetupController;
import org.briarproject.briar.android.login.SetupControllerImpl; import org.briarproject.briar.android.account.SetupControllerImpl;
import org.briarproject.briar.android.navdrawer.NavDrawerController; import org.briarproject.briar.android.navdrawer.NavDrawerController;
import org.briarproject.briar.android.navdrawer.NavDrawerControllerImpl; import org.briarproject.briar.android.navdrawer.NavDrawerControllerImpl;
@@ -48,8 +48,8 @@ public class ActivityModule {
@ActivityScope @ActivityScope
@Provides @Provides
PasswordController providePasswordController( ChangePasswordController providePasswordController(
PasswordControllerImpl passwordController) { ChangePasswordControllerImpl passwordController) {
return passwordController; return passwordController;
} }

View File

@@ -172,6 +172,12 @@ public abstract class BaseActivity extends AppCompatActivity
.commit(); .commit();
} }
protected boolean isFragmentAdded(String fragmentTag) {
FragmentManager fm = getSupportFragmentManager();
Fragment f = fm.findFragmentByTag(fragmentTag);
return f != null && f.isAdded();
}
private boolean showScreenFilterWarning() { private boolean showScreenFilterWarning() {
// If the dialog is already visible, filter the tap // If the dialog is already visible, filter the tap
ScreenFilterDialogFragment f = findDialogFragment(); ScreenFilterDialogFragment f = findDialogFragment();

View File

@@ -15,8 +15,8 @@ import org.briarproject.briar.R;
import org.briarproject.briar.android.controller.BriarController; import org.briarproject.briar.android.controller.BriarController;
import org.briarproject.briar.android.controller.DbController; import org.briarproject.briar.android.controller.DbController;
import org.briarproject.briar.android.controller.handler.UiResultHandler; import org.briarproject.briar.android.controller.handler.UiResultHandler;
import org.briarproject.briar.android.login.PasswordActivity; import org.briarproject.briar.android.login.StartupActivity;
import org.briarproject.briar.android.login.UnlockActivity; import org.briarproject.briar.android.account.UnlockActivity;
import org.briarproject.briar.android.logout.ExitActivity; import org.briarproject.briar.android.logout.ExitActivity;
import org.briarproject.briar.api.android.LockManager; import org.briarproject.briar.api.android.LockManager;
@@ -86,8 +86,8 @@ public abstract class BriarActivity extends BaseActivity {
if (!briarController.accountSignedIn() && !isFinishing()) { if (!briarController.accountSignedIn() && !isFinishing()) {
// Also check that the activity isn't finishing already. // Also check that the activity isn't finishing already.
// This is possible if we finished in onActivityResult(). // This is possible if we finished in onActivityResult().
// Launching another PasswordActivity would cause a loop. // Launching another StartupActivity would cause a loop.
Intent i = new Intent(this, PasswordActivity.class); Intent i = new Intent(this, StartupActivity.class);
startActivityForResult(i, REQUEST_PASSWORD); startActivityForResult(i, REQUEST_PASSWORD);
} else if (lockManager.isLocked() && !isFinishing()) { } else if (lockManager.isLocked() && !isFinishing()) {
// Also check that the activity isn't finishing already. // Also check that the activity isn't finishing already.

View File

@@ -8,6 +8,7 @@ import android.support.annotation.CallSuper;
import org.briarproject.bramble.api.account.AccountManager; import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.SettingsManager; import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.briar.android.BriarService; import org.briarproject.briar.android.BriarService;
@@ -21,6 +22,7 @@ import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STARTING_SERVICES;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE; import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting; import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting;
@@ -34,6 +36,7 @@ public class BriarControllerImpl implements BriarController {
private final BriarServiceConnection serviceConnection; private final BriarServiceConnection serviceConnection;
private final AccountManager accountManager; private final AccountManager accountManager;
private final LifecycleManager lifecycleManager;
private final Executor databaseExecutor; private final Executor databaseExecutor;
private final SettingsManager settingsManager; private final SettingsManager settingsManager;
private final DozeWatchdog dozeWatchdog; private final DozeWatchdog dozeWatchdog;
@@ -44,11 +47,13 @@ public class BriarControllerImpl implements BriarController {
@Inject @Inject
BriarControllerImpl(BriarServiceConnection serviceConnection, BriarControllerImpl(BriarServiceConnection serviceConnection,
AccountManager accountManager, AccountManager accountManager,
LifecycleManager lifecycleManager,
@DatabaseExecutor Executor databaseExecutor, @DatabaseExecutor Executor databaseExecutor,
SettingsManager settingsManager, DozeWatchdog dozeWatchdog, SettingsManager settingsManager, DozeWatchdog dozeWatchdog,
Activity activity) { Activity activity) {
this.serviceConnection = serviceConnection; this.serviceConnection = serviceConnection;
this.accountManager = accountManager; this.accountManager = accountManager;
this.lifecycleManager = lifecycleManager;
this.databaseExecutor = databaseExecutor; this.databaseExecutor = databaseExecutor;
this.settingsManager = settingsManager; this.settingsManager = settingsManager;
this.dozeWatchdog = dozeWatchdog; this.dozeWatchdog = dozeWatchdog;
@@ -84,7 +89,8 @@ public class BriarControllerImpl implements BriarController {
@Override @Override
public boolean accountSignedIn() { public boolean accountSignedIn() {
return accountManager.hasDatabaseKey(); return accountManager.hasDatabaseKey() &&
lifecycleManager.getLifecycleState().isAfter(STARTING_SERVICES);
} }
@Override @Override

View File

@@ -31,7 +31,7 @@ public class ChangePasswordActivity extends BriarActivity
implements OnClickListener, OnEditorActionListener { implements OnClickListener, OnEditorActionListener {
@Inject @Inject
protected PasswordController passwordController; protected ChangePasswordController passwordController;
private TextInputLayout currentPasswordEntryWrapper; private TextInputLayout currentPasswordEntryWrapper;
private TextInputLayout newPasswordEntryWrapper; private TextInputLayout newPasswordEntryWrapper;

View File

@@ -4,13 +4,10 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.android.controller.handler.ResultHandler; import org.briarproject.briar.android.controller.handler.ResultHandler;
@NotNullByDefault @NotNullByDefault
public interface PasswordController { public interface ChangePasswordController {
float estimatePasswordStrength(String password); float estimatePasswordStrength(String password);
void validatePassword(String password,
ResultHandler<Boolean> resultHandler);
void changePassword(String oldPassword, String newPassword, void changePassword(String oldPassword, String newPassword,
ResultHandler<Boolean> resultHandler); ResultHandler<Boolean> resultHandler);

View File

@@ -11,14 +11,14 @@ import java.util.concurrent.Executor;
import javax.inject.Inject; import javax.inject.Inject;
@NotNullByDefault @NotNullByDefault
public class PasswordControllerImpl implements PasswordController { public class ChangePasswordControllerImpl implements ChangePasswordController {
protected final AccountManager accountManager; protected final AccountManager accountManager;
protected final Executor ioExecutor; protected final Executor ioExecutor;
private final PasswordStrengthEstimator strengthEstimator; private final PasswordStrengthEstimator strengthEstimator;
@Inject @Inject
PasswordControllerImpl(AccountManager accountManager, ChangePasswordControllerImpl(AccountManager accountManager,
@IoExecutor Executor ioExecutor, @IoExecutor Executor ioExecutor,
PasswordStrengthEstimator strengthEstimator) { PasswordStrengthEstimator strengthEstimator) {
this.accountManager = accountManager; this.accountManager = accountManager;
@@ -31,13 +31,6 @@ public class PasswordControllerImpl implements PasswordController {
return strengthEstimator.estimateStrength(password); return strengthEstimator.estimateStrength(password);
} }
@Override
public void validatePassword(String password,
ResultHandler<Boolean> resultHandler) {
ioExecutor.execute(() ->
resultHandler.onResult(accountManager.signIn(password)));
}
@Override @Override
public void changePassword(String oldPassword, String newPassword, public void changePassword(String oldPassword, String newPassword,
ResultHandler<Boolean> resultHandler) { ResultHandler<Boolean> resultHandler) {

View File

@@ -1,101 +0,0 @@
package org.briarproject.briar.android.login;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.widget.ImageView;
import android.widget.TextView;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState;
import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import javax.inject.Inject;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.COMPACTING_DATABASE;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.MIGRATING_DATABASE;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STARTING_SERVICES;
@ParametersNotNullByDefault
public class OpenDatabaseActivity extends BriarActivity
implements EventListener {
@Inject
LifecycleManager lifecycleManager;
@Inject
EventBus eventBus;
private TextView textView;
private ImageView imageView;
private boolean showingMigration = false, showingCompaction = false;
@Override
public void onCreate(@Nullable Bundle state) {
super.onCreate(state);
setContentView(R.layout.activity_open_database);
textView = findViewById(R.id.textView);
imageView = findViewById(R.id.imageView);
}
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@Override
public void onStart() {
super.onStart();
LifecycleState state = lifecycleManager.getLifecycleState();
if (state.isAfter(STARTING_SERVICES)) {
finishAndStartApp();
} else {
if (state == MIGRATING_DATABASE) showMigration();
else if (state == COMPACTING_DATABASE) showCompaction();
eventBus.addListener(this);
}
}
@Override
protected void onStop() {
super.onStop();
eventBus.removeListener(this);
}
@Override
public void eventOccurred(Event e) {
if (e instanceof LifecycleEvent) {
LifecycleState state = ((LifecycleEvent) e).getLifecycleState();
if (state.isAfter(STARTING_SERVICES)) finishAndStartApp();
else if (state == MIGRATING_DATABASE) showMigration();
else if (state == COMPACTING_DATABASE) showCompaction();
}
}
private void showMigration() {
if (showingMigration) return;
textView.setText(R.string.startup_migrate_database);
imageView.setImageResource(R.drawable.startup_migration);
showingMigration = true;
}
private void showCompaction() {
if (showingCompaction) return;
textView.setText(R.string.startup_compact_database);
imageView.setImageResource(R.drawable.startup_migration);
showingCompaction = true;
}
private void finishAndStartApp() {
startActivity(new Intent(this, NavDrawerActivity.class));
supportFinishAfterTransition();
}
}

View File

@@ -0,0 +1,78 @@
package org.briarproject.briar.android.login;
import android.arch.lifecycle.ViewModelProvider;
import android.arch.lifecycle.ViewModelProviders;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.fragment.BaseFragment;
import javax.inject.Inject;
import static org.briarproject.briar.android.login.StartupViewModel.State;
import static org.briarproject.briar.android.login.StartupViewModel.State.COMPACTING;
import static org.briarproject.briar.android.login.StartupViewModel.State.MIGRATING;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class OpenDatabaseFragment extends BaseFragment {
final static String TAG = OpenDatabaseFragment.class.getName();
@Inject
ViewModelProvider.Factory viewModelFactory;
private TextView textView;
private ImageView imageView;
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
}
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_open_database, container,
false);
textView = v.findViewById(R.id.textView);
imageView = v.findViewById(R.id.imageView);
StartupViewModel viewModel = ViewModelProviders.of(requireActivity(),
viewModelFactory).get(StartupViewModel.class);
viewModel.getState().observe(this, this::onStateChanged);
return v;
}
private void onStateChanged(State state) {
if (state == MIGRATING) showMigration();
else if (state == COMPACTING) showCompaction();
}
private void showMigration() {
textView.setText(R.string.startup_migrate_database);
imageView.setImageResource(R.drawable.startup_migration);
}
private void showCompaction() {
textView.setText(R.string.startup_compact_database);
imageView.setImageResource(R.drawable.startup_migration);
}
@Override
public String getUniqueTag() {
return TAG;
}
}

View File

@@ -1,173 +0,0 @@
package org.briarproject.briar.android.login;
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;
import android.text.TextWatcher;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BaseActivity;
import org.briarproject.briar.android.controller.BriarController;
import org.briarproject.briar.android.controller.handler.UiResultHandler;
import org.briarproject.briar.android.util.UiUtils;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import javax.inject.Inject;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
import static org.briarproject.briar.android.util.UiUtils.enterPressed;
public class PasswordActivity extends BaseActivity {
@Inject
AccountManager accountManager;
@Inject
AndroidNotificationManager notificationManager;
@Inject
PasswordController passwordController;
@Inject
BriarController briarController;
private Button signInButton;
private ProgressBar progress;
private TextInputLayout input;
private EditText password;
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
// fade-in after splash screen instead of default animation
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
if (!accountManager.accountExists()) {
setResult(RESULT_CANCELED);
finish();
return;
}
setContentView(R.layout.activity_password);
signInButton = findViewById(R.id.btn_sign_in);
progress = findViewById(R.id.progress_wheel);
input = findViewById(R.id.password_layout);
password = findViewById(R.id.edit_password);
password.setOnEditorActionListener((v, actionId, event) -> {
if (actionId == IME_ACTION_DONE || enterPressed(actionId, event)) {
validatePassword();
return true;
}
return false;
});
password.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
if (count > 0) UiUtils.setError(input, null, false);
}
@Override
public void afterTextChanged(Editable s) {
}
});
}
@Override
public void onStart() {
super.onStart();
// If the user has already signed in, clean up this instance
if (briarController.accountSignedIn()) {
setResult(RESULT_OK);
finish();
} else {
notificationManager.blockSignInNotification();
notificationManager.clearSignInNotification();
}
}
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@Override
public void onBackPressed() {
// Move task and activity to the background instead of showing another
// password prompt. onActivityResult() won't be called in BriarActivity
moveTaskToBack(true);
}
private void deleteAccount() {
accountManager.deleteAccount();
startActivity(new Intent(this, SetupActivity.class));
setResult(RESULT_CANCELED);
finish();
}
public void onSignInClick(View v) {
validatePassword();
}
public void onForgottenPasswordClick(View v) {
// TODO Encapsulate the dialog in a re-usable fragment
AlertDialog.Builder builder = new AlertDialog.Builder(this,
R.style.BriarDialogTheme);
builder.setTitle(R.string.dialog_title_lost_password);
builder.setMessage(R.string.dialog_message_lost_password);
builder.setPositiveButton(R.string.cancel, null);
builder.setNegativeButton(R.string.delete,
(dialog, which) -> deleteAccount());
AlertDialog dialog = builder.create();
dialog.show();
}
private void validatePassword() {
hideSoftKeyboard(password);
signInButton.setVisibility(INVISIBLE);
progress.setVisibility(VISIBLE);
passwordController.validatePassword(password.getText().toString(),
new UiResultHandler<Boolean>(this) {
@Override
public void onResultUi(@NonNull Boolean result) {
if (result) {
setResult(RESULT_OK);
supportFinishAfterTransition();
// don't show closing animation,
// but one for opening NavDrawerActivity
overridePendingTransition(R.anim.screen_new_in,
R.anim.screen_old_out);
} else {
tryAgain();
}
}
});
}
private void tryAgain() {
UiUtils.setError(input, getString(R.string.try_again), true);
signInButton.setVisibility(VISIBLE);
progress.setVisibility(INVISIBLE);
password.setText("");
// show the keyboard again
showSoftKeyboard(password);
}
}

View File

@@ -1,13 +1,16 @@
package org.briarproject.briar.android.login; package org.briarproject.briar.android.login;
import android.arch.lifecycle.ViewModelProvider;
import android.arch.lifecycle.ViewModelProviders;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder;
import android.support.design.widget.TextInputEditText; import android.support.design.widget.TextInputEditText;
import android.support.design.widget.TextInputLayout; import android.support.design.widget.TextInputLayout;
import android.support.v7.app.AlertDialog;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button; import android.widget.Button;
import android.widget.ProgressBar; import android.widget.ProgressBar;
@@ -15,34 +18,33 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.util.UiUtils; import org.briarproject.briar.android.fragment.BaseFragment;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject;
import static android.content.Context.INPUT_METHOD_SERVICE;
import static android.view.View.INVISIBLE; import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE; import static android.view.inputmethod.EditorInfo.IME_ACTION_DONE;
import static java.util.Objects.requireNonNull; import static org.briarproject.briar.android.util.UiUtils.enterPressed;
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK; import static org.briarproject.briar.android.util.UiUtils.hideSoftKeyboard;
import static org.briarproject.briar.android.util.UiUtils.setError;
import static org.briarproject.briar.android.util.UiUtils.showSoftKeyboard;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public class PasswordFragment extends SetupFragment { public class PasswordFragment extends BaseFragment implements TextWatcher {
private final static String TAG = PasswordFragment.class.getName(); final static String TAG = PasswordFragment.class.getName();
private TextInputLayout passwordEntryWrapper; @Inject
private TextInputLayout passwordConfirmationWrapper; ViewModelProvider.Factory viewModelFactory;
private TextInputEditText passwordEntry;
private TextInputEditText passwordConfirmation;
private StrengthMeter strengthMeter;
private Button nextButton;
private ProgressBar progressBar;
public static PasswordFragment newInstance() { private StartupViewModel viewModel;
return new PasswordFragment(); private Button signInButton;
} private ProgressBar progress;
private TextInputLayout input;
private TextInputEditText password;
@Override @Override
public void injectFragment(ActivityComponent component) { public void injectFragment(ActivityComponent component) {
@@ -53,78 +55,88 @@ public class PasswordFragment extends SetupFragment {
public View onCreateView(LayoutInflater inflater, public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) { @Nullable Bundle savedInstanceState) {
requireNonNull(getActivity()).setTitle(getString(R.string.setup_password_intro)); View v = inflater.inflate(R.layout.fragment_password, container,
View v = inflater.inflate(R.layout.fragment_setup_password, container,
false); false);
strengthMeter = v.findViewById(R.id.strength_meter); viewModel = ViewModelProviders.of(requireActivity(), viewModelFactory)
passwordEntryWrapper = v.findViewById(R.id.password_entry_wrapper); .get(StartupViewModel.class);
passwordEntry = v.findViewById(R.id.password_entry); viewModel.getPasswordValidated().observeEvent(this, valid -> {
passwordConfirmationWrapper = if (!valid) onPasswordInvalid();
v.findViewById(R.id.password_confirm_wrapper); });
passwordConfirmation = v.findViewById(R.id.password_confirm);
nextButton = v.findViewById(R.id.next);
progressBar = v.findViewById(R.id.progress);
passwordEntry.addTextChangedListener(this); signInButton = v.findViewById(R.id.btn_sign_in);
passwordConfirmation.addTextChangedListener(this); signInButton.setOnClickListener(view -> onSignInButtonClicked());
nextButton.setOnClickListener(this); progress = v.findViewById(R.id.progress_wheel);
input = v.findViewById(R.id.password_layout);
if (!setupController.needToShowDozeFragment()) { password = v.findViewById(R.id.edit_password);
nextButton.setText(R.string.create_account_button); password.setOnEditorActionListener((view, actionId, event) -> {
passwordConfirmation.setImeOptions(IME_ACTION_DONE); if (actionId == IME_ACTION_DONE || enterPressed(actionId, event)) {
} onSignInButtonClicked();
return true;
}
return false;
});
password.addTextChangedListener(this);
v.findViewById(R.id.btn_forgotten)
.setOnClickListener(view -> onForgottenPasswordClick());
return v; return v;
} }
@Override
public void onResume() {
super.onResume();
showSoftKeyboard(password);
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
if (count > 0) setError(input, null, false);
}
@Override
public void afterTextChanged(Editable s) {
}
private void onSignInButtonClicked() {
hideSoftKeyboard(password);
signInButton.setVisibility(INVISIBLE);
progress.setVisibility(VISIBLE);
viewModel.validatePassword(password.getText().toString());
}
private void onPasswordInvalid() {
setError(input, getString(R.string.try_again), true);
signInButton.setVisibility(VISIBLE);
progress.setVisibility(INVISIBLE);
password.setText(null);
// show the keyboard again
showSoftKeyboard(password);
}
public void onForgottenPasswordClick() {
// TODO Encapsulate the dialog in a re-usable fragment
AlertDialog.Builder builder = new AlertDialog.Builder(requireContext(),
R.style.BriarDialogTheme);
builder.setTitle(R.string.dialog_title_lost_password);
builder.setMessage(R.string.dialog_message_lost_password);
builder.setPositiveButton(R.string.cancel, null);
builder.setNegativeButton(R.string.delete,
(dialog, which) -> viewModel.deleteAccount());
AlertDialog dialog = builder.create();
dialog.show();
}
@Override @Override
public String getUniqueTag() { public String getUniqueTag() {
return TAG; return TAG;
} }
@Override
protected String getHelpText() {
return getString(R.string.setup_password_explanation);
}
@Override
public void onTextChanged(CharSequence authorName, int i, int i1, int i2) {
String password1 = passwordEntry.getText().toString();
String password2 = passwordConfirmation.getText().toString();
boolean passwordsMatch = password1.equals(password2);
strengthMeter
.setVisibility(password1.length() > 0 ? VISIBLE : INVISIBLE);
float strength = setupController.estimatePasswordStrength(password1);
strengthMeter.setStrength(strength);
boolean strongEnough = strength >= QUITE_WEAK;
UiUtils.setError(passwordEntryWrapper,
getString(R.string.password_too_weak),
password1.length() > 0 && !strongEnough);
UiUtils.setError(passwordConfirmationWrapper,
getString(R.string.passwords_do_not_match),
password2.length() > 0 && !passwordsMatch);
boolean enabled = passwordsMatch && strongEnough;
nextButton.setEnabled(enabled);
passwordConfirmation.setOnEditorActionListener(enabled ? this : null);
}
@Override
public void onClick(View view) {
IBinder token = passwordEntry.getWindowToken();
Object o = getContext().getSystemService(INPUT_METHOD_SERVICE);
((InputMethodManager) o).hideSoftInputFromWindow(token, 0);
setupController.setPassword(passwordEntry.getText().toString());
if (setupController.needToShowDozeFragment()) {
setupController.showDozeFragment();
} else {
nextButton.setVisibility(INVISIBLE);
progressBar.setVisibility(VISIBLE);
setupController.createAccount();
}
}
} }

View File

@@ -0,0 +1,125 @@
package org.briarproject.briar.android.login;
import android.arch.lifecycle.ViewModelProvider;
import android.arch.lifecycle.ViewModelProviders;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.BriarService;
import org.briarproject.briar.android.account.SetupActivity;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BaseActivity;
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
import org.briarproject.briar.android.login.StartupViewModel.State;
import javax.inject.Inject;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static org.briarproject.briar.android.login.StartupViewModel.State.SIGNED_IN;
import static org.briarproject.briar.android.login.StartupViewModel.State.SIGNED_OUT;
import static org.briarproject.briar.android.login.StartupViewModel.State.STARTED;
import static org.briarproject.briar.android.login.StartupViewModel.State.STARTING;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class StartupActivity extends BaseActivity implements
BaseFragmentListener {
@Inject
ViewModelProvider.Factory viewModelFactory;
private StartupViewModel viewModel;
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@Override
public void onCreate(@Nullable Bundle state) {
super.onCreate(state);
// fade-in after splash screen instead of default animation
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
setContentView(R.layout.activity_fragment_container);
viewModel = ViewModelProviders.of(this, viewModelFactory)
.get(StartupViewModel.class);
if (!viewModel.accountExists()) {
// TODO ideally we would not have to delete the account again
// The account needs to deleted again to remove the database folder,
// because if it exists, we assume the database also exists
// and when clearing app data, the folder does not get deleted.
viewModel.deleteAccount();
onAccountDeleted();
return;
}
viewModel.getAccountDeleted().observeEvent(this, deleted -> {
if (deleted) onAccountDeleted();
});
viewModel.getState().observe(this, this::onStateChanged);
}
@Override
public void onStart() {
super.onStart();
viewModel.clearSignInNotification();
}
@Override
public void onBackPressed() {
// Move task and activity to the background instead of showing another
// password prompt.
// onActivityResult() won't be called in BriarActivity
moveTaskToBack(true);
}
private void onStateChanged(State state) {
if (state == SIGNED_OUT) {
// Configuration changes such as screen rotation
// can cause this to get called again.
// Don't replace the fragment in that case to not lose view state.
if (!isFragmentAdded(PasswordFragment.TAG)) {
showInitialFragment(new PasswordFragment());
}
} else if (state == SIGNED_IN) {
startService(new Intent(this, BriarService.class));
} else if (state == STARTING) {
// Only show OpenDatabaseFragment if not already visible.
// This can happen because several LifecycleManager states are
// mapped to STARTING, so this can get called several times
// as the app's lifecycle advances.
if (!isFragmentAdded(OpenDatabaseFragment.TAG)) {
showNextFragment(new OpenDatabaseFragment());
}
} else if (state == STARTED) {
setResult(RESULT_OK);
supportFinishAfterTransition();
overridePendingTransition(R.anim.screen_new_in,
R.anim.screen_old_out);
}
}
private void onAccountDeleted() {
setResult(RESULT_CANCELED);
finish();
Intent i = new Intent(this, SetupActivity.class);
i.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP |
FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_TASK_ON_HOME);
startActivity(i);
}
@Override
public void runOnDbThread(Runnable runnable) {
// we don't need this and shouldn't be forced to implement it
throw new UnsupportedOperationException();
}
}

View File

@@ -0,0 +1,131 @@
package org.briarproject.briar.android.login;
import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.support.annotation.UiThread;
import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState;
import org.briarproject.bramble.api.lifecycle.event.LifecycleEvent;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.android.viewmodel.LiveEvent;
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import java.util.concurrent.Executor;
import javax.inject.Inject;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.COMPACTING_DATABASE;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.MIGRATING_DATABASE;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.STARTING_SERVICES;
import static org.briarproject.briar.android.login.StartupViewModel.State.COMPACTING;
import static org.briarproject.briar.android.login.StartupViewModel.State.MIGRATING;
import static org.briarproject.briar.android.login.StartupViewModel.State.SIGNED_IN;
import static org.briarproject.briar.android.login.StartupViewModel.State.SIGNED_OUT;
import static org.briarproject.briar.android.login.StartupViewModel.State.STARTED;
import static org.briarproject.briar.android.login.StartupViewModel.State.STARTING;
@NotNullByDefault
public class StartupViewModel extends AndroidViewModel
implements EventListener {
enum State {SIGNED_OUT, SIGNED_IN, STARTING, MIGRATING, COMPACTING, STARTED}
private final AccountManager accountManager;
private final AndroidNotificationManager notificationManager;
private final EventBus eventBus;
@IoExecutor
private final Executor ioExecutor;
private final MutableLiveEvent<Boolean> passwordValidated =
new MutableLiveEvent<>();
private final MutableLiveEvent<Boolean> accountDeleted =
new MutableLiveEvent<>();
private final MutableLiveData<State> state = new MutableLiveData<>();
@Inject
StartupViewModel(Application app,
AccountManager accountManager,
LifecycleManager lifecycleManager,
AndroidNotificationManager notificationManager,
EventBus eventBus,
@IoExecutor Executor ioExecutor) {
super(app);
this.accountManager = accountManager;
this.notificationManager = notificationManager;
this.eventBus = eventBus;
this.ioExecutor = ioExecutor;
updateState(lifecycleManager.getLifecycleState());
eventBus.addListener(this);
}
@Override
protected void onCleared() {
eventBus.removeListener(this);
}
@Override
public void eventOccurred(Event e) {
if (e instanceof LifecycleEvent) {
LifecycleState s = ((LifecycleEvent) e).getLifecycleState();
updateState(s);
}
}
@UiThread
private void updateState(LifecycleState s) {
if (accountManager.hasDatabaseKey()) {
if (s.isAfter(STARTING_SERVICES)) state.setValue(STARTED);
else if (s == MIGRATING_DATABASE) state.setValue(MIGRATING);
else if (s == COMPACTING_DATABASE) state.setValue(COMPACTING);
else state.setValue(STARTING);
} else {
state.setValue(SIGNED_OUT);
}
}
boolean accountExists() {
return accountManager.accountExists();
}
void clearSignInNotification() {
notificationManager.blockSignInNotification();
notificationManager.clearSignInNotification();
}
void validatePassword(String password) {
ioExecutor.execute(() -> {
boolean signedIn = accountManager.signIn(password);
passwordValidated.postEvent(signedIn);
if (signedIn) state.postValue(SIGNED_IN);
});
}
LiveEvent<Boolean> getPasswordValidated() {
return passwordValidated;
}
LiveEvent<Boolean> getAccountDeleted() {
return accountDeleted;
}
LiveData<State> getState() {
return state;
}
@UiThread
void deleteAccount() {
accountManager.deleteAccount();
accountDeleted.setEvent(true);
}
}

View File

@@ -40,7 +40,6 @@ import org.briarproject.bramble.plugin.tor.CircumventionProvider;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.Localizer; import org.briarproject.briar.android.Localizer;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import org.briarproject.briar.android.util.UiUtils; import org.briarproject.briar.android.util.UiUtils;
import java.util.ArrayList; import java.util.ArrayList;
@@ -53,6 +52,7 @@ import javax.inject.Inject;
import static android.app.Activity.RESULT_OK; import static android.app.Activity.RESULT_OK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.media.RingtoneManager.ACTION_RINGTONE_PICKER; import static android.media.RingtoneManager.ACTION_RINGTONE_PICKER;
import static android.media.RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI; import static android.media.RingtoneManager.EXTRA_RINGTONE_DEFAULT_URI;
@@ -80,6 +80,7 @@ import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD; import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_RINGTONE; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_RINGTONE;
import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.INTENT_SIGN_OUT; import static org.briarproject.briar.android.navdrawer.NavDrawerActivity.INTENT_SIGN_OUT;
import static org.briarproject.briar.android.util.UiUtils.hasScreenLock; import static org.briarproject.briar.android.util.UiUtils.hasScreenLock;
@@ -192,7 +193,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
// bring up parent activity, so it can change its theme as well // bring up parent activity, so it can change its theme as well
// upstream bug: https://issuetracker.google.com/issues/38352704 // upstream bug: https://issuetracker.google.com/issues/38352704
Intent intent = Intent intent =
new Intent(getActivity(), NavDrawerActivity.class); new Intent(getActivity(), ENTRY_ACTIVITY);
intent.setFlags( intent.setFlags(
FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK); FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK);
startActivity(intent); startActivity(intent);
@@ -575,9 +576,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
builder.setPositiveButton(R.string.sign_out_button, builder.setPositiveButton(R.string.sign_out_button,
(dialogInterface, i) -> { (dialogInterface, i) -> {
language.setValue(newValue); language.setValue(newValue);
Intent intent = new Intent(getContext(), Intent intent = new Intent(getContext(), ENTRY_ACTIVITY);
NavDrawerActivity.class); intent.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(INTENT_SIGN_OUT, true); intent.putExtra(INTENT_SIGN_OUT, true);
requireActivity().startActivity(intent); requireActivity().startActivity(intent);
requireActivity().finish(); requireActivity().finish();

View File

@@ -2,10 +2,8 @@ package org.briarproject.briar.android.splash;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.support.v7.preference.PreferenceManager;
import android.transition.Fade; import android.transition.Fade;
import org.briarproject.bramble.api.account.AccountManager; import org.briarproject.bramble.api.account.AccountManager;
@@ -15,10 +13,6 @@ import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BaseActivity; import org.briarproject.briar.android.activity.BaseActivity;
import org.briarproject.briar.android.login.OpenDatabaseActivity;
import org.briarproject.briar.android.login.SetupActivity;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import org.briarproject.briar.api.android.LockManager;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -27,27 +21,35 @@ import javax.inject.Inject;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.Build.VERSION.SDK_INT;
import static android.support.v7.preference.PreferenceManager.setDefaultValues;
import static java.lang.System.currentTimeMillis;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE; import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public class SplashScreenActivity extends BaseActivity { public class SplashScreenActivity extends BaseActivity {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(SplashScreenActivity.class.getName()); getLogger(SplashScreenActivity.class.getName());
@Inject @Inject
protected AccountManager accountManager; protected AccountManager accountManager;
@Inject @Inject
protected LockManager lockManager;
@Inject
protected AndroidExecutor androidExecutor; protected AndroidExecutor androidExecutor;
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@Override @Override
public void onCreate(@Nullable Bundle state) { public void onCreate(@Nullable Bundle state) {
super.onCreate(state); super.onCreate(state);
if (Build.VERSION.SDK_INT >= 21) { if (SDK_INT >= 21) {
getWindow().setExitTransition(new Fade()); getWindow().setExitTransition(new Fade());
} }
@@ -56,44 +58,21 @@ public class SplashScreenActivity extends BaseActivity {
setContentView(R.layout.splash); setContentView(R.layout.splash);
if (accountManager.hasDatabaseKey()) { if (accountManager.hasDatabaseKey()) {
if (lockManager.isLocked()) { startNextActivity(ENTRY_ACTIVITY);
// The database needs to be opened for the app to be locked.
// Start main activity right away. It will open UnlockActivity.
// Otherwise, we would end up with two screen unlock inputs.
startNextActivity(NavDrawerActivity.class);
} else {
startNextActivity(OpenDatabaseActivity.class);
}
finish(); finish();
} else { } else {
new Handler().postDelayed(() -> { new Handler().postDelayed(() -> {
startNextActivity(); if (currentTimeMillis() >= EXPIRY_DATE) {
LOG.info("Expired");
startNextActivity(ExpiredActivity.class);
} else {
startNextActivity(ENTRY_ACTIVITY);
}
supportFinishAfterTransition(); supportFinishAfterTransition();
}, 500); }, 500);
} }
} }
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
private void startNextActivity() {
if (System.currentTimeMillis() >= EXPIRY_DATE) {
LOG.info("Expired");
startNextActivity(ExpiredActivity.class);
} else {
if (accountManager.accountExists()) {
LOG.info("Account exists");
startNextActivity(OpenDatabaseActivity.class);
} else {
LOG.info("Account does not exist");
accountManager.deleteAccount();
startNextActivity(SetupActivity.class);
}
}
}
private void startNextActivity(Class<? extends Activity> activityClass) { private void startNextActivity(Class<? extends Activity> activityClass) {
Intent i = new Intent(this, activityClass); Intent i = new Intent(this, activityClass);
i.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP); i.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
@@ -101,8 +80,8 @@ public class SplashScreenActivity extends BaseActivity {
} }
private void setPreferencesDefaults() { private void setPreferencesDefaults() {
androidExecutor.runOnBackgroundThread(() -> androidExecutor.runOnBackgroundThread(
PreferenceManager.setDefaultValues(SplashScreenActivity.this, () -> setDefaultValues(SplashScreenActivity.this,
R.xml.panic_preferences, false)); R.xml.panic_preferences, false));
} }

View File

@@ -11,12 +11,12 @@ import android.widget.TextView;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity; import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import org.briarproject.briar.api.test.TestDataCreator; import org.briarproject.briar.api.test.TestDataCreator;
import javax.inject.Inject; import javax.inject.Inject;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static org.briarproject.briar.android.BriarApplication.ENTRY_ACTIVITY;
public class TestDataActivity extends BriarActivity { public class TestDataActivity extends BriarActivity {
@@ -154,7 +154,7 @@ public class TestDataActivity extends BriarActivity {
testDataCreator.createTestData(contactsSeekBar.getProgress() + 1, testDataCreator.createTestData(contactsSeekBar.getProgress() + 1,
messagesSeekBar.getProgress(), blogPostsSeekBar.getProgress(), messagesSeekBar.getProgress(), blogPostsSeekBar.getProgress(),
forumsSeekBar.getProgress(), forumPostsSeekBar.getProgress()); forumsSeekBar.getProgress(), forumPostsSeekBar.getProgress());
Intent intent = new Intent(this, NavDrawerActivity.class); Intent intent = new Intent(this, ENTRY_ACTIVITY);
intent.addFlags(FLAG_ACTIVITY_CLEAR_TOP); intent.addFlags(FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent); startActivity(intent);
finish(); finish();

View File

@@ -36,6 +36,7 @@ import android.transition.Transition;
import android.util.TypedValue; import android.util.TypedValue;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.View; import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView; import android.widget.TextView;
import org.acra.ACRA; import org.acra.ACRA;
@@ -56,6 +57,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.Build.MANUFACTURER; import static android.os.Build.MANUFACTURER;
import static android.os.Build.VERSION.SDK_INT; import static android.os.Build.VERSION.SDK_INT;
import static android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS; import static android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS;
import static android.support.v4.content.ContextCompat.getSystemService;
import static android.support.v4.view.ViewCompat.LAYOUT_DIRECTION_RTL; import static android.support.v4.view.ViewCompat.LAYOUT_DIRECTION_RTL;
import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_AUTO; import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_AUTO;
import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM; import static android.support.v7.app.AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
@@ -76,6 +78,8 @@ import static android.text.format.DateUtils.YEAR_IN_MILLIS;
import static android.view.KeyEvent.ACTION_DOWN; import static android.view.KeyEvent.ACTION_DOWN;
import static android.view.KeyEvent.KEYCODE_ENTER; import static android.view.KeyEvent.KEYCODE_ENTER;
import static android.view.inputmethod.EditorInfo.IME_NULL; import static android.view.inputmethod.EditorInfo.IME_NULL;
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
import static java.util.Objects.requireNonNull;
import static org.briarproject.briar.BuildConfig.APPLICATION_ID; import static org.briarproject.briar.BuildConfig.APPLICATION_ID;
import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE; import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE;
@@ -87,6 +91,18 @@ public class UiUtils {
public static final int TEASER_LENGTH = 320; public static final int TEASER_LENGTH = 320;
public static final float GREY_OUT = 0.5f; public static final float GREY_OUT = 0.5f;
public static void showSoftKeyboard(View view) {
InputMethodManager imm = requireNonNull(
getSystemService(view.getContext(), InputMethodManager.class));
imm.showSoftInput(view, SHOW_IMPLICIT);
}
public static void hideSoftKeyboard(View view) {
InputMethodManager imm = requireNonNull(
getSystemService(view.getContext(), InputMethodManager.class));
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
public static String getContactDisplayName(Author author, public static String getContactDisplayName(Author author,
@Nullable String alias) { @Nullable String alias) {
String name = author.getName(); String name = author.getName();

View File

@@ -7,6 +7,7 @@ import org.briarproject.briar.android.contact.add.remote.AddContactViewModel;
import org.briarproject.briar.android.contact.add.remote.PendingContactListViewModel; import org.briarproject.briar.android.contact.add.remote.PendingContactListViewModel;
import org.briarproject.briar.android.conversation.ConversationViewModel; import org.briarproject.briar.android.conversation.ConversationViewModel;
import org.briarproject.briar.android.conversation.ImageViewModel; import org.briarproject.briar.android.conversation.ImageViewModel;
import org.briarproject.briar.android.login.StartupViewModel;
import javax.inject.Singleton; import javax.inject.Singleton;
@@ -17,6 +18,11 @@ import dagger.multibindings.IntoMap;
@Module @Module
public abstract class ViewModelModule { public abstract class ViewModelModule {
@Binds
@IntoMap
@ViewModelKey(StartupViewModel.class)
abstract ViewModel bindStartupViewModel(StartupViewModel startupViewModel);
@Binds @Binds
@IntoMap @IntoMap
@ViewModelKey(ConversationViewModel.class) @ViewModelKey(ConversationViewModel.class)

View File

@@ -6,7 +6,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
tools:context=".android.login.UnlockActivity"> tools:context=".android.account.UnlockActivity">
<android.support.v7.widget.AppCompatImageView <android.support.v7.widget.AppCompatImageView
android:id="@+id/image" android:id="@+id/image"

View File

@@ -2,8 +2,10 @@
<ScrollView <ScrollView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
tools:context=".android.login.PasswordFragment">
<android.support.constraint.ConstraintLayout <android.support.constraint.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -21,7 +23,7 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:passwordToggleEnabled="true"> app:passwordToggleEnabled="true">
<EditText <android.support.design.widget.TextInputEditText
android:id="@+id/edit_password" android:id="@+id/edit_password"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -37,7 +39,6 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_medium" android:layout_marginTop="@dimen/margin_medium"
android:onClick="onSignInClick"
android:text="@string/sign_in_button" android:text="@string/sign_in_button"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@@ -54,12 +55,12 @@
app:layout_constraintTop_toTopOf="@id/btn_sign_in"/> app:layout_constraintTop_toTopOf="@id/btn_sign_in"/>
<TextView <TextView
android:id="@+id/btn_forgotten"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_large" android:layout_marginTop="@dimen/margin_large"
android:clickable="true" android:clickable="true"
android:focusable="true" android:focusable="true"
android:onClick="onForgottenPasswordClick"
android:text="@string/forgotten_password" android:text="@string/forgotten_password"
android:textColor="?android:attr/textColorLink" android:textColor="?android:attr/textColorLink"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"

View File

@@ -12,7 +12,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:padding="@dimen/margin_activity_vertical"> android:padding="@dimen/margin_activity_vertical">
<org.briarproject.briar.android.login.DozeView <org.briarproject.briar.android.account.DozeView
android:id="@+id/dozeView" android:id="@+id/dozeView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@@ -20,7 +20,7 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/> app:layout_constraintTop_toTopOf="parent"/>
<org.briarproject.briar.android.login.HuaweiView <org.briarproject.briar.android.account.HuaweiView
android:id="@+id/huaweiView" android:id="@+id/huaweiView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"

View File

@@ -1,4 +1,4 @@
package org.briarproject.briar.android.login; package org.briarproject.briar.android.account;
import android.support.design.widget.TextInputLayout; import android.support.design.widget.TextInputLayout;
import android.view.View; import android.view.View;
@@ -7,6 +7,10 @@ import android.widget.EditText;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.TestBriarApplication; import org.briarproject.briar.android.TestBriarApplication;
import org.briarproject.briar.android.account.SetPasswordFragment;
import org.briarproject.briar.android.account.SetupActivity;
import org.briarproject.briar.android.account.SetupController;
import org.briarproject.briar.android.login.StrengthMeter;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -29,9 +33,9 @@ import static org.robolectric.shadows.support.v4.SupportFragmentTestUtil.startFr
@RunWith(RobolectricTestRunner.class) @RunWith(RobolectricTestRunner.class)
@Config(sdk = 21, application = TestBriarApplication.class) @Config(sdk = 21, application = TestBriarApplication.class)
public class PasswordFragmentTest { public class SetPasswordFragmentTest {
private PasswordFragment passwordFragment = new PasswordFragment(); private SetPasswordFragment passwordFragment = new SetPasswordFragment();
private EditText passwordEntry; private EditText passwordEntry;
private EditText passwordConfirmation; private EditText passwordConfirmation;
private TextInputLayout passwordConfirmationWrapper; private TextInputLayout passwordConfirmationWrapper;

View File

@@ -1,10 +1,11 @@
package org.briarproject.briar.android.login; package org.briarproject.briar.android.account;
import android.support.design.widget.TextInputLayout; import android.support.design.widget.TextInputLayout;
import android.widget.EditText; import android.widget.EditText;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.TestBriarApplication; import org.briarproject.briar.android.TestBriarApplication;
import org.briarproject.briar.android.account.SetupActivity;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;

View File

@@ -1,4 +1,4 @@
package org.briarproject.briar.android.login; package org.briarproject.briar.android.account;
import org.briarproject.bramble.api.account.AccountManager; import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator; import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;

View File

@@ -45,7 +45,7 @@ public class ChangePasswordActivityTest {
private Button changePasswordButton; private Button changePasswordButton;
@Mock @Mock
private PasswordController passwordController; private ChangePasswordController passwordController;
@Captor @Captor
private ArgumentCaptor<ResultHandler<Boolean>> resultCaptor; private ArgumentCaptor<ResultHandler<Boolean>> resultCaptor;

View File

@@ -14,7 +14,7 @@ import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.assertTrue;
import static org.briarproject.bramble.util.StringUtils.getRandomString; import static org.briarproject.bramble.util.StringUtils.getRandomString;
public class PasswordControllerImplTest extends BrambleMockTestCase { public class ChangePasswordControllerImplTest extends BrambleMockTestCase {
private final AccountManager accountManager = private final AccountManager accountManager =
context.mock(AccountManager.class); context.mock(AccountManager.class);
@@ -33,7 +33,7 @@ public class PasswordControllerImplTest extends BrambleMockTestCase {
will(returnValue(true)); will(returnValue(true));
}}); }});
PasswordControllerImpl p = new PasswordControllerImpl(accountManager, ChangePasswordControllerImpl p = new ChangePasswordControllerImpl(accountManager,
ioExecutor, estimator); ioExecutor, estimator);
AtomicBoolean capturedResult = new AtomicBoolean(false); AtomicBoolean capturedResult = new AtomicBoolean(false);
@@ -48,7 +48,7 @@ public class PasswordControllerImplTest extends BrambleMockTestCase {
will(returnValue(false)); will(returnValue(false));
}}); }});
PasswordControllerImpl p = new PasswordControllerImpl(accountManager, ChangePasswordControllerImpl p = new ChangePasswordControllerImpl(accountManager,
ioExecutor, estimator); ioExecutor, estimator);
AtomicBoolean capturedResult = new AtomicBoolean(true); AtomicBoolean capturedResult = new AtomicBoolean(true);

View File

@@ -6,7 +6,8 @@ package org.briarproject.briar.android.login;
*/ */
public class TestChangePasswordActivity extends ChangePasswordActivity { public class TestChangePasswordActivity extends ChangePasswordActivity {
public void setPasswordController(PasswordController passwordController) { public void setPasswordController(
ChangePasswordController passwordController) {
this.passwordController = passwordController; this.passwordController = passwordController;
} }