@capgo/native-audio 8.2.12 → 8.2.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -19,6 +19,7 @@ import androidx.media3.exoplayer.source.ProgressiveMediaSource;
19
19
  import java.io.File;
20
20
  import java.util.ArrayList;
21
21
  import java.util.Map;
22
+ import java.util.concurrent.TimeUnit;
22
23
 
23
24
  @UnstableApi
24
25
  public class RemoteAudioAsset extends AudioAsset {
@@ -67,7 +68,7 @@ public class RemoteAudioAsset extends AudioAsset {
67
68
  initializePlayer(player);
68
69
  }
69
70
  } catch (Exception e) {
70
- Log.e(TAG, "Error initializing players", e);
71
+ logger.error("Error initializing players", e);
71
72
  }
72
73
  }
73
74
  }
@@ -76,7 +77,7 @@ public class RemoteAudioAsset extends AudioAsset {
76
77
 
77
78
  @UnstableApi
78
79
  private void initializePlayer(ExoPlayer player) {
79
- Log.d(TAG, "Initializing player");
80
+ logger.debug("Initializing player");
80
81
 
81
82
  // Initialize cache if not already done
82
83
  if (cache == null) {
@@ -136,17 +137,17 @@ public class RemoteAudioAsset extends AudioAsset {
136
137
 
137
138
  @Override
138
139
  public void onIsPlayingChanged(boolean isPlaying) {
139
- Log.d(TAG, "isPlaying changed to: " + isPlaying + ", state: " + getStateString(player.getPlaybackState()));
140
+ logger.debug("isPlaying changed to: " + isPlaying + ", state: " + getStateString(player.getPlaybackState()));
140
141
  }
141
142
 
142
143
  @Override
143
144
  public void onIsLoadingChanged(boolean isLoading) {
144
- Log.d(TAG, "isLoading changed to: " + isLoading + ", state: " + getStateString(player.getPlaybackState()));
145
+ logger.debug("isLoading changed to: " + isLoading + ", state: " + getStateString(player.getPlaybackState()));
145
146
  }
146
147
  }
147
148
  );
148
149
 
149
- Log.d(TAG, "Player initialization complete");
150
+ logger.debug("Player initialization complete");
150
151
  }
151
152
 
152
153
  private String getStateString(int state) {
@@ -165,7 +166,7 @@ public class RemoteAudioAsset extends AudioAsset {
165
166
  }
166
167
 
167
168
  @Override
168
- public void play(Double time) throws Exception {
169
+ public void play(double time, float volume) throws Exception {
169
170
  if (players.isEmpty()) {
170
171
  throw new Exception("No ExoPlayer available");
171
172
  }
@@ -185,7 +186,7 @@ public class RemoteAudioAsset extends AudioAsset {
185
186
  if (playbackState == Player.STATE_READY) {
186
187
  isPrepared = true;
187
188
  try {
188
- playInternal(player, time);
189
+ playInternal(player, time, volume);
189
190
  startCurrentTimeUpdates();
190
191
  } catch (Exception e) {
191
192
  Log.e(TAG, "Error playing after prepare", e);
@@ -198,10 +199,10 @@ public class RemoteAudioAsset extends AudioAsset {
198
199
  );
199
200
  } else {
200
201
  try {
201
- playInternal(player, time);
202
+ playInternal(player, time, volume);
202
203
  startCurrentTimeUpdates();
203
204
  } catch (Exception e) {
204
- Log.e(TAG, "Error playing", e);
205
+ logger.error("Error playing", e);
205
206
  }
206
207
  }
207
208
  }
@@ -211,16 +212,19 @@ public class RemoteAudioAsset extends AudioAsset {
211
212
  playIndex = (playIndex + 1) % players.size();
212
213
  }
213
214
 
214
- private void playInternal(final ExoPlayer player, final Double time) throws Exception {
215
+ private void playInternal(final ExoPlayer player, final double time, final float volume) throws Exception {
215
216
  owner
216
217
  .getActivity()
217
218
  .runOnUiThread(
218
219
  new Runnable() {
219
220
  @Override
220
221
  public void run() {
221
- if (time != null) {
222
+ if (time != 0) {
222
223
  player.seekTo(Math.round(time * 1000));
223
224
  }
225
+ if (volume != 0) {
226
+ player.setVolume(volume);
227
+ }
224
228
  player.play();
225
229
  }
226
230
  }
@@ -236,9 +240,11 @@ public class RemoteAudioAsset extends AudioAsset {
236
240
  new Runnable() {
237
241
  @Override
238
242
  public void run() {
243
+ cancelFade();
239
244
  for (ExoPlayer player : players) {
240
- if (player.isPlaying()) {
245
+ if (player != null && player.isPlaying()) {
241
246
  player.pause();
247
+ stopCurrentTimeUpdates();
242
248
  wasPlaying[0] = true;
243
249
  }
244
250
  }
@@ -257,7 +263,7 @@ public class RemoteAudioAsset extends AudioAsset {
257
263
  @Override
258
264
  public void run() {
259
265
  for (ExoPlayer player : players) {
260
- if (!player.isPlaying()) {
266
+ if (player != null && !player.isPlaying()) {
261
267
  player.play();
262
268
  }
263
269
  }
@@ -275,9 +281,11 @@ public class RemoteAudioAsset extends AudioAsset {
275
281
  new Runnable() {
276
282
  @Override
277
283
  public void run() {
284
+ cancelFade();
278
285
  for (ExoPlayer player : players) {
279
- if (player.isPlaying()) {
286
+ if (player != null && player.isPlaying()) {
280
287
  player.stop();
288
+ dispatchComplete();
281
289
  }
282
290
  // Reset the ExoPlayer to make it ready for future playback
283
291
  initializePlayer(player);
@@ -353,7 +361,7 @@ public class RemoteAudioAsset extends AudioAsset {
353
361
  }
354
362
 
355
363
  @Override
356
- public void setVolume(final float volume) throws Exception {
364
+ public void setVolume(final float volume, final double duration) throws Exception {
357
365
  this.volume = volume;
358
366
  owner
359
367
  .getActivity()
@@ -361,14 +369,35 @@ public class RemoteAudioAsset extends AudioAsset {
361
369
  new Runnable() {
362
370
  @Override
363
371
  public void run() {
372
+ cancelFade();
364
373
  for (ExoPlayer player : players) {
365
- player.setVolume(volume);
374
+ if (player == null) continue;
375
+ if (player.isPlaying() && duration > 0) {
376
+ fadeTo(player, (float) duration, volume);
377
+ } else {
378
+ player.setVolume(volume);
379
+ }
366
380
  }
367
381
  }
368
382
  }
369
383
  );
370
384
  }
371
385
 
386
+ @Override
387
+ public void setVolume(final float volume) throws Exception {
388
+ setVolume(volume, 0);
389
+ }
390
+
391
+ @Override
392
+ public float getVolume() throws Exception {
393
+ if (players.isEmpty()) {
394
+ throw new Exception("No ExoPlayer available");
395
+ }
396
+
397
+ final ExoPlayer player = players.get(playIndex);
398
+ return player != null ? player.getVolume() : 0;
399
+ }
400
+
372
401
  @Override
373
402
  public boolean isPlaying() throws Exception {
374
403
  if (players.isEmpty() || !isPrepared) return false;
@@ -379,7 +408,7 @@ public class RemoteAudioAsset extends AudioAsset {
379
408
 
380
409
  @Override
381
410
  public double getDuration() {
382
- Log.d(TAG, "getDuration called, players empty: " + players.isEmpty() + ", isPrepared: " + isPrepared);
411
+ logger.debug("getDuration called, players empty: " + players.isEmpty() + ", isPrepared: " + isPrepared);
383
412
  if (!players.isEmpty() && isPrepared) {
384
413
  final double[] duration = { 0 };
385
414
  owner
@@ -390,25 +419,25 @@ public class RemoteAudioAsset extends AudioAsset {
390
419
  public void run() {
391
420
  ExoPlayer player = players.get(playIndex);
392
421
  int state = player.getPlaybackState();
393
- Log.d(TAG, "Player state: " + state + " (READY=" + Player.STATE_READY + ")");
422
+ logger.debug("Player state: " + state + " (READY=" + Player.STATE_READY + ")");
394
423
  if (state == Player.STATE_READY) {
395
424
  long rawDuration = player.getDuration();
396
- Log.d(TAG, "Raw duration: " + rawDuration + ", TIME_UNSET=" + androidx.media3.common.C.TIME_UNSET);
425
+ logger.debug("Raw duration: " + rawDuration + ", TIME_UNSET=" + androidx.media3.common.C.TIME_UNSET);
397
426
  if (rawDuration != androidx.media3.common.C.TIME_UNSET) {
398
427
  duration[0] = rawDuration / 1000.0;
399
- Log.d(TAG, "Final duration in seconds: " + duration[0]);
428
+ logger.debug("Final duration in seconds: " + duration[0]);
400
429
  } else {
401
- Log.d(TAG, "Duration is TIME_UNSET");
430
+ logger.debug("Duration is TIME_UNSET");
402
431
  }
403
432
  } else {
404
- Log.d(TAG, "Player not in READY state");
433
+ logger.debug("Player not in READY state");
405
434
  }
406
435
  }
407
436
  }
408
437
  );
409
438
  return duration[0];
410
439
  }
411
- Log.d(TAG, "No players or not prepared for duration");
440
+ logger.debug("No players or not prepared for duration");
412
441
  return 0;
413
442
  }
414
443
 
@@ -425,7 +454,7 @@ public class RemoteAudioAsset extends AudioAsset {
425
454
  ExoPlayer player = players.get(playIndex);
426
455
  if (player.getPlaybackState() == Player.STATE_READY) {
427
456
  long rawPosition = player.getCurrentPosition();
428
- Log.d(TAG, "Raw position: " + rawPosition);
457
+ logger.debug("Raw position: " + rawPosition);
429
458
  position[0] = rawPosition / 1000.0;
430
459
  }
431
460
  }
@@ -481,7 +510,7 @@ public class RemoteAudioAsset extends AudioAsset {
481
510
  deleteDir(cacheDir);
482
511
  }
483
512
  } catch (Exception e) {
484
- Log.e(TAG, "Error clearing audio cache", e);
513
+ logger.error("Error clearing audio cache", e);
485
514
  }
486
515
  }
487
516
 
@@ -500,17 +529,7 @@ public class RemoteAudioAsset extends AudioAsset {
500
529
  return dir.delete();
501
530
  }
502
531
 
503
- public void setCompletionListener(AudioCompletionListener listener) {
504
- this.completionListener = listener;
505
- }
506
-
507
- protected void notifyCompletion() {
508
- if (completionListener != null) {
509
- completionListener.onCompletion(getAssetId());
510
- }
511
- }
512
-
513
- public void playWithFade(Double time) throws Exception {
532
+ public void playWithFadeIn(double time, float volume, float fadeInDurationMs) throws Exception {
514
533
  if (players.isEmpty()) {
515
534
  throw new Exception("No ExoPlayer available");
516
535
  }
@@ -522,38 +541,78 @@ public class RemoteAudioAsset extends AudioAsset {
522
541
  new Runnable() {
523
542
  @Override
524
543
  public void run() {
525
- if (!player.isPlaying()) {
526
- if (time != null) {
544
+ if (player != null && !player.isPlaying()) {
545
+ if (time != 0) {
527
546
  player.seekTo(Math.round(time * 1000));
528
547
  }
529
548
  player.setVolume(0);
530
549
  player.play();
531
550
  startCurrentTimeUpdates();
532
- fadeIn(player);
551
+ fadeIn(player, fadeInDurationMs, volume);
533
552
  }
534
553
  }
535
554
  }
536
555
  );
537
556
  }
538
557
 
539
- private void fadeIn(final ExoPlayer player) {
540
- final Handler handler = new Handler(Looper.getMainLooper());
541
- final Runnable fadeRunnable = new Runnable() {
542
- float currentVolume = 0;
558
+ private void fadeIn(final ExoPlayer player, float fadeInDurationMs, float volume) {
559
+ cancelFade();
560
+ fadeState = FadeState.FADE_IN;
561
+
562
+ final float targetVolume = volume;
563
+ final int steps = Math.max(1, (int) (fadeInDurationMs / FADE_DELAY_MS));
564
+ final float fadeStep = targetVolume / steps;
565
+
566
+ Log.d(
567
+ TAG,
568
+ "Beginning fade in at time " +
569
+ getCurrentPosition() +
570
+ " over " +
571
+ (fadeInDurationMs / 1000.0) +
572
+ "s to target volume " +
573
+ targetVolume +
574
+ " in " +
575
+ steps +
576
+ " steps (step duration: " +
577
+ (FADE_DELAY_MS / 1000.0) +
578
+ "s"
579
+ );
543
580
 
544
- @Override
545
- public void run() {
546
- if (currentVolume < initialVolume) {
547
- currentVolume += FADE_STEP;
548
- player.setVolume(currentVolume);
549
- handler.postDelayed(this, FADE_DELAY_MS);
581
+ fadeTask = fadeExecutor.scheduleWithFixedDelay(
582
+ new Runnable() {
583
+ float currentVolume = 0;
584
+
585
+ @Override
586
+ public void run() {
587
+ if (fadeState != FadeState.FADE_IN || currentVolume >= targetVolume) {
588
+ fadeState = FadeState.NONE;
589
+ cancelFade();
590
+ logger.debug("Fade in complete at time " + getCurrentPosition());
591
+ return;
592
+ }
593
+ final float previousCurrentVolume = currentVolume;
594
+ currentVolume += fadeStep;
595
+ final float resolvedTargetVolume = Math.min(currentVolume, targetVolume);
596
+ Log.v(
597
+ TAG,
598
+ "Fade in step: from " + previousCurrentVolume + " to " + currentVolume + " to target " + resolvedTargetVolume
599
+ );
600
+ owner
601
+ .getActivity()
602
+ .runOnUiThread(() -> {
603
+ if (player != null && player.isPlaying()) {
604
+ player.setVolume(currentVolume);
605
+ }
606
+ });
550
607
  }
551
- }
552
- };
553
- handler.post(fadeRunnable);
608
+ },
609
+ 0,
610
+ FADE_DELAY_MS,
611
+ TimeUnit.MILLISECONDS
612
+ );
554
613
  }
555
614
 
556
- public void stopWithFade() throws Exception {
615
+ public void stopWithFade(float fadeOutDurationMs, boolean asPause) throws Exception {
557
616
  if (players.isEmpty()) {
558
617
  return;
559
618
  }
@@ -561,53 +620,194 @@ public class RemoteAudioAsset extends AudioAsset {
561
620
  final ExoPlayer player = players.get(playIndex);
562
621
  owner
563
622
  .getActivity()
564
- .runOnUiThread(
565
- new Runnable() {
566
- @Override
567
- public void run() {
568
- if (player.isPlaying()) {
569
- fadeOut(player);
570
- }
571
- }
623
+ .runOnUiThread(() -> {
624
+ if (player != null && player.isPlaying()) {
625
+ fadeOut(player, fadeOutDurationMs, asPause);
572
626
  }
573
- );
627
+ });
574
628
  }
575
629
 
576
- private void fadeOut(final ExoPlayer player) {
577
- final Handler handler = new Handler(Looper.getMainLooper());
578
- final Runnable fadeRunnable = new Runnable() {
579
- float currentVolume = player.getVolume();
630
+ private void fadeOut(final ExoPlayer player, float fadeOutDurationMs, boolean asPause) {
631
+ cancelFade();
632
+ fadeState = FadeState.FADE_OUT;
633
+
634
+ final int steps = Math.max(1, (int) (fadeOutDurationMs / FADE_DELAY_MS));
635
+ final float initialVolume = player.getVolume();
636
+ final float fadeStep = initialVolume / steps;
637
+
638
+ Log.d(
639
+ TAG,
640
+ "Beginning fade out from volume " +
641
+ initialVolume +
642
+ " at time " +
643
+ getCurrentPosition() +
644
+ " over " +
645
+ (fadeOutDurationMs / 1000.0) +
646
+ "s in " +
647
+ steps +
648
+ " steps (step duration: " +
649
+ (FADE_DELAY_MS / 1000.0) +
650
+ "s)"
651
+ );
652
+
653
+ fadeTask = fadeExecutor.scheduleWithFixedDelay(
654
+ new Runnable() {
655
+ float currentVolume = initialVolume;
580
656
 
581
- @Override
582
- public void run() {
583
- if (currentVolume > FADE_STEP) {
584
- currentVolume -= FADE_STEP;
585
- player.setVolume(currentVolume);
586
- handler.postDelayed(this, FADE_DELAY_MS);
587
- } else {
588
- player.setVolume(0);
589
- player.stop();
657
+ @Override
658
+ public void run() {
659
+ if (fadeState != FadeState.FADE_OUT || currentVolume <= 0) {
660
+ fadeState = FadeState.NONE;
661
+ owner
662
+ .getActivity()
663
+ .runOnUiThread(() -> {
664
+ if (player != null && player.isPlaying()) {
665
+ if (asPause) {
666
+ player.pause();
667
+ logger.verbose("Faded out to pause at time " + getCurrentPosition());
668
+ } else {
669
+ player.setVolume(0);
670
+ player.stop();
671
+ logger.verbose("Faded out to stop at time " + getCurrentPosition());
672
+ }
673
+ }
674
+ });
675
+ cancelFade();
676
+ logger.verbose("Fade out complete at time " + getCurrentPosition());
677
+ return;
678
+ }
679
+ final float previousCurrentVolume = currentVolume;
680
+ currentVolume -= fadeStep;
681
+ final float thisTargetVolume = Math.max(currentVolume, 0);
682
+ logger.debug(
683
+ "Fade out step: from " + previousCurrentVolume + " to " + currentVolume + " to target " + thisTargetVolume
684
+ );
685
+ owner
686
+ .getActivity()
687
+ .runOnUiThread(() -> {
688
+ if (player != null && player.isPlaying()) {
689
+ player.setVolume(thisTargetVolume);
690
+ }
691
+ });
590
692
  }
693
+ },
694
+ 0,
695
+ FADE_DELAY_MS,
696
+ TimeUnit.MILLISECONDS
697
+ );
698
+ }
699
+
700
+ private void fadeTo(final ExoPlayer player, float fadeDurationMs, float targetVolume) {
701
+ cancelFade();
702
+ fadeState = FadeState.FADE_TO;
703
+
704
+ final int steps = Math.max(1, (int) (fadeDurationMs / FADE_DELAY_MS));
705
+ final float minVolume = zeroVolume;
706
+ final float maxVol = maxVolume;
707
+ final float initialVolume = Math.max(player.getVolume(), minVolume);
708
+ final float finalTargetVolume = Math.max(targetVolume, minVolume);
709
+
710
+ // Clamp values to avoid overflow/underflow and invalid pow inputs
711
+ final float safeInitialVolume = Math.max(initialVolume, minVolume);
712
+ final float safeFinalTargetVolume = Math.max(finalTargetVolume, minVolume);
713
+
714
+ double ratio;
715
+ if (steps <= 0 || safeInitialVolume <= 0f || safeFinalTargetVolume <= 0f) {
716
+ ratio = 1.0;
717
+ } else if (safeInitialVolume == safeFinalTargetVolume) {
718
+ ratio = 1.0;
719
+ } else {
720
+ ratio = Math.pow(safeFinalTargetVolume / safeInitialVolume, 1.0 / steps);
721
+ if (Double.isNaN(ratio) || Double.isInfinite(ratio) || ratio <= 0.0) {
722
+ ratio = 1.0;
591
723
  }
592
- };
593
- handler.post(fadeRunnable);
724
+ }
725
+
726
+ Log.d(
727
+ TAG,
728
+ "Beginning exponential fade from volume " +
729
+ initialVolume +
730
+ " to " +
731
+ finalTargetVolume +
732
+ " over " +
733
+ (fadeDurationMs / 1000.0) +
734
+ "s in " +
735
+ steps +
736
+ " steps (step duration: " +
737
+ (FADE_DELAY_MS / 1000.0) +
738
+ "s, ratio: " +
739
+ ratio +
740
+ ")"
741
+ );
742
+
743
+ double finalRatio = ratio;
744
+ fadeTask = fadeExecutor.scheduleWithFixedDelay(
745
+ new Runnable() {
746
+ int currentStep = 0;
747
+ float currentVolume = initialVolume;
748
+
749
+ @Override
750
+ public void run() {
751
+ if (fadeState != FadeState.FADE_TO || player == null || !player.isPlaying() || currentStep >= steps) {
752
+ fadeState = FadeState.NONE;
753
+ cancelFade();
754
+ logger.debug("Fade to complete at time " + getCurrentPosition());
755
+ return;
756
+ }
757
+ try {
758
+ if (finalRatio == 1.0) {
759
+ currentVolume = safeFinalTargetVolume;
760
+ } else {
761
+ currentVolume *= (float) finalRatio;
762
+ }
763
+ // Clamp volume between minVolume and maxVolume
764
+ currentVolume = Math.min(Math.max(currentVolume, minVolume), maxVol);
765
+ logger.verbose("Fade to step " + currentStep + ": volume set to " + currentVolume);
766
+ owner
767
+ .getActivity()
768
+ .runOnUiThread(() -> {
769
+ if (player != null && player.isPlaying()) {
770
+ player.setVolume(currentVolume);
771
+ }
772
+ });
773
+ currentStep++;
774
+ } catch (Exception e) {
775
+ logger.error("Error during fade to", e);
776
+ cancelFade();
777
+ }
778
+ }
779
+ },
780
+ 0,
781
+ FADE_DELAY_MS,
782
+ TimeUnit.MILLISECONDS
783
+ );
784
+ }
785
+
786
+ protected void cancelFade() {
787
+ if (fadeTask != null && !fadeTask.isCancelled()) {
788
+ fadeTask.cancel(true);
789
+ }
790
+ fadeState = FadeState.NONE;
791
+ fadeTask = null;
594
792
  }
595
793
 
596
794
  @Override
597
795
  protected void startCurrentTimeUpdates() {
598
- Log.d(TAG, "Starting timer updates in RemoteAudioAsset");
796
+ logger.debug("Starting timer updates");
599
797
  if (currentTimeHandler == null) {
600
798
  currentTimeHandler = new Handler(Looper.getMainLooper());
601
799
  }
800
+ // Reset completion status for this assetId
801
+ dispatchedCompleteMap.put(assetId, false);
602
802
 
603
803
  // Wait for player to be truly ready
604
804
  currentTimeHandler.postDelayed(
605
805
  new Runnable() {
606
806
  @Override
607
807
  public void run() {
608
- if (!players.isEmpty()) {
808
+ if (!players.isEmpty() && playIndex >= 0 && playIndex < players.size()) {
609
809
  ExoPlayer player = players.get(playIndex);
610
- if (player.getPlaybackState() == Player.STATE_READY) {
810
+ if (player != null && player.getPlaybackState() == Player.STATE_READY) {
611
811
  startTimeUpdateLoop();
612
812
  } else {
613
813
  // Check again in 100ms
@@ -625,33 +825,42 @@ public class RemoteAudioAsset extends AudioAsset {
625
825
  @Override
626
826
  public void run() {
627
827
  try {
628
- if (!players.isEmpty()) {
828
+ boolean isPaused = false;
829
+ if (!players.isEmpty() && playIndex >= 0 && playIndex < players.size()) {
629
830
  ExoPlayer player = players.get(playIndex);
630
- if (player.getPlaybackState() == Player.STATE_READY && player.isPlaying()) {
631
- double currentTime = player.getCurrentPosition() / 1000.0; // Get time directly
632
- Log.d(TAG, "Timer update: currentTime = " + currentTime);
633
- owner.notifyCurrentTime(assetId, currentTime);
634
- currentTimeHandler.postDelayed(this, 100);
635
- return;
831
+ if (player != null && player.getPlaybackState() == Player.STATE_READY) {
832
+ if (player.isPlaying()) {
833
+ double currentTime = player.getCurrentPosition() / 1000.0; // Get time directly
834
+ logger.debug("Play timer update: currentTime = " + currentTime);
835
+ if (owner != null) owner.notifyCurrentTime(assetId, currentTime);
836
+ currentTimeHandler.postDelayed(this, 100);
837
+ return;
838
+ } else if (!player.getPlayWhenReady()) {
839
+ isPaused = true;
840
+ }
636
841
  }
637
842
  }
638
- Log.d(TAG, "Stopping timer - not playing or not ready");
843
+ logger.debug("Stopping play timer - not playing or not ready");
639
844
  stopCurrentTimeUpdates();
845
+ if (isPaused) {
846
+ logger.verbose("Playback is paused, not dispatching complete");
847
+ } else {
848
+ logger.verbose("Playback is stopped, dispatching complete");
849
+ dispatchComplete();
850
+ }
640
851
  } catch (Exception e) {
641
- Log.e(TAG, "Error getting current time", e);
852
+ logger.error("Error getting current time", e);
642
853
  stopCurrentTimeUpdates();
643
854
  }
644
855
  }
645
856
  };
646
- currentTimeHandler.post(currentTimeRunnable);
647
- }
648
-
649
- @Override
650
- void stopCurrentTimeUpdates() {
651
- Log.d(TAG, "Stopping timer updates in RemoteAudioAsset");
652
- if (currentTimeHandler != null) {
653
- currentTimeHandler.removeCallbacks(currentTimeRunnable);
654
- currentTimeHandler = null;
857
+ try {
858
+ if (currentTimeHandler == null) {
859
+ currentTimeHandler = new Handler(Looper.getMainLooper());
860
+ }
861
+ currentTimeHandler.post(currentTimeRunnable);
862
+ } catch (Exception e) {
863
+ logger.error("Error starting current time updates", e);
655
864
  }
656
865
  }
657
866
  }