@@ -71,6 +71,8 @@ public final class SystemEventsBreadcrumbsIntegration implements Integration, Cl
7171 private volatile boolean isStopped = false ;
7272 private volatile IntentFilter filter = null ;
7373 private final @ NotNull AutoClosableReentrantLock receiverLock = new AutoClosableReentrantLock ();
74+ // Track previous battery state to avoid duplicate breadcrumbs when values haven't changed
75+ private @ Nullable BatteryState previousBatteryState ;
7476
7577 public SystemEventsBreadcrumbsIntegration (final @ NotNull Context context ) {
7678 this (context , getDefaultActionsInternal ());
@@ -331,7 +333,7 @@ public void onStop(@NonNull LifecycleOwner owner) {
331333 }
332334 }
333335
334- static final class SystemEventsBroadcastReceiver extends BroadcastReceiver {
336+ final class SystemEventsBroadcastReceiver extends BroadcastReceiver {
335337
336338 private static final long DEBOUNCE_WAIT_TIME_MS = 60 * 1000 ;
337339 private final @ NotNull IScopes scopes ;
@@ -350,19 +352,36 @@ public void onReceive(final Context context, final @NotNull Intent intent) {
350352 final @ Nullable String action = intent .getAction ();
351353 final boolean isBatteryChanged = ACTION_BATTERY_CHANGED .equals (action );
352354
353- // aligning with iOS which only captures battery status changes every minute at maximum
354- if (isBatteryChanged && batteryChangedDebouncer .checkForDebounce ()) {
355- return ;
355+ @ Nullable BatteryState batteryState = null ;
356+ if (isBatteryChanged ) {
357+ if (batteryChangedDebouncer .checkForDebounce ()) {
358+ // aligning with iOS which only captures battery status changes every minute at maximum
359+ return ;
360+ }
361+
362+ // For battery changes, check if the actual values have changed
363+ final @ Nullable Float batteryLevel = DeviceInfoUtil .getBatteryLevel (intent , options );
364+ final @ Nullable Integer currentBatteryLevel =
365+ batteryLevel != null ? batteryLevel .intValue () : null ;
366+ final @ Nullable Boolean currentChargingState = DeviceInfoUtil .isCharging (intent , options );
367+ batteryState = new BatteryState (currentBatteryLevel , currentChargingState );
368+
369+ // Only create breadcrumb if battery state has actually changed
370+ if (batteryState .equals (previousBatteryState )) {
371+ return ;
372+ }
373+
374+ previousBatteryState = batteryState ;
356375 }
357376
377+ final BatteryState state = batteryState ;
358378 final long now = System .currentTimeMillis ();
359379 try {
360380 options
361381 .getExecutorService ()
362382 .submit (
363383 () -> {
364- final Breadcrumb breadcrumb =
365- createBreadcrumb (now , intent , action , isBatteryChanged );
384+ final Breadcrumb breadcrumb = createBreadcrumb (now , intent , action , state );
366385 final Hint hint = new Hint ();
367386 hint .set (ANDROID_INTENT , intent );
368387 scopes .addBreadcrumb (breadcrumb , hint );
@@ -411,7 +430,7 @@ String getStringAfterDotFast(final @Nullable String str) {
411430 final long timeMs ,
412431 final @ NotNull Intent intent ,
413432 final @ Nullable String action ,
414- boolean isBatteryChanged ) {
433+ final @ Nullable BatteryState batteryState ) {
415434 final Breadcrumb breadcrumb = new Breadcrumb (timeMs );
416435 breadcrumb .setType ("system" );
417436 breadcrumb .setCategory ("device.event" );
@@ -420,14 +439,12 @@ String getStringAfterDotFast(final @Nullable String str) {
420439 breadcrumb .setData ("action" , shortAction );
421440 }
422441
423- if (isBatteryChanged ) {
424- final Float batteryLevel = DeviceInfoUtil .getBatteryLevel (intent , options );
425- if (batteryLevel != null ) {
426- breadcrumb .setData ("level" , batteryLevel );
442+ if (batteryState != null ) {
443+ if (batteryState .level != null ) {
444+ breadcrumb .setData ("level" , batteryState .level );
427445 }
428- final Boolean isCharging = DeviceInfoUtil .isCharging (intent , options );
429- if (isCharging != null ) {
430- breadcrumb .setData ("charging" , isCharging );
446+ if (batteryState .charging != null ) {
447+ breadcrumb .setData ("charging" , batteryState .charging );
431448 }
432449 } else {
433450 final Bundle extras = intent .getExtras ();
@@ -458,4 +475,26 @@ String getStringAfterDotFast(final @Nullable String str) {
458475 return breadcrumb ;
459476 }
460477 }
478+
479+ static final class BatteryState {
480+ private final @ Nullable Integer level ;
481+ private final @ Nullable Boolean charging ;
482+
483+ BatteryState (final @ Nullable Integer level , final @ Nullable Boolean charging ) {
484+ this .level = level ;
485+ this .charging = charging ;
486+ }
487+
488+ @ Override
489+ public boolean equals (final @ Nullable Object other ) {
490+ if (!(other instanceof BatteryState )) return false ;
491+ BatteryState that = (BatteryState ) other ;
492+ return Objects .equals (level , that .level ) && Objects .equals (charging , that .charging );
493+ }
494+
495+ @ Override
496+ public int hashCode () {
497+ return Objects .hash (level , charging );
498+ }
499+ }
461500}
0 commit comments