Move Plugin related code from activity to ViewModel

This commit is contained in:
Torsten Grote
2021-02-02 17:47:47 -03:00
parent bed87ed439
commit 5a55b3d7e3
4 changed files with 280 additions and 238 deletions

View File

@@ -21,6 +21,11 @@ import org.briarproject.briar.android.blog.RssFeedImportActivity;
import org.briarproject.briar.android.blog.RssFeedManageActivity;
import org.briarproject.briar.android.blog.WriteBlogPostActivity;
import org.briarproject.briar.android.contact.ContactListFragment;
import org.briarproject.briar.android.contact.add.nearby.ContactExchangeActivity;
import org.briarproject.briar.android.contact.add.nearby.ContactExchangeErrorFragment;
import org.briarproject.briar.android.contact.add.nearby.IntroFragment;
import org.briarproject.briar.android.contact.add.nearby.KeyAgreementActivity;
import org.briarproject.briar.android.contact.add.nearby.KeyAgreementFragment;
import org.briarproject.briar.android.contact.add.remote.AddContactActivity;
import org.briarproject.briar.android.contact.add.remote.LinkExchangeFragment;
import org.briarproject.briar.android.contact.add.remote.NicknameFragment;
@@ -36,10 +41,6 @@ import org.briarproject.briar.android.fragment.ScreenFilterDialogFragment;
import org.briarproject.briar.android.introduction.ContactChooserFragment;
import org.briarproject.briar.android.introduction.IntroductionActivity;
import org.briarproject.briar.android.introduction.IntroductionMessageFragment;
import org.briarproject.briar.android.contact.add.nearby.ContactExchangeActivity;
import org.briarproject.briar.android.contact.add.nearby.ContactExchangeErrorFragment;
import org.briarproject.briar.android.contact.add.nearby.KeyAgreementActivity;
import org.briarproject.briar.android.contact.add.nearby.KeyAgreementFragment;
import org.briarproject.briar.android.login.ChangePasswordActivity;
import org.briarproject.briar.android.login.OpenDatabaseFragment;
import org.briarproject.briar.android.login.PasswordFragment;
@@ -208,6 +209,8 @@ public interface ActivityComponent {
void inject(FeedFragment fragment);
void inject(IntroFragment fragment);
void inject(KeyAgreementFragment fragment);
void inject(LinkExchangeFragment fragment);

View File

@@ -1,6 +1,7 @@
package org.briarproject.briar.android.contact.add.nearby;
import android.app.Application;
import android.bluetooth.BluetoothAdapter;
import android.graphics.Bitmap;
import android.util.DisplayMetrics;
import android.widget.Toast;
@@ -30,8 +31,14 @@ import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStartedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementWaitingEvent;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.BluetoothConstants;
import org.briarproject.bramble.api.plugin.LanTcpConstants;
import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.Plugin.State;
import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
import org.briarproject.briar.R;
import org.briarproject.briar.android.contact.add.nearby.ContactAddingState.ContactExchangeFinished;
import org.briarproject.briar.android.contact.add.nearby.ContactAddingState.ContactExchangeStarted;
@@ -40,6 +47,8 @@ import org.briarproject.briar.android.contact.add.nearby.ContactAddingState.KeyA
import org.briarproject.briar.android.contact.add.nearby.ContactAddingState.KeyAgreementWaiting;
import org.briarproject.briar.android.contact.add.nearby.ContactExchangeResult.Error;
import org.briarproject.briar.android.contact.add.nearby.ContactExchangeResult.Success;
import org.briarproject.briar.android.viewmodel.LiveEvent;
import org.briarproject.briar.android.viewmodel.MutableLiveEvent;
import java.io.IOException;
import java.nio.charset.Charset;
@@ -55,12 +64,19 @@ import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
import static android.widget.Toast.LENGTH_LONG;
import static java.util.Objects.requireNonNull;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED;
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.briar.android.contact.add.nearby.ContactExchangeViewModel.BluetoothDecision.REFUSED;
import static org.briarproject.briar.android.contact.add.nearby.ContactExchangeViewModel.BluetoothDecision.UNKNOWN;
@NotNullByDefault
class ContactExchangeViewModel extends AndroidViewModel
@@ -69,22 +85,80 @@ class ContactExchangeViewModel extends AndroidViewModel
private static final Logger LOG =
getLogger(ContactExchangeViewModel.class.getName());
enum BluetoothDecision {
/**
* We haven't asked the user about Bluetooth discoverability.
*/
UNKNOWN,
/**
* The device doesn't have a Bluetooth adapter.
*/
NO_ADAPTER,
/**
* We're waiting for the user to accept or refuse discoverability.
*/
WAITING,
/**
* The user has accepted discoverability.
*/
ACCEPTED,
/**
* The user has refused discoverability.
*/
REFUSED
}
@SuppressWarnings("CharsetObjectCanBeUsed") // Requires minSdkVersion >= 19
private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
private final EventBus eventBus;
private final Executor ioExecutor;
private final PluginManager pluginManager;
private final PayloadEncoder payloadEncoder;
private final PayloadParser payloadParser;
private final Provider<KeyAgreementTask> keyAgreementTaskProvider;
private final ContactExchangeManager contactExchangeManager;
private final ConnectionManager connectionManager;
/**
* Set to true when the continue button is clicked, and false when the QR
* code fragment is shown. This prevents the QR code fragment from being
* shown automatically before the continue button has been clicked.
*/
private final MutableLiveData<Boolean> wasContinueClicked =
new MutableLiveData<>(false);
private final MutableLiveEvent<Boolean> showQrCodeFragment =
new MutableLiveEvent<>();
private final MutableLiveEvent<TransportId> transportStateChanged =
new MutableLiveEvent<>();
private final MutableLiveData<ContactAddingState> state =
new MutableLiveData<>();
final QrCodeDecoder qrCodeDecoder;
@Nullable
private final BluetoothAdapter bt;
@Nullable
private final Plugin wifiPlugin, bluetoothPlugin;
// UiThread
BluetoothDecision bluetoothDecision = BluetoothDecision.UNKNOWN;
/**
* Records whether we've enabled the wifi plugin so we don't enable it more
* than once.
*/
private boolean hasEnabledWifi = false;
/**
* Records whether we've enabled the Bluetooth plugin so we don't enable it
* more than once.
*/
private boolean hasEnabledBluetooth = false;
@Nullable
private KeyAgreementTask task;
private volatile boolean gotLocalPayload = false, gotRemotePayload = false;
@@ -93,6 +167,7 @@ class ContactExchangeViewModel extends AndroidViewModel
ContactExchangeViewModel(Application app,
EventBus eventBus,
@IoExecutor Executor ioExecutor,
PluginManager pluginManager,
PayloadEncoder payloadEncoder,
PayloadParser payloadParser,
Provider<KeyAgreementTask> keyAgreementTaskProvider,
@@ -101,11 +176,15 @@ class ContactExchangeViewModel extends AndroidViewModel
super(app);
this.eventBus = eventBus;
this.ioExecutor = ioExecutor;
this.pluginManager = pluginManager;
this.payloadEncoder = payloadEncoder;
this.payloadParser = payloadParser;
this.keyAgreementTaskProvider = keyAgreementTaskProvider;
this.contactExchangeManager = contactExchangeManager;
this.connectionManager = connectionManager;
bt = BluetoothAdapter.getDefaultAdapter();
wifiPlugin = pluginManager.getPlugin(LanTcpConstants.ID);
bluetoothPlugin = pluginManager.getPlugin(BluetoothConstants.ID);
qrCodeDecoder = new QrCodeDecoder(ioExecutor, this);
eventBus.addListener(this);
}
@@ -117,12 +196,95 @@ class ContactExchangeViewModel extends AndroidViewModel
stopListening();
}
@UiThread
void onContinueClicked() {
if (bluetoothDecision == REFUSED) {
bluetoothDecision = UNKNOWN; // Ask again
}
wasContinueClicked.setValue(true);
}
@UiThread
boolean isBluetoothSupported() {
return bt != null && bluetoothPlugin != null;
}
@UiThread
boolean isWifiReady() {
if (wifiPlugin == null) return true; // Continue without wifi
State state = wifiPlugin.getState();
// Wait for plugin to become enabled
return state == ACTIVE || state == INACTIVE;
}
@UiThread
boolean isBluetoothReady() {
if (bt == null || bluetoothPlugin == null) {
// Continue without Bluetooth
return true;
}
if (bluetoothDecision == BluetoothDecision.UNKNOWN ||
bluetoothDecision == BluetoothDecision.WAITING ||
bluetoothDecision == BluetoothDecision.REFUSED) {
// Wait for user to accept
return false;
}
if (bt.getScanMode() != SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
// Wait for adapter to become discoverable
return false;
}
// Wait for plugin to become active
return bluetoothPlugin.getState() == ACTIVE;
}
@UiThread
void enableWifiIfWeShould() {
if (hasEnabledWifi) return;
if (wifiPlugin == null) return;
State state = wifiPlugin.getState();
if (state == STARTING_STOPPING || state == DISABLED) {
LOG.info("Enabling wifi plugin");
hasEnabledWifi = true;
pluginManager.setPluginEnabled(LanTcpConstants.ID, true);
}
}
@UiThread
void enableBluetoothIfWeShould() {
if (bluetoothDecision != BluetoothDecision.ACCEPTED) return;
if (hasEnabledBluetooth) return;
if (bluetoothPlugin == null || !isBluetoothSupported()) return;
State state = bluetoothPlugin.getState();
if (state == STARTING_STOPPING || state == DISABLED) {
LOG.info("Enabling Bluetooth plugin");
hasEnabledBluetooth = true;
pluginManager.setPluginEnabled(BluetoothConstants.ID, true);
}
}
@UiThread
void startAddingContact() {
// If we return to the intro fragment, the continue button needs to be
// clicked again before showing the QR code fragment
wasContinueClicked.setValue(false);
// If we return to the intro fragment, ask for Bluetooth
// discoverability again before showing the QR code fragment
bluetoothDecision = UNKNOWN;
// If we return to the intro fragment, we may need to enable wifi and
// Bluetooth again
hasEnabledWifi = false;
hasEnabledBluetooth = false;
// start to listen with a KeyAgreementTask
startListening();
showQrCodeFragment.setEvent(true);
}
/**
* Call this once Bluetooth and Wi-Fi are ready to be used.
* It is possible to call this more than once over the ViewModel's lifetime.
*/
@UiThread
void startListening() {
private void startListening() {
KeyAgreementTask oldTask = task;
KeyAgreementTask newTask = keyAgreementTaskProvider.get();
task = newTask;
@@ -142,7 +304,20 @@ class ContactExchangeViewModel extends AndroidViewModel
@Override
public void eventOccurred(Event e) {
if (e instanceof KeyAgreementListeningEvent) {
if (e instanceof TransportStateEvent) {
TransportStateEvent t = (TransportStateEvent) e;
if (t.getTransportId().equals(BluetoothConstants.ID)) {
if (LOG.isLoggable(INFO)) {
LOG.info("Bluetooth state changed to " + t.getState());
}
transportStateChanged.setEvent(t.getTransportId());
} else if (t.getTransportId().equals(LanTcpConstants.ID)) {
if (LOG.isLoggable(INFO)) {
LOG.info("Wifi state changed to " + t.getState());
}
transportStateChanged.setEvent(t.getTransportId());
}
} else if (e instanceof KeyAgreementListeningEvent) {
LOG.info("KeyAgreementListeningEvent received");
KeyAgreementListeningEvent event = (KeyAgreementListeningEvent) e;
onLocalPayloadReceived(event.getLocalPayload());
@@ -257,6 +432,22 @@ class ContactExchangeViewModel extends AndroidViewModel
}
}
LiveData<Boolean> getWasContinueClicked() {
return wasContinueClicked;
}
/**
* Receives an event when the transport state of the WiFi or Bluetooth
* plugins changes.
*/
LiveEvent<TransportId> getTransportStateChanged() {
return transportStateChanged;
}
LiveEvent<Boolean> getShowQrCodeFragment() {
return showQrCodeFragment;
}
LiveData<ContactAddingState> getState() {
return state;
}

View File

@@ -1,6 +1,5 @@
package org.briarproject.briar.android.contact.add.nearby;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -10,9 +9,13 @@ import android.widget.ScrollView;
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.annotation.Nullable;
import javax.inject.Inject;
import androidx.lifecycle.ViewModelProvider;
import static android.view.View.FOCUS_DOWN;
@@ -20,33 +23,27 @@ import static android.view.View.FOCUS_DOWN;
@ParametersNotNullByDefault
public class IntroFragment extends BaseFragment {
interface IntroScreenSeenListener {
void showNextScreen();
}
public static final String TAG = IntroFragment.class.getName();
private IntroScreenSeenListener screenSeenListener;
@Inject
ViewModelProvider.Factory viewModelFactory;
private ContactExchangeViewModel viewModel;
private ScrollView scrollView;
public static IntroFragment newInstance() {
Bundle args = new Bundle();
IntroFragment fragment = new IntroFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
screenSeenListener = (IntroScreenSeenListener) context;
}
@Override
public String getUniqueTag() {
return TAG;
public void injectFragment(ActivityComponent component) {
component.inject(this);
viewModel = new ViewModelProvider(requireActivity(), viewModelFactory)
.get(ContactExchangeViewModel.class);
}
@Nullable
@@ -59,7 +56,7 @@ public class IntroFragment extends BaseFragment {
false);
scrollView = v.findViewById(R.id.scrollView);
View button = v.findViewById(R.id.continueButton);
button.setOnClickListener(view -> screenSeenListener.showNextScreen());
button.setOnClickListener(view -> viewModel.onContinueClicked());
return v;
}
@@ -69,4 +66,9 @@ public class IntroFragment extends BaseFragment {
scrollView.post(() -> scrollView.fullScroll(FOCUS_DOWN));
}
@Override
public String getUniqueTag() {
return TAG;
}
}

View File

@@ -1,6 +1,5 @@
package org.briarproject.briar.android.contact.add.nearby;
import android.bluetooth.BluetoothAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -8,21 +7,12 @@ import android.content.IntentFilter;
import android.os.Bundle;
import android.view.MenuItem;
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.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.BluetoothConstants;
import org.briarproject.bramble.api.plugin.LanTcpConstants;
import org.briarproject.bramble.api.plugin.Plugin;
import org.briarproject.bramble.api.plugin.Plugin.State;
import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.contact.add.nearby.IntroFragment.IntroScreenSeenListener;
import org.briarproject.briar.android.contact.add.nearby.ContactExchangeViewModel.BluetoothDecision;
import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
@@ -43,51 +33,21 @@ import static android.Manifest.permission.ACCESS_FINE_LOCATION;
import static android.Manifest.permission.CAMERA;
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.Build.VERSION.SDK_INT;
import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.api.plugin.Plugin.State.ACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.DISABLED;
import static org.briarproject.bramble.api.plugin.Plugin.State.INACTIVE;
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_BLUETOOTH_DISCOVERABLE;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA_LOCATION;
import static org.briarproject.briar.android.contact.add.nearby.ContactExchangeViewModel.BluetoothDecision.ACCEPTED;
import static org.briarproject.briar.android.contact.add.nearby.ContactExchangeViewModel.BluetoothDecision.REFUSED;
import static org.briarproject.briar.android.contact.add.nearby.ContactExchangeViewModel.BluetoothDecision.UNKNOWN;
import static org.briarproject.briar.android.util.UiUtils.getGoToSettingsListener;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public abstract class KeyAgreementActivity extends BriarActivity implements
BaseFragmentListener, IntroScreenSeenListener, EventListener {
private enum BluetoothDecision {
/**
* We haven't asked the user about Bluetooth discoverability.
*/
UNKNOWN,
/**
* The device doesn't have a Bluetooth adapter.
*/
NO_ADAPTER,
/**
* We're waiting for the user to accept or refuse discoverability.
*/
WAITING,
/**
* The user has accepted discoverability.
*/
ACCEPTED,
/**
* The user has refused discoverability.
*/
REFUSED
}
public abstract class KeyAgreementActivity extends BriarActivity
implements BaseFragmentListener {
private enum Permission {
UNKNOWN, GRANTED, SHOW_RATIONALE, PERMANENTLY_DENIED
@@ -96,16 +56,9 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
private static final Logger LOG =
getLogger(KeyAgreementActivity.class.getName());
@Inject
ViewModelProvider.Factory viewModelFactory;
@Inject
EventBus eventBus;
@Inject
PluginManager pluginManager;
protected ContactExchangeViewModel viewModel;
/**
@@ -116,31 +69,9 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
*/
private boolean isResumed = false;
/**
* Set to true when the continue button is clicked, and false when the QR
* code fragment is shown. This prevents the QR code fragment from being
* shown automatically before the continue button has been clicked.
*/
private boolean continueClicked = false;
/**
* Records whether we've enabled the wifi plugin so we don't enable it more
* than once.
*/
private boolean hasEnabledWifi = false;
/**
* Records whether we've enabled the Bluetooth plugin so we don't enable it
* more than once.
*/
private boolean hasEnabledBluetooth = false;
private Permission cameraPermission = Permission.UNKNOWN;
private Permission locationPermission = Permission.UNKNOWN;
private BluetoothDecision bluetoothDecision = BluetoothDecision.UNKNOWN;
private BroadcastReceiver bluetoothReceiver = null;
private Plugin wifiPlugin = null, bluetoothPlugin = null;
private BluetoothAdapter bt = null;
@Override
public void injectActivity(ActivityComponent component) {
@@ -162,9 +93,42 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
IntentFilter filter = new IntentFilter(ACTION_SCAN_MODE_CHANGED);
bluetoothReceiver = new BluetoothStateReceiver();
registerReceiver(bluetoothReceiver, filter);
wifiPlugin = pluginManager.getPlugin(LanTcpConstants.ID);
bluetoothPlugin = pluginManager.getPlugin(BluetoothConstants.ID);
bt = BluetoothAdapter.getDefaultAdapter();
viewModel.getWasContinueClicked().observe(this, clicked -> {
if (clicked && checkPermissions()) showQrCodeFragmentIfAllowed();
});
viewModel.getTransportStateChanged().observeEvent(this,
t -> showQrCodeFragmentIfAllowed());
viewModel.getShowQrCodeFragment().observeEvent(this, show -> {
if (show) showQrCodeFragment();
});
}
@Override
public void onStart() {
super.onStart();
// Permissions may have been granted manually while we were stopped
cameraPermission = Permission.UNKNOWN;
locationPermission = Permission.UNKNOWN;
}
@Override
protected void onPostResume() {
super.onPostResume();
isResumed = true;
// Workaround for
// https://code.google.com/p/android/issues/detail?id=190966
showQrCodeFragmentIfAllowed();
}
@Override
protected void onPause() {
super.onPause();
isResumed = false;
}
@Override
protected void onStop() {
super.onStop();
}
@Override
@@ -182,45 +146,22 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
return super.onOptionsItemSelected(item);
}
@Override
public void onStart() {
super.onStart();
eventBus.addListener(this);
// Permissions may have been granted manually while we were stopped
cameraPermission = Permission.UNKNOWN;
locationPermission = Permission.UNKNOWN;
}
@Override
protected void onPostResume() {
super.onPostResume();
isResumed = true;
// Workaround for
// https://code.google.com/p/android/issues/detail?id=190966
showQrCodeFragmentIfAllowed();
}
@SuppressWarnings("StatementWithEmptyBody")
private void showQrCodeFragmentIfAllowed() {
boolean continueClicked = // never set to null
requireNonNull(viewModel.getWasContinueClicked().getValue());
if (isResumed && continueClicked && areEssentialPermissionsGranted()) {
if (isWifiReady() && isBluetoothReady()) {
if (viewModel.isWifiReady() && viewModel.isBluetoothReady()) {
LOG.info("Wifi and Bluetooth are ready");
viewModel.startListening();
showQrCodeFragment();
viewModel.startAddingContact();
} else {
if (shouldEnableWifi()) {
LOG.info("Enabling wifi plugin");
hasEnabledWifi = true;
pluginManager.setPluginEnabled(LanTcpConstants.ID, true);
}
if (bluetoothDecision == BluetoothDecision.UNKNOWN) {
viewModel.enableWifiIfWeShould();
if (viewModel.bluetoothDecision == UNKNOWN) {
requestBluetoothDiscoverable();
} else if (bluetoothDecision == BluetoothDecision.REFUSED) {
} else if (viewModel.bluetoothDecision == REFUSED) {
// Ask again when the user clicks "continue"
} else if (shouldEnableBluetooth()) {
LOG.info("Enabling Bluetooth plugin");
hasEnabledBluetooth = true;
pluginManager.setPluginEnabled(BluetoothConstants.ID, true);
} else {
viewModel.enableBluetoothIfWeShould();
}
}
}
@@ -229,119 +170,42 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
private boolean areEssentialPermissionsGranted() {
return cameraPermission == Permission.GRANTED &&
(SDK_INT < 23 || locationPermission == Permission.GRANTED ||
!isBluetoothSupported());
}
private boolean isBluetoothSupported() {
return bt != null && bluetoothPlugin != null;
}
private boolean isWifiReady() {
if (wifiPlugin == null) return true; // Continue without wifi
State state = wifiPlugin.getState();
// Wait for plugin to become enabled
return state == ACTIVE || state == INACTIVE;
}
private boolean isBluetoothReady() {
if (!isBluetoothSupported()) {
// Continue without Bluetooth
return true;
}
if (bluetoothDecision == BluetoothDecision.UNKNOWN ||
bluetoothDecision == BluetoothDecision.WAITING ||
bluetoothDecision == BluetoothDecision.REFUSED) {
// Wait for user to accept
return false;
}
if (bt.getScanMode() != SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
// Wait for adapter to become discoverable
return false;
}
// Wait for plugin to become active
return bluetoothPlugin.getState() == ACTIVE;
}
private boolean shouldEnableWifi() {
if (hasEnabledWifi) return false;
if (wifiPlugin == null) return false;
State state = wifiPlugin.getState();
return state == STARTING_STOPPING || state == DISABLED;
!viewModel.isBluetoothSupported());
}
private void requestBluetoothDiscoverable() {
if (!isBluetoothSupported()) {
bluetoothDecision = BluetoothDecision.NO_ADAPTER;
if (!viewModel.isBluetoothSupported()) {
viewModel.bluetoothDecision = BluetoothDecision.NO_ADAPTER;
showQrCodeFragmentIfAllowed();
} else {
Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE);
if (i.resolveActivity(getPackageManager()) != null) {
LOG.info("Asking for Bluetooth discoverability");
bluetoothDecision = BluetoothDecision.WAITING;
viewModel.bluetoothDecision = BluetoothDecision.WAITING;
startActivityForResult(i, REQUEST_BLUETOOTH_DISCOVERABLE);
} else {
bluetoothDecision = BluetoothDecision.NO_ADAPTER;
viewModel.bluetoothDecision = BluetoothDecision.NO_ADAPTER;
showQrCodeFragmentIfAllowed();
}
}
}
private boolean shouldEnableBluetooth() {
if (bluetoothDecision != BluetoothDecision.ACCEPTED) return false;
if (hasEnabledBluetooth) return false;
if (!isBluetoothSupported()) return false;
State state = bluetoothPlugin.getState();
return state == STARTING_STOPPING || state == DISABLED;
}
@Override
protected void onPause() {
super.onPause();
isResumed = false;
}
@Override
protected void onStop() {
super.onStop();
eventBus.removeListener(this);
}
@Override
public void showNextScreen() {
continueClicked = true;
if (bluetoothDecision == BluetoothDecision.REFUSED) {
bluetoothDecision = BluetoothDecision.UNKNOWN; // Ask again
}
if (checkPermissions()) showQrCodeFragmentIfAllowed();
}
@Override
public void onActivityResult(int request, int result,
@Nullable Intent data) {
if (request == REQUEST_BLUETOOTH_DISCOVERABLE) {
if (result == RESULT_CANCELED) {
LOG.info("Bluetooth discoverability was refused");
bluetoothDecision = BluetoothDecision.REFUSED;
viewModel.bluetoothDecision = REFUSED;
} else {
LOG.info("Bluetooth discoverability was accepted");
bluetoothDecision = BluetoothDecision.ACCEPTED;
viewModel.bluetoothDecision = ACCEPTED;
}
showQrCodeFragmentIfAllowed();
} else super.onActivityResult(request, result, data);
}
private void showQrCodeFragment() {
// If we return to the intro fragment, the continue button needs to be
// clicked again before showing the QR code fragment
continueClicked = false;
// If we return to the intro fragment, ask for Bluetooth
// discoverability again before showing the QR code fragment
bluetoothDecision = BluetoothDecision.UNKNOWN;
// If we return to the intro fragment, we may need to enable wifi and
// Bluetooth again
hasEnabledWifi = false;
hasEnabledBluetooth = false;
// FIXME #824
FragmentManager fm = getSupportFragmentManager();
if (fm.findFragmentByTag(KeyAgreementFragment.TAG) == null) {
@@ -362,7 +226,7 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
R.string.permission_camera_denied_body);
return false;
}
if (isBluetoothSupported() &&
if (viewModel.isBluetoothSupported() &&
locationPermission == Permission.PERMANENTLY_DENIED) {
showDenialDialog(R.string.permission_location_title,
R.string.permission_location_denied_body);
@@ -406,7 +270,7 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
private void requestPermissions() {
String[] permissions;
if (isBluetoothSupported()) {
if (viewModel.isBluetoothSupported()) {
permissions = new String[] {CAMERA, ACCESS_FINE_LOCATION};
} else {
permissions = new String[] {CAMERA};
@@ -430,7 +294,7 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
} else {
cameraPermission = Permission.PERMANENTLY_DENIED;
}
if (isBluetoothSupported()) {
if (viewModel.isBluetoothSupported()) {
if (gotPermission(ACCESS_FINE_LOCATION, permissions,
grantResults)) {
locationPermission = Permission.GRANTED;
@@ -463,24 +327,6 @@ public abstract class KeyAgreementActivity extends BriarActivity implements
permission);
}
@Override
public void eventOccurred(Event e) {
if (e instanceof TransportStateEvent) {
TransportStateEvent t = (TransportStateEvent) e;
if (t.getTransportId().equals(BluetoothConstants.ID)) {
if (LOG.isLoggable(INFO)) {
LOG.info("Bluetooth state changed to " + t.getState());
}
showQrCodeFragmentIfAllowed();
} else if (t.getTransportId().equals(LanTcpConstants.ID)) {
if (LOG.isLoggable(INFO)) {
LOG.info("Wifi state changed to " + t.getState());
}
showQrCodeFragmentIfAllowed();
}
}
}
private class BluetoothStateReceiver extends BroadcastReceiver {
@Override