diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java index d1f3ad737..ce359bd35 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java @@ -28,6 +28,7 @@ import org.briarproject.briar.android.contact.add.remote.LinkExchangeFragment; import org.briarproject.briar.android.contact.add.remote.NicknameFragment; import org.briarproject.briar.android.contact.add.remote.PendingContactListActivity; import org.briarproject.briar.android.conversation.AliasDialogFragment; +import org.briarproject.briar.android.conversation.BluetoothConnecterDialogFragment; import org.briarproject.briar.android.conversation.ConversationActivity; import org.briarproject.briar.android.conversation.ConversationSettingsDialog; import org.briarproject.briar.android.conversation.ImageActivity; @@ -236,4 +237,6 @@ public interface ActivityComponent { void inject(ConversationSettingsDialog dialog); + void inject( + BluetoothConnecterDialogFragment bluetoothConnecterDialogFragment); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/AddNearbyContactPermissionManager.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/AddNearbyContactPermissionManager.java index b0bc75fbe..dc8fca71f 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/AddNearbyContactPermissionManager.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/AddNearbyContactPermissionManager.java @@ -1,10 +1,6 @@ package org.briarproject.briar.android.contact.add.nearby; -import android.content.ActivityNotFoundException; import android.content.Context; -import android.content.Intent; -import android.location.LocationManager; -import android.widget.Toast; import org.briarproject.briar.R; @@ -19,11 +15,11 @@ import static android.Manifest.permission.ACCESS_FINE_LOCATION; import static android.Manifest.permission.CAMERA; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.Build.VERSION.SDK_INT; -import static android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS; -import static android.widget.Toast.LENGTH_LONG; import static androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale; import static androidx.core.content.ContextCompat.checkSelfPermission; import static org.briarproject.briar.android.util.UiUtils.getGoToSettingsListener; +import static org.briarproject.briar.android.util.UiUtils.isLocationEnabled; +import static org.briarproject.briar.android.util.UiUtils.showLocationDialog; class AddNearbyContactPermissionManager { @@ -51,19 +47,6 @@ class AddNearbyContactPermissionManager { locationPermission = Permission.UNKNOWN; } - /** - * @return true if location is enabled, - * or it isn't required due to this being a SDK < 28 device. - */ - static boolean isLocationEnabled(Context ctx) { - if (SDK_INT >= 28) { - LocationManager lm = ctx.getSystemService(LocationManager.class); - return lm.isLocationEnabled(); - } else { - return true; - } - } - static boolean areEssentialPermissionsGranted(Context ctx, boolean isBluetoothSupported) { int ok = PERMISSION_GRANTED; @@ -106,7 +89,7 @@ class AddNearbyContactPermissionManager { } else if (locationPermission == Permission.SHOW_RATIONALE) { showRationale(R.string.permission_location_title, R.string.permission_location_request_body); - } else if (isLocationEnabled(ctx)) { + } else if (locationEnabled) { requestPermissions(); } else { showLocationDialog(ctx); @@ -135,25 +118,6 @@ class AddNearbyContactPermissionManager { builder.show(); } - private static void showLocationDialog(Context ctx) { - AlertDialog.Builder builder = - new AlertDialog.Builder(ctx, R.style.BriarDialogTheme); - builder.setTitle(R.string.permission_location_setting_title); - builder.setMessage(R.string.permission_location_setting_body); - builder.setNegativeButton(R.string.cancel, null); - builder.setPositiveButton(R.string.permission_location_setting_button, - (dialog, which) -> { - Intent i = new Intent(ACTION_LOCATION_SOURCE_SETTINGS); - try { - ctx.startActivity(i); - } catch (ActivityNotFoundException e) { - Toast.makeText(ctx, R.string.error_start_activity, - LENGTH_LONG).show(); - } - }); - builder.show(); - } - private void requestPermissions() { String[] permissions; if (isBluetoothSupported) { diff --git a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/AddNearbyContactViewModel.java b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/AddNearbyContactViewModel.java index 2a284f685..a7a18dee1 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/AddNearbyContactViewModel.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/contact/add/nearby/AddNearbyContactViewModel.java @@ -82,11 +82,11 @@ 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.AddNearbyContactPermissionManager.areEssentialPermissionsGranted; -import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactPermissionManager.isLocationEnabled; import static org.briarproject.briar.android.contact.add.nearby.AddNearbyContactViewModel.BluetoothDecision.NO_ADAPTER; 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.util.UiUtils.handleException; +import static org.briarproject.briar.android.util.UiUtils.isLocationEnabled; @NotNullByDefault class AddNearbyContactViewModel extends AndroidViewModel diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/BluetoothConnecter.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/BluetoothConnecter.java index 54e28bd47..c3a04d4d2 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/BluetoothConnecter.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/BluetoothConnecter.java @@ -6,6 +6,8 @@ import android.bluetooth.BluetoothAdapter; import android.content.Context; import android.widget.Toast; +import org.briarproject.bramble.api.connection.ConnectionRegistry; +import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.lifecycle.IoExecutor; import org.briarproject.bramble.api.plugin.BluetoothConstants; import org.briarproject.bramble.api.plugin.Plugin; @@ -21,90 +23,130 @@ import java.util.logging.Logger; import javax.inject.Inject; import androidx.activity.result.ActivityResultLauncher; +import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.annotation.UiThread; import androidx.appcompat.app.AlertDialog; import static android.Manifest.permission.ACCESS_FINE_LOCATION; +import static android.os.Build.VERSION.SDK_INT; import static androidx.core.app.ActivityCompat.shouldShowRequestPermissionRationale; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.briar.android.util.UiUtils.getGoToSettingsListener; +import static org.briarproject.briar.android.util.UiUtils.isLocationEnabled; +import static org.briarproject.briar.android.util.UiUtils.showLocationDialog; class BluetoothConnecter { private final Logger LOG = getLogger(BluetoothConnecter.class.getName()); + private enum Permission { + UNKNOWN, GRANTED, SHOW_RATIONALE, PERMANENTLY_DENIED + } + private final Application app; private final Executor ioExecutor; private final AndroidExecutor androidExecutor; - + private final ConnectionRegistry connectionRegistry; private final BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter(); private final Plugin bluetoothPlugin; + private Permission locationPermission = Permission.UNKNOWN; + @Inject BluetoothConnecter(Application app, PluginManager pluginManager, @IoExecutor Executor ioExecutor, - AndroidExecutor androidExecutor) { + AndroidExecutor androidExecutor, + ConnectionRegistry connectionRegistry) { this.app = app; this.ioExecutor = ioExecutor; this.androidExecutor = androidExecutor; this.bluetoothPlugin = pluginManager.getPlugin(BluetoothConstants.ID); + this.connectionRegistry = connectionRegistry; } - static void showDialog(Context ctx, - ActivityResultLauncher permissionRequest) { - new AlertDialog.Builder(ctx, R.style.BriarDialogTheme) - .setTitle(R.string.dialog_title_connect_via_bluetooth) - .setMessage(R.string.dialog_message_connect_via_bluetooth) - .setPositiveButton(R.string.start, (dialog, which) -> - permissionRequest.launch(ACCESS_FINE_LOCATION)) - .setNegativeButton(R.string.cancel, null) - .show(); + boolean isConnectedViaBluetooth(ContactId contactId) { + return connectionRegistry.isConnected(contactId, BluetoothConstants.ID); + } + + boolean isDiscovering() { + // TODO bluetoothPlugin.isDiscovering() + return false; + } + + /** + * Call this when the using activity or fragment starts, + * because permissions might have changed while it was stopped. + */ + void resetPermissions() { + locationPermission = Permission.UNKNOWN; } @UiThread - void onLocationPermissionResult(Activity activity, boolean result, - ActivityResultLauncher bluetoothDiscoverableRequest) { - if (result) { - if (isBluetoothSupported()) { - bluetoothDiscoverableRequest.launch(120); - } else { - showToast(R.string.toast_connect_via_bluetooth_error); - } + void onLocationPermissionResult(Activity activity, + @Nullable Boolean result) { + if (result != null && result) { + locationPermission = Permission.GRANTED; } else if (shouldShowRequestPermissionRationale(activity, ACCESS_FINE_LOCATION)) { - showToast(R.string.permission_location_denied_body); + locationPermission = Permission.SHOW_RATIONALE; } else { - showRationale(activity); + locationPermission = Permission.PERMANENTLY_DENIED; } } - private boolean isBluetoothSupported() { - return bt != null && bluetoothPlugin != null; + boolean isBluetoothNotSupported() { + // When this class is instantiated before we are logged in + // (like when returning to a killed activity), bluetoothPlugin will be + // null and we consider bluetooth not supported. + return bt == null || bluetoothPlugin == null; } - private void showRationale(Context ctx) { + boolean areRequirementsFulfilled(Context ctx, + ActivityResultLauncher permissionRequest) { + boolean permissionGranted = + SDK_INT < 23 || locationPermission == Permission.GRANTED; + boolean locationEnabled = isLocationEnabled(ctx); + if (permissionGranted && locationEnabled) return true; + + if (locationPermission == Permission.PERMANENTLY_DENIED) { + showDenialDialog(ctx); + } else if (locationPermission == Permission.SHOW_RATIONALE) { + showRationale(ctx, permissionRequest); + } else if (!locationEnabled) { + showLocationDialog(ctx); + } + return false; + } + + private void showDenialDialog(Context ctx) { new AlertDialog.Builder(ctx, R.style.BriarDialogTheme) .setTitle(R.string.permission_location_title) - .setMessage(R.string.permission_location_request_body) + .setMessage(R.string.permission_location_denied_body) .setPositiveButton(R.string.ok, getGoToSettingsListener(ctx)) .setNegativeButton(R.string.cancel, null) .show(); } - @UiThread - void onBluetoothDiscoverable(boolean result, ContactItem contact) { - if (result) { - connect(contact); - } else { - showToast(R.string.toast_connect_via_bluetooth_not_discoverable); - } + private void showRationale(Context ctx, + ActivityResultLauncher permissionRequest) { + new AlertDialog.Builder(ctx, R.style.BriarDialogTheme) + .setTitle(R.string.permission_location_title) + .setMessage(R.string.permission_location_request_body) + .setPositiveButton(R.string.ok, (dialog, which) -> + permissionRequest.launch(ACCESS_FINE_LOCATION)) + .show(); } - private void connect(ContactItem contact) { + @UiThread + void onBluetoothDiscoverable(ContactItem contact) { + connect(contact.getContact().getId()); + } + + private void connect(ContactId contactId) { // TODO // * enable bluetooth connections setting, if not enabled // * wait for plugin to become active diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/BluetoothConnecterDialogFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/BluetoothConnecterDialogFragment.java new file mode 100644 index 000000000..46f0f9b4a --- /dev/null +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/BluetoothConnecterDialogFragment.java @@ -0,0 +1,143 @@ +package org.briarproject.briar.android.conversation; + +import android.app.Activity; +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.view.View; +import android.widget.Button; +import android.widget.Toast; + +import org.briarproject.bramble.api.contact.ContactId; +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.BaseActivity; +import org.briarproject.briar.android.contact.ContactItem; +import org.briarproject.briar.android.util.RequestBluetoothDiscoverable; + +import javax.inject.Inject; + +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts.RequestPermission; +import androidx.annotation.Nullable; +import androidx.annotation.StringRes; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.DialogFragment; +import androidx.lifecycle.ViewModelProvider; + +import static android.Manifest.permission.ACCESS_FINE_LOCATION; +import static android.content.DialogInterface.BUTTON_POSITIVE; +import static android.widget.Toast.LENGTH_LONG; +import static java.util.Objects.requireNonNull; + +@MethodsNotNullByDefault +@ParametersNotNullByDefault +public class BluetoothConnecterDialogFragment extends DialogFragment { + + final static String TAG = BluetoothConnecterDialogFragment.class.getName(); + + @Inject + ViewModelProvider.Factory viewModelFactory; + + private ConversationViewModel viewModel; + private BluetoothConnecter bluetoothConnecter; + + private final ActivityResultLauncher bluetoothDiscoverableRequest = + registerForActivityResult(new RequestBluetoothDiscoverable(), + this::onBluetoothDiscoverable); + private final ActivityResultLauncher permissionRequest = + registerForActivityResult(new RequestPermission(), + this::onPermissionRequestResult); + + @Override + public void onAttach(Context ctx) { + super.onAttach(ctx); + ((BaseActivity) requireActivity()).getActivityComponent().inject(this); + viewModel = new ViewModelProvider(requireActivity(), viewModelFactory) + .get(ConversationViewModel.class); + bluetoothConnecter = viewModel.getBluetoothConnecter(); + } + + @Override + public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { + Context ctx = requireContext(); + return new AlertDialog.Builder(ctx, R.style.BriarDialogTheme) + .setTitle(R.string.dialog_title_connect_via_bluetooth) + .setMessage(R.string.dialog_message_connect_via_bluetooth) + // actual listener gets set in onResume() + .setPositiveButton(R.string.start, null) + .setNegativeButton(R.string.cancel, null) + .setCancelable(false) // keep it open until dismissed + .create(); + } + + @Override + public void onStart() { + super.onStart(); + if (bluetoothConnecter.isBluetoothNotSupported()) { + showToast(R.string.toast_connect_via_bluetooth_error); + dismiss(); + return; + } + // MenuItem only gets enabled after contactItem has loaded + ContactItem contact = + requireNonNull(viewModel.getContactItem().getValue()); + ContactId contactId = contact.getContact().getId(); + if (bluetoothConnecter.isConnectedViaBluetooth(contactId)) { + showToast(R.string.toast_connect_via_bluetooth_success); + dismiss(); + return; + } + if (bluetoothConnecter.isDiscovering()) { + // TODO showToast(R.string.toast_connect_via_bluetooth_discovering); + dismiss(); + return; + } + bluetoothConnecter.resetPermissions(); + } + + @Override + public void onResume() { + super.onResume(); + // Set the click listener for the START button here + // to prevent it from automatically dismissing the dialog. + // The dialog is shown in onStart(), so we set the listener here later. + AlertDialog dialog = (AlertDialog) getDialog(); + Button positiveButton = (Button) dialog.getButton(BUTTON_POSITIVE); + positiveButton.setOnClickListener(this::onStartClicked); + } + + private void onStartClicked(View v) { + // The dialog starts a permission request which comes back as true + // if the permission is already granted. So it is a generic entry point. + permissionRequest.launch(ACCESS_FINE_LOCATION); + } + + private void onPermissionRequestResult(@Nullable Boolean result) { + Activity a = requireActivity(); + // update permission result in BluetoothConnecter + bluetoothConnecter.onLocationPermissionResult(a, result); + // if requirements are fulfilled, request Bluetooth discoverability + if (bluetoothConnecter.areRequirementsFulfilled(a, permissionRequest)) { + bluetoothDiscoverableRequest.launch(120); // for 2min + } + } + + private void onBluetoothDiscoverable(@Nullable Boolean result) { + if (result != null && result) { + // MenuItem only gets enabled after contactItem has loaded + ContactItem contact = + requireNonNull(viewModel.getContactItem().getValue()); + bluetoothConnecter.onBluetoothDiscoverable(contact); + dismiss(); + } else { + showToast(R.string.toast_connect_via_bluetooth_not_discoverable); + } + } + + private void showToast(@StringRes int stringRes) { + Toast.makeText(requireContext(), stringRes, LENGTH_LONG).show(); + } + +} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java index 3bab9a903..77088fd63 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/conversation/ConversationActivity.java @@ -47,7 +47,6 @@ import org.briarproject.briar.android.activity.BriarActivity; import org.briarproject.briar.android.attachment.AttachmentItem; import org.briarproject.briar.android.attachment.AttachmentRetriever; import org.briarproject.briar.android.blog.BlogActivity; -import org.briarproject.briar.android.contact.ContactItem; import org.briarproject.briar.android.conversation.ConversationVisitor.AttachmentCache; import org.briarproject.briar.android.conversation.ConversationVisitor.TextCache; import org.briarproject.briar.android.forum.ForumActivity; @@ -92,8 +91,6 @@ import java.util.logging.Logger; import javax.inject.Inject; -import androidx.activity.result.ActivityResultLauncher; -import androidx.activity.result.contract.ActivityResultContracts.RequestPermission; import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.appcompat.app.AlertDialog; @@ -102,6 +99,7 @@ import androidx.appcompat.widget.Toolbar; import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityOptionsCompat; import androidx.core.content.ContextCompat; +import androidx.fragment.app.FragmentManager; import androidx.lifecycle.LiveData; import androidx.lifecycle.Observer; import androidx.lifecycle.ViewModelProvider; @@ -214,21 +212,6 @@ public class ConversationActivity extends BriarActivity private volatile ContactId contactId; - private final ActivityResultLauncher bluetoothDiscoverableRequest = - registerForActivityResult(new RequestBluetoothDiscoverable(), r -> { - // MenuItem only gets enabled after contactItem has loaded - ContactItem contact = - requireNonNull(viewModel.getContactItem().getValue()); - BluetoothConnecter bc = viewModel.getBluetoothConnecter(); - bc.onBluetoothDiscoverable(r, contact); - }); - private final ActivityResultLauncher permissionRequest = - registerForActivityResult(new RequestPermission(), result -> { - BluetoothConnecter bc = viewModel.getBluetoothConnecter(); - bc.onLocationPermissionResult(this, result, - bluetoothDiscoverableRequest); - }); - @Override public void onCreate(@Nullable Bundle state) { if (SDK_INT >= 21) { @@ -424,7 +407,9 @@ public class ConversationActivity extends BriarActivity onAutoDeleteTimerNoticeClicked(); return true; } else if (itemId == R.id.action_connect_via_bluetooth) { - BluetoothConnecter.showDialog(this, permissionRequest); + FragmentManager fm = getSupportFragmentManager(); + new BluetoothConnecterDialogFragment().show(fm, + BluetoothConnecterDialogFragment.TAG); return true; } else if (itemId == R.id.action_delete_all_messages) { askToDeleteAllMessages(); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/conversation/RequestBluetoothDiscoverable.java b/briar-android/src/main/java/org/briarproject/briar/android/conversation/RequestBluetoothDiscoverable.java deleted file mode 100644 index 95023f6bf..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/conversation/RequestBluetoothDiscoverable.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.briarproject.briar.android.conversation; - -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 -class RequestBluetoothDiscoverable - extends ActivityResultContract { - - @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; - } -} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java index e971c8c9b..e89b07100 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/util/UiUtils.java @@ -3,11 +3,13 @@ package org.briarproject.briar.android.util; import android.annotation.SuppressLint; import android.annotation.TargetApi; import android.app.KeyguardManager; +import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface.OnClickListener; import android.content.Intent; import android.content.res.Resources; import android.graphics.drawable.Drawable; +import android.location.LocationManager; import android.net.Uri; import android.os.PowerManager; import android.text.Spannable; @@ -72,6 +74,7 @@ import static android.content.Intent.EXTRA_MIME_TYPES; 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_LOCATION_SOURCE_SETTINGS; import static android.provider.Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.FORMAT_ABBREV_ALL; @@ -331,6 +334,19 @@ public class UiUtils { return i; } + /** + * @return true if location is enabled, + * or it isn't required due to this being a SDK < 28 device. + */ + public static boolean isLocationEnabled(Context ctx) { + if (SDK_INT >= 28) { + LocationManager lm = ctx.getSystemService(LocationManager.class); + return lm.isLocationEnabled(); + } else { + return true; + } + } + public static boolean isSamsung7() { return (SDK_INT == 24 || SDK_INT == 25) && MANUFACTURER.equalsIgnoreCase("Samsung"); @@ -474,6 +490,25 @@ public class UiUtils { return isoCode; } + public static void showLocationDialog(Context ctx) { + AlertDialog.Builder builder = + new AlertDialog.Builder(ctx, R.style.BriarDialogTheme); + builder.setTitle(R.string.permission_location_setting_title); + builder.setMessage(R.string.permission_location_setting_body); + builder.setNegativeButton(R.string.cancel, null); + builder.setPositiveButton(R.string.permission_location_setting_button, + (dialog, which) -> { + Intent i = new Intent(ACTION_LOCATION_SOURCE_SETTINGS); + try { + ctx.startActivity(i); + } catch (ActivityNotFoundException e) { + Toast.makeText(ctx, R.string.error_start_activity, + LENGTH_LONG).show(); + } + }); + builder.show(); + } + public static Drawable getDialogIcon(Context ctx, @DrawableRes int resId) { Drawable icon = VectorDrawableCompat.create(ctx.getResources(), resId, null);