mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-19 14:19:53 +01:00
Refactor more code into AddNearbyContactViewModel
thus concentrating the logic there needing less back and forth with the activity
This commit is contained in:
@@ -11,7 +11,6 @@ public interface RequestCodes {
|
|||||||
int REQUEST_RINGTONE = 7;
|
int REQUEST_RINGTONE = 7;
|
||||||
int REQUEST_PERMISSION_CAMERA_LOCATION = 8;
|
int REQUEST_PERMISSION_CAMERA_LOCATION = 8;
|
||||||
int REQUEST_DOZE_WHITELISTING = 9;
|
int REQUEST_DOZE_WHITELISTING = 9;
|
||||||
int REQUEST_BLUETOOTH_DISCOVERABLE = 10;
|
|
||||||
int REQUEST_UNLOCK = 11;
|
int REQUEST_UNLOCK = 11;
|
||||||
int REQUEST_KEYGUARD_UNLOCK = 12;
|
int REQUEST_KEYGUARD_UNLOCK = 12;
|
||||||
int REQUEST_ATTACH_IMAGE = 13;
|
int REQUEST_ATTACH_IMAGE = 13;
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
package org.briarproject.briar.android.contact.add.nearby;
|
package org.briarproject.briar.android.contact.add.nearby;
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.NullSafety;
|
|
||||||
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;
|
||||||
@@ -21,25 +17,25 @@ import org.briarproject.briar.android.contact.add.nearby.AddContactState.Failed;
|
|||||||
import org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision;
|
import org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
||||||
|
import org.briarproject.briar.android.util.RequestBluetoothDiscoverable;
|
||||||
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import androidx.activity.result.ActivityResultLauncher;
|
||||||
import androidx.appcompat.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.lifecycle.ViewModelProvider;
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
|
|
||||||
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
|
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
|
||||||
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||||
import static android.widget.Toast.LENGTH_LONG;
|
import static android.widget.Toast.LENGTH_LONG;
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
import static java.util.logging.Logger.getLogger;
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_BLUETOOTH_DISCOVERABLE;
|
|
||||||
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.ACCEPTED;
|
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.ACCEPTED;
|
||||||
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.REFUSED;
|
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.REFUSED;
|
||||||
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.UNKNOWN;
|
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -55,14 +51,9 @@ public class AddNearbyContactActivity extends BriarActivity
|
|||||||
private AddNearbyContactViewModel viewModel;
|
private AddNearbyContactViewModel viewModel;
|
||||||
private AddNearbyContactPermissionManager permissionManager;
|
private AddNearbyContactPermissionManager permissionManager;
|
||||||
|
|
||||||
/**
|
private final ActivityResultLauncher<Integer> bluetoothLauncher =
|
||||||
* Set to true in onPostResume() and false in onPause(). This prevents the
|
registerForActivityResult(new RequestBluetoothDiscoverable(),
|
||||||
* QR code fragment from being shown if onRequestPermissionsResult() is
|
this::onBluetoothDiscoverableResult);
|
||||||
* called while the activity is paused, which could cause a crash due to
|
|
||||||
* https://issuetracker.google.com/issues/37067655.
|
|
||||||
*/
|
|
||||||
private boolean isResumed = false;
|
|
||||||
private BroadcastReceiver bluetoothReceiver = null;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void injectActivity(ActivityComponent component) {
|
public void injectActivity(ActivityComponent component) {
|
||||||
@@ -79,21 +70,14 @@ public class AddNearbyContactActivity extends BriarActivity
|
|||||||
setContentView(R.layout.activity_fragment_container_toolbar);
|
setContentView(R.layout.activity_fragment_container_toolbar);
|
||||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(toolbar);
|
||||||
NullSafety.requireNonNull(getSupportActionBar())
|
requireNonNull(getSupportActionBar()).setDisplayHomeAsUpEnabled(true);
|
||||||
.setDisplayHomeAsUpEnabled(true);
|
|
||||||
if (state == null) {
|
if (state == null) {
|
||||||
showInitialFragment(AddNearbyContactIntroFragment.newInstance());
|
showInitialFragment(AddNearbyContactIntroFragment.newInstance());
|
||||||
}
|
}
|
||||||
IntentFilter filter = new IntentFilter(ACTION_SCAN_MODE_CHANGED);
|
viewModel.getCheckPermissions().observeEvent(this, check ->
|
||||||
bluetoothReceiver = new BluetoothStateReceiver();
|
permissionManager.checkPermissions()); // never false
|
||||||
registerReceiver(bluetoothReceiver, filter);
|
viewModel.getRequestBluetoothDiscoverable().observeEvent(this, r ->
|
||||||
viewModel.getWasContinueClicked().observe(this, clicked -> {
|
requestBluetoothDiscoverable()); // never false
|
||||||
if (clicked && permissionManager.checkPermissions()) {
|
|
||||||
showQrCodeFragmentIfAllowed();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
viewModel.getTransportStateChanged().observeEvent(this,
|
|
||||||
t -> showQrCodeFragmentIfAllowed());
|
|
||||||
viewModel.getShowQrCodeFragment().observeEvent(this, show -> {
|
viewModel.getShowQrCodeFragment().observeEvent(this, show -> {
|
||||||
if (show) showQrCodeFragment();
|
if (show) showQrCodeFragment();
|
||||||
});
|
});
|
||||||
@@ -113,37 +97,23 @@ public class AddNearbyContactActivity extends BriarActivity
|
|||||||
@Override
|
@Override
|
||||||
protected void onPostResume() {
|
protected void onPostResume() {
|
||||||
super.onPostResume();
|
super.onPostResume();
|
||||||
isResumed = true;
|
viewModel.setIsActivityResumed(true);
|
||||||
// Workaround for
|
|
||||||
// https://code.google.com/p/android/issues/detail?id=190966
|
|
||||||
showQrCodeFragmentIfAllowed();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
isResumed = false;
|
viewModel.setIsActivityResumed(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void onBluetoothDiscoverableResult(boolean discoverable) {
|
||||||
public void onDestroy() {
|
if (discoverable) {
|
||||||
super.onDestroy();
|
LOG.info("Bluetooth discoverability was accepted");
|
||||||
if (bluetoothReceiver != null) unregisterReceiver(bluetoothReceiver);
|
viewModel.setBluetoothDecision(ACCEPTED);
|
||||||
}
|
} else {
|
||||||
|
LOG.info("Bluetooth discoverability was refused");
|
||||||
@Override
|
viewModel.setBluetoothDecision(REFUSED);
|
||||||
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");
|
|
||||||
viewModel.bluetoothDecision = REFUSED;
|
|
||||||
} else {
|
|
||||||
LOG.info("Bluetooth discoverability was accepted");
|
|
||||||
viewModel.bluetoothDecision = ACCEPTED;
|
|
||||||
}
|
|
||||||
showQrCodeFragmentIfAllowed();
|
|
||||||
} else super.onActivityResult(request, result, data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -161,14 +131,16 @@ public class AddNearbyContactActivity extends BriarActivity
|
|||||||
super.onRequestPermissionsResult(requestCode, permissions,
|
super.onRequestPermissionsResult(requestCode, permissions,
|
||||||
grantResults);
|
grantResults);
|
||||||
permissionManager.onRequestPermissionsResult(requestCode, permissions,
|
permissionManager.onRequestPermissionsResult(requestCode, permissions,
|
||||||
grantResults, this::showQrCodeFragmentIfAllowed);
|
grantResults, viewModel::showQrCodeFragmentIfAllowed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
if (viewModel.getState().getValue() instanceof Failed) {
|
if (viewModel.getState().getValue() instanceof Failed) {
|
||||||
// finish this activity when going back in failed state
|
// re-create this activity when going back in failed state
|
||||||
supportFinishAfterTransition();
|
Intent i = new Intent(this, AddNearbyContactActivity.class);
|
||||||
|
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
startActivity(i);
|
||||||
} else {
|
} else {
|
||||||
super.onBackPressed();
|
super.onBackPressed();
|
||||||
}
|
}
|
||||||
@@ -176,41 +148,15 @@ public class AddNearbyContactActivity extends BriarActivity
|
|||||||
|
|
||||||
private void requestBluetoothDiscoverable() {
|
private void requestBluetoothDiscoverable() {
|
||||||
if (!viewModel.isBluetoothSupported()) {
|
if (!viewModel.isBluetoothSupported()) {
|
||||||
viewModel.bluetoothDecision = BluetoothDecision.NO_ADAPTER;
|
viewModel.setBluetoothDecision(BluetoothDecision.NO_ADAPTER);
|
||||||
showQrCodeFragmentIfAllowed();
|
|
||||||
} else {
|
} else {
|
||||||
Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE);
|
Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE);
|
||||||
if (i.resolveActivity(getPackageManager()) != null) {
|
if (i.resolveActivity(getPackageManager()) != null) {
|
||||||
LOG.info("Asking for Bluetooth discoverability");
|
LOG.info("Asking for Bluetooth discoverability");
|
||||||
viewModel.bluetoothDecision = BluetoothDecision.WAITING;
|
viewModel.setBluetoothDecision(BluetoothDecision.WAITING);
|
||||||
startActivityForResult(i, REQUEST_BLUETOOTH_DISCOVERABLE);
|
bluetoothLauncher.launch(120); // 2min discoverable
|
||||||
} else {
|
} else {
|
||||||
viewModel.bluetoothDecision = BluetoothDecision.NO_ADAPTER;
|
viewModel.setBluetoothDecision(BluetoothDecision.NO_ADAPTER);
|
||||||
showQrCodeFragmentIfAllowed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("StatementWithEmptyBody")
|
|
||||||
private void showQrCodeFragmentIfAllowed() {
|
|
||||||
boolean continueClicked = // never set to null
|
|
||||||
NullSafety.requireNonNull(
|
|
||||||
viewModel.getWasContinueClicked().getValue());
|
|
||||||
boolean permissionsGranted =
|
|
||||||
permissionManager.areEssentialPermissionsGranted();
|
|
||||||
if (isResumed && continueClicked && permissionsGranted) {
|
|
||||||
if (viewModel.isWifiReady() && viewModel.isBluetoothReady()) {
|
|
||||||
LOG.info("Wifi and Bluetooth are ready");
|
|
||||||
viewModel.startAddingContact();
|
|
||||||
} else {
|
|
||||||
viewModel.enableWifiIfWeShould();
|
|
||||||
if (viewModel.bluetoothDecision == UNKNOWN) {
|
|
||||||
requestBluetoothDiscoverable();
|
|
||||||
} else if (viewModel.bluetoothDecision == REFUSED) {
|
|
||||||
// Ask again when the user clicks "continue"
|
|
||||||
} else {
|
|
||||||
viewModel.enableBluetoothIfWeShould();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -233,11 +179,6 @@ public class AddNearbyContactActivity extends BriarActivity
|
|||||||
((ContactExchangeFinished) state).result;
|
((ContactExchangeFinished) state).result;
|
||||||
onContactExchangeResult(result);
|
onContactExchangeResult(result);
|
||||||
} else if (state instanceof Failed) {
|
} else if (state instanceof Failed) {
|
||||||
// Remove navigation icon, so user can't go back when failed
|
|
||||||
// ErrorFragment will finish or relaunch this activity
|
|
||||||
Toolbar toolbar = findViewById(R.id.toolbar);
|
|
||||||
toolbar.setNavigationIcon(null);
|
|
||||||
|
|
||||||
Boolean qrCodeTooOld = ((Failed) state).qrCodeTooOld;
|
Boolean qrCodeTooOld = ((Failed) state).qrCodeTooOld;
|
||||||
onAddingContactFailed(qrCodeTooOld);
|
onAddingContactFailed(qrCodeTooOld);
|
||||||
}
|
}
|
||||||
@@ -286,11 +227,4 @@ public class AddNearbyContactActivity extends BriarActivity
|
|||||||
showNextFragment(new AddNearbyContactErrorFragment());
|
showNextFragment(new AddNearbyContactErrorFragment());
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BluetoothStateReceiver extends BroadcastReceiver {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
LOG.info("Bluetooth scan mode changed");
|
|
||||||
showQrCodeFragmentIfAllowed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,10 +171,8 @@ public class AddNearbyContactFragment extends BaseFragment
|
|||||||
status.setText(R.string.waiting_for_contact_to_scan);
|
status.setText(R.string.waiting_for_contact_to_scan);
|
||||||
} else if (state instanceof KeyAgreementStarted) {
|
} else if (state instanceof KeyAgreementStarted) {
|
||||||
qrCodeView.setVisibility(INVISIBLE);
|
qrCodeView.setVisibility(INVISIBLE);
|
||||||
statusView.setVisibility(VISIBLE);
|
|
||||||
status.setText(R.string.authenticating_with_device);
|
status.setText(R.string.authenticating_with_device);
|
||||||
} else if (state instanceof ContactExchangeStarted) {
|
} else if (state instanceof ContactExchangeStarted) {
|
||||||
statusView.setVisibility(VISIBLE);
|
|
||||||
status.setText(R.string.exchanging_contact_details);
|
status.setText(R.string.exchanging_contact_details);
|
||||||
} else if (state instanceof Failed) {
|
} else if (state instanceof Failed) {
|
||||||
// the activity will replace this fragment with an error fragment
|
// the activity will replace this fragment with an error fragment
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.briarproject.briar.android.contact.add.nearby;
|
package org.briarproject.briar.android.contact.add.nearby;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.BaseActivity;
|
import org.briarproject.briar.android.activity.BaseActivity;
|
||||||
|
|
||||||
@@ -11,6 +13,8 @@ import static android.Manifest.permission.ACCESS_FINE_LOCATION;
|
|||||||
import static android.Manifest.permission.CAMERA;
|
import static android.Manifest.permission.CAMERA;
|
||||||
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
|
||||||
import static android.os.Build.VERSION.SDK_INT;
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
import static androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale;
|
||||||
|
import static androidx.core.content.ContextCompat.checkSelfPermission;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA_LOCATION;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA_LOCATION;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.getGoToSettingsListener;
|
import static org.briarproject.briar.android.util.UiUtils.getGoToSettingsListener;
|
||||||
|
|
||||||
@@ -37,6 +41,15 @@ class AddNearbyContactPermissionManager {
|
|||||||
locationPermission = Permission.UNKNOWN;
|
locationPermission = Permission.UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static boolean areEssentialPermissionsGranted(Context ctx,
|
||||||
|
boolean isBluetoothSupported) {
|
||||||
|
int ok = PERMISSION_GRANTED;
|
||||||
|
return checkSelfPermission(ctx, CAMERA) == ok &&
|
||||||
|
(SDK_INT < 23 ||
|
||||||
|
checkSelfPermission(ctx, ACCESS_FINE_LOCATION) == ok ||
|
||||||
|
!isBluetoothSupported);
|
||||||
|
}
|
||||||
|
|
||||||
boolean areEssentialPermissionsGranted() {
|
boolean areEssentialPermissionsGranted() {
|
||||||
return cameraPermission == Permission.GRANTED &&
|
return cameraPermission == Permission.GRANTED &&
|
||||||
(SDK_INT < 23 || locationPermission == Permission.GRANTED ||
|
(SDK_INT < 23 || locationPermission == Permission.GRANTED ||
|
||||||
@@ -147,8 +160,7 @@ class AddNearbyContactPermissionManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldShowRationale(String permission) {
|
private boolean shouldShowRationale(String permission) {
|
||||||
return ActivityCompat
|
return shouldShowRequestPermissionRationale(ctx, permission);
|
||||||
.shouldShowRequestPermissionRationale(ctx, permission);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,10 @@ package org.briarproject.briar.android.contact.add.nearby;
|
|||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.bluetooth.BluetoothAdapter;
|
import android.bluetooth.BluetoothAdapter;
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.IntentFilter;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
@@ -39,6 +43,7 @@ import org.briarproject.bramble.api.plugin.PluginManager;
|
|||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportStateEvent;
|
||||||
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeFinished;
|
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeFinished;
|
||||||
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult.Error;
|
import org.briarproject.briar.android.contact.add.nearby.AddContactState.ContactExchangeResult.Error;
|
||||||
@@ -64,6 +69,7 @@ import androidx.lifecycle.AndroidViewModel;
|
|||||||
import androidx.lifecycle.LiveData;
|
import androidx.lifecycle.LiveData;
|
||||||
import androidx.lifecycle.MutableLiveData;
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
|
||||||
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
|
import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE;
|
||||||
import static android.widget.Toast.LENGTH_LONG;
|
import static android.widget.Toast.LENGTH_LONG;
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
@@ -75,6 +81,7 @@ 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.INACTIVE;
|
||||||
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
|
import static org.briarproject.bramble.api.plugin.Plugin.State.STARTING_STOPPING;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
|
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactPermissionManager.areEssentialPermissionsGranted;
|
||||||
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.REFUSED;
|
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.REFUSED;
|
||||||
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.UNKNOWN;
|
import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.UNKNOWN;
|
||||||
|
|
||||||
@@ -116,6 +123,7 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
|||||||
private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
|
private static final Charset ISO_8859_1 = Charset.forName("ISO-8859-1");
|
||||||
|
|
||||||
private final EventBus eventBus;
|
private final EventBus eventBus;
|
||||||
|
private final AndroidExecutor androidExecutor;
|
||||||
private final Executor ioExecutor;
|
private final Executor ioExecutor;
|
||||||
private final PluginManager pluginManager;
|
private final PluginManager pluginManager;
|
||||||
private final PayloadEncoder payloadEncoder;
|
private final PayloadEncoder payloadEncoder;
|
||||||
@@ -124,28 +132,28 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
|||||||
private final ContactExchangeManager contactExchangeManager;
|
private final ContactExchangeManager contactExchangeManager;
|
||||||
private final ConnectionManager connectionManager;
|
private final ConnectionManager connectionManager;
|
||||||
|
|
||||||
/**
|
private final MutableLiveEvent<Boolean> checkPermissions =
|
||||||
* 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<>();
|
new MutableLiveEvent<>();
|
||||||
private final MutableLiveEvent<TransportId> transportStateChanged =
|
private final MutableLiveEvent<Boolean> requestBluetoothDiscoverable =
|
||||||
|
new MutableLiveEvent<>();
|
||||||
|
private final MutableLiveEvent<Boolean> showQrCodeFragment =
|
||||||
new MutableLiveEvent<>();
|
new MutableLiveEvent<>();
|
||||||
private final MutableLiveData<AddContactState> state =
|
private final MutableLiveData<AddContactState> state =
|
||||||
new MutableLiveData<>();
|
new MutableLiveData<>();
|
||||||
|
|
||||||
final QrCodeDecoder qrCodeDecoder;
|
final QrCodeDecoder qrCodeDecoder;
|
||||||
|
final BroadcastReceiver bluetoothReceiver = new BluetoothStateReceiver();
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private final BluetoothAdapter bt;
|
private final BluetoothAdapter bt;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final Plugin wifiPlugin, bluetoothPlugin;
|
private final Plugin wifiPlugin, bluetoothPlugin;
|
||||||
|
|
||||||
// UiThread
|
// UiThread
|
||||||
BluetoothDecision bluetoothDecision = BluetoothDecision.UNKNOWN;
|
private BluetoothDecision bluetoothDecision = BluetoothDecision.UNKNOWN;
|
||||||
|
|
||||||
|
private boolean wasContinueClicked = false;
|
||||||
|
private boolean isActivityResumed = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Records whether we've enabled the wifi plugin so we don't enable it more
|
* Records whether we've enabled the wifi plugin so we don't enable it more
|
||||||
@@ -166,6 +174,7 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
|||||||
@Inject
|
@Inject
|
||||||
AddNearbyContactViewModel(Application app,
|
AddNearbyContactViewModel(Application app,
|
||||||
EventBus eventBus,
|
EventBus eventBus,
|
||||||
|
AndroidExecutor androidExecutor,
|
||||||
@IoExecutor Executor ioExecutor,
|
@IoExecutor Executor ioExecutor,
|
||||||
PluginManager pluginManager,
|
PluginManager pluginManager,
|
||||||
PayloadEncoder payloadEncoder,
|
PayloadEncoder payloadEncoder,
|
||||||
@@ -175,6 +184,7 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
|||||||
ConnectionManager connectionManager) {
|
ConnectionManager connectionManager) {
|
||||||
super(app);
|
super(app);
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
|
this.androidExecutor = androidExecutor;
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
this.pluginManager = pluginManager;
|
this.pluginManager = pluginManager;
|
||||||
this.payloadEncoder = payloadEncoder;
|
this.payloadEncoder = payloadEncoder;
|
||||||
@@ -185,13 +195,16 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
|||||||
bt = BluetoothAdapter.getDefaultAdapter();
|
bt = BluetoothAdapter.getDefaultAdapter();
|
||||||
wifiPlugin = pluginManager.getPlugin(LanTcpConstants.ID);
|
wifiPlugin = pluginManager.getPlugin(LanTcpConstants.ID);
|
||||||
bluetoothPlugin = pluginManager.getPlugin(BluetoothConstants.ID);
|
bluetoothPlugin = pluginManager.getPlugin(BluetoothConstants.ID);
|
||||||
qrCodeDecoder = new QrCodeDecoder(ioExecutor, this);
|
qrCodeDecoder = new QrCodeDecoder(androidExecutor, ioExecutor, this);
|
||||||
eventBus.addListener(this);
|
eventBus.addListener(this);
|
||||||
|
IntentFilter filter = new IntentFilter(ACTION_SCAN_MODE_CHANGED);
|
||||||
|
getApplication().registerReceiver(bluetoothReceiver, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCleared() {
|
protected void onCleared() {
|
||||||
super.onCleared();
|
super.onCleared();
|
||||||
|
getApplication().unregisterReceiver(bluetoothReceiver);
|
||||||
eventBus.removeListener(this);
|
eventBus.removeListener(this);
|
||||||
stopListening();
|
stopListening();
|
||||||
}
|
}
|
||||||
@@ -201,7 +214,9 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
|||||||
if (bluetoothDecision == REFUSED) {
|
if (bluetoothDecision == REFUSED) {
|
||||||
bluetoothDecision = UNKNOWN; // Ask again
|
bluetoothDecision = UNKNOWN; // Ask again
|
||||||
}
|
}
|
||||||
wasContinueClicked.setValue(true);
|
wasContinueClicked = true;
|
||||||
|
checkPermissions.setEvent(true);
|
||||||
|
showQrCodeFragmentIfAllowed();
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@@ -266,7 +281,7 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
|||||||
void startAddingContact() {
|
void startAddingContact() {
|
||||||
// If we return to the intro fragment, the continue button needs to be
|
// If we return to the intro fragment, the continue button needs to be
|
||||||
// clicked again before showing the QR code fragment
|
// clicked again before showing the QR code fragment
|
||||||
wasContinueClicked.setValue(false);
|
wasContinueClicked = false;
|
||||||
// If we return to the intro fragment, ask for Bluetooth
|
// If we return to the intro fragment, ask for Bluetooth
|
||||||
// discoverability again before showing the QR code fragment
|
// discoverability again before showing the QR code fragment
|
||||||
bluetoothDecision = UNKNOWN;
|
bluetoothDecision = UNKNOWN;
|
||||||
@@ -310,12 +325,12 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
|||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
LOG.info("Bluetooth state changed to " + t.getState());
|
LOG.info("Bluetooth state changed to " + t.getState());
|
||||||
}
|
}
|
||||||
transportStateChanged.setEvent(t.getTransportId());
|
showQrCodeFragmentIfAllowed();
|
||||||
} else if (t.getTransportId().equals(LanTcpConstants.ID)) {
|
} else if (t.getTransportId().equals(LanTcpConstants.ID)) {
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (LOG.isLoggable(INFO)) {
|
||||||
LOG.info("Wifi state changed to " + t.getState());
|
LOG.info("Wifi state changed to " + t.getState());
|
||||||
}
|
}
|
||||||
transportStateChanged.setEvent(t.getTransportId());
|
showQrCodeFragmentIfAllowed();
|
||||||
}
|
}
|
||||||
} else if (e instanceof KeyAgreementListeningEvent) {
|
} else if (e instanceof KeyAgreementListeningEvent) {
|
||||||
LOG.info("KeyAgreementListeningEvent received");
|
LOG.info("KeyAgreementListeningEvent received");
|
||||||
@@ -344,6 +359,28 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("StatementWithEmptyBody")
|
||||||
|
@UiThread
|
||||||
|
void showQrCodeFragmentIfAllowed() {
|
||||||
|
boolean permissionsGranted = areEssentialPermissionsGranted(
|
||||||
|
getApplication(), isBluetoothSupported());
|
||||||
|
if (isActivityResumed && wasContinueClicked && permissionsGranted) {
|
||||||
|
if (isWifiReady() && isBluetoothReady()) {
|
||||||
|
LOG.info("Wifi and Bluetooth are ready");
|
||||||
|
startAddingContact();
|
||||||
|
} else {
|
||||||
|
enableWifiIfWeShould();
|
||||||
|
if (bluetoothDecision == UNKNOWN) {
|
||||||
|
requestBluetoothDiscoverable.setEvent(true);
|
||||||
|
} else if (bluetoothDecision == REFUSED) {
|
||||||
|
// Ask again when the user clicks "continue"
|
||||||
|
} else {
|
||||||
|
enableBluetoothIfWeShould();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This sets the QR code by setting the state to KeyAgreementListening.
|
* This sets the QR code by setting the state to KeyAgreementListening.
|
||||||
*/
|
*/
|
||||||
@@ -383,8 +420,8 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
|||||||
state.postValue(new AddContactState.Failed(e.isTooOld()));
|
state.postValue(new AddContactState.Failed(e.isTooOld()));
|
||||||
} catch (IOException | IllegalArgumentException e) {
|
} catch (IOException | IllegalArgumentException e) {
|
||||||
LOG.log(WARNING, "QR Code Invalid", e);
|
LOG.log(WARNING, "QR Code Invalid", e);
|
||||||
Toast.makeText(getApplication(), R.string.qr_code_invalid,
|
androidExecutor.runOnUiThread(() -> Toast.makeText(getApplication(),
|
||||||
LENGTH_LONG).show();
|
R.string.qr_code_invalid, LENGTH_LONG).show());
|
||||||
resetPayloadFlags();
|
resetPayloadFlags();
|
||||||
state.postValue(new AddContactState.Failed());
|
state.postValue(new AddContactState.Failed());
|
||||||
}
|
}
|
||||||
@@ -423,6 +460,15 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class BluetoothStateReceiver extends BroadcastReceiver {
|
||||||
|
@UiThread
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
LOG.info("Bluetooth scan mode changed");
|
||||||
|
showQrCodeFragmentIfAllowed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void tryToClose(DuplexTransportConnection conn) {
|
private void tryToClose(DuplexTransportConnection conn) {
|
||||||
try {
|
try {
|
||||||
conn.getReader().dispose(true, true);
|
conn.getReader().dispose(true, true);
|
||||||
@@ -432,16 +478,33 @@ class AddNearbyContactViewModel extends AndroidViewModel
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LiveData<Boolean> getWasContinueClicked() {
|
/**
|
||||||
return wasContinueClicked;
|
* Set to true in onPostResume() and false in onPause(). This prevents the
|
||||||
|
* QR code fragment from being shown if onRequestPermissionsResult() is
|
||||||
|
* called while the activity is paused, which could cause a crash due to
|
||||||
|
* https://issuetracker.google.com/issues/37067655.
|
||||||
|
* TODO check if this is still happening when using new permission requesting
|
||||||
|
*/
|
||||||
|
@UiThread
|
||||||
|
void setIsActivityResumed(boolean resumed) {
|
||||||
|
isActivityResumed = resumed;
|
||||||
|
// Workaround for
|
||||||
|
// https://code.google.com/p/android/issues/detail?id=190966
|
||||||
|
showQrCodeFragmentIfAllowed();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@UiThread
|
||||||
* Receives an event when the transport state of the WiFi or Bluetooth
|
void setBluetoothDecision(BluetoothDecision decision) {
|
||||||
* plugins changes.
|
bluetoothDecision = decision;
|
||||||
*/
|
showQrCodeFragmentIfAllowed();
|
||||||
LiveEvent<TransportId> getTransportStateChanged() {
|
}
|
||||||
return transportStateChanged;
|
|
||||||
|
LiveEvent<Boolean> getCheckPermissions() {
|
||||||
|
return checkPermissions;
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveEvent<Boolean> getRequestBluetoothDiscoverable() {
|
||||||
|
return requestBluetoothDiscoverable;
|
||||||
}
|
}
|
||||||
|
|
||||||
LiveEvent<Boolean> getShowQrCodeFragment() {
|
LiveEvent<Boolean> getShowQrCodeFragment() {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
|||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -35,6 +36,7 @@ class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
|||||||
|
|
||||||
private static final Logger LOG = getLogger(QrCodeDecoder.class.getName());
|
private static final Logger LOG = getLogger(QrCodeDecoder.class.getName());
|
||||||
|
|
||||||
|
private final AndroidExecutor androidExecutor;
|
||||||
private final Executor ioExecutor;
|
private final Executor ioExecutor;
|
||||||
private final Reader reader = new QRCodeReader();
|
private final Reader reader = new QRCodeReader();
|
||||||
private final ResultCallback callback;
|
private final ResultCallback callback;
|
||||||
@@ -42,7 +44,9 @@ class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
|||||||
private Camera camera = null;
|
private Camera camera = null;
|
||||||
private int cameraIndex = 0;
|
private int cameraIndex = 0;
|
||||||
|
|
||||||
QrCodeDecoder(@IoExecutor Executor ioExecutor, ResultCallback callback) {
|
QrCodeDecoder(AndroidExecutor androidExecutor,
|
||||||
|
@IoExecutor Executor ioExecutor, ResultCallback callback) {
|
||||||
|
this.androidExecutor = androidExecutor;
|
||||||
this.ioExecutor = ioExecutor;
|
this.ioExecutor = ioExecutor;
|
||||||
this.callback = callback;
|
this.callback = callback;
|
||||||
}
|
}
|
||||||
@@ -104,9 +108,9 @@ class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
|
|||||||
LOG.warning("Invalid preview frame");
|
LOG.warning("Invalid preview frame");
|
||||||
} finally {
|
} finally {
|
||||||
reader.reset();
|
reader.reset();
|
||||||
|
androidExecutor.runOnUiThread(this::askForPreviewFrame);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
askForPreviewFrame();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BinaryBitmap binarize(byte[] data, int width, int height,
|
private static BinaryBitmap binarize(byte[] data, int width, int height,
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package org.briarproject.briar.android.util;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import androidx.activity.result.contract.ActivityResultContract;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import static android.app.Activity.RESULT_CANCELED;
|
||||||
|
import static android.bluetooth.BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE;
|
||||||
|
import static android.bluetooth.BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
public class RequestBluetoothDiscoverable
|
||||||
|
extends ActivityResultContract<Integer, Boolean> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Intent createIntent(Context context, Integer duration) {
|
||||||
|
Intent i = new Intent(ACTION_REQUEST_DISCOVERABLE);
|
||||||
|
i.putExtra(EXTRA_DISCOVERABLE_DURATION, duration);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Boolean parseResult(int resultCode, @Nullable Intent intent) {
|
||||||
|
return resultCode != RESULT_CANCELED;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,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.contact.add.nearby.KeyAgreementActivity">
|
tools:context=".android.contact.add.nearby.AddNearbyContactActivity">
|
||||||
|
|
||||||
<include layout="@layout/toolbar" />
|
<include layout="@layout/toolbar" />
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user