diff --git a/README.md b/README.md
index df4f7628f..6ccbc2f58 100644
--- a/README.md
+++ b/README.md
@@ -97,6 +97,35 @@ To only build plugins that are of interest to you, add a `gradle.skip` file in a
If you want to contribute a feature or fix browse our [issues](https://github.com/RADAR-base/radar-commons-android/issues), and please make a pull request.
+## Publishing Schemas to Maven Local
+
+While the plugin is in development phase schemas are not published centrally. To access the java generated file from avro schemas first publish them to maven local.
+To publish schemas locally to Maven, please follow these steps:
+
+1. Change your working directory to java-sdk in RADAR-Schemas.
+
+2. Run the following commands in your terminal:
+```shell
+./gradlew build
+./gradlew publishToMavenLocal
+```
+
+3. In your build.gradle file, add the following to your repositories block:
+```gradle
+repositories {
+ mavenLocal()
+}
+```
+
+4. In the same build.gradle file, add the following to your dependencies block, replacing `$radarSchemasVersion` with its corresponding value:
+```gradle
+dependencies {
+ implementation "org.radarbase:radar-schemas-commons:$radarSchemasVersion"
+}
+```
+
+You can find the version value in the metadata of your local Maven repository. It should end in `SNAPSHOT`.
+
## Licensing
Code made in the RADAR-base platform is licensed under the Apache License 2.0, as listed in see the LICENSE file in this directory. Plugins that have additional licensing requirements list them in their README.
diff --git a/avro-android/src/main/AndroidManifest.xml b/avro-android/src/main/AndroidManifest.xml
index 9b65eb06c..d30de6534 100644
--- a/avro-android/src/main/AndroidManifest.xml
+++ b/avro-android/src/main/AndroidManifest.xml
@@ -1 +1,2 @@
+
diff --git a/build.gradle b/build.gradle
index afbf2af12..e05355f71 100644
--- a/build.gradle
+++ b/build.gradle
@@ -41,10 +41,10 @@ allprojects {
ext.issueUrl = 'https://github.com/' + githubRepoName + '/issues'
ext.website = 'http://radar-base.org'
- version = "1.2.3"
+ version = "1.2.4"
group = 'org.radarbase'
- ext.versionCode = 51
+ ext.versionCode = 52
}
subprojects {
diff --git a/plugins/radar-android-application-status/src/main/AndroidManifest.xml b/plugins/radar-android-application-status/src/main/AndroidManifest.xml
index 8003fa0a4..635aabfd0 100644
--- a/plugins/radar-android-application-status/src/main/AndroidManifest.xml
+++ b/plugins/radar-android-application-status/src/main/AndroidManifest.xml
@@ -2,6 +2,8 @@
-
+
diff --git a/plugins/radar-android-application-status/src/main/res/values/strings.xml b/plugins/radar-android-application-status/src/main/res/values/strings.xml
index eec4a9a52..bb4b5ae86 100644
--- a/plugins/radar-android-application-status/src/main/res/values/strings.xml
+++ b/plugins/radar-android-application-status/src/main/res/values/strings.xml
@@ -1,6 +1,4 @@
Application
- Monitors how much data the app is collecting and
- server status.
-
+ Monitors how much data the app is collecting and server status.
diff --git a/plugins/radar-android-audio/build.gradle b/plugins/radar-android-audio/build.gradle
index fab0c3eb4..b860a0d55 100644
--- a/plugins/radar-android-audio/build.gradle
+++ b/plugins/radar-android-audio/build.gradle
@@ -10,9 +10,6 @@ android {
ndk {
moduleName rootProject.name
}
- packagingOptions {
- doNotStrip '**.so'
- }
}
// Encapsulates your external native build configurations.
diff --git a/plugins/radar-android-audio/src/main/AndroidManifest.xml b/plugins/radar-android-audio/src/main/AndroidManifest.xml
index e51b3a9b1..5059bf293 100644
--- a/plugins/radar-android-audio/src/main/AndroidManifest.xml
+++ b/plugins/radar-android-audio/src/main/AndroidManifest.xml
@@ -8,6 +8,8 @@
+ android:foregroundServiceType="microphone"
+ android:exported="false"
+ android:description="@string/audio_description" />
diff --git a/plugins/radar-android-audio/src/main/java/org/radarbase/passive/audio/OpenSmileAudioService.kt b/plugins/radar-android-audio/src/main/java/org/radarbase/passive/audio/OpenSmileAudioService.kt
index 120ae6466..9cb3a00a5 100644
--- a/plugins/radar-android-audio/src/main/java/org/radarbase/passive/audio/OpenSmileAudioService.kt
+++ b/plugins/radar-android-audio/src/main/java/org/radarbase/passive/audio/OpenSmileAudioService.kt
@@ -39,9 +39,9 @@ class OpenSmileAudioService : SourceService() {
manager as OpensmileAudioManager
manager.setRecordRate(config.getLong(AUDIO_RECORD_RATE_S, DEFAULT_RECORD_RATE))
manager.config = OpensmileAudioManager.AudioConfiguration(
- config.getString(AUDIO_CONFIG_FILE, "ComParE_2016.conf"),
- config.getLong(AUDIO_DURATION_S, 15L),
- TimeUnit.SECONDS
+ config.getString(AUDIO_CONFIG_FILE, "ComParE_2016.conf"),
+ config.getLong(AUDIO_DURATION_S, 15L),
+ TimeUnit.SECONDS,
)
}
diff --git a/plugins/radar-android-audio/src/main/java/org/radarbase/passive/audio/OpensmileAudioManager.kt b/plugins/radar-android-audio/src/main/java/org/radarbase/passive/audio/OpensmileAudioManager.kt
index 017fbf8ff..3f8bf249a 100644
--- a/plugins/radar-android-audio/src/main/java/org/radarbase/passive/audio/OpensmileAudioManager.kt
+++ b/plugins/radar-android-audio/src/main/java/org/radarbase/passive/audio/OpensmileAudioManager.kt
@@ -135,7 +135,7 @@ class OpensmileAudioManager constructor(service: OpenSmileAudioService) : Abstra
?.list { _, name -> name.startsWith("audio_") && name.endsWith(".bin") }
?.forEach { File(audioDir.parentFile, it).delete() }
- audioDir.walk().filter { it.startsWith("audio_") && it.endsWith(".bin") }
+ audioDir.walk().filter { it.startsWith("audio_") && it.path.endsWith(".bin") }
.forEach { it.delete() }
}
}
diff --git a/plugins/radar-android-empatica/src/main/AndroidManifest.xml b/plugins/radar-android-empatica/src/main/AndroidManifest.xml
index cacff25f5..b0266246b 100644
--- a/plugins/radar-android-empatica/src/main/AndroidManifest.xml
+++ b/plugins/radar-android-empatica/src/main/AndroidManifest.xml
@@ -4,16 +4,24 @@
-
-
-
-
-
+
+
+
+
+
+
+ android:foregroundServiceType="location"
+ android:exported="false"
+ android:description="@string/empatica_e4_explanation" />
diff --git a/plugins/radar-android-empatica/src/main/java/org/radarbase/passive/empatica/E4Manager.kt b/plugins/radar-android-empatica/src/main/java/org/radarbase/passive/empatica/E4Manager.kt
index 15f4b7dfe..347069489 100644
--- a/plugins/radar-android-empatica/src/main/java/org/radarbase/passive/empatica/E4Manager.kt
+++ b/plugins/radar-android-empatica/src/main/java/org/radarbase/passive/empatica/E4Manager.kt
@@ -26,7 +26,6 @@ import com.empatica.empalink.config.EmpaStatus
import com.empatica.empalink.delegate.EmpaDataDelegate
import com.empatica.empalink.delegate.EmpaSessionManagerDelegate
import com.empatica.empalink.delegate.EmpaStatusDelegate
-import org.radarbase.android.RadarApplication.Companion.radarApp
import org.radarbase.android.source.AbstractSourceManager
import org.radarbase.android.source.SourceStatusListener
import org.radarbase.android.util.BluetoothStateReceiver.Companion.bluetoothIsEnabled
@@ -46,6 +45,9 @@ class E4Manager(
EmpaDataDelegate,
EmpaStatusDelegate,
EmpaSessionManagerDelegate {
+ private val notificationHandler: NotificationHandler by lazy {
+ NotificationHandler(service)
+ }
private var doNotify: Boolean = false
private val accelerationTopic = createCache("android_empatica_e4_acceleration", EmpaticaE4Acceleration())
private val batteryLevelTopic = createCache("android_empatica_e4_battery_level", EmpaticaE4BatteryLevel())
@@ -122,7 +124,7 @@ class E4Manager(
EmpaStatus.CONNECTING -> hasBeenConnecting = true
EmpaStatus.CONNECTED -> {
status = SourceStatusListener.Status.CONNECTED
- service.radarApp.notificationHandler.manager?.cancel(EMPATICA_DISCONNECTED_NOTIFICATION_ID)
+ notificationHandler.manager?.cancel(EMPATICA_DISCONNECTED_NOTIFICATION_ID)
}
EmpaStatus.DISCONNECTED ->
// The device manager disconnected from a device. Before it ever makes a connection,
@@ -230,10 +232,11 @@ class E4Manager(
override fun disconnect() {
if (status != SourceStatusListener.Status.UNAVAILABLE && !isClosed && doNotify) {
- service.radarApp.notificationHandler.notify(
- id = EMPATICA_DISCONNECTED_NOTIFICATION_ID,
- channel = NotificationHandler.NOTIFICATION_CHANNEL_ALERT,
- includeStartIntent = true) {
+ notificationHandler.notify(
+ id = EMPATICA_DISCONNECTED_NOTIFICATION_ID,
+ channel = NotificationHandler.NOTIFICATION_CHANNEL_ALERT,
+ includeStartIntent = true,
+ ) {
setContentTitle(service.getString(R.string.notification_empatica_disconnected_title))
setContentText(service.getString(R.string.notification_empatica_disconnected_text))
}
diff --git a/plugins/radar-android-empatica/src/main/java/org/radarbase/passive/empatica/E4Provider.kt b/plugins/radar-android-empatica/src/main/java/org/radarbase/passive/empatica/E4Provider.kt
index a4d262a8d..fbf5faa48 100644
--- a/plugins/radar-android-empatica/src/main/java/org/radarbase/passive/empatica/E4Provider.kt
+++ b/plugins/radar-android-empatica/src/main/java/org/radarbase/passive/empatica/E4Provider.kt
@@ -21,6 +21,7 @@ import android.content.pm.PackageManager
import android.os.Build
import org.radarbase.android.RadarService
import org.radarbase.android.source.SourceProvider
+import org.radarbase.android.util.BluetoothStateReceiver.Companion.bluetoothPermissionList
open class E4Provider(radarService: RadarService) : SourceProvider(radarService) {
override val serviceClass = E4Service::class.java
@@ -32,19 +33,7 @@ open class E4Provider(radarService: RadarService) : SourceProvider(rada
"org.radarbase.passive.empatica.E4Provider",
"org.radarcns.empatica.E4ServiceProvider")
- override val permissionsNeeded = buildList {
- add(ACCESS_COARSE_LOCATION)
- add(ACCESS_FINE_LOCATION)
- add(BLUETOOTH)
- add(BLUETOOTH_ADMIN)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- add(BLUETOOTH_SCAN)
- add(BLUETOOTH_CONNECT)
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- add(ACCESS_BACKGROUND_LOCATION)
- }
- }
+ override val permissionsNeeded = bluetoothPermissionList
override val featuresNeeded = listOf(PackageManager.FEATURE_BLUETOOTH, PackageManager.FEATURE_BLUETOOTH_LE)
diff --git a/plugins/radar-android-faros/src/main/AndroidManifest.xml b/plugins/radar-android-faros/src/main/AndroidManifest.xml
index 685b73533..0f27076ab 100644
--- a/plugins/radar-android-faros/src/main/AndroidManifest.xml
+++ b/plugins/radar-android-faros/src/main/AndroidManifest.xml
@@ -3,16 +3,24 @@
-
-
-
-
+
+
+
+
+
+ android:foregroundServiceType="location"
+ android:exported="false"
+ android:description="@string/farosDescription"/>
diff --git a/plugins/radar-android-faros/src/main/java/org/radarbase/passive/bittium/FarosManager.kt b/plugins/radar-android-faros/src/main/java/org/radarbase/passive/bittium/FarosManager.kt
index cfcdc72ce..5a20854d2 100644
--- a/plugins/radar-android-faros/src/main/java/org/radarbase/passive/bittium/FarosManager.kt
+++ b/plugins/radar-android-faros/src/main/java/org/radarbase/passive/bittium/FarosManager.kt
@@ -35,6 +35,9 @@ class FarosManager internal constructor(
private val farosFactory: FarosSdkFactory,
private val handler: SafeHandler
) : AbstractSourceManager(service), FarosDeviceListener, FarosSdkListener {
+ private val notificationHandler by lazy {
+ NotificationHandler(this.service)
+ }
private var doNotify: Boolean = false
private val accelerationTopic: DataCache = createCache("android_bittium_faros_acceleration", BittiumFarosAcceleration())
private val ecgTopic: DataCache = createCache("android_bittium_faros_ecg", BittiumFarosEcg())
@@ -78,7 +81,7 @@ class FarosManager internal constructor(
startMeasurements()
}
}
- service.radarApp.notificationHandler.manager?.cancel(FAROS_DISCONNECTED_NOTIFICATION_ID)
+ notificationHandler.manager?.cancel(FAROS_DISCONNECTED_NOTIFICATION_ID)
SourceStatusListener.Status.CONNECTED
}
FarosDeviceListener.CONNECTING -> SourceStatusListener.Status.CONNECTING
@@ -194,10 +197,11 @@ class FarosManager internal constructor(
override fun disconnect() {
if (!isClosed && doNotify) {
- service.radarApp.notificationHandler.notify(
- id = FAROS_DISCONNECTED_NOTIFICATION_ID,
- channel = NotificationHandler.NOTIFICATION_CHANNEL_ALERT,
- includeStartIntent = true) {
+ notificationHandler.notify(
+ id = FAROS_DISCONNECTED_NOTIFICATION_ID,
+ channel = NotificationHandler.NOTIFICATION_CHANNEL_ALERT,
+ includeStartIntent = true,
+ ) {
setContentTitle(service.getString(R.string.notification_faros_disconnected_title))
setContentText(service.getString(R.string.notification_faros_disconnected_text))
}
diff --git a/plugins/radar-android-faros/src/main/java/org/radarbase/passive/bittium/FarosProvider.kt b/plugins/radar-android-faros/src/main/java/org/radarbase/passive/bittium/FarosProvider.kt
index 394df1293..20fc7cc40 100644
--- a/plugins/radar-android-faros/src/main/java/org/radarbase/passive/bittium/FarosProvider.kt
+++ b/plugins/radar-android-faros/src/main/java/org/radarbase/passive/bittium/FarosProvider.kt
@@ -16,12 +16,11 @@
package org.radarbase.passive.bittium
-import android.Manifest.permission.*
import android.content.pm.PackageManager
-import android.os.Build
import org.radarbase.android.BuildConfig
import org.radarbase.android.RadarService
import org.radarbase.android.source.SourceProvider
+import org.radarbase.android.util.BluetoothStateReceiver.Companion.bluetoothPermissionList
class FarosProvider(radarService: RadarService) : SourceProvider(radarService) {
override val description: String?
@@ -38,19 +37,7 @@ class FarosProvider(radarService: RadarService) : SourceProvider(rad
override val hasDetailView: Boolean = true
- override val permissionsNeeded: List = buildList {
- add(ACCESS_COARSE_LOCATION)
- add(ACCESS_FINE_LOCATION)
- add(BLUETOOTH)
- add(BLUETOOTH_ADMIN)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- add(BLUETOOTH_SCAN)
- add(BLUETOOTH_CONNECT)
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- add(ACCESS_BACKGROUND_LOCATION)
- }
- }
+ override val permissionsNeeded: List = bluetoothPermissionList
override val featuresNeeded: List = listOf(PackageManager.FEATURE_BLUETOOTH)
diff --git a/plugins/radar-android-faros/src/main/java/org/radarbase/passive/bittium/FarosState.kt b/plugins/radar-android-faros/src/main/java/org/radarbase/passive/bittium/FarosState.kt
index 1ff638c35..23d5432d9 100644
--- a/plugins/radar-android-faros/src/main/java/org/radarbase/passive/bittium/FarosState.kt
+++ b/plugins/radar-android-faros/src/main/java/org/radarbase/passive/bittium/FarosState.kt
@@ -48,12 +48,10 @@ class FarosState : BaseSourceState() {
this.acceleration[2] = z
}
- override fun toString(): String {
- return "FarosDeviceStatus{" +
- "batteryLevel=" + batteryLevel +
- ", heartRate=" + heartRate +
- ", temperature=" + temperature +
- ", acceleration=" + Arrays.toString(acceleration) +
- '}'.toString()
- }
+ override fun toString(): String = "FarosDeviceStatus{" +
+ "batteryLevel=" + batteryLevel +
+ ", heartRate=" + heartRate +
+ ", temperature=" + temperature +
+ ", acceleration=" + acceleration.contentToString() +
+ '}'
}
diff --git a/plugins/radar-android-login-oauth2/src/main/java/org/radarbase/android/auth/oauth2/OAuth2LoginManager.kt b/plugins/radar-android-login-oauth2/src/main/java/org/radarbase/android/auth/oauth2/OAuth2LoginManager.kt
index 60054c3f7..9340ae320 100644
--- a/plugins/radar-android-login-oauth2/src/main/java/org/radarbase/android/auth/oauth2/OAuth2LoginManager.kt
+++ b/plugins/radar-android-login-oauth2/src/main/java/org/radarbase/android/auth/oauth2/OAuth2LoginManager.kt
@@ -20,15 +20,16 @@ import android.app.Activity
import org.json.JSONException
import org.radarbase.android.RadarApplication.Companion.radarApp
import org.radarbase.android.auth.*
+import org.radarbase.android.auth.portal.ManagementPortalLoginManager
import org.radarbase.producer.AuthenticationException
/**
* Authenticates against the RADAR Management Portal.
*/
class OAuth2LoginManager(
- private val service: AuthService,
- private val projectIdClaim: String,
- private val userIdClaim: String
+ private val service: AuthService,
+ private val projectIdClaim: String,
+ private val userIdClaim: String
) : LoginManager, LoginListener {
private val stateManager: OAuth2StateManager = OAuth2StateManager(service)
@@ -55,16 +56,8 @@ class OAuth2LoginManager(
return true
}
- override fun invalidate(authState: AppAuthState, disableRefresh: Boolean): AppAuthState? {
- return when {
- authState.tokenType != LoginManager.AUTH_TYPE_BEARER -> return null
- disableRefresh -> authState.alter {
- attributes -= LOGIN_REFRESH_TOKEN
- isPrivacyPolicyAccepted = false
- }
- else -> authState
- }
- }
+ override fun invalidate(authState: AppAuthState, disableRefresh: Boolean): AppAuthState? =
+ authState.takeIf { it.authenticationSource == OAUTH2_SOURCE_TYPE }
override val sourceTypes: List = OAUTH2_SOURCE_TYPES
@@ -89,16 +82,16 @@ class OAuth2LoginManager(
override fun loginSucceeded(manager: LoginManager?, authState: AppAuthState) {
val token = authState.token
if (token == null) {
- this.service.loginFailed(this,
+ loginFailed(this,
IllegalArgumentException("Cannot login using OAuth2 without a token"))
return
}
try {
processJwt(authState, Jwt.parse(token)).let {
- this.service.loginSucceeded(this, it)
+ service.loginSucceeded(this, it)
}
} catch (ex: JSONException) {
- this.service.loginFailed(this, ex)
+ loginFailed(this, ex)
}
}
diff --git a/plugins/radar-android-phone-telephony/src/main/AndroidManifest.xml b/plugins/radar-android-phone-telephony/src/main/AndroidManifest.xml
index 6dd3ff31d..134fec4a0 100644
--- a/plugins/radar-android-phone-telephony/src/main/AndroidManifest.xml
+++ b/plugins/radar-android-phone-telephony/src/main/AndroidManifest.xml
@@ -5,6 +5,8 @@
-
+
diff --git a/plugins/radar-android-phone-usage/src/main/AndroidManifest.xml b/plugins/radar-android-phone-usage/src/main/AndroidManifest.xml
index 9ed10e4b9..4c3ec3781 100644
--- a/plugins/radar-android-phone-usage/src/main/AndroidManifest.xml
+++ b/plugins/radar-android-phone-usage/src/main/AndroidManifest.xml
@@ -7,6 +7,8 @@
android:name="android.permission.PACKAGE_USAGE_STATS"/>
-
+
diff --git a/plugins/radar-android-phone-usage/src/main/java/org/radarbase/passive/phone/usage/PhoneUsageManager.kt b/plugins/radar-android-phone-usage/src/main/java/org/radarbase/passive/phone/usage/PhoneUsageManager.kt
index cefc06ad2..102d813b1 100644
--- a/plugins/radar-android-phone-usage/src/main/java/org/radarbase/passive/phone/usage/PhoneUsageManager.kt
+++ b/plugins/radar-android-phone-usage/src/main/java/org/radarbase/passive/phone/usage/PhoneUsageManager.kt
@@ -173,7 +173,7 @@ class PhoneUsageManager(context: PhoneUsageService) : AbstractSourceManager UsageEventType.BACKGROUND
CONFIGURATION_CHANGE -> UsageEventType.CONFIG
SHORTCUT_INVOCATION_COMPAT -> UsageEventType.SHORTCUT
- USER_INTERACTION_COMPAT -> UsageEventType.INTERACTION
+ USER_INTERACTION -> UsageEventType.INTERACTION
else -> UsageEventType.OTHER
}
@@ -223,8 +223,7 @@ class PhoneUsageManager(context: PhoneUsageService) : AbstractSourceManager= 25) SHORTCUT_INVOCATION else 8
- private val USER_INTERACTION_COMPAT = if (Build.VERSION.SDK_INT >= 23) USER_INTERACTION else 7
+ private val SHORTCUT_INVOCATION_COMPAT = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) SHORTCUT_INVOCATION else 8
private const val LAST_PACKAGE_NAME = "org.radarcns.phone.packageName"
private const val LAST_EVENT_TIMESTAMP = "org.radarcns.phone.timestamp"
diff --git a/plugins/radar-android-phone-usage/src/main/java/org/radarbase/passive/phone/usage/PhoneUsageProvider.kt b/plugins/radar-android-phone-usage/src/main/java/org/radarbase/passive/phone/usage/PhoneUsageProvider.kt
index 4efc54b42..188184de4 100644
--- a/plugins/radar-android-phone-usage/src/main/java/org/radarbase/passive/phone/usage/PhoneUsageProvider.kt
+++ b/plugins/radar-android-phone-usage/src/main/java/org/radarbase/passive/phone/usage/PhoneUsageProvider.kt
@@ -17,7 +17,6 @@
package org.radarbase.passive.phone.usage
import android.Manifest.permission.PACKAGE_USAGE_STATS
-import android.os.Build
import org.radarbase.android.BuildConfig
import org.radarbase.android.RadarService
import org.radarbase.android.source.BaseSourceState
diff --git a/plugins/radar-android-phone/src/main/AndroidManifest.xml b/plugins/radar-android-phone/src/main/AndroidManifest.xml
index 3f25ff405..60f484d19 100644
--- a/plugins/radar-android-phone/src/main/AndroidManifest.xml
+++ b/plugins/radar-android-phone/src/main/AndroidManifest.xml
@@ -4,21 +4,34 @@
-
-
+
+
+
-
+
+
-
+
-
+ android:foregroundServiceType="location"
+ android:exported="false"
+ android:description="@string/phone_location_description" />
+
+ android:foregroundServiceType="location"
+ android:exported="false"
+ android:description="@string/phone_bluetooth_description" />
diff --git a/plugins/radar-android-phone/src/main/java/org/radarbase/passive/phone/PhoneBluetoothManager.kt b/plugins/radar-android-phone/src/main/java/org/radarbase/passive/phone/PhoneBluetoothManager.kt
index c39348159..496d1b9a1 100644
--- a/plugins/radar-android-phone/src/main/java/org/radarbase/passive/phone/PhoneBluetoothManager.kt
+++ b/plugins/radar-android-phone/src/main/java/org/radarbase/passive/phone/PhoneBluetoothManager.kt
@@ -31,6 +31,7 @@ import org.radarbase.android.source.AbstractSourceManager
import org.radarbase.android.source.BaseSourceState
import org.radarbase.android.source.SourceStatusListener
import org.radarbase.android.util.BluetoothStateReceiver.Companion.bluetoothAdapter
+import org.radarbase.android.util.BluetoothStateReceiver.Companion.hasBluetoothPermission
import org.radarbase.android.util.OfflineProcessor
import org.radarcns.kafka.ObservationKey
import org.radarcns.passive.phone.PhoneBluetoothDevices
@@ -65,14 +66,11 @@ class PhoneBluetoothManager(service: PhoneBluetoothService) : AbstractSourceMana
logger.error("Bluetooth is not available.")
return
}
+ if (!service.hasBluetoothPermission) {
+ logger.error("Cannot initiate Bluetooth scan without scan permissions")
+ return
+ }
if (bluetoothAdapter.isEnabled) {
- val scanPermission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- Manifest.permission.BLUETOOTH_SCAN
- } else Manifest.permission.BLUETOOTH_ADMIN
- if (ActivityCompat.checkSelfPermission(service, scanPermission) != PackageManager.PERMISSION_GRANTED) {
- logger.error("Cannot initiate Bluetooth scan without scan permissions")
- return
- }
val filter = IntentFilter().apply {
addAction(BluetoothDevice.ACTION_FOUND)
addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
diff --git a/plugins/radar-android-phone/src/main/java/org/radarbase/passive/phone/PhoneBluetoothProvider.kt b/plugins/radar-android-phone/src/main/java/org/radarbase/passive/phone/PhoneBluetoothProvider.kt
index 989f17de6..5755ba8d7 100644
--- a/plugins/radar-android-phone/src/main/java/org/radarbase/passive/phone/PhoneBluetoothProvider.kt
+++ b/plugins/radar-android-phone/src/main/java/org/radarbase/passive/phone/PhoneBluetoothProvider.kt
@@ -16,13 +16,12 @@
package org.radarbase.passive.phone
-import android.Manifest.permission.*
import android.content.pm.PackageManager
-import android.os.Build
import org.radarbase.android.BuildConfig
import org.radarbase.android.RadarService
import org.radarbase.android.source.BaseSourceState
import org.radarbase.android.source.SourceProvider
+import org.radarbase.android.util.BluetoothStateReceiver.Companion.bluetoothPermissionList
import org.radarbase.passive.phone.PhoneSensorProvider.Companion.MODEL
import org.radarbase.passive.phone.PhoneSensorProvider.Companion.PRODUCER
@@ -44,19 +43,7 @@ open class PhoneBluetoothProvider(radarService: RadarService) : SourceProvider = buildList {
- add(ACCESS_COARSE_LOCATION)
- add(ACCESS_FINE_LOCATION)
- add(BLUETOOTH)
- add(BLUETOOTH_ADMIN)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
- add(BLUETOOTH_SCAN)
- add(BLUETOOTH_CONNECT)
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
- add(ACCESS_BACKGROUND_LOCATION)
- }
- }
+ override val permissionsNeeded: List = bluetoothPermissionList
override val featuresNeeded: List = listOf(PackageManager.FEATURE_BLUETOOTH)
diff --git a/plugins/radar-android-phone/src/main/java/org/radarbase/passive/phone/PhoneLocationManager.kt b/plugins/radar-android-phone/src/main/java/org/radarbase/passive/phone/PhoneLocationManager.kt
index 94f875abd..6fdcc021f 100644
--- a/plugins/radar-android-phone/src/main/java/org/radarbase/passive/phone/PhoneLocationManager.kt
+++ b/plugins/radar-android-phone/src/main/java/org/radarbase/passive/phone/PhoneLocationManager.kt
@@ -140,6 +140,7 @@ class PhoneLocationManager(context: PhoneLocationService) : AbstractSourceManage
longitude, accuracy, altitude, speed, bearing, timestamp)
}
+ @Deprecated("This callback will never be invoked on Android Q and above.")
override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
override fun onProviderEnabled(provider: String) {}
diff --git a/plugins/radar-android-phone/src/main/java/org/radarbase/passive/phone/PhoneSensorManager.kt b/plugins/radar-android-phone/src/main/java/org/radarbase/passive/phone/PhoneSensorManager.kt
index 3f19e35a7..88d1043ad 100644
--- a/plugins/radar-android-phone/src/main/java/org/radarbase/passive/phone/PhoneSensorManager.kt
+++ b/plugins/radar-android-phone/src/main/java/org/radarbase/passive/phone/PhoneSensorManager.kt
@@ -306,8 +306,9 @@ class PhoneSensorManager(context: PhoneSensorService) : AbstractSourceManager SparseArray.computeIfAbsent(key: Int, compute: () -> T) = get(key)
diff --git a/plugins/radar-android-phone/src/main/java/org/radarbase/passive/phone/PhoneSensorService.kt b/plugins/radar-android-phone/src/main/java/org/radarbase/passive/phone/PhoneSensorService.kt
index a966a1492..44be7f9e8 100644
--- a/plugins/radar-android-phone/src/main/java/org/radarbase/passive/phone/PhoneSensorService.kt
+++ b/plugins/radar-android-phone/src/main/java/org/radarbase/passive/phone/PhoneSensorService.kt
@@ -21,7 +21,6 @@ import android.util.SparseIntArray
import org.radarbase.android.config.SingleRadarConfiguration
import org.radarbase.android.source.SourceManager
import org.radarbase.android.source.SourceService
-import org.slf4j.LoggerFactory
import java.util.concurrent.TimeUnit
/**
diff --git a/plugins/radar-android-ppg/src/main/AndroidManifest.xml b/plugins/radar-android-ppg/src/main/AndroidManifest.xml
index 32999094e..2589b27db 100644
--- a/plugins/radar-android-ppg/src/main/AndroidManifest.xml
+++ b/plugins/radar-android-ppg/src/main/AndroidManifest.xml
@@ -8,7 +8,9 @@
-
+
diff --git a/plugins/radar-android-ppg/src/main/res/values/strings.xml b/plugins/radar-android-ppg/src/main/res/values/strings.xml
index 818805178..f62827721 100644
--- a/plugins/radar-android-ppg/src/main/res/values/strings.xml
+++ b/plugins/radar-android-ppg/src/main/res/values/strings.xml
@@ -9,4 +9,5 @@
Stop
PPG
PPG measurement
+ Service that measure PPG using the camera of a phone.
diff --git a/plugins/radar-android-weather/src/main/AndroidManifest.xml b/plugins/radar-android-weather/src/main/AndroidManifest.xml
index d5e0d80bc..aef0517fc 100644
--- a/plugins/radar-android-weather/src/main/AndroidManifest.xml
+++ b/plugins/radar-android-weather/src/main/AndroidManifest.xml
@@ -8,6 +8,8 @@
+ android:foregroundServiceType="location"
+ android:exported="false"
+ android:description="@string/weather_api_description" />
diff --git a/plugins/radar-android-weather/src/main/java/org/radarbase/passive/weather/WeatherApiProvider.kt b/plugins/radar-android-weather/src/main/java/org/radarbase/passive/weather/WeatherApiProvider.kt
index 156bb4648..58ef33a50 100644
--- a/plugins/radar-android-weather/src/main/java/org/radarbase/passive/weather/WeatherApiProvider.kt
+++ b/plugins/radar-android-weather/src/main/java/org/radarbase/passive/weather/WeatherApiProvider.kt
@@ -19,11 +19,13 @@ package org.radarbase.passive.weather
import android.Manifest.permission.*
import android.content.pm.PackageManager
import android.os.Build
+import androidx.annotation.Keep
import org.radarbase.android.BuildConfig
import org.radarbase.android.RadarService
import org.radarbase.android.source.BaseSourceState
import org.radarbase.android.source.SourceProvider
+@Keep
open class WeatherApiProvider(radarService: RadarService) : SourceProvider(radarService) {
override val description: String?
get() = radarService.getString(R.string.weather_api_description)
diff --git a/plugins/radar-android-weather/src/main/java/org/radarbase/passive/weather/WeatherApiService.kt b/plugins/radar-android-weather/src/main/java/org/radarbase/passive/weather/WeatherApiService.kt
index 88cfd7f5f..8a72d598c 100644
--- a/plugins/radar-android-weather/src/main/java/org/radarbase/passive/weather/WeatherApiService.kt
+++ b/plugins/radar-android-weather/src/main/java/org/radarbase/passive/weather/WeatherApiService.kt
@@ -46,8 +46,9 @@ class WeatherApiService : SourceService() {
manager as WeatherApiManager
manager.setQueryInterval(config.getLong(WEATHER_QUERY_INTERVAL, WEATHER_QUERY_INTERVAL_DEFAULT), TimeUnit.SECONDS)
manager.setSource(
- config.getString(WEATHER_API_SOURCE, WEATHER_API_SOURCE_DEFAULT),
- config.optString(WEATHER_API_KEY))
+ config.getString(WEATHER_API_SOURCE, WEATHER_API_SOURCE_DEFAULT),
+ config.optString(WEATHER_API_KEY),
+ )
}
companion object {
diff --git a/radar-commons-android/src/main/java/org/radarbase/android/AbstractRadarApplication.kt b/radar-commons-android/src/main/java/org/radarbase/android/AbstractRadarApplication.kt
index 63c6df5fa..d5f1f8230 100644
--- a/radar-commons-android/src/main/java/org/radarbase/android/AbstractRadarApplication.kt
+++ b/radar-commons-android/src/main/java/org/radarbase/android/AbstractRadarApplication.kt
@@ -31,7 +31,7 @@ abstract class AbstractRadarApplication : Application(), RadarApplication {
private lateinit var innerNotificationHandler: NotificationHandler
override val notificationHandler: NotificationHandler
- get() = innerNotificationHandler.apply { onCreate() }
+ get() = innerNotificationHandler
override lateinit var configuration: RadarConfiguration
@@ -51,7 +51,7 @@ abstract class AbstractRadarApplication : Application(), RadarApplication {
// initialize crashlytics
HandroidLoggerAdapter.DEBUG = BuildConfig.DEBUG
HandroidLoggerAdapter.APP_NAME = packageName
- HandroidLoggerAdapter.enableLoggingToCrashlytics()
+ HandroidLoggerAdapter.enableLoggingToFirebaseCrashlytics()
}
/**
diff --git a/radar-commons-android/src/main/java/org/radarbase/android/RadarApplication.kt b/radar-commons-android/src/main/java/org/radarbase/android/RadarApplication.kt
index 56b63873d..ad747983a 100644
--- a/radar-commons-android/src/main/java/org/radarbase/android/RadarApplication.kt
+++ b/radar-commons-android/src/main/java/org/radarbase/android/RadarApplication.kt
@@ -30,11 +30,6 @@ interface RadarApplication {
val configuration: RadarConfiguration
- /** Large icon bitmap. */
- val largeIcon: Bitmap
- /** Small icon drawable resource ID. */
- val smallIcon: Int
-
val mainActivity: Class
val loginActivity: Class
diff --git a/radar-commons-android/src/main/java/org/radarbase/android/RadarService.kt b/radar-commons-android/src/main/java/org/radarbase/android/RadarService.kt
index bdf691f78..4eac4080c 100644
--- a/radar-commons-android/src/main/java/org/radarbase/android/RadarService.kt
+++ b/radar-commons-android/src/main/java/org/radarbase/android/RadarService.kt
@@ -108,6 +108,7 @@ abstract class RadarService : LifecycleService(), ServerStatusListener, LoginLis
add(POST_NOTIFICATIONS)
}
}
+ private lateinit var notificationHandler: NotificationHandler
override fun onBind(intent: Intent): IBinder? {
super.onBind(intent)
@@ -124,6 +125,7 @@ abstract class RadarService : LifecycleService(), ServerStatusListener, LoginLis
override fun onCreate() {
super.onCreate()
serverStatus = ServerStatusListener.Status.DISABLED
+ notificationHandler = NotificationHandler(this)
binder = createBinder()
mHandler = SafeHandler.getInstance("RadarService", THREAD_PRIORITY_BACKGROUND).apply {
start()
@@ -186,7 +188,7 @@ abstract class RadarService : LifecycleService(), ServerStatusListener, LoginLis
.filter { it.needsBluetooth() }
.forEach { it.stopRecording() }
- bluetoothNotification = radarApp.notificationHandler.notify(
+ bluetoothNotification = notificationHandler.notify(
BLUETOOTH_NOTIFICATION,
NotificationHandler.NOTIFICATION_CHANNEL_ALERT,
false
@@ -213,7 +215,7 @@ abstract class RadarService : LifecycleService(), ServerStatusListener, LoginLis
protected open fun createForegroundNotification(): Notification {
val mainIntent = Intent(this, radarApp.mainActivity)
- return radarApp.notificationHandler.create(
+ return notificationHandler.create(
NOTIFICATION_CHANNEL_INFO,
true
) {
diff --git a/radar-commons-android/src/main/java/org/radarbase/android/util/BluetoothEnforcer.kt b/radar-commons-android/src/main/java/org/radarbase/android/util/BluetoothEnforcer.kt
index 4d0763a57..e4b05873b 100644
--- a/radar-commons-android/src/main/java/org/radarbase/android/util/BluetoothEnforcer.kt
+++ b/radar-commons-android/src/main/java/org/radarbase/android/util/BluetoothEnforcer.kt
@@ -13,6 +13,7 @@ import org.radarbase.android.RadarApplication.Companion.radarConfig
import org.radarbase.android.RadarConfiguration.Companion.ENABLE_BLUETOOTH_REQUESTS
import org.radarbase.android.RadarService
import org.radarbase.android.util.BluetoothStateReceiver.Companion.bluetoothIsEnabled
+import org.slf4j.LoggerFactory
import java.util.concurrent.TimeUnit
class BluetoothEnforcer(
@@ -114,10 +115,14 @@ class BluetoothEnforcer(
.putLong(LAST_REQUEST, System.currentTimeMillis())
.apply()
- context.startActivityForResult(Intent().apply {
- action = BluetoothAdapter.ACTION_REQUEST_ENABLE
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- }, REQUEST_ENABLE_BT)
+ try {
+ context.startActivityForResult(Intent().apply {
+ action = BluetoothAdapter.ACTION_REQUEST_ENABLE
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }, REQUEST_ENABLE_BT)
+ } catch (ex: SecurityException) {
+ logger.warn("Cannot request Bluetooth to be enabled - no permission")
+ }
}
isRequestingBluetooth = false
}, 1000L)
@@ -134,5 +139,7 @@ class BluetoothEnforcer(
const val REQUEST_ENABLE_BT: Int = 6944
private const val LAST_REQUEST: String = "lastRequest"
private const val BLUETOOTH_REQUEST_COOLDOWN = "bluetooth_request_cooldown"
+
+ private val logger = LoggerFactory.getLogger(BluetoothEnforcer::class.java)
}
}
diff --git a/radar-commons-android/src/main/java/org/radarbase/android/util/BluetoothStateReceiver.kt b/radar-commons-android/src/main/java/org/radarbase/android/util/BluetoothStateReceiver.kt
index da51f2f8f..d2b8403f8 100644
--- a/radar-commons-android/src/main/java/org/radarbase/android/util/BluetoothStateReceiver.kt
+++ b/radar-commons-android/src/main/java/org/radarbase/android/util/BluetoothStateReceiver.kt
@@ -1,5 +1,6 @@
package org.radarbase.android.util
+import android.Manifest
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothAdapter.STATE_CONNECTED
import android.bluetooth.BluetoothManager
@@ -8,6 +9,9 @@ import android.content.Context
import android.content.Context.BLUETOOTH_SERVICE
import android.content.Intent
import android.content.IntentFilter
+import android.content.pm.PackageManager
+import android.os.Build
+import androidx.core.app.ActivityCompat
import org.slf4j.LoggerFactory
class BluetoothStateReceiver(
@@ -54,7 +58,11 @@ class BluetoothStateReceiver(
@Synchronized
override fun unregister() {
if (isRegistered) {
- context.unregisterReceiver(receiver)
+ try {
+ context.unregisterReceiver(receiver)
+ } catch (ex: Exception) {
+ logger.debug("Failed to unregister BluetoothStateReceiver: {}", ex.toString())
+ }
isRegistered = false
}
}
@@ -67,5 +75,25 @@ class BluetoothStateReceiver(
val Context.bluetoothIsEnabled: Boolean
get() = bluetoothAdapter?.isEnabled == true
+
+ val bluetoothPermissionList: List = buildList {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ add(Manifest.permission.BLUETOOTH_SCAN)
+ add(Manifest.permission.BLUETOOTH_CONNECT)
+ } else {
+ add(Manifest.permission.ACCESS_COARSE_LOCATION)
+ add(Manifest.permission.ACCESS_FINE_LOCATION)
+ add(Manifest.permission.BLUETOOTH)
+ add(Manifest.permission.BLUETOOTH_ADMIN)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ add(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
+ }
+ }
+ }
+
+ val Context.hasBluetoothPermission: Boolean
+ get() = bluetoothPermissionList.all { permission ->
+ ActivityCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED
+ }
}
}
diff --git a/radar-commons-android/src/main/java/org/radarbase/android/util/NotificationHandler.kt b/radar-commons-android/src/main/java/org/radarbase/android/util/NotificationHandler.kt
index 195c13789..f972c5a10 100644
--- a/radar-commons-android/src/main/java/org/radarbase/android/util/NotificationHandler.kt
+++ b/radar-commons-android/src/main/java/org/radarbase/android/util/NotificationHandler.kt
@@ -12,20 +12,21 @@ import android.media.AudioAttributes
import android.media.RingtoneManager
import android.os.Build
import androidx.annotation.RequiresApi
+import androidx.appcompat.content.res.AppCompatResources.getDrawable
+import androidx.core.graphics.drawable.toBitmap
import org.radarbase.android.R
-import org.radarbase.android.RadarApplication
import org.slf4j.LoggerFactory
/**
* Handle notifications and notification channels.
*/
-class NotificationHandler(private val context: Context) {
- private var isCreated: Boolean = false
-
+class NotificationHandler(
+ private val context: Context
+) {
val manager : NotificationManager?
get() = context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager?
- fun onCreate() {
+ init {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannels()
}
@@ -34,12 +35,7 @@ class NotificationHandler(private val context: Context) {
@RequiresApi(api = Build.VERSION_CODES.O)
private fun createNotificationChannels() {
manager?.run {
- synchronized(this) {
- if (isCreated) {
- return
- }
- isCreated = true
- }
+ if (!shouldCreate()) return
logger.debug("Creating notification channels")
createNotificationChannel(NOTIFICATION_CHANNEL_INFO,
NotificationManager.IMPORTANCE_LOW,
@@ -164,10 +160,13 @@ class NotificationHandler(private val context: Context) {
}
private fun Notification.Builder.updateNotificationAppSettings(includeIntent: Boolean) {
- (context.applicationContext as? RadarApplication)?.let { app ->
- setLargeIcon(app.largeIcon)
- setSmallIcon(app.smallIcon)
- }
+ setLargeIcon(
+ getDrawable(context, R.mipmap.ic_launcher)
+ ?.toBitmap()
+ )
+ setSmallIcon(
+ R.drawable.ic_bt_connected
+ )
if (includeIntent) {
val intent = context.packageManager.getLaunchIntentForPackage(context.packageName)
@@ -205,6 +204,18 @@ class NotificationHandler(private val context: Context) {
const val NOTIFICATION_CHANNEL_FINAL_ALERT = "org.radarbase.android.NotificationHandler.FINAL_ALERT"
private const val NOTIFICATION_REQUEST_CODE = 27581
+
+ private val syncObject = Any()
+ private var isCreated = false
+
+ private fun shouldCreate(): Boolean = synchronized(syncObject) {
+ if (!isCreated) {
+ isCreated = true
+ true
+ } else {
+ false
+ }
+ }
}
data class NotificationRegistration(
diff --git a/radar-commons-android/src/main/java/org/radarbase/android/util/PermissionHandler.kt b/radar-commons-android/src/main/java/org/radarbase/android/util/PermissionHandler.kt
index ac3cc695d..517759197 100644
--- a/radar-commons-android/src/main/java/org/radarbase/android/util/PermissionHandler.kt
+++ b/radar-commons-android/src/main/java/org/radarbase/android/util/PermissionHandler.kt
@@ -13,19 +13,17 @@ import android.content.pm.PackageManager.PERMISSION_DENIED
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.location.LocationManager
import android.net.Uri
-import android.os.Build
import android.os.Bundle
import android.os.PowerManager
import android.os.Process
import android.provider.Settings
-import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
-import androidx.lifecycle.LifecycleService
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import org.radarbase.android.R
import org.radarbase.android.RadarService
+import org.radarbase.android.RadarService.Companion.ACCESS_BACKGROUND_LOCATION_COMPAT
import org.slf4j.LoggerFactory
@@ -47,7 +45,7 @@ open class PermissionHandler(
val result = if (granted) PERMISSION_GRANTED else PERMISSION_DENIED
broadcaster.send(RadarService.ACTION_PERMISSIONS_GRANTED) {
- putExtra(RadarService.EXTRA_PERMISSIONS, arrayOf(Context.LOCATION_SERVICE))
+ putExtra(RadarService.EXTRA_PERMISSIONS, arrayOf(LOCATION_SERVICE))
putExtra(RadarService.EXTRA_GRANT_RESULTS, intArrayOf(result))
}
@@ -90,8 +88,24 @@ open class PermissionHandler(
when {
currentlyNeeded.isEmpty() -> logger.info("Already requested all permissions.")
- Context.LOCATION_SERVICE in currentlyNeeded -> {
- addRequestingPermissions(Context.LOCATION_SERVICE)
+ ACCESS_COARSE_LOCATION in currentlyNeeded || ACCESS_FINE_LOCATION in currentlyNeeded -> {
+ val locationPermissions = buildSet(2) {
+ if (ACCESS_COARSE_LOCATION in currentlyNeeded) {
+ add(ACCESS_COARSE_LOCATION)
+ }
+ if (ACCESS_FINE_LOCATION in currentlyNeeded) {
+ add(ACCESS_FINE_LOCATION)
+ }
+ }
+ addRequestingPermissions(locationPermissions)
+ requestLocationPermissions(locationPermissions)
+ }
+ ACCESS_BACKGROUND_LOCATION_COMPAT in currentlyNeeded -> {
+ addRequestingPermissions(ACCESS_BACKGROUND_LOCATION_COMPAT)
+ requestBackgroundLocationPermissions()
+ }
+ LOCATION_SERVICE in currentlyNeeded -> {
+ addRequestingPermissions(LOCATION_SERVICE)
requestLocationProvider()
}
SYSTEM_ALERT_WINDOW in currentlyNeeded -> {
@@ -108,19 +122,41 @@ open class PermissionHandler(
}
else -> {
addRequestingPermissions(currentlyNeeded)
- try {
- ActivityCompat.requestPermissions(
- activity,
- currentlyNeeded.toTypedArray(),
- REQUEST_ENABLE_PERMISSIONS,
- )
- } catch (ex: IllegalStateException) {
- logger.warn("Cannot request permission on closing activity")
- }
+ requestPermissions(currentlyNeeded)
+ }
+ }
+ }
+
+ private fun requestBackgroundLocationPermissions() {
+ requestPermissions(setOf(ACCESS_BACKGROUND_LOCATION_COMPAT))
+ }
+
+ private fun requestLocationPermissions(locationPermissions: Set) {
+ alertDialog {
+ setView(R.layout.location_dialog)
+ setPositiveButton(android.R.string.ok) { dialog, _ ->
+ dialog.dismiss()
+ requestPermissions(locationPermissions)
+ }
+ setNegativeButton(android.R.string.cancel) { dialog, _ ->
+ dialog.cancel()
+ requestPermissions()
}
}
}
+ private fun requestPermissions(permissions: Set) {
+ try {
+ ActivityCompat.requestPermissions(
+ activity,
+ permissions.toTypedArray(),
+ REQUEST_ENABLE_PERMISSIONS,
+ )
+ } catch (ex: IllegalStateException) {
+ logger.warn("Cannot request permission on closing activity")
+ }
+ }
+
private fun resetRequestingPermission() {
isRequestingPermissions.clear()
isRequestingPermissionsTime = java.lang.Long.MAX_VALUE
@@ -146,8 +182,8 @@ open class PermissionHandler(
try {
activity.runOnUiThread {
AlertDialog.Builder(activity, android.R.style.Theme_Material_Dialog_Alert)
- .apply(configure)
- .show()
+ .apply(configure)
+ .show()
}
} catch (ex: IllegalStateException) {
logger.warn("Cannot show dialog on closing activity")
@@ -156,24 +192,36 @@ open class PermissionHandler(
private fun requestLocationProvider() {
alertDialog {
- setTitle(R.string.enable_location_title)
- setMessage(R.string.enable_location)
+ setView(R.layout.location_dialog)
setPositiveButton(android.R.string.ok) { dialog, _ ->
- dialog.cancel()
+ dialog.dismiss()
Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
.startActivityForResult(LOCATION_REQUEST_CODE)
}
- setIcon(android.R.drawable.ic_dialog_alert)
+ setNegativeButton(android.R.string.cancel) { dialog, _ ->
+ dialog.cancel()
+ requestPermissions()
+ }
}
}
private fun requestSystemWindowPermissions() {
// Show alert dialog to the user saying a separate permission is needed
// Launch the settings activity if the user prefers
- Intent(
- Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
- Uri.parse("package:" + activity.packageName)
- ).startActivityForResult(ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE)
+ alertDialog {
+ setView(R.layout.system_window_dialog)
+ setPositiveButton(android.R.string.ok) { dialog, _ ->
+ dialog.dismiss()
+ Intent(
+ Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
+ Uri.parse("package:" + activity.packageName)
+ ).startActivityForResult(ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE)
+ }
+ setNegativeButton(android.R.string.cancel) { dialog, _ ->
+ dialog.cancel()
+ requestPermissions()
+ }
+ }
}
private fun Intent.startActivityForResult(code: Int) {
@@ -189,32 +237,44 @@ open class PermissionHandler(
@SuppressLint("BatteryLife")
private fun requestDisableBatteryOptimization() {
- Intent(
- Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
- Uri.parse("package:" + activity.packageName)
- ).startActivityForResult(BATTERY_OPT_CODE)
+ alertDialog {
+ setView(R.layout.disable_battery_optimization_dialog)
+ setPositiveButton(android.R.string.ok) { dialog, _ ->
+ dialog.dismiss()
+ Intent(
+ Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
+ Uri.parse("package:" + activity.packageName)
+ ).startActivityForResult(BATTERY_OPT_CODE)
+ }
+ setNegativeButton(android.R.string.cancel) { dialog, _ ->
+ dialog.cancel()
+ requestPermissions()
+ }
+ }
}
private fun requestPackageUsageStats() {
alertDialog {
- setTitle(R.string.enable_package_usage_title)
- setMessage(R.string.enable_package_usage)
+ setView(R.layout.usage_tracking_dialog)
setPositiveButton(android.R.string.ok) { dialog, _ ->
- dialog.cancel()
+ dialog.dismiss()
var intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS)
if (intent.resolveActivity(activity.packageManager) == null) {
intent = Intent(Settings.ACTION_SETTINGS)
}
intent.startActivityForResult(USAGE_REQUEST_CODE)
}
- setIcon(android.R.drawable.ic_dialog_alert)
+ setNegativeButton(android.R.string.cancel) { dialog, _ ->
+ dialog.cancel()
+ requestPermissions()
+ }
}
}
fun onActivityResult(requestCode: Int, resultCode: Int) {
when (requestCode) {
LOCATION_REQUEST_CODE -> onPermissionRequestResult(
- Context.LOCATION_SERVICE,
+ LOCATION_SERVICE,
resultCode == Activity.RESULT_OK
)
USAGE_REQUEST_CODE -> onPermissionRequestResult(
@@ -307,7 +367,7 @@ open class PermissionHandler(
private const val ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE = 232619697 and 0xFFFF
fun Context.isPermissionGranted(permission: String): Boolean = when (permission) {
- LOCATION_SERVICE -> applySystemService(Context.LOCATION_SERVICE) { locationManager ->
+ LOCATION_SERVICE -> applySystemService(LOCATION_SERVICE) { locationManager ->
locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
|| locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
} ?: true
diff --git a/radar-commons-android/src/main/res/drawable/baseline_apps_green_700_48dp.xml b/radar-commons-android/src/main/res/drawable/baseline_apps_green_700_48dp.xml
new file mode 100644
index 000000000..e9fdb61f4
--- /dev/null
+++ b/radar-commons-android/src/main/res/drawable/baseline_apps_green_700_48dp.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/radar-commons-android/src/main/res/drawable/baseline_battery_alert_green_700_48dp.xml b/radar-commons-android/src/main/res/drawable/baseline_battery_alert_green_700_48dp.xml
new file mode 100644
index 000000000..dda78069a
--- /dev/null
+++ b/radar-commons-android/src/main/res/drawable/baseline_battery_alert_green_700_48dp.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/radar-commons-android/src/main/res/drawable/baseline_location_on_green_700_48dp.xml b/radar-commons-android/src/main/res/drawable/baseline_location_on_green_700_48dp.xml
new file mode 100644
index 000000000..548eea793
--- /dev/null
+++ b/radar-commons-android/src/main/res/drawable/baseline_location_on_green_700_48dp.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/radar-commons-android/src/main/res/drawable/baseline_warning_yellow_700_48dp.xml b/radar-commons-android/src/main/res/drawable/baseline_warning_yellow_700_48dp.xml
new file mode 100644
index 000000000..81ed5dcb1
--- /dev/null
+++ b/radar-commons-android/src/main/res/drawable/baseline_warning_yellow_700_48dp.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/radar-commons-android/src/main/res/layout/disable_battery_optimization_dialog.xml b/radar-commons-android/src/main/res/layout/disable_battery_optimization_dialog.xml
new file mode 100644
index 000000000..7a4b03af2
--- /dev/null
+++ b/radar-commons-android/src/main/res/layout/disable_battery_optimization_dialog.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/radar-commons-android/src/main/res/layout/location_dialog.xml b/radar-commons-android/src/main/res/layout/location_dialog.xml
new file mode 100644
index 000000000..835227439
--- /dev/null
+++ b/radar-commons-android/src/main/res/layout/location_dialog.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/radar-commons-android/src/main/res/layout/system_window_dialog.xml b/radar-commons-android/src/main/res/layout/system_window_dialog.xml
new file mode 100644
index 000000000..783add06c
--- /dev/null
+++ b/radar-commons-android/src/main/res/layout/system_window_dialog.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/radar-commons-android/src/main/res/layout/usage_tracking_dialog.xml b/radar-commons-android/src/main/res/layout/usage_tracking_dialog.xml
new file mode 100644
index 000000000..104d76640
--- /dev/null
+++ b/radar-commons-android/src/main/res/layout/usage_tracking_dialog.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/radar-commons-android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/radar-commons-android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..036d09bc5
--- /dev/null
+++ b/radar-commons-android/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/radar-commons-android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/radar-commons-android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 000000000..036d09bc5
--- /dev/null
+++ b/radar-commons-android/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/radar-commons-android/src/main/res/mipmap-hdpi/ic_launcher.png b/radar-commons-android/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..7f0b6f262
Binary files /dev/null and b/radar-commons-android/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/radar-commons-android/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/radar-commons-android/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..acbe056db
Binary files /dev/null and b/radar-commons-android/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ
diff --git a/radar-commons-android/src/main/res/mipmap-hdpi/ic_launcher_round.png b/radar-commons-android/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 000000000..e4c2043d7
Binary files /dev/null and b/radar-commons-android/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/radar-commons-android/src/main/res/mipmap-mdpi/ic_launcher.png b/radar-commons-android/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..8abf26e13
Binary files /dev/null and b/radar-commons-android/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/radar-commons-android/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/radar-commons-android/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..dd3acc727
Binary files /dev/null and b/radar-commons-android/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ
diff --git a/radar-commons-android/src/main/res/mipmap-mdpi/ic_launcher_round.png b/radar-commons-android/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 000000000..2d25218a9
Binary files /dev/null and b/radar-commons-android/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/radar-commons-android/src/main/res/mipmap-xhdpi/ic_launcher.png b/radar-commons-android/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..13bbbffa6
Binary files /dev/null and b/radar-commons-android/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/radar-commons-android/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/radar-commons-android/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..dfb48176d
Binary files /dev/null and b/radar-commons-android/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ
diff --git a/radar-commons-android/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/radar-commons-android/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..b6ac8fbad
Binary files /dev/null and b/radar-commons-android/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/radar-commons-android/src/main/res/mipmap-xxhdpi/ic_launcher.png b/radar-commons-android/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..bfff54799
Binary files /dev/null and b/radar-commons-android/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/radar-commons-android/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/radar-commons-android/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..237c7b3c3
Binary files /dev/null and b/radar-commons-android/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ
diff --git a/radar-commons-android/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/radar-commons-android/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..1552bd9d6
Binary files /dev/null and b/radar-commons-android/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/radar-commons-android/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/radar-commons-android/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..6d4112673
Binary files /dev/null and b/radar-commons-android/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/radar-commons-android/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/radar-commons-android/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..6355c5fa4
Binary files /dev/null and b/radar-commons-android/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ
diff --git a/radar-commons-android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/radar-commons-android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..b0e505172
Binary files /dev/null and b/radar-commons-android/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/radar-commons-android/src/main/res/values/strings.xml b/radar-commons-android/src/main/res/values/strings.xml
index fefb968e3..0736c56b5 100644
--- a/radar-commons-android/src/main/res/values/strings.xml
+++ b/radar-commons-android/src/main/res/values/strings.xml
@@ -8,9 +8,37 @@
,
Failed to login to authorization service, please retry
Failed to log in
-
- Location]]>
+
+ pRMT App uses your \"Location in Background\"
+ This app collects location data to enable Bluetooth Low Energy and/or location tracking as
+ part of the study even when the app is closed or not in use.
+ The researcher(s) only use the collected data in the study you consented to.
+
+ pRMT App uses your \"Location\"
+ Location information is needed to enable Bluetooth Low Energy and/or location tracking as part of the study.
+
+ Enable \"Usage tracking\"
+ Enable usage access to keep track of what kind of applications you use.
+ After click \"OK\", select RADAR pRMT from the list and enable \"Usage access\"
+
+ Enable \"Display over other apps\"
+ Allow this app to display on top of other apps.
+ After click \"OK\", select RADAR pRMT from the list and enable \"Display over other apps\".
+
+
+ Enable \"No Restrictions\" for background apps
+ \"No Restrictions for background apps\" means that the battery saver does not restrict app\'s activity.
+ After click \"OK\", select \"No restrictions\".
+
+ Location information is needed to enable Bluetooth Low Energy and/or location tracking as part of the study.
+
+ Background location is needed to keep passively collecting data while the app is in the background, either for Bluetooth devices or for actual, relative location.
+
+
+
+ Location]]>
Enable usage access to keep track of what kind of applications you use.
+
Enable location tracking
Enable usage tracking