diff --git a/android_app/app/build.gradle b/android_app/app/build.gradle index e25c2ba2..aa7cb1a8 100644 --- a/android_app/app/build.gradle +++ b/android_app/app/build.gradle @@ -2,12 +2,12 @@ apply plugin: 'com.android.application' apply plugin: "androidx.navigation.safeargs" android { - compileSdkVersion 32 + compileSdkVersion 33 defaultConfig { applicationId "com.health.openscale" testApplicationId "com.health.openscale.test" minSdkVersion 23 - targetSdkVersion 32 + targetSdkVersion 33 versionCode 61 versionName "2.4.6" @@ -139,9 +139,9 @@ android { dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' - implementation 'com.google.android.material:material:1.8.0-alpha03' + implementation 'com.google.android.material:material:1.8.0-rc01' implementation 'androidx.legacy:legacy-support-v4:1.0.0' - implementation 'androidx.appcompat:appcompat:1.5.1' + implementation 'androidx.appcompat:appcompat:1.6.0' implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.preference:preference:1.2.0' diff --git a/android_app/app/src/main/java/com/health/openscale/gui/MainActivity.java b/android_app/app/src/main/java/com/health/openscale/gui/MainActivity.java index cbdf1662..d5ef1ffc 100644 --- a/android_app/app/src/main/java/com/health/openscale/gui/MainActivity.java +++ b/android_app/app/src/main/java/com/health/openscale/gui/MainActivity.java @@ -18,6 +18,7 @@ package com.health.openscale.gui; import android.Manifest; import android.app.AlertDialog; +import android.app.Dialog; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothManager; import android.content.ActivityNotFoundException; @@ -28,12 +29,14 @@ import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.res.Configuration; import android.graphics.Typeface; +import android.location.LocationManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.preference.PreferenceManager; +import android.provider.Settings; import android.text.Editable; import android.text.Html; import android.text.InputFilter; @@ -51,10 +54,13 @@ import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.widget.Toolbar; +import androidx.core.content.ContextCompat; import androidx.core.content.FileProvider; import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; @@ -77,7 +83,6 @@ import com.health.openscale.gui.measurement.MeasurementEntryFragment; import com.health.openscale.gui.preferences.BluetoothSettingsFragment; import com.health.openscale.gui.preferences.UserSettingsFragment; import com.health.openscale.gui.slides.AppIntroActivity; -import com.health.openscale.gui.utils.PermissionHelper; import java.io.File; import java.util.ArrayList; @@ -100,7 +105,6 @@ public class MainActivity extends AppCompatActivity private static final int IMPORT_DATA_REQUEST = 100; private static final int EXPORT_DATA_REQUEST = 101; - private static final int ENABLE_BLUETOOTH_REQUEST = 102; private static final int APPINTRO_REQUEST = 103; private AppBarConfiguration mAppBarConfiguration; @@ -532,7 +536,6 @@ public class MainActivity extends AppCompatActivity boolean hasBluetooth = bluetoothManager.getAdapter() != null; if (!hasBluetooth) { - bluetoothStatus.setEnabled(false); setBluetoothStatusIcon(R.drawable.ic_bluetooth_disabled); } // Just search for a bluetooth device just once at the start of the app and if start preference enabled @@ -571,6 +574,54 @@ public class MainActivity extends AppCompatActivity return; } + Timber.d("Main Activity Bluetooth permission check"); + + int targetSdkVersion = getApplicationInfo().targetSdkVersion; + + final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); + BluetoothAdapter btAdapter = bluetoothManager.getAdapter(); + + // Check if Bluetooth is enabled + if (btAdapter == null || !btAdapter.isEnabled()) { + Timber.d("Bluetooth is not enabled"); + Toast.makeText(this, "Bluetooth " + getResources().getString(R.string.info_is_not_enable), Toast.LENGTH_SHORT).show(); + setBluetoothStatusIcon(R.drawable.ic_bluetooth_disabled); + return; + } + + // Check if Bluetooth 4.x is available + if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { + Timber.d("No Bluetooth 4.x available"); + Toast.makeText(this, "Bluetooth 4.x " + getResources().getString(R.string.info_is_not_available), Toast.LENGTH_SHORT).show(); + setBluetoothStatusIcon(R.drawable.ic_bluetooth_disabled); + return; + } + + // Check if GPS or Network location service is enabled + LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); + if (!(locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER))) { + Timber.d("No GPS or Network location service is enabled, ask user for permission"); + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.permission_bluetooth_info_title); + builder.setIcon(R.drawable.ic_preferences_about); + builder.setMessage(R.string.permission_location_service_info); + builder.setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialogInterface, int i) { + // Show location settings when the user acknowledges the alert dialog + Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); + startActivity(intent); + } + }); + + Dialog alertDialog = builder.create(); + alertDialog.setCanceledOnTouchOutside(false); + alertDialog.show(); + + setBluetoothStatusIcon(R.drawable.ic_bluetooth_disabled); + return; + } + String deviceName = prefs.getString( BluetoothSettingsFragment.PREFERENCE_KEY_BLUETOOTH_DEVICE_NAME, ""); String hwAddress = prefs.getString( @@ -582,21 +633,58 @@ public class MainActivity extends AppCompatActivity return; } - BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE); - if (!bluetoothManager.getAdapter().isEnabled()) { - setBluetoothStatusIcon(R.drawable.ic_bluetooth_connection_lost); - Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); - startActivityForResult(enableBtIntent, ENABLE_BLUETOOTH_REQUEST); - return; - } + String[] requiredPermissions; - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.S) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && targetSdkVersion >= Build.VERSION_CODES.S) { Timber.d("SDK >= 31 request for Bluetooth Scan and Bluetooth connect permissions"); - requestPermissions(new String[]{Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT}, PermissionHelper.PERMISSIONS_REQUEST_ACCESS_BLUETOOTH); - return; + requiredPermissions = new String[]{Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT}; + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && targetSdkVersion >= Build.VERSION_CODES.Q) { + Timber.d("SDK >= 29 request for Access fine location permission"); + requiredPermissions = new String[]{Manifest.permission.ACCESS_FINE_LOCATION}; + } else { + Timber.d("SDK < 29 request for coarse location permission"); + requiredPermissions = new String[]{Manifest.permission.ACCESS_FINE_LOCATION}; } - connectToBluetooth(); + if (hasPermissions(requiredPermissions)) { + connectToBluetooth(); + } else if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + Timber.d("No access fine location permission granted"); + + builder.setMessage(R.string.permission_bluetooth_info) + .setTitle(R.string.permission_bluetooth_info_title) + .setIcon(R.drawable.ic_preferences_about) + .setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + requestPermissionBluetoothLauncher.launch(requiredPermissions); + } + }); + + Dialog alertDialog = builder.create(); + alertDialog.setCanceledOnTouchOutside(false); + alertDialog.show(); + } else if (shouldShowRequestPermissionRationale(Manifest.permission.BLUETOOTH_SCAN)) { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + Timber.d("No access Bluetooth scan permission granted"); + + builder.setMessage(R.string.permission_bluetooth_info) + .setTitle(R.string.permission_bluetooth_info_title) + .setIcon(R.drawable.ic_preferences_about) + .setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + requestPermissionBluetoothLauncher.launch(requiredPermissions); + } + }); + + Dialog alertDialog = builder.create(); + alertDialog.setCanceledOnTouchOutside(false); + alertDialog.show(); + } else { + requestPermissionBluetoothLauncher.launch(requiredPermissions); + } } private void connectToBluetooth() { @@ -900,48 +988,12 @@ public class MainActivity extends AppCompatActivity startActivity(Intent.createChooser(intent, getResources().getString(R.string.label_share))); } - @Override - public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - - switch (requestCode) { - case PermissionHelper.PERMISSIONS_REQUEST_ACCESS_BLUETOOTH: { - boolean allGranted = true; - for (int result : grantResults) { - if (result != PackageManager.PERMISSION_GRANTED) { - allGranted = false; - break; - } - } - - if (allGranted) { - Timber.d("All Bluetooth permissions granted"); - connectToBluetooth(); - } else { - Timber.d("At least one Bluetooth permission was not granted"); - Toast.makeText(this, R.string.permission_not_granted, Toast.LENGTH_SHORT).show(); - } - break; - } - } - } - @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); OpenScale openScale = OpenScale.getInstance(); - if (requestCode == ENABLE_BLUETOOTH_REQUEST) { - if (resultCode == RESULT_OK) { - invokeConnectToBluetoothDevice(); - } - else { - Toast.makeText(this, "Bluetooth " + getResources().getString(R.string.info_is_not_enable), Toast.LENGTH_SHORT).show(); - } - return; - } - if (requestCode == APPINTRO_REQUEST) { if (openScale.getSelectedScaleUserId() == -1) { MobileNavigationDirections.ActionNavMobileNavigationToNavUsersettings action = MobileNavigationDirections.actionNavMobileNavigationToNavUsersettings(); @@ -992,4 +1044,29 @@ public class MainActivity extends AppCompatActivity break; } } + + private boolean hasPermissions(String[] permissions) { + if (permissions != null) { + for (String permission : permissions) { + if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) { + Timber.d("Permission is not granted: " + permission); + return false; + } + Timber.d("Permission already granted: " + permission); + } + return true; + } + return false; + } + + private ActivityResultLauncher requestPermissionBluetoothLauncher = + registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), isGranted -> { + if (isGranted.containsValue(false)) { + Timber.d("At least one Bluetooth permission was not granted"); + Toast.makeText(this, getString(R.string.label_bluetooth_title) + ": " + getString(R.string.permission_not_granted), Toast.LENGTH_SHORT).show(); + } + else { + connectToBluetooth(); + } + }); } diff --git a/android_app/app/src/main/java/com/health/openscale/gui/preferences/BackupPreferences.java b/android_app/app/src/main/java/com/health/openscale/gui/preferences/BackupPreferences.java index 0f111086..e973c7e0 100644 --- a/android_app/app/src/main/java/com/health/openscale/gui/preferences/BackupPreferences.java +++ b/android_app/app/src/main/java/com/health/openscale/gui/preferences/BackupPreferences.java @@ -17,6 +17,7 @@ package com.health.openscale.gui.preferences; import static android.app.Activity.RESULT_OK; +import android.Manifest; import android.content.ComponentName; import android.content.Intent; import android.content.SharedPreferences; @@ -28,6 +29,9 @@ import android.view.Menu; import android.view.MenuInflater; import android.widget.Toast; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.preference.CheckBoxPreference; import androidx.preference.Preference; @@ -37,7 +41,6 @@ import com.health.openscale.R; import com.health.openscale.core.OpenScale; import com.health.openscale.core.alarm.AlarmBackupHandler; import com.health.openscale.core.alarm.ReminderBootReceiver; -import com.health.openscale.gui.utils.PermissionHelper; import java.io.IOException; @@ -138,7 +141,12 @@ public class BackupPreferences extends PreferenceFragmentCompat implements Share if (autoBackup.isChecked()) { isAutoBackupAskForPermission = true; - PermissionHelper.requestWritePermission(fragment); + if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) + == PackageManager.PERMISSION_GRANTED) { + exportBackup(); + } else { + requestPermissionExportLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE); + } } } return true; @@ -148,10 +156,16 @@ public class BackupPreferences extends PreferenceFragmentCompat implements Share private class onClickListenerImportBackup implements Preference.OnPreferenceClickListener { @Override public boolean onPreferenceClick(Preference preference) { - if (PermissionHelper.requestReadPermission(fragment)) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { importBackup(); + } else { + if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.READ_EXTERNAL_STORAGE) + == PackageManager.PERMISSION_GRANTED) { + importBackup(); + } else { + requestPermissionImportLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE); + } } - return true; } } @@ -161,8 +175,13 @@ public class BackupPreferences extends PreferenceFragmentCompat implements Share public boolean onPreferenceClick(Preference preference) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { exportBackup(); - } else if (PermissionHelper.requestWritePermission(fragment)) { - exportBackup(); + } else { + if (ContextCompat.checkSelfPermission(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) + == PackageManager.PERMISSION_GRANTED) { + exportBackup(); + } else { + requestPermissionExportLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE); + } } return true; @@ -232,36 +251,35 @@ public class BackupPreferences extends PreferenceFragmentCompat implements Share } @Override - public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { - switch (requestCode) { - case PermissionHelper.PERMISSIONS_REQUEST_ACCESS_READ_STORAGE: - if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + menu.clear(); + } + + private ActivityResultLauncher requestPermissionImportLauncher = + registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> { + if (isGranted) { importBackup(); - } else { + } + else { Toast.makeText(getContext(), getResources().getString(R.string.permission_not_granted), Toast.LENGTH_SHORT).show(); } - break; - case PermissionHelper.PERMISSIONS_REQUEST_ACCESS_WRITE_STORAGE: - if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + }); + + private ActivityResultLauncher requestPermissionExportLauncher = + registerForActivityResult(new ActivityResultContracts.RequestPermission(), isGranted -> { + if (isGranted) { if (isAutoBackupAskForPermission) { autoBackup.setChecked(true); } else { exportBackup(); } - - } else { + } + else { if (isAutoBackupAskForPermission) { autoBackup.setChecked(false); } Toast.makeText(getContext(), getResources().getString(R.string.permission_not_granted), Toast.LENGTH_SHORT).show(); } - break; - } - } - - @Override - public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - menu.clear(); - } + }); } diff --git a/android_app/app/src/main/java/com/health/openscale/gui/preferences/BluetoothSettingsFragment.java b/android_app/app/src/main/java/com/health/openscale/gui/preferences/BluetoothSettingsFragment.java index 2af1ec6c..b68c3e7f 100644 --- a/android_app/app/src/main/java/com/health/openscale/gui/preferences/BluetoothSettingsFragment.java +++ b/android_app/app/src/main/java/com/health/openscale/gui/preferences/BluetoothSettingsFragment.java @@ -15,9 +15,14 @@ */ package com.health.openscale.gui.preferences; -import android.app.Activity; +import static android.content.Context.LOCATION_SERVICE; + +import android.Manifest; import android.app.AlertDialog; +import android.app.Dialog; +import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothManager; import android.bluetooth.le.ScanResult; import android.content.Context; import android.content.DialogInterface; @@ -26,12 +31,15 @@ import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.graphics.Color; import android.graphics.PorterDuff; +import android.location.LocationManager; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.preference.PreferenceManager; +import android.provider.Settings; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.style.ForegroundColorSpan; @@ -48,6 +56,9 @@ import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; +import androidx.activity.result.ActivityResultLauncher; +import androidx.activity.result.contract.ActivityResultContracts; +import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.navigation.Navigation; @@ -56,7 +67,6 @@ import com.health.openscale.core.OpenScale; import com.health.openscale.core.bluetooth.BluetoothCommunication; import com.health.openscale.core.bluetooth.BluetoothFactory; import com.health.openscale.gui.utils.ColorUtil; -import com.health.openscale.gui.utils.PermissionHelper; import com.welie.blessed.BluetoothCentralManager; import com.welie.blessed.BluetoothCentralManagerCallback; import com.welie.blessed.BluetoothPeripheral; @@ -99,11 +109,110 @@ public class BluetoothSettingsFragment extends Fragment { @Override public void onResume() { - if (PermissionHelper.requestBluetoothPermission(this)) { - startBluetoothDiscovery(); + super.onResume(); + + Timber.d("Bluetooth settings Bluetooth permission check"); + + int targetSdkVersion = getActivity().getApplicationInfo().targetSdkVersion; + + final BluetoothManager bluetoothManager = (BluetoothManager) getActivity().getSystemService(Context.BLUETOOTH_SERVICE); + BluetoothAdapter btAdapter = bluetoothManager.getAdapter(); + + // Check if Bluetooth is enabled + if (btAdapter == null || !btAdapter.isEnabled()) { + Timber.d("Bluetooth is not enabled"); + Toast.makeText(getContext(), "Bluetooth " + getContext().getResources().getString(R.string.info_is_not_enable), Toast.LENGTH_SHORT).show(); + stepNavigationBack(); + return; } - super.onResume(); + // Check if Bluetooth 4.x is available + if (!getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { + Timber.d("No Bluetooth 4.x available"); + Toast.makeText(getContext(), "Bluetooth 4.x " + getContext().getResources().getString(R.string.info_is_not_available), Toast.LENGTH_SHORT).show(); + stepNavigationBack(); + return; + } + + // Check if GPS or Network location service is enabled + LocationManager locationManager = (LocationManager) getActivity().getSystemService(LOCATION_SERVICE); + if (!(locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER))) { + Timber.d("No GPS or Network location service is enabled, ask user for permission"); + + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setTitle(R.string.permission_bluetooth_info_title); + builder.setIcon(R.drawable.ic_preferences_about); + builder.setMessage(R.string.permission_location_service_info); + builder.setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialogInterface, int i) { + // Show location settings when the user acknowledges the alert dialog + Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); + getActivity().startActivity(intent); + } + }); + + Dialog alertDialog = builder.create(); + alertDialog.setCanceledOnTouchOutside(false); + alertDialog.show(); + + stepNavigationBack(); + return; + } + + String[] requiredPermissions; + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && targetSdkVersion >= Build.VERSION_CODES.S) { + Timber.d("SDK >= 31 request for Bluetooth Scan and Bluetooth connect permissions"); + requiredPermissions = new String[]{Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT}; + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && targetSdkVersion >= Build.VERSION_CODES.Q) { + Timber.d("SDK >= 29 request for Access fine location permission"); + requiredPermissions = new String[]{Manifest.permission.ACCESS_FINE_LOCATION}; + } else { + Timber.d("SDK < 29 request for coarse location permission"); + requiredPermissions = new String[]{Manifest.permission.ACCESS_FINE_LOCATION}; + } + + if (hasPermissions(requiredPermissions)) { + startBluetoothDiscovery(); + } else if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + Timber.d("No access fine location permission granted"); + + builder.setMessage(R.string.permission_bluetooth_info) + .setTitle(R.string.permission_bluetooth_info_title) + .setIcon(R.drawable.ic_preferences_about) + .setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + requestPermissionBluetoothLauncher.launch(requiredPermissions); + } + }); + + Dialog alertDialog = builder.create(); + alertDialog.setCanceledOnTouchOutside(false); + alertDialog.show(); + + } else if (shouldShowRequestPermissionRationale(Manifest.permission.BLUETOOTH_SCAN)) { + AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); + Timber.d("No access Bluetooth scan permission granted"); + + builder.setMessage(R.string.permission_bluetooth_info) + .setTitle(R.string.permission_bluetooth_info_title) + .setIcon(R.drawable.ic_preferences_about) + .setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + dialog.dismiss(); + requestPermissionBluetoothLauncher.launch(requiredPermissions); + } + }); + + Dialog alertDialog = builder.create(); + alertDialog.setCanceledOnTouchOutside(false); + alertDialog.show(); + + } else { + requestPermissionBluetoothLauncher.launch(requiredPermissions); + } } @Override @@ -375,47 +484,38 @@ public class BluetoothSettingsFragment extends Fragment { } } - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - if (requestCode == PermissionHelper.ENABLE_BLUETOOTH_REQUEST) { - if (resultCode == Activity.RESULT_OK) { - if (PermissionHelper.requestBluetoothPermission(this)) { - startBluetoothDiscovery(); - } - } + private void stepNavigationBack() { + if (getActivity().findViewById(R.id.nav_host_fragment) != null) { + Navigation.findNavController(requireActivity(), R.id.nav_host_fragment).getPreviousBackStackEntry().getSavedStateHandle().set("update", true); + Navigation.findNavController(requireActivity(), R.id.nav_host_fragment).navigateUp(); + } else { + getActivity().finish(); } - - super.onActivityResult(requestCode, resultCode, data); } - @Override - public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { - switch (requestCode) { - case PermissionHelper.PERMISSIONS_REQUEST_ACCESS_BLUETOOTH: { - boolean allGranted = true; - for (int result : grantResults) { - if (result != PackageManager.PERMISSION_GRANTED) { - allGranted = false; - break; - } + private boolean hasPermissions(String[] permissions) { + if (permissions != null) { + for (String permission : permissions) { + if (ContextCompat.checkSelfPermission(getContext(), permission) != PackageManager.PERMISSION_GRANTED) { + Timber.d("Permission is not granted: " + permission); + return false; } + Timber.d("Permission already granted: " + permission); + } + return true; + } + return false; + } - if (allGranted) { - Timber.d("All Bluetooth permissions granted"); - startBluetoothDiscovery(); - } else { + private ActivityResultLauncher requestPermissionBluetoothLauncher = + registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions(), isGranted -> { + if (isGranted.containsValue(false)) { Timber.d("At least one Bluetooth permission was not granted"); - Toast.makeText(requireContext(), R.string.permission_not_granted, Toast.LENGTH_SHORT).show(); - - if (getActivity().findViewById(R.id.nav_host_fragment) != null){ - Navigation.findNavController(requireActivity(), R.id.nav_host_fragment).getPreviousBackStackEntry().getSavedStateHandle().set("update", true); - Navigation.findNavController(requireActivity(), R.id.nav_host_fragment).navigateUp(); - } else { - getActivity().finish(); - } + Toast.makeText(requireContext(), getString(R.string.label_bluetooth_title) + ": " + getString(R.string.permission_not_granted), Toast.LENGTH_SHORT).show(); + stepNavigationBack(); } - break; - } - } - } + else { + startBluetoothDiscovery(); + } + }); } diff --git a/android_app/app/src/main/java/com/health/openscale/gui/utils/PermissionHelper.java b/android_app/app/src/main/java/com/health/openscale/gui/utils/PermissionHelper.java deleted file mode 100644 index 8f73c617..00000000 --- a/android_app/app/src/main/java/com/health/openscale/gui/utils/PermissionHelper.java +++ /dev/null @@ -1,156 +0,0 @@ -/* Copyright (C) 2018 olie.xdev -* -* This program is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 3 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program. If not, see -*/ -package com.health.openscale.gui.utils; - -import static android.content.Context.LOCATION_SERVICE; - -import android.Manifest; -import android.app.AlertDialog; -import android.app.Dialog; -import android.bluetooth.BluetoothAdapter; -import android.bluetooth.BluetoothManager; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.location.LocationManager; -import android.os.Build; -import android.provider.Settings; -import android.widget.Toast; - -import androidx.fragment.app.Fragment; - -import com.health.openscale.R; - -import timber.log.Timber; - -public class PermissionHelper { - public final static int PERMISSIONS_REQUEST_ACCESS_BLUETOOTH = 1; - public final static int PERMISSIONS_REQUEST_ACCESS_READ_STORAGE = 2; - public final static int PERMISSIONS_REQUEST_ACCESS_WRITE_STORAGE = 3; - - public final static int ENABLE_BLUETOOTH_REQUEST = 5; - - public static boolean requestBluetoothPermission(final Fragment fragment) { - final BluetoothManager bluetoothManager = (BluetoothManager) fragment.getActivity().getSystemService(Context.BLUETOOTH_SERVICE); - BluetoothAdapter btAdapter = bluetoothManager.getAdapter(); - - if (btAdapter == null || !btAdapter.isEnabled()) { - Toast.makeText(fragment.getContext(), "Bluetooth " + fragment.getContext().getResources().getString(R.string.info_is_not_enable), Toast.LENGTH_SHORT).show(); - - if (btAdapter != null) { - Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); - fragment.getActivity().startActivityForResult(enableBtIntent, ENABLE_BLUETOOTH_REQUEST); - } - return false; - } - - // Check if Bluetooth 4.x is available - if (!fragment.getActivity().getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { - Toast.makeText(fragment.getContext(), "Bluetooth 4.x " + fragment.getContext().getResources().getString(R.string.info_is_not_available), Toast.LENGTH_SHORT).show(); - return false; - } - - int targetSdkVersion = fragment.getActivity().getApplicationInfo().targetSdkVersion; - - String[] requiredPermissions; - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && targetSdkVersion >= Build.VERSION_CODES.S) { - Timber.d("SDK >= 31 request for Bluetooth Scan and Bluetooth connect permissions"); - requiredPermissions = new String[]{Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT}; - fragment.requestPermissions(requiredPermissions, PERMISSIONS_REQUEST_ACCESS_BLUETOOTH); - return false; - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && targetSdkVersion >= Build.VERSION_CODES.Q) { - Timber.d("SDK >= 29 request for Access fine location permission"); - return requestLocationPermission(fragment, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}); - } else { - Timber.d("SDK < 29 request for coarse location permission"); - return requestLocationPermission(fragment, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}); - } - } - - private static boolean requestLocationPermission(final Fragment fragment, String[] requiredPermissions) { - if (requestLocationServicePermission(fragment)) { - if (fragment.getContext().checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { - AlertDialog.Builder builder = new AlertDialog.Builder(fragment.getActivity()); - Timber.d("No access fine location permission granted"); - - builder.setMessage(R.string.permission_bluetooth_info) - .setTitle(R.string.permission_bluetooth_info_title) - .setIcon(R.drawable.ic_preferences_about) - .setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int id) { - dialog.dismiss(); - fragment.requestPermissions(requiredPermissions, PERMISSIONS_REQUEST_ACCESS_BLUETOOTH); - } - }); - - Dialog alertDialog = builder.create(); - alertDialog.setCanceledOnTouchOutside(false); - alertDialog.show(); - return false; - } - } - - return true; - } - - private static boolean requestLocationServicePermission(final Fragment fragment) { - LocationManager locationManager = (LocationManager) fragment.getActivity().getSystemService(LOCATION_SERVICE); - if (!(locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER))) { - Timber.d("No GPS or Network location service is enabled, ask user for permission"); - - AlertDialog.Builder builder = new AlertDialog.Builder(fragment.getContext()); - builder.setTitle(R.string.permission_bluetooth_info_title); - builder.setIcon(R.drawable.ic_preferences_about); - builder.setMessage(R.string.permission_location_service_info); - builder.setPositiveButton(R.string.label_ok, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialogInterface, int i) { - // Show location settings when the user acknowledges the alert dialog - Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); - fragment.getActivity().startActivity(intent); - } - }); - - Dialog alertDialog = builder.create(); - alertDialog.setCanceledOnTouchOutside(false); - alertDialog.show(); - return false; - } - - return true; - } - - public static boolean requestReadPermission(final Fragment fragment) { - if (fragment.getContext().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - fragment.requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST_ACCESS_READ_STORAGE); - } else { - return true; - } - - return false; - } - - public static boolean requestWritePermission(final Fragment fragment) { - if (fragment.getContext().checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - fragment.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSIONS_REQUEST_ACCESS_WRITE_STORAGE); - } else { - return true; - } - - return false; - } -}