77import android .content .Context ;
88import android .content .Intent ;
99import android .content .SharedPreferences ;
10+ import android .os .Handler ;
1011import android .os .IBinder ;
12+ import android .os .Looper ;
1113import android .os .SystemClock ;
1214import android .util .Log ;
1315
@@ -60,21 +62,27 @@ public class RecordingService extends Service {
6062 private static final long TEST_RECORDING_MS = 10_000L ;
6163 private static final long EVENT_PRE_ROLL_MS = 60_000L ;
6264 private static final long EVENT_POST_ROLL_MS = 60_000L ;
65+ private static final long ERROR_OVERLAY_DELAY_MS = 5_000L ;
6366
6467 private static volatile boolean sServiceRunning = false ;
6568
6669 private final Object eventLock = new Object ();
6770 private final Object stateLock = new Object ();
71+ private final Handler mainHandler = new Handler (Looper .getMainLooper ());
6872 private Thread worker ;
6973 private volatile boolean stopRequested = false ;
7074 private volatile boolean segmentStopRequested = false ;
7175 private volatile boolean oemPauseRequested = false ;
76+ private volatile int pendingErrorSubtitleResId = 0 ;
77+ private volatile int pendingErrorNotificationResId = 0 ;
78+ private volatile int pendingErrorGeneration = 0 ;
79+ private volatile boolean errorOverlayShown = false ;
7280
7381 public static boolean isRunning () {
7482 return sServiceRunning ;
7583 }
7684
77- public static void startIfNeeded (Context context ) {
85+ public static void startIfDashcamEnabled (Context context ) {
7886 SharedPreferences prefs = UiPrefs .getPrefs (context );
7987 boolean enabled = prefs .getBoolean (DashcamSettingsController .KEY_ENABLED , false );
8088 if (!enabled )
@@ -646,6 +654,8 @@ private void publishStatus(String status, int activeCameras, int totalCameras, S
646654 }
647655 }
648656
657+ updateDashcamOverlayState (status , lastError );
658+
649659 Intent intent = new Intent (ACTION_STATUS_CHANGED );
650660 intent .setPackage (getPackageName ());
651661 intent .putExtra (EXTRA_STATUS , status );
@@ -655,6 +665,77 @@ private void publishStatus(String status, int activeCameras, int totalCameras, S
655665 sendBroadcast (intent );
656666 }
657667
668+ private void updateDashcamOverlayState (String status , String lastError ) {
669+ if (STATUS_ERROR .equals (status ) || STATUS_PARTIAL .equals (status )) {
670+ scheduleDelayedErrorOverlay (lastError );
671+ return ;
672+ }
673+
674+ cancelPendingErrorOverlay ();
675+ if (errorOverlayShown && STATUS_RECORDING .equals (status )) {
676+ errorOverlayShown = false ;
677+ DashcamEventOverlayService .showRecordingRecovered (this );
678+ } else if (!STATUS_RECORDING .equals (status )) {
679+ errorOverlayShown = false ;
680+ }
681+ }
682+
683+ private void scheduleDelayedErrorOverlay (String lastError ) {
684+ OverlayMessageSpec spec = mapRecordingError (lastError );
685+ if (spec == null ) {
686+ cancelPendingErrorOverlay ();
687+ return ;
688+ }
689+ if (errorOverlayShown
690+ && pendingErrorSubtitleResId == spec .subtitleResId
691+ && pendingErrorNotificationResId == spec .notificationTextResId ) {
692+ return ;
693+ }
694+
695+ pendingErrorSubtitleResId = spec .subtitleResId ;
696+ pendingErrorNotificationResId = spec .notificationTextResId ;
697+ final int generation = ++pendingErrorGeneration ;
698+ mainHandler .removeCallbacksAndMessages (this );
699+ mainHandler .postAtTime (() -> {
700+ if (generation != pendingErrorGeneration ) {
701+ return ;
702+ }
703+ errorOverlayShown = true ;
704+ DashcamEventOverlayService .showRecordingError (
705+ RecordingService .this ,
706+ pendingErrorSubtitleResId ,
707+ pendingErrorNotificationResId );
708+ }, this , SystemClock .uptimeMillis () + ERROR_OVERLAY_DELAY_MS );
709+ }
710+
711+ private void cancelPendingErrorOverlay () {
712+ pendingErrorGeneration ++;
713+ pendingErrorSubtitleResId = 0 ;
714+ pendingErrorNotificationResId = 0 ;
715+ mainHandler .removeCallbacksAndMessages (this );
716+ }
717+
718+ private OverlayMessageSpec mapRecordingError (String lastError ) {
719+ if ("storage not writable" .equals (lastError )) {
720+ return new OverlayMessageSpec (
721+ R .string .dashcam_recording_error_overlay_subtitle_storage ,
722+ R .string .notification_dashcam_recording_error_storage_text );
723+ }
724+ if ("grid start failed" .equals (lastError )) {
725+ return new OverlayMessageSpec (
726+ R .string .dashcam_recording_error_overlay_subtitle_start_failed ,
727+ R .string .notification_dashcam_recording_error_start_failed_text );
728+ }
729+ if ("grid stop timeout" .equals (lastError )) {
730+ return new OverlayMessageSpec (
731+ R .string .dashcam_recording_error_overlay_subtitle_stop_failed ,
732+ R .string .notification_dashcam_recording_error_stop_failed_text );
733+ }
734+ return new OverlayMessageSpec (
735+ R .string .dashcam_recording_error_overlay_subtitle_generic ,
736+ R .string .notification_dashcam_recording_error_text );
737+ }
738+
658739 private SharedPreferences prefs () {
659740 return UiPrefs .getPrefs (this );
660741 }
@@ -679,6 +760,7 @@ public IBinder onBind(Intent intent) {
679760 public void onDestroy () {
680761 sServiceRunning = false ;
681762 stopRequested = true ;
763+ cancelPendingErrorOverlay ();
682764 if (worker != null ) {
683765 worker .interrupt ();
684766 try {
@@ -729,4 +811,14 @@ private static final class EventCopyJob {
729811 this .baseNames = baseNames ;
730812 }
731813 }
814+
815+ private static final class OverlayMessageSpec {
816+ final int subtitleResId ;
817+ final int notificationTextResId ;
818+
819+ OverlayMessageSpec (int subtitleResId , int notificationTextResId ) {
820+ this .subtitleResId = subtitleResId ;
821+ this .notificationTextResId = notificationTextResId ;
822+ }
823+ }
732824}
0 commit comments