Skip to content

Commit e83db32

Browse files
authored
Merge pull request #2815 from DataDog/jmoskovich/rum-11120/battery-attributes
RUM-11120: Add battery and display attributes
2 parents c6b7a43 + c0de10f commit e83db32

37 files changed

+1355
-471
lines changed

detekt_custom.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ datadog:
137137
- "android.graphics.drawable.LayerDrawable.getDrawable(kotlin.Int):java.lang.IndexOutOfBoundsException"
138138
- "android.net.ConnectivityManager.registerDefaultNetworkCallback(android.net.ConnectivityManager.NetworkCallback):java.lang.IllegalArgumentException,java.lang.SecurityException"
139139
- "android.net.ConnectivityManager.unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback):java.lang.SecurityException"
140+
- "android.provider.Settings.System.getInt(android.content.ContentResolver?, kotlin.String?):android.provider.Settings.SettingNotFoundException"
140141
- "android.util.Base64.encodeToString(kotlin.ByteArray?, kotlin.Int):java.lang.AssertionError"
141142
- "android.view.Choreographer.getInstance():java.lang.IllegalStateException"
142143
- "android.view.Choreographer.postFrameCallback():java.lang.IllegalArgumentException"
@@ -389,6 +390,7 @@ datadog:
389390
- "android.net.ConnectivityManager.NetworkCallback.onLost(android.net.Network)"
390391
- "android.net.ConnectivityManager.getNetworkCapabilities(android.net.Network?)"
391392
- "android.net.NetworkCapabilities.hasTransport(kotlin.Int)"
393+
- "android.os.BatteryManager.getIntProperty(kotlin.Int)"
392394
- "android.os.Bundle.get(kotlin.String?)"
393395
- "android.os.Bundle.getString(kotlin.String?)"
394396
- "android.os.Bundle.keySet()"
@@ -411,6 +413,7 @@ datadog:
411413
- "android.os.SystemClock.elapsedRealtime()"
412414
- "android.provider.Settings.Global.getUriFor(kotlin.String?)"
413415
- "android.provider.Settings.Secure.getUriFor(kotlin.String?)"
416+
- "android.provider.Settings.System.getUriFor(kotlin.String?)"
414417
- "android.util.Log.e(kotlin.String?, kotlin.String)"
415418
- "android.util.Log.e(kotlin.String?, kotlin.String?, kotlin.Throwable?)"
416419
- "android.util.Log.getStackTraceString(kotlin.Throwable?)"

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/Rum.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,9 @@ object Rum {
141141
lastInteractionIdentifier = rumFeature.lastInteractionIdentifier,
142142
slowFramesListener = rumFeature.slowFramesListener,
143143
rumSessionTypeOverride = rumFeature.configuration.rumSessionTypeOverride,
144-
accessibilitySnapshotManager = rumFeature.accessibilitySnapshotManager
144+
accessibilitySnapshotManager = rumFeature.accessibilitySnapshotManager,
145+
batteryInfoProvider = rumFeature.batteryInfoProvider,
146+
displayInfoProvider = rumFeature.displayInfoProvider
145147
)
146148
}
147149

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/RumFeature.kt

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,20 @@ import com.datadog.android.rum.configuration.SlowFramesConfiguration
4242
import com.datadog.android.rum.configuration.VitalsUpdateFrequency
4343
import com.datadog.android.rum.internal.anr.ANRDetectorRunnable
4444
import com.datadog.android.rum.internal.debug.UiRumDebugListener
45+
import com.datadog.android.rum.internal.domain.InfoProvider
4546
import com.datadog.android.rum.internal.domain.RumDataWriter
46-
import com.datadog.android.rum.internal.domain.accessibility.AccessibilityReader
47+
import com.datadog.android.rum.internal.domain.accessibility.AccessibilityInfo
4748
import com.datadog.android.rum.internal.domain.accessibility.AccessibilitySnapshotManager
48-
import com.datadog.android.rum.internal.domain.accessibility.DatadogAccessibilityReader
49+
import com.datadog.android.rum.internal.domain.accessibility.DefaultAccessibilityReader
4950
import com.datadog.android.rum.internal.domain.accessibility.DefaultAccessibilitySnapshotManager
5051
import com.datadog.android.rum.internal.domain.accessibility.NoOpAccessibilityReader
5152
import com.datadog.android.rum.internal.domain.accessibility.NoOpAccessibilitySnapshotManager
53+
import com.datadog.android.rum.internal.domain.battery.BatteryInfo
54+
import com.datadog.android.rum.internal.domain.battery.DefaultBatteryInfoProvider
55+
import com.datadog.android.rum.internal.domain.battery.NoOpBatteryInfoProvider
56+
import com.datadog.android.rum.internal.domain.display.DefaultDisplayInfoProvider
57+
import com.datadog.android.rum.internal.domain.display.DisplayInfo
58+
import com.datadog.android.rum.internal.domain.display.NoOpDisplayInfoProvider
5259
import com.datadog.android.rum.internal.domain.event.RumEventMapper
5360
import com.datadog.android.rum.internal.domain.event.RumEventMetaDeserializer
5461
import com.datadog.android.rum.internal.domain.event.RumEventMetaSerializer
@@ -151,9 +158,11 @@ internal class RumFeature(
151158
internal var initialResourceIdentifier: InitialResourceIdentifier = NoOpInitialResourceIdentifier()
152159
internal var lastInteractionIdentifier: LastInteractionIdentifier? = NoOpLastInteractionIdentifier()
153160
internal var slowFramesListener: SlowFramesListener? = null
154-
internal var accessibilityReader: AccessibilityReader = NoOpAccessibilityReader()
161+
internal var accessibilityReader: InfoProvider<AccessibilityInfo> = NoOpAccessibilityReader()
155162
internal var accessibilitySnapshotManager: AccessibilitySnapshotManager =
156163
NoOpAccessibilitySnapshotManager()
164+
internal var batteryInfoProvider: InfoProvider<BatteryInfo> = NoOpBatteryInfoProvider()
165+
internal var displayInfoProvider: InfoProvider<DisplayInfo> = NoOpDisplayInfoProvider()
157166

158167
private val lateCrashEventHandler by lazy { lateCrashReporterFactory(sdkCore as InternalSdkCore) }
159168

@@ -166,8 +175,10 @@ internal class RumFeature(
166175
this.appContext = appContext
167176

168177
if (configuration.collectAccessibility) {
169-
accessibilityReader =
170-
DatadogAccessibilityReader(applicationContext = appContext, internalLogger = sdkCore.internalLogger)
178+
accessibilityReader = DefaultAccessibilityReader(
179+
internalLogger = sdkCore.internalLogger,
180+
applicationContext = appContext
181+
)
171182
accessibilitySnapshotManager = DefaultAccessibilitySnapshotManager(accessibilityReader)
172183
}
173184

@@ -193,6 +204,13 @@ internal class RumFeature(
193204
telemetryConfigurationSampleRate = configuration.telemetryConfigurationSampleRate
194205
backgroundEventTracking = configuration.backgroundEventTracking
195206
trackFrustrations = configuration.trackFrustrations
207+
batteryInfoProvider = DefaultBatteryInfoProvider(
208+
applicationContext = appContext
209+
)
210+
displayInfoProvider = DefaultDisplayInfoProvider(
211+
applicationContext = appContext,
212+
internalLogger = sdkCore.internalLogger
213+
)
196214

197215
configuration.viewTrackingStrategy?.let { viewTrackingStrategy = it }
198216
actionTrackingStrategy = if (configuration.userActionTracking) {
@@ -306,17 +324,26 @@ internal class RumFeature(
306324
vitalExecutorService = NoOpScheduledExecutorService()
307325
sessionListener = NoOpRumSessionListener()
308326

327+
cleanupInfoProviders()
328+
329+
GlobalRumMonitor.unregister(sdkCore)
330+
}
331+
332+
// endregion
333+
334+
private fun cleanupInfoProviders() {
309335
if (configuration.collectAccessibility) {
310336
accessibilityReader.cleanup()
311337
accessibilityReader = NoOpAccessibilityReader()
312338
accessibilitySnapshotManager = NoOpAccessibilitySnapshotManager()
313339
}
314340

315-
GlobalRumMonitor.unregister(sdkCore)
341+
batteryInfoProvider.cleanup()
342+
batteryInfoProvider = NoOpBatteryInfoProvider()
343+
displayInfoProvider.cleanup()
344+
displayInfoProvider = NoOpDisplayInfoProvider()
316345
}
317346

318-
// endregion
319-
320347
private fun createDataWriter(
321348
configuration: Configuration,
322349
sdkCore: InternalSdkCore
Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,11 @@
44
* Copyright 2016-Present Datadog, Inc.
55
*/
66

7-
package com.datadog.android.rum.internal.domain.accessibility
7+
package com.datadog.android.rum.internal.domain
88

9-
import com.datadog.tools.annotation.NoOpImplementation
9+
internal interface InfoData
1010

11-
@NoOpImplementation
12-
internal interface AccessibilityReader {
13-
fun getState(): Map<String, Any>
11+
internal interface InfoProvider<T : InfoData> {
12+
fun getState(): T
1413
fun cleanup()
1514
}

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/accessibility/Accessibility.kt

Lines changed: 0 additions & 60 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
* Copyright 2016-Present Datadog, Inc.
5+
*/
6+
7+
package com.datadog.android.rum.internal.domain.accessibility
8+
9+
import com.datadog.android.rum.internal.domain.InfoData
10+
11+
/**
12+
* Represents the accessibility settings state of the device.
13+
*
14+
* @property textSize The font scale factor (1.0 = normal, >1.0 = larger, <1.0 = smaller)
15+
* @property isScreenReaderEnabled Whether touch exploration is enabled (TalkBack, etc.)
16+
* @property isColorInversionEnabled Whether color inversion is enabled
17+
* @property isClosedCaptioningEnabled Whether closed captions are enabled
18+
* @property isReducedAnimationsEnabled Whether animations are disabled/reduced
19+
* @property isScreenPinningEnabled Whether the device is in single-app mode
20+
* @property isRtlEnabled Whether right to left layout is enabled
21+
*/
22+
internal data class AccessibilityInfo(
23+
val textSize: String? = null,
24+
val isScreenReaderEnabled: Boolean? = null,
25+
val isColorInversionEnabled: Boolean? = null,
26+
val isClosedCaptioningEnabled: Boolean? = null,
27+
val isReducedAnimationsEnabled: Boolean? = null,
28+
val isScreenPinningEnabled: Boolean? = null,
29+
val isRtlEnabled: Boolean? = null
30+
) : InfoData

features/dd-sdk-android-rum/src/main/kotlin/com/datadog/android/rum/internal/domain/accessibility/AccessibilitySnapshotManager.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ import com.datadog.tools.annotation.NoOpImplementation
1010

1111
@NoOpImplementation
1212
internal interface AccessibilitySnapshotManager {
13-
fun latestSnapshot(): Accessibility
13+
fun latestSnapshot(): AccessibilityInfo
1414
}
Lines changed: 29 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,11 @@ import android.view.View
2222
import android.view.accessibility.AccessibilityManager
2323
import android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener
2424
import com.datadog.android.api.InternalLogger
25-
import java.util.concurrent.atomic.AtomicBoolean
25+
import com.datadog.android.rum.internal.domain.InfoProvider
2626
import java.util.concurrent.atomic.AtomicLong
2727

2828
@Suppress("TooManyFunctions")
29-
internal class DatadogAccessibilityReader(
29+
internal class DefaultAccessibilityReader(
3030
private val internalLogger: InternalLogger,
3131
private val applicationContext: Context,
3232
private val resources: Resources = applicationContext.resources,
@@ -37,14 +37,7 @@ internal class DatadogAccessibilityReader(
3737
private val secureWrapper: SecureWrapper = SecureWrapper(),
3838
private val globalWrapper: GlobalWrapper = GlobalWrapper(),
3939
private val handler: Handler = Handler(Looper.getMainLooper())
40-
) : AccessibilityReader, ComponentCallbacks {
41-
42-
@Volatile
43-
private var currentState = Accessibility()
44-
45-
private var lastPollTime: AtomicLong = AtomicLong(0)
46-
47-
private var isInitialized = AtomicBoolean(false)
40+
) : InfoProvider<AccessibilityInfo>, ComponentCallbacks {
4841

4942
private val displayInversionListener = object : ContentObserver(handler) {
5043
override fun onChange(selfChange: Boolean, uri: Uri?) {
@@ -72,15 +65,22 @@ internal class DatadogAccessibilityReader(
7265
updateState { it.copy(isScreenReaderEnabled = newScreenReaderEnabled) }
7366
}
7467

68+
@Volatile
69+
private var currentState = AccessibilityInfo()
70+
71+
private var lastPollTime: AtomicLong = AtomicLong(0)
72+
73+
init {
74+
registerListeners()
75+
buildInitialState()
76+
}
77+
7578
override fun cleanup() {
76-
if (isInitialized.get()) {
77-
accessibilityManager?.removeTouchExplorationStateChangeListener(touchListener)
78-
applicationContext.contentResolver.unregisterContentObserver(animationDurationListener)
79-
applicationContext.contentResolver.unregisterContentObserver(captioningListener)
80-
applicationContext.contentResolver.unregisterContentObserver(displayInversionListener)
81-
applicationContext.unregisterComponentCallbacks(this)
82-
isInitialized.set(false)
83-
}
79+
accessibilityManager?.removeTouchExplorationStateChangeListener(touchListener)
80+
applicationContext.contentResolver.unregisterContentObserver(animationDurationListener)
81+
applicationContext.contentResolver.unregisterContentObserver(captioningListener)
82+
applicationContext.contentResolver.unregisterContentObserver(displayInversionListener)
83+
applicationContext.unregisterComponentCallbacks(this)
8484
}
8585

8686
override fun onLowMemory() {
@@ -96,34 +96,24 @@ internal class DatadogAccessibilityReader(
9696
}
9797

9898
@Synchronized
99-
override fun getState(): Map<String, Any> {
100-
ensureInitialized()
101-
99+
override fun getState(): AccessibilityInfo {
102100
val currentTime = System.currentTimeMillis()
103101
val shouldPoll = currentTime - lastPollTime.get() >= POLL_THRESHOLD
104102
if (shouldPoll) {
105103
lastPollTime.set(currentTime)
106104
pollForAttributesWithoutListeners()
107105
}
108106

109-
return currentState.toMap()
107+
return currentState
110108
}
111109

112110
@Synchronized
113-
private fun updateState(updater: (Accessibility) -> Accessibility) {
111+
private fun updateState(updater: (AccessibilityInfo) -> AccessibilityInfo) {
114112
currentState = updater(currentState)
115113
}
116114

117-
private fun ensureInitialized() {
118-
if (!isInitialized.get()) {
119-
registerListeners()
120-
currentState = buildInitialState()
121-
isInitialized.set(true)
122-
}
123-
}
124-
125-
private fun buildInitialState(): Accessibility {
126-
return Accessibility(
115+
private fun buildInitialState() {
116+
currentState = AccessibilityInfo(
127117
textSize = getTextSize(),
128118
isScreenReaderEnabled = isScreenReaderEnabled(accessibilityManager),
129119
isColorInversionEnabled = isDisplayInversionEnabled(),
@@ -159,11 +149,11 @@ internal class DatadogAccessibilityReader(
159149
}
160150

161151
private fun isDisplayInversionEnabled(): Boolean? {
162-
return getSecureInt(ACCESSIBILITY_DISPLAY_INVERSION_ENABLED)
152+
return isSettingEnabled(ACCESSIBILITY_DISPLAY_INVERSION_ENABLED)
163153
}
164154

165155
private fun isClosedCaptioningEnabled(): Boolean? {
166-
return getSecureInt(CAPTIONING_ENABLED_KEY)
156+
return isSettingEnabled(CAPTIONING_ENABLED_KEY)
167157
}
168158

169159
private fun isLockToScreenEnabled(): Boolean? {
@@ -187,12 +177,14 @@ internal class DatadogAccessibilityReader(
187177
}
188178
}
189179

190-
private fun getSecureInt(key: String): Boolean? {
180+
private fun isSettingEnabled(key: String): Boolean? {
191181
return secureWrapper.getInt(
192182
internalLogger = internalLogger,
193183
applicationContext = applicationContext,
194184
key = key
195-
)
185+
)?.let {
186+
it == 1
187+
}
196188
}
197189

198190
private fun getTextSize(): String {

0 commit comments

Comments
 (0)