[android] Combine Password and OpenDatabase Activity into StartupActivity

This commit is contained in:
Torsten Grote
2019-04-29 16:15:35 -03:00
parent 451edba467
commit 5ece6505da
41 changed files with 696 additions and 484 deletions

View File

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

View File

@@ -22,7 +22,6 @@ import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.briar.R;
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.LockManager;
@@ -48,6 +47,7 @@ import static java.util.logging.Level.INFO;
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.SUCCESS;
import static org.briarproject.briar.android.activity.ActivityComponent.ENTRY_ACTIVITY;
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.ONGOING_CHANNEL_ID;
@@ -182,7 +182,7 @@ public class BriarService extends Service {
NotificationManager nm = (NotificationManager) o;
nm.notify(FAILURE_NOTIFICATION_ID, b.build());
// 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.putExtra(EXTRA_STARTUP_FAILED, true);
startActivity(i);

View File

@@ -1,4 +1,4 @@
package org.briarproject.briar.android.login;
package org.briarproject.briar.android.account;
import android.os.Bundle;
import android.support.design.widget.TextInputEditText;

View File

@@ -1,4 +1,4 @@
package org.briarproject.briar.android.login;
package org.briarproject.briar.android.account;
import android.annotation.SuppressLint;
import android.content.Intent;
@@ -14,7 +14,7 @@ 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.PowerView.OnCheckedChangedListener;
import org.briarproject.briar.android.account.PowerView.OnCheckedChangedListener;
import org.briarproject.briar.android.util.UiUtils;
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;

View File

@@ -1,4 +1,4 @@
package org.briarproject.briar.android.login;
package org.briarproject.briar.android.account;
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.os.Parcel;

View File

@@ -0,0 +1,130 @@
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);
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.content.Intent;
@@ -15,8 +15,11 @@ import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener
import javax.annotation.Nullable;
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.activity.ActivityComponent.ENTRY_ACTIVITY;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
@@ -86,7 +89,7 @@ public class SetupActivity extends BaseActivity
void showPasswordFragment() {
if (authorName == null) throw new IllegalStateException();
showNextFragment(PasswordFragment.newInstance());
showNextFragment(SetPasswordFragment.newInstance());
}
@TargetApi(23)
@@ -97,8 +100,9 @@ public class SetupActivity extends BaseActivity
}
void showApp() {
Intent i = new Intent(this, OpenDatabaseActivity.class);
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME);
Intent i = new Intent(this, ENTRY_ACTIVITY);
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME |
FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
supportFinishAfterTransition();
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;
@NotNullByDefault
public interface SetupController extends PasswordController {
public interface SetupController {
void setSetupActivity(SetupActivity setupActivity);
@@ -11,6 +11,8 @@ public interface SetupController extends PasswordController {
void setAuthorName(String authorName);
float estimatePasswordStrength(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;
@@ -15,12 +15,15 @@ import java.util.logging.Logger;
import javax.inject.Inject;
@NotNullByDefault
public class SetupControllerImpl extends PasswordControllerImpl
implements SetupController {
public class SetupControllerImpl implements SetupController {
private static final Logger LOG =
Logger.getLogger(SetupControllerImpl.class.getName());
private final AccountManager accountManager;
private final PasswordStrengthEstimator strengthEstimator;
@IoExecutor
private final Executor ioExecutor;
@Nullable
private volatile SetupActivity setupActivity;
@@ -28,7 +31,9 @@ public class SetupControllerImpl extends PasswordControllerImpl
SetupControllerImpl(AccountManager accountManager,
@IoExecutor Executor ioExecutor,
PasswordStrengthEstimator strengthEstimator) {
super(accountManager, ioExecutor, strengthEstimator);
this.accountManager = accountManager;
this.strengthEstimator = strengthEstimator;
this.ioExecutor = ioExecutor;
}
@Override
@@ -51,6 +56,11 @@ public class SetupControllerImpl extends PasswordControllerImpl
setupActivity.setAuthorName(authorName);
}
@Override
public float estimatePasswordStrength(String password) {
return strengthEstimator.estimateStrength(password);
}
@Override
public void setPassword(String password) {
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.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.content.Intent;

View File

@@ -4,6 +4,11 @@ import android.app.Activity;
import org.briarproject.briar.android.AndroidComponent;
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.BlogFragment;
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.KeyAgreementActivity;
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.DozeFragment;
import org.briarproject.briar.android.login.OpenDatabaseActivity;
import org.briarproject.briar.android.login.PasswordActivity;
import org.briarproject.briar.android.login.OpenDatabaseFragment;
import org.briarproject.briar.android.login.PasswordFragment;
import org.briarproject.briar.android.login.SetupActivity;
import org.briarproject.briar.android.login.UnlockActivity;
import org.briarproject.briar.android.login.StartupActivity;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import org.briarproject.briar.android.panic.PanicPreferencesActivity;
import org.briarproject.briar.android.panic.PanicResponderActivity;
@@ -90,18 +91,18 @@ import dagger.Component;
dependencies = AndroidComponent.class)
public interface ActivityComponent {
Class<? extends Activity> ENTRY_ACTIVITY = NavDrawerActivity.class;
Activity activity();
void inject(SplashScreenActivity activity);
void inject(StartupActivity activity);
void inject(SetupActivity activity);
void inject(OpenDatabaseActivity activity);
void inject(NavDrawerActivity activity);
void inject(PasswordActivity activity);
void inject(PanicResponderActivity activity);
void inject(PanicPreferencesActivity activity);
@@ -177,12 +178,17 @@ public interface ActivityComponent {
void inject(PendingContactListActivity activity);
// Fragments
void inject(AuthorNameFragment fragment);
void inject(PasswordFragment fragment);
void inject(SetPasswordFragment fragment);
void inject(DozeFragment fragment);
void inject(PasswordFragment imageFragment);
void inject(OpenDatabaseFragment activity);
void inject(ContactListFragment 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.DbController;
import org.briarproject.briar.android.controller.DbControllerImpl;
import org.briarproject.briar.android.login.PasswordController;
import org.briarproject.briar.android.login.PasswordControllerImpl;
import org.briarproject.briar.android.login.SetupController;
import org.briarproject.briar.android.login.SetupControllerImpl;
import org.briarproject.briar.android.login.ChangePasswordController;
import org.briarproject.briar.android.login.ChangePasswordControllerImpl;
import org.briarproject.briar.android.account.SetupController;
import org.briarproject.briar.android.account.SetupControllerImpl;
import org.briarproject.briar.android.navdrawer.NavDrawerController;
import org.briarproject.briar.android.navdrawer.NavDrawerControllerImpl;
@@ -48,8 +48,8 @@ public class ActivityModule {
@ActivityScope
@Provides
PasswordController providePasswordController(
PasswordControllerImpl passwordController) {
ChangePasswordController providePasswordController(
ChangePasswordControllerImpl passwordController) {
return passwordController;
}

View File

@@ -15,8 +15,8 @@ import org.briarproject.briar.R;
import org.briarproject.briar.android.controller.BriarController;
import org.briarproject.briar.android.controller.DbController;
import org.briarproject.briar.android.controller.handler.UiResultHandler;
import org.briarproject.briar.android.login.PasswordActivity;
import org.briarproject.briar.android.login.UnlockActivity;
import org.briarproject.briar.android.login.StartupActivity;
import org.briarproject.briar.android.account.UnlockActivity;
import org.briarproject.briar.android.logout.ExitActivity;
import org.briarproject.briar.api.android.LockManager;
@@ -86,8 +86,8 @@ public abstract class BriarActivity extends BaseActivity {
if (!briarController.accountSignedIn() && !isFinishing()) {
// Also check that the activity isn't finishing already.
// This is possible if we finished in onActivityResult().
// Launching another PasswordActivity would cause a loop.
Intent i = new Intent(this, PasswordActivity.class);
// Launching another StartupActivity would cause a loop.
Intent i = new Intent(this, StartupActivity.class);
startActivityForResult(i, REQUEST_PASSWORD);
} else if (lockManager.isLocked() && !isFinishing()) {
// 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.db.DatabaseExecutor;
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.SettingsManager;
import org.briarproject.briar.android.BriarService;
@@ -21,6 +22,7 @@ import java.util.logging.Logger;
import javax.inject.Inject;
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.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting;
@@ -34,6 +36,7 @@ public class BriarControllerImpl implements BriarController {
private final BriarServiceConnection serviceConnection;
private final AccountManager accountManager;
private final LifecycleManager lifecycleManager;
private final Executor databaseExecutor;
private final SettingsManager settingsManager;
private final DozeWatchdog dozeWatchdog;
@@ -44,11 +47,13 @@ public class BriarControllerImpl implements BriarController {
@Inject
BriarControllerImpl(BriarServiceConnection serviceConnection,
AccountManager accountManager,
LifecycleManager lifecycleManager,
@DatabaseExecutor Executor databaseExecutor,
SettingsManager settingsManager, DozeWatchdog dozeWatchdog,
Activity activity) {
this.serviceConnection = serviceConnection;
this.accountManager = accountManager;
this.lifecycleManager = lifecycleManager;
this.databaseExecutor = databaseExecutor;
this.settingsManager = settingsManager;
this.dozeWatchdog = dozeWatchdog;
@@ -84,7 +89,8 @@ public class BriarControllerImpl implements BriarController {
@Override
public boolean accountSignedIn() {
return accountManager.hasDatabaseKey();
return accountManager.hasDatabaseKey() &&
lifecycleManager.getLifecycleState().isAfter(STARTING_SERVICES);
}
@Override

View File

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

View File

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

View File

@@ -11,14 +11,14 @@ import java.util.concurrent.Executor;
import javax.inject.Inject;
@NotNullByDefault
public class PasswordControllerImpl implements PasswordController {
public class ChangePasswordControllerImpl implements ChangePasswordController {
protected final AccountManager accountManager;
protected final Executor ioExecutor;
private final PasswordStrengthEstimator strengthEstimator;
@Inject
PasswordControllerImpl(AccountManager accountManager,
ChangePasswordControllerImpl(AccountManager accountManager,
@IoExecutor Executor ioExecutor,
PasswordStrengthEstimator strengthEstimator) {
this.accountManager = accountManager;
@@ -31,13 +31,6 @@ public class PasswordControllerImpl implements PasswordController {
return strengthEstimator.estimateStrength(password);
}
@Override
public void validatePassword(String password,
ResultHandler<Boolean> resultHandler) {
ioExecutor.execute(() ->
resultHandler.onResult(accountManager.signIn(password)));
}
@Override
public void changePassword(String oldPassword, String newPassword,
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 = PasswordFragment.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,48 +1,50 @@
package org.briarproject.briar.android.login;
import android.arch.lifecycle.ViewModelProvider;
import android.arch.lifecycle.ViewModelProviders;
import android.os.Bundle;
import android.os.IBinder;
import android.support.design.widget.TextInputEditText;
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.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
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.util.UiUtils;
import org.briarproject.briar.android.fragment.BaseFragment;
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.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.enterPressed;
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
@ParametersNotNullByDefault
public class PasswordFragment extends SetupFragment {
public class PasswordFragment extends BaseFragment implements TextWatcher {
private final static String TAG = PasswordFragment.class.getName();
private TextInputLayout passwordEntryWrapper;
private TextInputLayout passwordConfirmationWrapper;
private TextInputEditText passwordEntry;
private TextInputEditText passwordConfirmation;
private StrengthMeter strengthMeter;
private Button nextButton;
private ProgressBar progressBar;
@Inject
ViewModelProvider.Factory viewModelFactory;
public static PasswordFragment newInstance() {
return new PasswordFragment();
}
private StartupViewModel viewModel;
private Button signInButton;
private ProgressBar progress;
private TextInputLayout input;
private EditText password;
@Override
public void injectFragment(ActivityComponent component) {
@@ -53,78 +55,85 @@ public class PasswordFragment extends SetupFragment {
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,
View v = inflater.inflate(R.layout.fragment_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);
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);
viewModel = ViewModelProviders.of(requireActivity(), viewModelFactory)
.get(StartupViewModel.class);
viewModel.getPasswordValidated().observe(this, this::onPasswordValidated);
passwordEntry.addTextChangedListener(this);
passwordConfirmation.addTextChangedListener(this);
nextButton.setOnClickListener(this);
if (!setupController.needToShowDozeFragment()) {
nextButton.setText(R.string.create_account_button);
passwordConfirmation.setImeOptions(IME_ACTION_DONE);
}
signInButton = v.findViewById(R.id.btn_sign_in);
signInButton.setOnClickListener(view -> onSignInButtonClicked());
progress = v.findViewById(R.id.progress_wheel);
input = v.findViewById(R.id.password_layout);
password = v.findViewById(R.id.edit_password);
password.setOnEditorActionListener((view, actionId, event) -> {
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;
}
@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 onPasswordValidated(@Nullable Boolean valid) {
if (valid != null && !valid) {
setError(input, getString(R.string.try_again), true);
signInButton.setVisibility(VISIBLE);
progress.setVisibility(INVISIBLE);
password.setText(null);
// show the keyboard again
showSoftKeyboard(password);
// reset validation state for configuration changes
viewModel.getPasswordValidated().setValue(null);
}
}
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
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;
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,120 @@
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 android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
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 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(true);
return;
}
viewModel.getAccountDeleted().observe(this, this::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) {
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
FragmentManager fm = getSupportFragmentManager();
Fragment f = fm.findFragmentByTag(OpenDatabaseFragment.TAG);
if (f == null || !f.isVisible()) {
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(boolean accountDeleted) {
if (accountDeleted) {
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);
startActivity(i);
}
}
@Override
public void runOnDbThread(Runnable runnable) {
// we don't need this and shouldn't be forced to implement it
throw new AssertionError();
}
}

View File

@@ -0,0 +1,129 @@
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.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 MutableLiveData<Boolean> passwordValidated =
new MutableLiveData<>();
private final MutableLiveData<Boolean> accountDeleted =
new MutableLiveData<>();
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.postValue(signedIn);
if (signedIn) state.postValue(SIGNED_IN);
});
}
MutableLiveData<Boolean> getPasswordValidated() {
return passwordValidated;
}
LiveData<Boolean> getAccountDeleted() {
return accountDeleted;
}
LiveData<State> getState() {
return state;
}
@UiThread
void deleteAccount() {
accountManager.deleteAccount();
accountDeleted.setValue(true);
}
}

View File

@@ -40,7 +40,6 @@ import org.briarproject.bramble.plugin.tor.CircumventionProvider;
import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.R;
import org.briarproject.briar.android.Localizer;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import org.briarproject.briar.android.util.UiUtils;
import java.util.ArrayList;
@@ -80,6 +79,7 @@ import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now;
import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
import static org.briarproject.briar.android.activity.ActivityComponent.ENTRY_ACTIVITY;
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.util.UiUtils.hasScreenLock;
@@ -192,7 +192,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
// bring up parent activity, so it can change its theme as well
// upstream bug: https://issuetracker.google.com/issues/38352704
Intent intent =
new Intent(getActivity(), NavDrawerActivity.class);
new Intent(getActivity(), ENTRY_ACTIVITY);
intent.setFlags(
FLAG_ACTIVITY_CLEAR_TASK | FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
@@ -575,8 +575,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
builder.setPositiveButton(R.string.sign_out_button,
(dialogInterface, i) -> {
language.setValue(newValue);
Intent intent = new Intent(getContext(),
NavDrawerActivity.class);
Intent intent = new Intent(getContext(), ENTRY_ACTIVITY);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra(INTENT_SIGN_OUT, true);
requireActivity().startActivity(intent);

View File

@@ -2,10 +2,8 @@ package org.briarproject.briar.android.splash;
import android.app.Activity;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.preference.PreferenceManager;
import android.transition.Fade;
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.android.activity.ActivityComponent;
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;
@@ -27,27 +21,35 @@ import javax.inject.Inject;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
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.activity.ActivityComponent.ENTRY_ACTIVITY;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class SplashScreenActivity extends BaseActivity {
private static final Logger LOG =
Logger.getLogger(SplashScreenActivity.class.getName());
getLogger(SplashScreenActivity.class.getName());
@Inject
protected AccountManager accountManager;
@Inject
protected LockManager lockManager;
@Inject
protected AndroidExecutor androidExecutor;
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@Override
public void onCreate(@Nullable Bundle state) {
super.onCreate(state);
if (Build.VERSION.SDK_INT >= 21) {
if (SDK_INT >= 21) {
getWindow().setExitTransition(new Fade());
}
@@ -56,44 +58,21 @@ public class SplashScreenActivity extends BaseActivity {
setContentView(R.layout.splash);
if (accountManager.hasDatabaseKey()) {
if (lockManager.isLocked()) {
// 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);
}
startNextActivity(ENTRY_ACTIVITY);
finish();
} else {
new Handler().postDelayed(() -> {
startNextActivity();
if (currentTimeMillis() >= EXPIRY_DATE) {
LOG.info("Expired");
startNextActivity(ExpiredActivity.class);
} else {
startNextActivity(ENTRY_ACTIVITY);
}
supportFinishAfterTransition();
}, 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) {
Intent i = new Intent(this, activityClass);
i.addFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
@@ -101,8 +80,8 @@ public class SplashScreenActivity extends BaseActivity {
}
private void setPreferencesDefaults() {
androidExecutor.runOnBackgroundThread(() ->
PreferenceManager.setDefaultValues(SplashScreenActivity.this,
androidExecutor.runOnBackgroundThread(
() -> setDefaultValues(SplashScreenActivity.this,
R.xml.panic_preferences, false));
}

View File

@@ -11,12 +11,12 @@ import android.widget.TextView;
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 org.briarproject.briar.api.test.TestDataCreator;
import javax.inject.Inject;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static org.briarproject.briar.android.activity.ActivityComponent.ENTRY_ACTIVITY;
public class TestDataActivity extends BriarActivity {
@@ -154,7 +154,7 @@ public class TestDataActivity extends BriarActivity {
testDataCreator.createTestData(contactsSeekBar.getProgress() + 1,
messagesSeekBar.getProgress(), blogPostsSeekBar.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);
startActivity(intent);
finish();

View File

@@ -36,6 +36,7 @@ import android.transition.Transition;
import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
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.VERSION.SDK_INT;
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.v7.app.AppCompatDelegate.MODE_NIGHT_AUTO;
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.KEYCODE_ENTER;
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.android.TestingConstants.EXPIRY_DATE;
@@ -87,6 +91,18 @@ public class UiUtils {
public static final int TEASER_LENGTH = 320;
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,
@Nullable String alias) {
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.conversation.ConversationViewModel;
import org.briarproject.briar.android.conversation.ImageViewModel;
import org.briarproject.briar.android.login.StartupViewModel;
import javax.inject.Singleton;
@@ -17,6 +18,11 @@ import dagger.multibindings.IntoMap;
@Module
public abstract class ViewModelModule {
@Binds
@IntoMap
@ViewModelKey(StartupViewModel.class)
abstract ViewModel bindStartupViewModel(StartupViewModel startupViewModel);
@Binds
@IntoMap
@ViewModelKey(ConversationViewModel.class)

View File

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

View File

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

View File

@@ -12,7 +12,7 @@
android:layout_height="wrap_content"
android:padding="@dimen/margin_activity_vertical">
<org.briarproject.briar.android.login.DozeView
<org.briarproject.briar.android.account.DozeView
android:id="@+id/dozeView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -20,7 +20,7 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<org.briarproject.briar.android.login.HuaweiView
<org.briarproject.briar.android.account.HuaweiView
android:id="@+id/huaweiView"
android:layout_width="match_parent"
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.view.View;
@@ -7,6 +7,10 @@ import android.widget.EditText;
import org.briarproject.briar.R;
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.Test;
import org.junit.runner.RunWith;
@@ -29,9 +33,9 @@ import static org.robolectric.shadows.support.v4.SupportFragmentTestUtil.startFr
@RunWith(RobolectricTestRunner.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 passwordConfirmation;
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.widget.EditText;
import org.briarproject.briar.R;
import org.briarproject.briar.android.TestBriarApplication;
import org.briarproject.briar.android.account.SetupActivity;
import org.junit.Assert;
import org.junit.Before;
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.crypto.PasswordStrengthEstimator;

View File

@@ -45,7 +45,7 @@ public class ChangePasswordActivityTest {
private Button changePasswordButton;
@Mock
private PasswordController passwordController;
private ChangePasswordController passwordController;
@Captor
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 org.briarproject.bramble.util.StringUtils.getRandomString;
public class PasswordControllerImplTest extends BrambleMockTestCase {
public class ChangePasswordControllerImplTest extends BrambleMockTestCase {
private final AccountManager accountManager =
context.mock(AccountManager.class);
@@ -33,7 +33,7 @@ public class PasswordControllerImplTest extends BrambleMockTestCase {
will(returnValue(true));
}});
PasswordControllerImpl p = new PasswordControllerImpl(accountManager,
ChangePasswordControllerImpl p = new ChangePasswordControllerImpl(accountManager,
ioExecutor, estimator);
AtomicBoolean capturedResult = new AtomicBoolean(false);
@@ -48,7 +48,7 @@ public class PasswordControllerImplTest extends BrambleMockTestCase {
will(returnValue(false));
}});
PasswordControllerImpl p = new PasswordControllerImpl(accountManager,
ChangePasswordControllerImpl p = new ChangePasswordControllerImpl(accountManager,
ioExecutor, estimator);
AtomicBoolean capturedResult = new AtomicBoolean(true);

View File

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