mirror of
https://github.com/YTVanced/VancedMicroG
synced 2024-12-03 16:27:26 +00:00
EN: Request users to enable Bluetoooth/Location access
This commit is contained in:
parent
3dad397dd1
commit
a814c7de7e
21 changed files with 489 additions and 72 deletions
|
@ -54,6 +54,7 @@ public class ForegroundServiceContext extends ContextWrapper {
|
|||
context.getSystemService(NotificationManager.class).createNotificationChannel(channel);
|
||||
return new Notification.Builder(context, channel.getId())
|
||||
.setOngoing(true)
|
||||
.setSmallIcon(android.R.drawable.stat_notify_error)
|
||||
.setContentTitle("Running in background")
|
||||
.setContentText("microG " + context.getClass().getSimpleName() + " is running in background.")
|
||||
.build();
|
||||
|
|
|
@ -9,6 +9,9 @@
|
|||
|
||||
<uses-sdk tools:overrideLibrary="com.db.williamchart" />
|
||||
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
|
||||
|
||||
<application>
|
||||
<activity
|
||||
android:name="org.microg.gms.nearby.core.ui.ExposureNotificationsConfirmActivity"
|
||||
|
@ -26,6 +29,7 @@
|
|||
android:theme="@style/Theme.AppCompat.DayNight">
|
||||
<intent-filter android:priority="-100">
|
||||
<action android:name="com.google.android.gms.settings.EXPOSURE_NOTIFICATION_SETTINGS" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
|
|
@ -92,9 +92,11 @@ class ExposureNotificationsAppPreferencesFragment : PreferenceFragmentCompat() {
|
|||
}
|
||||
|
||||
reportedExposures.removeAll()
|
||||
reportedExposures.addPreference(reportedExposuresNone)
|
||||
if (mergedExposures.isNullOrEmpty()) {
|
||||
reportedExposures.addPreference(reportedExposuresNone)
|
||||
reportedExposuresNone.isVisible = true
|
||||
} else {
|
||||
reportedExposuresNone.isVisible = false
|
||||
for (exposure in mergedExposures) {
|
||||
val minAttenuation = exposure.subs.map { it.attenuation }.minOrNull() ?: exposure.attenuation
|
||||
val nearby = exposure.attenuation < 63 || minAttenuation < 55
|
||||
|
|
|
@ -5,18 +5,26 @@
|
|||
|
||||
package org.microg.gms.nearby.core.ui
|
||||
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.location.LocationManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.ResultReceiver
|
||||
import android.provider.Settings
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.location.LocationManagerCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import org.microg.gms.nearby.exposurenotification.*
|
||||
import org.microg.gms.ui.getApplicationInfoIfExists
|
||||
|
||||
|
||||
class ExposureNotificationsConfirmActivity : AppCompatActivity() {
|
||||
private var resultCode: Int = RESULT_CANCELED
|
||||
set(value) {
|
||||
|
@ -44,6 +52,8 @@ class ExposureNotificationsConfirmActivity : AppCompatActivity() {
|
|||
findViewById<TextView>(R.id.grant_permission_summary).text = getString(R.string.exposure_confirm_permission_description, selfApplicationInfo?.loadLabel(packageManager)
|
||||
?: packageName)
|
||||
checkPermissions()
|
||||
checkBluetooth()
|
||||
checkLocation()
|
||||
}
|
||||
CONFIRM_ACTION_STOP -> {
|
||||
findViewById<TextView>(android.R.id.title).text = getString(R.string.exposure_confirm_stop_title)
|
||||
|
@ -72,8 +82,28 @@ class ExposureNotificationsConfirmActivity : AppCompatActivity() {
|
|||
findViewById<Button>(R.id.grant_permission_button).setOnClickListener {
|
||||
requestPermissions()
|
||||
}
|
||||
findViewById<Button>(R.id.enable_bluetooth_button).setOnClickListener {
|
||||
requestBluetooth()
|
||||
}
|
||||
findViewById<Button>(R.id.enable_location_button).setOnClickListener {
|
||||
requestLocation()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (permissionNeedsHandling) checkPermissions()
|
||||
if (bluetoothNeedsHandling) checkBluetooth()
|
||||
if (locationNeedsHandling) checkLocation()
|
||||
}
|
||||
|
||||
private fun updateButton() {
|
||||
findViewById<Button>(android.R.id.button1).isEnabled = !permissionNeedsHandling && !bluetoothNeedsHandling && !locationNeedsHandling
|
||||
}
|
||||
|
||||
// Permissions
|
||||
private var permissionNeedsHandling: Boolean = false
|
||||
private var permissionRequestCode = 33
|
||||
private val permissions by lazy {
|
||||
if (Build.VERSION.SDK_INT >= 29) {
|
||||
arrayOf("android.permission.ACCESS_BACKGROUND_LOCATION", "android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION")
|
||||
|
@ -81,22 +111,70 @@ class ExposureNotificationsConfirmActivity : AppCompatActivity() {
|
|||
arrayOf("android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_FINE_LOCATION")
|
||||
}
|
||||
}
|
||||
private var requestCode = 33
|
||||
private fun checkPermissions() {
|
||||
val needRequest = Build.VERSION.SDK_INT >= 23 && permissions.any { ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED }
|
||||
findViewById<Button>(android.R.id.button1).isEnabled = !needRequest
|
||||
findViewById<View>(R.id.grant_permission_view).visibility = if (needRequest) View.VISIBLE else View.GONE
|
||||
permissionNeedsHandling = Build.VERSION.SDK_INT >= 23 && permissions.any { ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED }
|
||||
findViewById<View>(R.id.grant_permission_view).visibility = if (permissionNeedsHandling) View.VISIBLE else View.GONE
|
||||
updateButton()
|
||||
}
|
||||
|
||||
private fun requestPermissions() {
|
||||
if (Build.VERSION.SDK_INT >= 23) {
|
||||
requestPermissions(permissions, ++requestCode)
|
||||
requestPermissions(permissions, ++permissionRequestCode)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
if (requestCode == this.requestCode) checkPermissions()
|
||||
if (requestCode == this.permissionRequestCode) checkPermissions()
|
||||
}
|
||||
|
||||
// Bluetooth
|
||||
private var bluetoothNeedsHandling: Boolean = false
|
||||
private var bluetoothRequestCode = 112
|
||||
private fun checkBluetooth() {
|
||||
val adapter = BluetoothAdapter.getDefaultAdapter()
|
||||
bluetoothNeedsHandling = adapter?.isEnabled != true
|
||||
findViewById<View>(R.id.enable_bluetooth_view).visibility = if (adapter?.isEnabled == false) View.VISIBLE else View.GONE
|
||||
updateButton()
|
||||
}
|
||||
|
||||
private fun requestBluetooth() {
|
||||
val adapter = BluetoothAdapter.getDefaultAdapter()
|
||||
findViewById<View>(R.id.enable_bluetooth_spinner).visibility = View.VISIBLE
|
||||
findViewById<View>(R.id.enable_bluetooth_button).visibility = View.INVISIBLE
|
||||
lifecycleScope.launchWhenStarted {
|
||||
if (adapter != null && !adapter.enableAsync(this@ExposureNotificationsConfirmActivity)) {
|
||||
requestBluetoothViaIntent()
|
||||
} else {
|
||||
checkBluetooth()
|
||||
}
|
||||
findViewById<View>(R.id.enable_bluetooth_spinner).visibility = View.INVISIBLE
|
||||
findViewById<View>(R.id.enable_bluetooth_button).visibility = View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestBluetoothViaIntent() {
|
||||
val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
|
||||
startActivityForResult(intent, ++bluetoothRequestCode)
|
||||
}
|
||||
|
||||
// Location
|
||||
private var locationNeedsHandling: Boolean = false
|
||||
private var locationRequestCode = 231
|
||||
private fun checkLocation() {
|
||||
locationNeedsHandling = !LocationManagerCompat.isLocationEnabled(getSystemService(Context.LOCATION_SERVICE) as LocationManager)
|
||||
findViewById<View>(R.id.enable_location_view).visibility = if (locationNeedsHandling) View.VISIBLE else View.GONE
|
||||
updateButton()
|
||||
}
|
||||
|
||||
private fun requestLocation() {
|
||||
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
|
||||
startActivityForResult(intent, ++locationRequestCode)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == bluetoothRequestCode) checkBluetooth()
|
||||
}
|
||||
|
||||
override fun finish() {
|
||||
|
|
|
@ -12,16 +12,16 @@ import android.location.LocationManager
|
|||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.core.location.LocationManagerCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceCategory
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import org.microg.gms.nearby.exposurenotification.AdvertiserService
|
||||
import org.microg.gms.nearby.exposurenotification.ExposureDatabase
|
||||
import org.microg.gms.nearby.exposurenotification.ScannerService
|
||||
import org.microg.gms.nearby.exposurenotification.getExposureNotificationsServiceInfo
|
||||
import org.microg.gms.nearby.exposurenotification.*
|
||||
import org.microg.gms.ui.AppIconPreference
|
||||
import org.microg.gms.ui.getApplicationInfoIfExists
|
||||
import org.microg.gms.ui.navigate
|
||||
|
@ -37,6 +37,7 @@ class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() {
|
|||
private lateinit var exposureAppsNone: Preference
|
||||
private lateinit var collectedRpis: Preference
|
||||
private lateinit var advertisingId: Preference
|
||||
private var turningBluetoothOn: Boolean = false
|
||||
private val handler = Handler()
|
||||
private val updateStatusRunnable = Runnable { updateStatus() }
|
||||
private val updateContentRunnable = Runnable { updateContent() }
|
||||
|
@ -63,8 +64,19 @@ class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() {
|
|||
}
|
||||
|
||||
exposureBluetoothOff.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
||||
val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
|
||||
startActivity(intent)
|
||||
lifecycleScope.launchWhenStarted {
|
||||
turningBluetoothOn = true
|
||||
it.isVisible = false
|
||||
val adapter = BluetoothAdapter.getDefaultAdapter()
|
||||
if (adapter != null && !adapter.enableAsync(requireContext())) {
|
||||
turningBluetoothOn = false
|
||||
val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
|
||||
startActivityForResult(intent, 144)
|
||||
} else {
|
||||
turningBluetoothOn = false
|
||||
updateStatus()
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
|
@ -88,23 +100,6 @@ class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() {
|
|||
handler.removeCallbacks(updateContentRunnable)
|
||||
}
|
||||
|
||||
private fun isLocationEnabled(): Boolean {
|
||||
val lm = requireContext().getSystemService(LOCATION_SERVICE) as LocationManager
|
||||
var gpsEnabled = false
|
||||
var networkEnabled = false
|
||||
|
||||
try {
|
||||
gpsEnabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER)
|
||||
} catch (ex: Exception) {
|
||||
}
|
||||
|
||||
try {
|
||||
networkEnabled = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
|
||||
} catch (ex: Exception) {
|
||||
}
|
||||
return gpsEnabled || networkEnabled
|
||||
}
|
||||
|
||||
private fun updateStatus() {
|
||||
lifecycleScope.launchWhenResumed {
|
||||
handler.postDelayed(updateStatusRunnable, UPDATE_STATUS_INTERVAL)
|
||||
|
@ -114,8 +109,8 @@ class ExposureNotificationsPreferencesFragment : PreferenceFragmentCompat() {
|
|||
val bluetoothSupported = ScannerService.isSupported(requireContext())
|
||||
val advertisingSupported = if (bluetoothSupported == true) AdvertiserService.isSupported(requireContext()) else bluetoothSupported
|
||||
|
||||
exposureLocationOff.isVisible = enabled && bluetoothSupported != false && !isLocationEnabled()
|
||||
exposureBluetoothOff.isVisible = enabled && bluetoothSupported == null
|
||||
exposureLocationOff.isVisible = enabled && bluetoothSupported != false && !LocationManagerCompat.isLocationEnabled(requireContext().getSystemService(LOCATION_SERVICE) as LocationManager)
|
||||
exposureBluetoothOff.isVisible = enabled && bluetoothSupported == null && !turningBluetoothOn
|
||||
exposureBluetoothUnsupported.isVisible = enabled && bluetoothSupported == false
|
||||
exposureBluetoothNoAdvertisement.isVisible = enabled && bluetoothSupported == true && advertisingSupported != true
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
|
@ -41,23 +42,40 @@
|
|||
android:padding="16dp"
|
||||
tools:text="Your phone needs to use Bluetooth to securely collect and share IDs with other phones that are nearby.\n\nCorona Warn can notify you if you were exposed to someone who reported to be diagnosed positive.\n\nThe date, duration, and signal strength associated with an exposure will be shared with the app." />
|
||||
|
||||
<LinearLayout
|
||||
<RelativeLayout
|
||||
android:id="@+id/grant_permission_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="?attr/colorAccent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/grant_permission_icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_alignTop="@id/grant_permission_summary"
|
||||
android:layout_alignBottom="@id/grant_permission_summary"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:src="@drawable/ic_alert"
|
||||
app:tint="?attr/colorPrimary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/grant_permission_summary"
|
||||
style="@style/TextAppearance.AppCompat.Small.Inverse"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_toRightOf="@id/grant_permission_icon"
|
||||
android:layout_weight="1"
|
||||
android:padding="16dp"
|
||||
android:text="@string/exposure_confirm_permission_description" />
|
||||
|
||||
<Button
|
||||
|
@ -65,11 +83,121 @@
|
|||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:padding="16dp"
|
||||
android:layout_below="@id/grant_permission_summary"
|
||||
android:layout_alignLeft="@id/grant_permission_summary"
|
||||
android:layout_marginLeft="-16dp"
|
||||
android:text="@string/exposure_confirm_permission_button"
|
||||
android:textColor="?android:attr/textColorPrimaryInverse" />
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/enable_bluetooth_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="?attr/colorAccent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/enable_bluetooth_icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_alignTop="@id/enable_bluetooth_summary"
|
||||
android:layout_alignBottom="@id/enable_bluetooth_summary"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:src="@drawable/ic_bluetooth_off"
|
||||
app:tint="?attr/colorPrimary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/enable_bluetooth_summary"
|
||||
style="@style/TextAppearance.AppCompat.Small.Inverse"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_toRightOf="@id/enable_bluetooth_icon"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/exposure_confirm_bluetooth_description" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/enable_bluetooth_button"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/enable_bluetooth_summary"
|
||||
android:layout_alignLeft="@id/enable_bluetooth_summary"
|
||||
android:layout_marginLeft="-16dp"
|
||||
android:text="@string/exposure_confirm_button"
|
||||
android:textColor="?android:attr/textColorPrimaryInverse" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/enable_bluetooth_spinner"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignLeft="@id/enable_bluetooth_button"
|
||||
android:layout_alignTop="@id/enable_bluetooth_button"
|
||||
android:layout_alignRight="@id/enable_bluetooth_button"
|
||||
android:layout_alignBottom="@id/enable_bluetooth_button"
|
||||
android:indeterminate="true"
|
||||
android:indeterminateTint="?attr/colorPrimary"
|
||||
android:padding="8dp"
|
||||
android:visibility="invisible" />
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/enable_location_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:background="?attr/colorAccent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingBottom="8dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/enable_location_icon"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_alignTop="@id/enable_location_summary"
|
||||
android:layout_alignBottom="@id/enable_location_summary"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_centerVertical="true"
|
||||
android:src="@drawable/ic_location_off"
|
||||
app:tint="?attr/colorPrimary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/enable_location_summary"
|
||||
style="@style/TextAppearance.AppCompat.Small.Inverse"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_toRightOf="@id/enable_location_icon"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/exposure_confirm_location_description" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/enable_location_button"
|
||||
style="@style/Widget.AppCompat.Button.Borderless"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/enable_location_summary"
|
||||
android:layout_alignLeft="@id/enable_location_summary"
|
||||
android:layout_marginLeft="-16dp"
|
||||
android:text="@string/exposure_confirm_button"
|
||||
android:textColor="?android:attr/textColorPrimaryInverse" />
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="service_name_exposure">Exposure Notifications</string>
|
||||
<string name="pref_exposure_enable_info_summary">Öffne eine App, die Exposure Notifications unterstützt, um diese zu aktivieren.</string>
|
||||
<string name="pref_exposure_error_bluetooth_off_summary">Aktiviere Bluetooth, um Exposure Notifications zu nutzen.</string>
|
||||
<string name="pref_exposure_error_location_off_summary">Aktiviere Ortungsdienste, um Exposure Notifications zu nutzen.</string>
|
||||
<string name="pref_exposure_error_bluetooth_off_title">Bluetooth einschalten</string>
|
||||
<string name="pref_exposure_error_location_off_title">Standortzugriff verwalten</string>
|
||||
<string name="pref_exposure_error_bluetooth_unsupported_summary">Leider ist dein Gerät nicht mit Exposure Notifications kompatibel.</string>
|
||||
<string name="pref_exposure_error_bluetooth_no_advertise_summary">Leider ist dein Gerät nicht vollständig mit Exposure Notifications kompatibel. Du wirst Warnungen über Risikokontakte erhalten, aber nicht andere Benachrichtigen können.</string>
|
||||
<string name="prefcat_exposure_apps_title">Apps, die Exposure Notifications nutzen</string>
|
||||
|
@ -56,4 +56,9 @@ Das Datum, die Zeitdauer und die Signalstärke, die einem Kontakt zugeordnet wur
|
|||
|
||||
Deine Identität oder das Testergebnis werden nicht geteilt."</string>
|
||||
<string name="exposure_confirm_keys_button">Teilen</string>
|
||||
<string name="exposure_confirm_permission_description"><xliff:g example="microG Services Core">%1$s</xliff:g> benötigt zusätzliche Berechtigungen.</string>
|
||||
<string name="exposure_confirm_permission_button">Erteilen</string>
|
||||
<string name="exposure_confirm_bluetooth_description">Bluetooth muss eingeschaltet sein.</string>
|
||||
<string name="exposure_confirm_location_description">Standortzugriff muss eingeschaltet sein.</string>
|
||||
<string name="exposure_confirm_button">Aktivieren</string>
|
||||
</resources>
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="service_name_exposure">Exposure Notifications</string>
|
||||
<string name="pref_exposure_enable_info_summary">To enable Exposure Notifications, open any app supporting it.</string>
|
||||
<string name="pref_exposure_error_bluetooth_off_summary">To use Exposure Notifications, enable Bluetooth in system settings.</string>
|
||||
<string name="pref_exposure_error_location_off_summary">To use Exposure Notifications, enable Location access in system settings.</string>
|
||||
<string name="pref_exposure_error_bluetooth_off_title">Enable Bluetooth</string>
|
||||
<string name="pref_exposure_error_location_off_title">Open Location settings</string>
|
||||
<string name="pref_exposure_error_bluetooth_unsupported_summary">Unfortunately, your device is not compatible with Exposure Notifications.</string>
|
||||
<string name="pref_exposure_error_bluetooth_no_advertise_summary">Unfortunately, your device is only partially compatible with Exposure Notifications. You can be notified for risk contacts but won\'t be able to notify others.</string>
|
||||
<string name="prefcat_exposure_apps_title">Apps using Exposure Notifications</string>
|
||||
|
@ -66,6 +66,9 @@ The date, duration, and signal strength associated with an exposure will be shar
|
|||
|
||||
Your identity or test result won't be shared with other people."</string>
|
||||
<string name="exposure_confirm_keys_button">Share</string>
|
||||
<string name="exposure_confirm_permission_description">You need to grant permissions to <xliff:g example="microG Services Core">%1$s</xliff:g> for Exposure Notifications to function correctly.</string>
|
||||
<string name="exposure_confirm_permission_description"><xliff:g example="microG Services Core">%1$s</xliff:g> needs additional permissions.</string>
|
||||
<string name="exposure_confirm_permission_button">Grant</string>
|
||||
<string name="exposure_confirm_bluetooth_description">Bluetooth needs to be enabled.</string>
|
||||
<string name="exposure_confirm_location_description">Location access is required.</string>
|
||||
<string name="exposure_confirm_button">Enable</string>
|
||||
</resources>
|
||||
|
|
|
@ -18,14 +18,16 @@
|
|||
<Preference
|
||||
android:icon="@drawable/ic_bluetooth_off"
|
||||
android:key="pref_exposure_error_bluetooth_off"
|
||||
android:summary="@string/pref_exposure_error_bluetooth_off_summary"
|
||||
android:title="@string/pref_exposure_error_bluetooth_off_title"
|
||||
android:summary="@string/exposure_confirm_bluetooth_description"
|
||||
app:isPreferenceVisible="false"
|
||||
tools:isPreferenceVisible="true" />
|
||||
|
||||
<Preference
|
||||
android:icon="@drawable/ic_location_off"
|
||||
android:key="pref_exposure_error_location_off"
|
||||
android:summary="@string/pref_exposure_error_location_off_summary"
|
||||
android:title="@string/pref_exposure_error_location_off_title"
|
||||
android:summary="@string/exposure_confirm_location_description"
|
||||
app:isPreferenceVisible="false"
|
||||
tools:isPreferenceVisible="true" />
|
||||
|
||||
|
@ -60,8 +62,8 @@
|
|||
android:title="@string/pref_exposure_collected_rpis_title"
|
||||
tools:summary="@string/pref_exposure_collected_rpis_summary" />
|
||||
<Preference
|
||||
android:key="pref_exposure_advertising_id"
|
||||
android:enabled="false"
|
||||
android:key="pref_exposure_advertising_id"
|
||||
android:title="@string/pref_exposure_advertising_id_title"
|
||||
tools:summary="9a799d68-925f-4c0c-a73c-b418f22a1250" />
|
||||
</PreferenceCategory>
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
android:enabled="false"
|
||||
android:key="pref_exposure_app_report_none"
|
||||
android:order="0"
|
||||
android:title="@string/list_no_item_none" />
|
||||
android:title="@string/list_no_item_none"
|
||||
app:isPreferenceVisible="false"
|
||||
tools:isPreferenceVisible="true" />
|
||||
<Preference
|
||||
android:enabled="false"
|
||||
android:key="pref_exposure_app_report_updated"
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
<service android:name="org.microg.gms.nearby.exposurenotification.ScannerService" />
|
||||
<service android:name="org.microg.gms.nearby.exposurenotification.AdvertiserService" />
|
||||
<service android:name="org.microg.gms.nearby.exposurenotification.CleanupService" />
|
||||
<service android:name="org.microg.gms.nearby.exposurenotification.NotifyService" />
|
||||
|
||||
<service android:name="org.microg.gms.nearby.exposurenotification.ExposureNotificationService">
|
||||
<intent-filter>
|
||||
|
|
|
@ -10,15 +10,17 @@ import android.app.AlarmManager
|
|||
import android.app.PendingIntent
|
||||
import android.app.PendingIntent.FLAG_ONE_SHOT
|
||||
import android.app.PendingIntent.FLAG_UPDATE_CURRENT
|
||||
import android.app.Service
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothAdapter.*
|
||||
import android.bluetooth.le.*
|
||||
import android.bluetooth.le.AdvertiseSettings.*
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.os.*
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LifecycleService
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
|
@ -36,7 +38,7 @@ class AdvertiserService : LifecycleService() {
|
|||
private var advertising = false
|
||||
private var wantStartAdvertising = false
|
||||
private val advertiser: BluetoothLeAdvertiser?
|
||||
get() = BluetoothAdapter.getDefaultAdapter()?.bluetoothLeAdvertiser
|
||||
get() = getDefaultAdapter()?.bluetoothLeAdvertiser
|
||||
private val alarmManager: AlarmManager
|
||||
get() = getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
||||
private val callback: AdvertiseCallback = object : AdvertiseCallback() {
|
||||
|
@ -54,10 +56,10 @@ class AdvertiserService : LifecycleService() {
|
|||
private var setCallback: Any? = null
|
||||
private val trigger = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (intent?.action == "android.bluetooth.adapter.action.STATE_CHANGED") {
|
||||
when (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1)) {
|
||||
BluetoothAdapter.STATE_TURNING_OFF, BluetoothAdapter.STATE_OFF -> stopOrRestartAdvertising()
|
||||
BluetoothAdapter.STATE_ON -> startAdvertisingIfNeeded()
|
||||
if (intent?.action == ACTION_STATE_CHANGED) {
|
||||
when (intent.getIntExtra(EXTRA_STATE, -1)) {
|
||||
STATE_TURNING_OFF, STATE_OFF -> stopOrRestartAdvertising()
|
||||
STATE_ON -> startAdvertisingIfNeeded()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +69,7 @@ class AdvertiserService : LifecycleService() {
|
|||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
registerReceiver(trigger, IntentFilter().also { it.addAction("android.bluetooth.adapter.action.STATE_CHANGED") })
|
||||
registerReceiver(trigger, IntentFilter().apply { addAction(ACTION_STATE_CHANGED) })
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
|
@ -234,11 +236,11 @@ class AdvertiserService : LifecycleService() {
|
|||
}
|
||||
|
||||
fun isSupported(context: Context): Boolean? {
|
||||
val adapter = BluetoothAdapter.getDefaultAdapter()
|
||||
val adapter = getDefaultAdapter()
|
||||
return when {
|
||||
adapter == null -> false
|
||||
Build.VERSION.SDK_INT >= 26 && (adapter.isLeExtendedAdvertisingSupported || adapter.isLePeriodicAdvertisingSupported) -> true
|
||||
adapter.state != BluetoothAdapter.STATE_ON -> null
|
||||
adapter.state != STATE_ON -> null
|
||||
adapter.bluetoothLeAdvertiser != null -> true
|
||||
else -> false
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ class CleanupService : LifecycleService() {
|
|||
ForegroundServiceContext.completeForegroundService(this, intent, TAG)
|
||||
Log.d(TAG, "CleanupService.start: $intent")
|
||||
super.onStartCommand(intent, flags, startId)
|
||||
if (isNeeded(this)) {
|
||||
if (isNeeded(this, true)) {
|
||||
lifecycleScope.launchWhenStarted {
|
||||
withContext(Dispatchers.IO) {
|
||||
var workPending = true
|
||||
|
@ -51,9 +51,9 @@ class CleanupService : LifecycleService() {
|
|||
}
|
||||
|
||||
companion object {
|
||||
fun isNeeded(context: Context): Boolean {
|
||||
fun isNeeded(context: Context, now: Boolean = false): Boolean {
|
||||
return ExposurePreferences(context).let {
|
||||
it.enabled && it.lastCleanup < System.currentTimeMillis() - CLEANUP_INTERVAL
|
||||
(it.enabled && !now) || it.lastCleanup < System.currentTimeMillis() - CLEANUP_INTERVAL
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,9 +7,8 @@ package org.microg.gms.nearby.exposurenotification
|
|||
|
||||
import android.app.Activity
|
||||
import android.app.PendingIntent
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.content.*
|
||||
import android.os.*
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.Lifecycle
|
||||
|
@ -19,6 +18,8 @@ import com.google.android.gms.common.api.Status
|
|||
import com.google.android.gms.nearby.exposurenotification.*
|
||||
import com.google.android.gms.nearby.exposurenotification.ExposureNotificationStatusCodes.*
|
||||
import com.google.android.gms.nearby.exposurenotification.internal.*
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import org.microg.gms.common.Constants
|
||||
|
@ -71,7 +72,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun confirmPermission(permission: String): Status {
|
||||
private suspend fun confirmPermission(permission: String, force: Boolean = false): Status {
|
||||
return ExposureDatabase.with(context) { database ->
|
||||
when {
|
||||
tempGrantedPermissions.contains(packageName to permission) -> {
|
||||
|
@ -79,7 +80,7 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
|
|||
tempGrantedPermissions.remove(packageName to permission)
|
||||
Status.SUCCESS
|
||||
}
|
||||
database.hasPermission(packageName, PackageUtils.firstSignatureDigest(context, packageName)!!, permission) -> {
|
||||
!force && database.hasPermission(packageName, PackageUtils.firstSignatureDigest(context, packageName)!!, permission) -> {
|
||||
Status.SUCCESS
|
||||
}
|
||||
!hasConfirmActivity() -> {
|
||||
|
@ -103,11 +104,16 @@ class ExposureNotificationServiceImpl(private val context: Context, private val
|
|||
override fun start(params: StartParams) {
|
||||
lifecycleScope.launchWhenStarted {
|
||||
val isAuthorized = ExposureDatabase.with(context) { it.isAppAuthorized(packageName) }
|
||||
val adapter = BluetoothAdapter.getDefaultAdapter()
|
||||
val status = if (isAuthorized && ExposurePreferences(context).enabled) {
|
||||
Status.SUCCESS
|
||||
} else if (adapter == null) {
|
||||
Status(FAILED_NOT_SUPPORTED, "No Bluetooth Adapter available.")
|
||||
} else {
|
||||
val status = confirmPermission(CONFIRM_ACTION_START)
|
||||
val status = confirmPermission(CONFIRM_ACTION_START, !adapter.isEnabled)
|
||||
if (status.isSuccess) {
|
||||
val context = context
|
||||
adapter.enableAsync(context)
|
||||
ExposurePreferences(context).enabled = true
|
||||
ExposureDatabase.with(context) { database ->
|
||||
database.authorizeApp(packageName)
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.nearby.exposurenotification
|
||||
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.util.Log
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.withTimeout
|
||||
|
||||
suspend fun BluetoothAdapter.enableAsync(context: Context): Boolean {
|
||||
val deferred = CompletableDeferred<Unit>()
|
||||
val receiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||
override fun onReceive(receiverContext: Context?, intent: Intent?) {
|
||||
if (intent?.action == BluetoothAdapter.ACTION_STATE_CHANGED) {
|
||||
val state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)
|
||||
if (state == BluetoothAdapter.STATE_ON) deferred.complete(Unit)
|
||||
}
|
||||
}
|
||||
}
|
||||
context.registerReceiver(receiver, IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED))
|
||||
if (!isEnabled) {
|
||||
try {
|
||||
enable()
|
||||
withTimeout(5000) { deferred.await() }
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed enabling Bluetooth")
|
||||
}
|
||||
}
|
||||
context.unregisterReceiver(receiver)
|
||||
return isEnabled
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package org.microg.gms.nearby.exposurenotification
|
||||
|
||||
import android.annotation.TargetApi
|
||||
import android.app.*
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.graphics.Color
|
||||
import android.location.LocationManager
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.location.LocationManagerCompat
|
||||
import androidx.lifecycle.LifecycleService
|
||||
import org.microg.gms.common.ForegroundServiceContext
|
||||
import org.microg.gms.nearby.core.R
|
||||
|
||||
class NotifyService : LifecycleService() {
|
||||
private val notificationId = NotifyService::class.java.name.hashCode()
|
||||
private val trigger = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent?) {
|
||||
updateNotification()
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(26)
|
||||
private fun createNotificationChannel(): String {
|
||||
val channel = NotificationChannel("exposure-notifications", "Exposure Notifications", NotificationManager.IMPORTANCE_HIGH)
|
||||
channel.setSound(null, null)
|
||||
channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||
channel.setShowBadge(true)
|
||||
if (Build.VERSION.SDK_INT >= 29) {
|
||||
channel.setAllowBubbles(false)
|
||||
}
|
||||
channel.vibrationPattern = LongArray(0)
|
||||
getSystemService(NotificationManager::class.java).createNotificationChannel(channel)
|
||||
return channel.id
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
private fun updateNotification() {
|
||||
val location = !LocationManagerCompat.isLocationEnabled(getSystemService(Context.LOCATION_SERVICE) as LocationManager)
|
||||
val bluetooth = BluetoothAdapter.getDefaultAdapter()?.state.let { it != BluetoothAdapter.STATE_ON && it != BluetoothAdapter.STATE_TURNING_ON }
|
||||
Log.d(TAG, "notify: location: $location, bluetooth: $bluetooth")
|
||||
|
||||
val text: String = when {
|
||||
location && bluetooth -> getString(R.string.exposure_notify_off_bluetooth_location)
|
||||
location -> getString(R.string.exposure_notify_off_location)
|
||||
bluetooth -> getString(R.string.exposure_notify_off_bluetooth)
|
||||
else -> {
|
||||
NotificationManagerCompat.from(this).cancel(notificationId)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 26) {
|
||||
NotificationCompat.Builder(this, createNotificationChannel())
|
||||
} else {
|
||||
NotificationCompat.Builder(this)
|
||||
}.apply {
|
||||
val typedValue = TypedValue()
|
||||
try {
|
||||
var resolved = theme.resolveAttribute(androidx.appcompat.R.attr.colorError, typedValue, true)
|
||||
if (!resolved && Build.VERSION.SDK_INT >= 26) resolved = theme.resolveAttribute(android.R.attr.colorError, typedValue, true)
|
||||
color = if (resolved) {
|
||||
ContextCompat.getColor(this@NotifyService, typedValue.resourceId)
|
||||
} else {
|
||||
Color.RED
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= 26) setColorized(true)
|
||||
} catch (e: Exception) {
|
||||
// Ignore
|
||||
}
|
||||
setSmallIcon(R.drawable.ic_virus_outline)
|
||||
setContentTitle(getString(R.string.exposure_notify_off_title))
|
||||
setContentText(text)
|
||||
setStyle(NotificationCompat.BigTextStyle())
|
||||
try {
|
||||
val intent = Intent(Constants.ACTION_EXPOSURE_NOTIFICATION_SETTINGS).apply { `package` = packageName }
|
||||
intent.resolveActivity(packageManager)
|
||||
setContentIntent(PendingIntent.getActivity(this@NotifyService, notificationId, Intent(Constants.ACTION_EXPOSURE_NOTIFICATION_SETTINGS).apply { `package` = packageName }, PendingIntent.FLAG_UPDATE_CURRENT))
|
||||
} catch (e: Exception) {
|
||||
// Ignore
|
||||
}
|
||||
}.let {
|
||||
NotificationManagerCompat.from(this).notify(notificationId, it.build())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
registerReceiver(trigger, IntentFilter().apply {
|
||||
addAction(BluetoothAdapter.ACTION_STATE_CHANGED)
|
||||
if (Build.VERSION.SDK_INT >= 19) addAction(LocationManager.MODE_CHANGED_ACTION)
|
||||
addAction(LocationManager.PROVIDERS_CHANGED_ACTION)
|
||||
})
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
ForegroundServiceContext.completeForegroundService(this, intent, TAG)
|
||||
Log.d(TAG, "NotifyService.start: $intent")
|
||||
super.onStartCommand(intent, flags, startId)
|
||||
updateNotification()
|
||||
return Service.START_STICKY
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
NotificationManagerCompat.from(this).cancel(notificationId)
|
||||
unregisterReceiver(trigger)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun isNeeded(context: Context): Boolean {
|
||||
return ExposurePreferences(context).let { it.enabled }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,8 +9,6 @@ import android.annotation.SuppressLint
|
|||
import android.annotation.TargetApi
|
||||
import android.app.AlarmManager
|
||||
import android.app.PendingIntent
|
||||
import android.app.Service
|
||||
import android.bluetooth.BluetoothAdapter
|
||||
import android.bluetooth.BluetoothAdapter.*
|
||||
import android.bluetooth.le.*
|
||||
import android.content.BroadcastReceiver
|
||||
|
@ -50,7 +48,7 @@ class ScannerService : LifecycleService() {
|
|||
}
|
||||
private val trigger = object : BroadcastReceiver() {
|
||||
override fun onReceive(context: Context?, intent: Intent?) {
|
||||
if (intent?.action == "android.bluetooth.adapter.action.STATE_CHANGED") {
|
||||
if (intent?.action == ACTION_STATE_CHANGED) {
|
||||
when (intent.getIntExtra(EXTRA_STATE, -1)) {
|
||||
STATE_TURNING_OFF, STATE_OFF -> stopScan()
|
||||
STATE_ON -> startScanIfNeeded()
|
||||
|
@ -104,7 +102,7 @@ class ScannerService : LifecycleService() {
|
|||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
registerReceiver(trigger, IntentFilter().also { it.addAction("android.bluetooth.adapter.action.STATE_CHANGED") })
|
||||
registerReceiver(trigger, IntentFilter().apply { addAction(ACTION_STATE_CHANGED) })
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
@ -174,10 +172,10 @@ class ScannerService : LifecycleService() {
|
|||
}
|
||||
|
||||
fun isSupported(context: Context): Boolean? {
|
||||
val adapter = BluetoothAdapter.getDefaultAdapter()
|
||||
val adapter = getDefaultAdapter()
|
||||
return when {
|
||||
adapter == null -> false
|
||||
adapter.state != BluetoothAdapter.STATE_ON -> null
|
||||
adapter.state != STATE_ON -> null
|
||||
adapter.bluetoothLeScanner != null -> true
|
||||
else -> false
|
||||
}
|
||||
|
|
|
@ -29,5 +29,9 @@ class ServiceTrigger : BroadcastReceiver() {
|
|||
Log.d(TAG, "Trigger ${CleanupService::class.java}")
|
||||
serviceContext.startService(Intent(context, CleanupService::class.java))
|
||||
}
|
||||
if (NotifyService.isNeeded(context)) {
|
||||
Log.d(TAG, "Trigger ${NotifyService::class.java}")
|
||||
serviceContext.startService(Intent(context, NotifyService::class.java))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
10
play-services-nearby-core/src/main/res/values-de/strings.xml
Normal file
10
play-services-nearby-core/src/main/res/values-de/strings.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="exposure_notify_off_title">Exposure Notifications deaktiviert</string>
|
||||
<string name="exposure_notify_off_bluetooth">Bluetooth muss eingeschaltet sein, um Exposure Notifications zu nutzen.</string>
|
||||
<string name="exposure_notify_off_location">Standortzugriff muss eingeschaltet sein, um Exposure Notifications zu nutzen.</string>
|
||||
<string name="exposure_notify_off_bluetooth_location">Bluetooth und Standortzugriff müssen eingeschaltet sein, um Exposure Notifications zu nutzen.</string>
|
||||
</resources>
|
10
play-services-nearby-core/src/main/res/values/strings.xml
Normal file
10
play-services-nearby-core/src/main/res/values/strings.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ SPDX-FileCopyrightText: 2020, microG Project Team
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="exposure_notify_off_title">Exposure Notifications inactive</string>
|
||||
<string name="exposure_notify_off_bluetooth">Bluetooth needs to be enabled to receive Exposure Notifications.</string>
|
||||
<string name="exposure_notify_off_location">Location access is required to receive Exposure Notifications.</string>
|
||||
<string name="exposure_notify_off_bluetooth_location">Bluetooth and Location access need to be enabled to receive Exposure Notifications.</string>
|
||||
</resources>
|
Loading…
Reference in a new issue