@@ -38,15 +38,16 @@ public class DashcamEventOverlayService extends Service {
3838 private static final String EXTRA_SUBTITLE_RES_ID = "subtitle_res_id" ;
3939 private static final String EXTRA_NOTIFICATION_TEXT_RES_ID = "notification_text_res_id" ;
4040
41- // Base dimensions (SMALL = 1.0x). Apply scale factor for MEDIUM (1.6x) and LARGE (2.2x).
41+ // Base dimensions (SMALL = 1.0x). Apply scale factor for MEDIUM (1.6x) and
42+ // LARGE (2.2x).
4243 private static final float TITLE_BASE_SP = 16f ;
4344 private static final float SUBTITLE_BASE_SP = 13f ;
4445 private static final float DOT_BASE_DP = 12f ;
4546 private static final float PADDING_H_BASE_DP = 18f ;
4647 private static final float PADDING_V_BASE_DP = 14f ;
4748 private static final float DOT_TEXT_GAP_BASE_DP = 12f ;
4849 private static final float SUBTITLE_TOP_BASE_DP = 2f ;
49- private static final float [] BANNER_SIZE_SCALES = {1.0f , 1.6f , 2.2f };
50+ private static final float [] BANNER_SIZE_SCALES = { 1.0f , 1.6f , 2.2f };
5051
5152 public enum BannerType {
5253 EVENT (
@@ -86,6 +87,8 @@ public enum BannerType {
8687 final int subtitleRes ;
8788 final int notificationRes ;
8889
90+ // Bundles a banner's settings group, accent color, and string resources into
91+ // one enum constant.
8992 BannerType (DashcamSettingsController .BannerGroup group , int dotColor ,
9093 int titleRes , int subtitleRes , int notificationRes ) {
9194 this .group = group ;
@@ -109,10 +112,13 @@ public enum BannerType {
109112
110113 // ---------- Public API: respects per-group enabled toggle ----------
111114
115+ // Shows the standard event confirmation banner, if the EVENT group is enabled.
112116 public static void showConfirmation (Context context ) {
113117 show (context , BannerType .EVENT , BannerType .EVENT .subtitleRes , BannerType .EVENT .notificationRes , false );
114118 }
115119
120+ // Shows the event banner with "future events only" wording, if the EVENT group
121+ // is enabled.
116122 public static void showFutureOnlyConfirmation (Context context ) {
117123 show (
118124 context ,
@@ -122,29 +128,42 @@ public static void showFutureOnlyConfirmation(Context context) {
122128 false );
123129 }
124130
131+ // Shows the OEM-pause banner, if the PAUSE_RESUME group is enabled.
125132 public static void showOemPause (Context context ) {
126- show (context , BannerType .OEM_PAUSE , BannerType .OEM_PAUSE .subtitleRes , BannerType .OEM_PAUSE .notificationRes , false );
133+ show (context , BannerType .OEM_PAUSE , BannerType .OEM_PAUSE .subtitleRes , BannerType .OEM_PAUSE .notificationRes ,
134+ false );
127135 }
128136
137+ // Shows the OEM-resume banner, if the PAUSE_RESUME group is enabled.
129138 public static void showOemResume (Context context ) {
130- show (context , BannerType .OEM_RESUME , BannerType .OEM_RESUME .subtitleRes , BannerType .OEM_RESUME .notificationRes , false );
139+ show (context , BannerType .OEM_RESUME , BannerType .OEM_RESUME .subtitleRes , BannerType .OEM_RESUME .notificationRes ,
140+ false );
131141 }
132142
143+ // Shows the recording-error banner with caller-supplied subtitle/notification
144+ // text, if the ERROR_RECOVERED group is enabled.
133145 public static void showRecordingError (Context context , int subtitleResId , int notificationTextResId ) {
134146 show (context , BannerType .RECORDING_ERROR , subtitleResId , notificationTextResId , false );
135147 }
136148
149+ // Shows the recording-recovered banner, if the ERROR_RECOVERED group is
150+ // enabled.
137151 public static void showRecordingRecovered (Context context ) {
138152 show (context , BannerType .RECORDING_RECOVERED , BannerType .RECORDING_RECOVERED .subtitleRes ,
139153 BannerType .RECORDING_RECOVERED .notificationRes , false );
140154 }
141155
142- // ---------- Test API: bypasses enabled check, used by settings preview buttons ----------
156+ // ---------- Test API: bypasses enabled check, used by settings preview buttons
157+ // ----------
143158
159+ // Forces the event confirmation banner regardless of the enabled toggle
160+ // (settings preview).
144161 public static void showConfirmationForced (Context context ) {
145162 show (context , BannerType .EVENT , BannerType .EVENT .subtitleRes , BannerType .EVENT .notificationRes , true );
146163 }
147164
165+ // Forces the "future events only" event banner regardless of the enabled toggle
166+ // (settings preview).
148167 public static void showFutureOnlyConfirmationForced (Context context ) {
149168 show (
150169 context ,
@@ -154,23 +173,35 @@ public static void showFutureOnlyConfirmationForced(Context context) {
154173 true );
155174 }
156175
176+ // Forces the OEM-pause banner regardless of the enabled toggle (settings
177+ // preview).
157178 public static void showOemPauseForced (Context context ) {
158- show (context , BannerType .OEM_PAUSE , BannerType .OEM_PAUSE .subtitleRes , BannerType .OEM_PAUSE .notificationRes , true );
179+ show (context , BannerType .OEM_PAUSE , BannerType .OEM_PAUSE .subtitleRes , BannerType .OEM_PAUSE .notificationRes ,
180+ true );
159181 }
160182
183+ // Forces the OEM-resume banner regardless of the enabled toggle (settings
184+ // preview).
161185 public static void showOemResumeForced (Context context ) {
162- show (context , BannerType .OEM_RESUME , BannerType .OEM_RESUME .subtitleRes , BannerType .OEM_RESUME .notificationRes , true );
186+ show (context , BannerType .OEM_RESUME , BannerType .OEM_RESUME .subtitleRes , BannerType .OEM_RESUME .notificationRes ,
187+ true );
163188 }
164189
190+ // Forces the recording-error banner regardless of the enabled toggle (settings
191+ // preview).
165192 public static void showRecordingErrorForced (Context context , int subtitleResId , int notificationTextResId ) {
166193 show (context , BannerType .RECORDING_ERROR , subtitleResId , notificationTextResId , true );
167194 }
168195
196+ // Forces the recording-recovered banner regardless of the enabled toggle
197+ // (settings preview).
169198 public static void showRecordingRecoveredForced (Context context ) {
170199 show (context , BannerType .RECORDING_RECOVERED , BannerType .RECORDING_RECOVERED .subtitleRes ,
171200 BannerType .RECORDING_RECOVERED .notificationRes , true );
172201 }
173202
203+ // Checks the per-group enabled toggle (unless forced) and starts the service
204+ // with the chosen banner's data
174205 private static void show (Context context , BannerType type , int subtitleResId ,
175206 int notificationTextResId , boolean force ) {
176207 if (!force ) {
@@ -186,13 +217,19 @@ private static void show(Context context, BannerType type, int subtitleResId,
186217 context .startForegroundService (intent );
187218 }
188219
220+ // Grabs the WindowManager service and ensures the notification channel exists
221+ // before the service is used.
189222 @ Override
190223 public void onCreate () {
191224 super .onCreate ();
192225 windowManager = (WindowManager ) getSystemService (WINDOW_SERVICE );
193226 NotificationChannelHelper .ensureChannel (this , CHANNEL_ID , R .string .notification_channel_dashcam_event_overlay );
194227 }
195228
229+ // Reads the requested banner from the intent, posts the foreground
230+ // notification, (re)shows the overlay
231+ // with the right size/color/text, plays the tone, and schedules the banner to
232+ // auto-hide.
196233 @ Override
197234 public int onStartCommand (Intent intent , int flags , int startId ) {
198235 BannerType type = readBannerType (intent );
@@ -226,8 +263,11 @@ public int onStartCommand(Intent intent, int flags, int startId) {
226263 return START_NOT_STICKY ;
227264 }
228265
266+ // Resolves the BannerType from the intent's ordinal extra, falling back to
267+ // EVENT if missing/out of range.
229268 private BannerType readBannerType (Intent intent ) {
230- if (intent == null ) return BannerType .EVENT ;
269+ if (intent == null )
270+ return BannerType .EVENT ;
231271 int ordinal = intent .getIntExtra (EXTRA_TYPE_ORDINAL , BannerType .EVENT .ordinal ());
232272 BannerType [] all = BannerType .values ();
233273 if (ordinal < 0 || ordinal >= all .length ) {
@@ -236,6 +276,8 @@ private BannerType readBannerType(Intent intent) {
236276 return all [ordinal ];
237277 }
238278
279+ // Plays the banner's notification tone at the group's configured volume
280+ // (skips/stops if volume is 0).
239281 private void playBannerTone (DashcamSettingsController .BannerGroup group , SharedPreferences prefs ) {
240282 int volumePct = DashcamSettingsController .getBannerVolume (prefs , group );
241283 if (volumePct <= 0 ) {
@@ -244,18 +286,20 @@ private void playBannerTone(DashcamSettingsController.BannerGroup group, SharedP
244286 }
245287 releaseBannerTone ();
246288
247- // No audio focus request: requesting AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK reliably ducks
248- // Android Auto, but its volume sometimes doesn't recover when we abandon focus until the
249- // user pauses + resumes playback. For a sub-second notification tone, mixing over music is
289+ // No audio focus request: requesting AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK
290+ // reliably ducks
291+ // Android Auto, but its volume sometimes doesn't recover when we abandon focus
292+ // until the
293+ // user pauses + resumes playback. For a sub-second notification tone, mixing
294+ // over music is
250295 // good enough — user can compensate via the per-group banner volume.
251296 AudioAttributes attributes = new AudioAttributes .Builder ()
252297 .setUsage (AudioAttributes .USAGE_ASSISTANCE_SONIFICATION )
253298 .setContentType (AudioAttributes .CONTENT_TYPE_SONIFICATION )
254299 .build ();
255300
256301 MediaPlayer player = new MediaPlayer ();
257- try (AssetFileDescriptor afd =
258- getResources ().openRawResourceFd (R .raw .notification_sound_7062_henrycena82595 )) {
302+ try (AssetFileDescriptor afd = getResources ().openRawResourceFd (R .raw .notification_sound_7062_henrycena82595 )) {
259303 if (afd == null ) {
260304 player .release ();
261305 return ;
@@ -280,6 +324,8 @@ private void playBannerTone(DashcamSettingsController.BannerGroup group, SharedP
280324 }
281325 }
282326
327+ // Stops and releases the active tone MediaPlayer, if any, swallowing any
328+ // cleanup errors.
283329 private void releaseBannerTone () {
284330 if (bannerTonePlayer != null ) {
285331 try {
@@ -294,6 +340,9 @@ private void releaseBannerTone() {
294340 }
295341 }
296342
343+ // Inflates the overlay layout, grabs its child view references, and adds it as
344+ // a non-touchable
345+ // system overlay window anchored to the bottom-center of the screen.
297346 private void showOverlayWindow () {
298347 if (windowManager == null ) {
299348 return ;
@@ -317,18 +366,19 @@ private void showOverlayWindow() {
317366 windowManager .addView (overlayView , params );
318367 }
319368
369+ // Scales title/subtitle text size, dot size, margins, and padding according to
370+ // the configured banner size.
320371 private void applyBannerSize (int sizeIndex ) {
321- if (overlayView == null ) return ;
322- float scale = BANNER_SIZE_SCALES [
323- Math .max (0 , Math .min (BANNER_SIZE_SCALES .length - 1 , sizeIndex ))];
372+ if (overlayView == null )
373+ return ;
374+ float scale = BANNER_SIZE_SCALES [ Math .max (0 , Math .min (BANNER_SIZE_SCALES .length - 1 , sizeIndex ))];
324375
325376 if (titleView != null ) {
326377 titleView .setTextSize (TypedValue .COMPLEX_UNIT_SP , TITLE_BASE_SP * scale );
327378 }
328379 if (subtitleView != null ) {
329380 subtitleView .setTextSize (TypedValue .COMPLEX_UNIT_SP , SUBTITLE_BASE_SP * scale );
330- ViewGroup .MarginLayoutParams subParams =
331- (ViewGroup .MarginLayoutParams ) subtitleView .getLayoutParams ();
381+ ViewGroup .MarginLayoutParams subParams = (ViewGroup .MarginLayoutParams ) subtitleView .getLayoutParams ();
332382 if (subParams != null ) {
333383 subParams .topMargin = dpToPx (SUBTITLE_TOP_BASE_DP * scale );
334384 subtitleView .setLayoutParams (subParams );
@@ -342,8 +392,8 @@ private void applyBannerSize(int sizeIndex) {
342392 dotView .setLayoutParams (dotParams );
343393 }
344394 if (textContainerView != null ) {
345- ViewGroup .MarginLayoutParams textParams =
346- ( ViewGroup . MarginLayoutParams ) textContainerView .getLayoutParams ();
395+ ViewGroup .MarginLayoutParams textParams = ( ViewGroup . MarginLayoutParams ) textContainerView
396+ .getLayoutParams ();
347397 if (textParams != null ) {
348398 textParams .setMarginStart (dpToPx (DOT_TEXT_GAP_BASE_DP * scale ));
349399 textContainerView .setLayoutParams (textParams );
@@ -354,11 +404,15 @@ private void applyBannerSize(int sizeIndex) {
354404 overlayView .setPadding (padH , padV , padH , padV );
355405 }
356406
407+ // Tints the status dot view's background with the banner type's accent color.
357408 private void applyDotColor (int colorArgb ) {
358- if (dotView == null ) return ;
409+ if (dotView == null )
410+ return ;
359411 dotView .setBackgroundTintList (ColorStateList .valueOf (colorArgb ));
360412 }
361413
414+ // Sets the title and subtitle text view contents from the given string
415+ // resources.
362416 private void bindText (int titleResId , int subtitleResId ) {
363417 if (titleView != null ) {
364418 titleView .setText (titleResId );
@@ -368,10 +422,14 @@ private void bindText(int titleResId, int subtitleResId) {
368422 }
369423 }
370424
425+ // Converts a dp value to pixels based on the device's current display density.
371426 private int dpToPx (float dp ) {
372427 return Math .round (dp * getResources ().getDisplayMetrics ().density );
373428 }
374429
430+ // Cancels the pending auto-hide callback, releases the tone player, and removes
431+ // the overlay
432+ // view from the WindowManager when the service is torn down.
375433 @ Override
376434 public void onDestroy () {
377435 super .onDestroy ();
@@ -388,6 +446,7 @@ public void onDestroy() {
388446 stopForeground (true );
389447 }
390448
449+ // Unbindable: this is a started (not bound) service.
391450 @ Nullable
392451 @ Override
393452 public IBinder onBind (Intent intent ) {
0 commit comments