diff --git a/Corona-Warn-App/src/main/AndroidManifest.xml b/Corona-Warn-App/src/main/AndroidManifest.xml index c0208b74d0c28e425600d0f5634c0729e41994f8..e1533a7fb164fc5994d7fc97748d07ea5f0f0824 100644 --- a/Corona-Warn-App/src/main/AndroidManifest.xml +++ b/Corona-Warn-App/src/main/AndroidManifest.xml @@ -11,6 +11,7 @@ android:required="true" /> <uses-feature android:name="android.hardware.bluetooth" /> + <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.BLUETOOTH" android:required="true" /> diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/SettingsRepository.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/SettingsRepository.kt index 5cf13c0629f878b0f3f2c31d4b485d3597640799..d556fb829201288fcb745ae453f6bc8cb84d8e4d 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/SettingsRepository.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/storage/SettingsRepository.kt @@ -25,6 +25,7 @@ object SettingsRepository { val isManualKeyRetrievalEnabled = MutableLiveData(true) val isConnectionEnabled = MutableLiveData(true) val isBluetoothEnabled = MutableLiveData(true) + val isLocationEnabled = MutableLiveData(true) val isBackgroundJobEnabled = MutableLiveData(true) val isBackgroundPriorityEnabled = MutableLiveData(false) val manualKeyRetrievalTime = MutableLiveData<Long>() @@ -95,6 +96,15 @@ object SettingsRepository { isBluetoothEnabled.postValue(value) } + /** + * Refresh global location state to point out that tracing isn't working + * + * @see ConnectivityHelper + */ + fun updateLocationEnabled(value: Boolean) { + isLocationEnabled.postValue(value) + } + /** * Refresh global bluetooth state to point out that tracing isn't working * diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt index 0abb83ed3ddaa5fd31884b49de05054d4201d1cd..afb0a44be48cac15f526bdfff3843400c2252eb4 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/main/MainActivity.kt @@ -60,6 +60,19 @@ class MainActivity : AppCompatActivity() { } } + /** + * Register location callback. + */ + private val callbackLocation = object : ConnectivityHelper.LocationCallback() { + override fun onLocationAvailable() { + settingsViewModel.updateLocationEnabled(true) + } + + override fun onLocationUnavailable() { + settingsViewModel.updateLocationEnabled(false) + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) @@ -73,6 +86,7 @@ class MainActivity : AppCompatActivity() { super.onResume() ConnectivityHelper.registerNetworkStatusCallback(this, callbackNetwork) ConnectivityHelper.registerBluetoothStatusCallback(this, callbackBluetooth) + ConnectivityHelper.registerLocationStatusCallback(this, callbackLocation) settingsViewModel.updateBackgroundJobEnabled(ConnectivityHelper.isBackgroundJobEnabled(this)) scheduleWork() } @@ -84,6 +98,7 @@ class MainActivity : AppCompatActivity() { super.onPause() ConnectivityHelper.unregisterNetworkStatusCallback(this, callbackNetwork) ConnectivityHelper.unregisterBluetoothStatusCallback(this, callbackBluetooth) + ConnectivityHelper.unregisterLocationStatusCallback(this, callbackLocation) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/viewmodel/SettingsViewModel.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/viewmodel/SettingsViewModel.kt index c3e32a21d2382ab22e58c905758041298d1f4d49..486954f1ac74cc6912e24ec6619ba8cf616092c9 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/viewmodel/SettingsViewModel.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/ui/viewmodel/SettingsViewModel.kt @@ -21,6 +21,8 @@ class SettingsViewModel : ViewModel() { SettingsRepository.isConnectionEnabled val isBluetoothEnabled: LiveData<Boolean> = SettingsRepository.isBluetoothEnabled + val isLocationEnabled: LiveData<Boolean> = + SettingsRepository.isLocationEnabled // Will impact UI if background activity is not permitted, persistent storing is not necessary val isBackgroundJobEnabled: LiveData<Boolean> = SettingsRepository.isBackgroundJobEnabled @@ -100,6 +102,15 @@ class SettingsViewModel : ViewModel() { SettingsRepository.updateBluetoothEnabled(value) } + /** + * Update location enabled + * + * @param value + */ + fun updateLocationEnabled(value: Boolean) { + SettingsRepository.updateLocationEnabled(value) + } + /** * Update background job enabled * diff --git a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ConnectivityHelper.kt b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ConnectivityHelper.kt index d14c4aa8471aca304bffa9c99b25a3b3d5cd7508..907cfc76a28ff8a8bac44b3dd7187189f8916e40 100644 --- a/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ConnectivityHelper.kt +++ b/Corona-Warn-App/src/main/java/de/rki/coronawarnapp/util/ConnectivityHelper.kt @@ -6,11 +6,13 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.location.LocationManager import android.net.ConnectivityManager import android.net.Network import android.net.NetworkCapabilities import android.net.NetworkRequest import android.os.Build +import androidx.core.location.LocationManagerCompat import de.rki.coronawarnapp.exception.ExceptionCategory import de.rki.coronawarnapp.exception.reporting.report import timber.log.Timber @@ -71,6 +73,63 @@ object ConnectivityHelper { callback.recevier = null } + /** + * Register location state change listener. + * + * @param context the context + * @param callback the location state callback + * + */ + fun registerLocationStatusCallback(context: Context, callback: LocationCallback) { + val receiver = object : BroadcastReceiver() { + var isGpsEnabled: Boolean = false + var isNetworkEnabled: Boolean = false + + override fun onReceive(context: Context, intent: Intent) { + intent.action?.let { act -> + if (act.matches("android.location.PROVIDERS_CHANGED".toRegex())) { + val locationManager = + context.getSystemService(Context.LOCATION_SERVICE) as LocationManager + isGpsEnabled = + locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) + isNetworkEnabled = + locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) + + if (isGpsEnabled || isNetworkEnabled) { + callback.onLocationAvailable() + Timber.d("Location enabled") + } else { + callback.onLocationUnavailable() + Timber.d("Location disabled") + } + } + } + } + } + callback.recevier = receiver + context.registerReceiver( + callback.recevier, + IntentFilter("android.location.PROVIDERS_CHANGED") + ) + // location state doesn't change when you register + if (isLocationEnabled(context)) + callback.onLocationAvailable() + else + callback.onLocationUnavailable() + } + + /** + * Unregister location state change listener. + * + * @param context the context + * @param callback the location state callback + * + */ + fun unregisterLocationStatusCallback(context: Context, callback: LocationCallback) { + context.unregisterReceiver(callback.recevier) + callback.recevier = null + } + /** * Unregister network state change callback. * @@ -158,6 +217,17 @@ object ConnectivityHelper { return bAdapter.isEnabled } + /** + * Get location enabled status. + * + * @return current location status + * + */ + fun isLocationEnabled(context: Context): Boolean { + val locationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManager + return LocationManagerCompat.isLocationEnabled(locationManager) + } + /** * Get network enabled status. * @@ -190,6 +260,24 @@ object ConnectivityHelper { abstract fun onBluetoothUnavailable() } + /** + * Abstract location state change callback. + * + * @see BroadcastReceiver + */ + abstract class LocationCallback { + var recevier: BroadcastReceiver? = null + + /** + * Called when location is turned on. + */ + abstract fun onLocationAvailable() + + /** + * Called when location is turned off. + */ + abstract fun onLocationUnavailable() + } /** * Abstract network state change callback. *