Skip to content

Commit 58981c3

Browse files
committed
feat: SAIC Broadcast trigger for oem app should pause dashcam recording to fix race condion for blocked chanels during recoding + fix: Notification Banner
1 parent 02dec78 commit 58981c3

10 files changed

Lines changed: 269 additions & 36 deletions

File tree

‎app/build.gradle‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ android {
1414
applicationId "com.drivehub.kamera"
1515
minSdk 28
1616
targetSdk 36
17-
versionCode 2
17+
versionCode 3
1818
versionName "0.8"
1919
buildConfigField "boolean", "IS_BETA", "true"
2020

‎app/src/main/AndroidManifest.xml‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,17 @@
7878
</intent-filter>
7979
</receiver>
8080

81+
<receiver
82+
android:name=".OemAvmReceiver"
83+
android:enabled="true"
84+
android:exported="true">
85+
<intent-filter>
86+
<action android:name="com.saicmotor.360camera.show" />
87+
<action android:name="com.saicmotor.360camera.close" />
88+
<action android:name="com.saicmotor.hmi.aroundview.mod.ACTION_STOP" />
89+
</intent-filter>
90+
</receiver>
91+
8192
<provider
8293
android:name="androidx.core.content.FileProvider"
8394
android:authorities="${applicationId}.fileprovider"

‎app/src/main/java/com/drivehub/kamera/DashcamEventOverlayService.java‎

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,41 @@ public class DashcamEventOverlayService extends Service {
2323
private static final String CHANNEL_ID = "mg4_dashcam_event_overlay";
2424
private static final int NOTIF_ID = 103;
2525
private static final long AUTO_HIDE_MS = 4_000L;
26+
private static final String EXTRA_TITLE_RES_ID = "title_res_id";
27+
private static final String EXTRA_SUBTITLE_RES_ID = "subtitle_res_id";
28+
private static final String EXTRA_NOTIFICATION_TEXT_RES_ID = "notification_text_res_id";
2629

2730
private final Handler mainHandler = new Handler(Looper.getMainLooper());
2831
private final Runnable hideRunnable = this::stopSelf;
2932

3033
private WindowManager windowManager;
3134
private View overlayView;
35+
private android.widget.TextView titleView;
36+
private android.widget.TextView subtitleView;
3237

3338
public static void showConfirmation(Context context) {
39+
showBanner(
40+
context,
41+
R.string.dashcam_event_overlay_title,
42+
R.string.dashcam_event_overlay_subtitle,
43+
R.string.notification_dashcam_event_overlay_text
44+
);
45+
}
46+
47+
public static void showOemPause(Context context) {
48+
showBanner(
49+
context,
50+
R.string.dashcam_oem_pause_overlay_title,
51+
R.string.dashcam_oem_pause_overlay_subtitle,
52+
R.string.notification_dashcam_oem_pause_overlay_text
53+
);
54+
}
55+
56+
private static void showBanner(Context context, int titleResId, int subtitleResId, int notificationTextResId) {
3457
Intent intent = new Intent(context, DashcamEventOverlayService.class);
58+
intent.putExtra(EXTRA_TITLE_RES_ID, titleResId);
59+
intent.putExtra(EXTRA_SUBTITLE_RES_ID, subtitleResId);
60+
intent.putExtra(EXTRA_NOTIFICATION_TEXT_RES_ID, notificationTextResId);
3561
context.startForegroundService(intent);
3662
}
3763

@@ -44,10 +70,19 @@ public void onCreate() {
4470

4571
@Override
4672
public int onStartCommand(Intent intent, int flags, int startId) {
73+
int titleResId = intent != null
74+
? intent.getIntExtra(EXTRA_TITLE_RES_ID, R.string.dashcam_event_overlay_title)
75+
: R.string.dashcam_event_overlay_title;
76+
int subtitleResId = intent != null
77+
? intent.getIntExtra(EXTRA_SUBTITLE_RES_ID, R.string.dashcam_event_overlay_subtitle)
78+
: R.string.dashcam_event_overlay_subtitle;
79+
int notificationTextResId = intent != null
80+
? intent.getIntExtra(EXTRA_NOTIFICATION_TEXT_RES_ID, R.string.notification_dashcam_event_overlay_text)
81+
: R.string.notification_dashcam_event_overlay_text;
4782
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
4883
.setSmallIcon(android.R.drawable.ic_menu_camera)
4984
.setContentTitle(getString(R.string.app_name))
50-
.setContentText(getString(R.string.notification_dashcam_event_overlay_text))
85+
.setContentText(getString(notificationTextResId))
5186
.setOngoing(true)
5287
.setPriority(NotificationCompat.PRIORITY_LOW)
5388
.build();
@@ -56,6 +91,7 @@ public int onStartCommand(Intent intent, int flags, int startId) {
5691
if (overlayView == null) {
5792
showOverlayWindow();
5893
}
94+
bindText(titleResId, subtitleResId);
5995
mainHandler.removeCallbacks(hideRunnable);
6096
mainHandler.postDelayed(hideRunnable, AUTO_HIDE_MS);
6197
return START_NOT_STICKY;
@@ -78,9 +114,20 @@ private void showOverlayWindow() {
78114
params.y = dpToPx(32);
79115

80116
overlayView = LayoutInflater.from(this).inflate(R.layout.overlay_dashcam_event, null, false);
117+
titleView = overlayView.findViewById(R.id.tvDashcamOverlayTitle);
118+
subtitleView = overlayView.findViewById(R.id.tvDashcamOverlaySubtitle);
81119
windowManager.addView(overlayView, params);
82120
}
83121

122+
private void bindText(int titleResId, int subtitleResId) {
123+
if (titleView != null) {
124+
titleView.setText(titleResId);
125+
}
126+
if (subtitleView != null) {
127+
subtitleView.setText(subtitleResId);
128+
}
129+
}
130+
84131
private int dpToPx(int dp) {
85132
return Math.round(dp * getResources().getDisplayMetrics().density);
86133
}
@@ -92,6 +139,8 @@ public void onDestroy() {
92139
windowManager.removeView(overlayView);
93140
}
94141
overlayView = null;
142+
titleView = null;
143+
subtitleView = null;
95144
stopForeground(true);
96145
super.onDestroy();
97146
}

‎app/src/main/java/com/drivehub/kamera/DashcamSettingsController.java‎

Lines changed: 42 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ final class DashcamSettingsController {
2525
private static final String KEY_SIGNATURE = "recordingSignature";
2626
private static final String KEY_SHOW_SPEED = "recordingShowSpeed";
2727
private static final int REQ_STORAGE = 1337;
28-
private static final int DEFAULT_RECORDING_FPS = 15;
28+
private static final int DEFAULT_RECORDING_FPS = 25;
2929
private static final int MIN_RECORDING_FPS = 1;
3030
private static final int MAX_RECORDING_FPS = 60;
3131
private static final int MAX_SIGNATURE_LENGTH = 40;
32-
private static final String RECORDS_DIR_NAME = "mg4_cam_records";
32+
private static final String RECORDS_DIR_NAME = "dashcam";
3333

3434
private final MainActivity activity;
3535
private boolean syncingEnabled;
@@ -47,8 +47,7 @@ void bind(
4747
EditText etSignature,
4848
Switch swShowSpeed,
4949
TextView tvRecordsPath,
50-
Button btnExportUsb
51-
) {
50+
Button btnExportUsb) {
5251
int segmentMin = prefs.getInt(KEY_SEGMENT_MIN, 3);
5352
int totalMin = Math.max(segmentMin, prefs.getInt(KEY_TOTAL_MIN, 30));
5453
int recordingFps = getRecordingFps(prefs);
@@ -59,16 +58,17 @@ void bind(
5958
swEnabled.setChecked(prefs.getBoolean(KEY_ENABLED, false));
6059
syncingEnabled = false;
6160
swEnabled.setOnCheckedChangeListener((buttonView, checked) -> {
62-
if (syncingEnabled) return;
61+
if (syncingEnabled)
62+
return;
6363
prefs.edit().putBoolean(KEY_ENABLED, checked).apply();
6464
if (checked) {
6565
if (!hasStoragePermission()) {
6666
ActivityCompat.requestPermissions(
6767
activity,
68-
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
69-
REQ_STORAGE
70-
);
71-
Toast.makeText(activity, R.string.settings_storage_permission_required, Toast.LENGTH_SHORT).show();
68+
new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },
69+
REQ_STORAGE);
70+
Toast.makeText(activity, R.string.settings_storage_permission_required, Toast.LENGTH_SHORT)
71+
.show();
7272
}
7373
saveFields(prefs, etSegmentMin, etTotalMin, etRecordingFps, etSignature, false);
7474
RecordingService.startIfNeeded(activity);
@@ -96,25 +96,24 @@ void bind(
9696
}
9797
if (swShowSpeed != null) {
9898
swShowSpeed.setChecked(shouldShowSpeed(prefs));
99-
swShowSpeed.setOnCheckedChangeListener((buttonView, checked) ->
100-
prefs.edit().putBoolean(KEY_SHOW_SPEED, checked).apply()
101-
);
99+
swShowSpeed.setOnCheckedChangeListener(
100+
(buttonView, checked) -> prefs.edit().putBoolean(KEY_SHOW_SPEED, checked).apply());
102101
}
103102
bindFields(prefs, etSegmentMin, etTotalMin, etRecordingFps, etSignature);
104103

105104
if (tvRecordsPath != null) {
106105
tvRecordsPath.setText(getRecordsBaseDir().getAbsolutePath());
107106
}
108107
if (btnExportUsb != null) {
109-
btnExportUsb.setVisibility(findMountedUsbRoot() == null ? android.view.View.GONE : android.view.View.VISIBLE);
110-
btnExportUsb.setOnClickListener(v ->
111-
Toast.makeText(activity, R.string.settings_usb_export_todo, Toast.LENGTH_LONG).show()
112-
);
108+
btnExportUsb
109+
.setVisibility(findMountedUsbRoot() == null ? android.view.View.GONE : android.view.View.VISIBLE);
110+
btnExportUsb.setOnClickListener(
111+
v -> Toast.makeText(activity, R.string.settings_usb_export_todo, Toast.LENGTH_LONG).show());
113112
}
114113
}
115114

116115
private void bindFields(SharedPreferences prefs, EditText etSegmentMin, EditText etTotalMin,
117-
EditText etRecordingFps, EditText etSignature) {
116+
EditText etRecordingFps, EditText etSignature) {
118117
android.text.TextWatcher watcher = new SimpleTextWatcher() {
119118
@Override
120119
public void afterTextChanged(Editable s) {
@@ -129,27 +128,31 @@ public void afterTextChanged(Editable s) {
129128
}
130129

131130
private void bindEditText(EditText editText, android.text.TextWatcher watcher, Runnable onBlur) {
132-
if (editText == null) return;
131+
if (editText == null)
132+
return;
133133
editText.addTextChangedListener(watcher);
134134
editText.setOnFocusChangeListener((v, hasFocus) -> {
135-
if (!hasFocus) onBlur.run();
135+
if (!hasFocus)
136+
onBlur.run();
136137
});
137138
}
138139

139140
private void saveFields(SharedPreferences prefs, EditText etSegmentMin, EditText etTotalMin,
140-
EditText etRecordingFps, EditText etSignature, boolean normalizeFields) {
141+
EditText etRecordingFps, EditText etSignature, boolean normalizeFields) {
141142
int segmentMin = parsePositiveInt(textOf(etSegmentMin), 3);
142143
int totalMin = parsePositiveInt(textOf(etTotalMin), 30);
143144
int recordingFps = clampRecordingFps(parsePositiveInt(textOf(etRecordingFps), DEFAULT_RECORDING_FPS));
144145
String signature = normalizeSignature(textOf(etSignature));
145-
if (totalMin < segmentMin) totalMin = segmentMin;
146+
if (totalMin < segmentMin)
147+
totalMin = segmentMin;
146148
prefs.edit()
147149
.putInt(KEY_SEGMENT_MIN, segmentMin)
148150
.putInt(KEY_TOTAL_MIN, totalMin)
149151
.putInt(KEY_RECORDING_FPS, recordingFps)
150152
.putString(KEY_SIGNATURE, signature)
151153
.apply();
152-
if (!normalizeFields) return;
154+
if (!normalizeFields)
155+
return;
153156
normalizeField(etSegmentMin, segmentMin);
154157
normalizeField(etTotalMin, totalMin);
155158
normalizeField(etRecordingFps, recordingFps);
@@ -173,18 +176,22 @@ private static int clampRecordingFps(int fps) {
173176
}
174177

175178
private void normalizeField(EditText editText, int value) {
176-
if (editText == null) return;
179+
if (editText == null)
180+
return;
177181
String normalized = String.valueOf(value);
178182
String current = textOf(editText);
179-
if (normalized.equals(current)) return;
183+
if (normalized.equals(current))
184+
return;
180185
editText.setText(normalized);
181186
editText.setSelection(editText.getText().length());
182187
}
183188

184189
private void normalizeField(EditText editText, String value) {
185-
if (editText == null) return;
190+
if (editText == null)
191+
return;
186192
String current = textOf(editText);
187-
if (value.equals(current)) return;
193+
if (value.equals(current))
194+
return;
188195
editText.setText(value);
189196
editText.setSelection(editText.getText().length());
190197
}
@@ -203,7 +210,8 @@ private int parsePositiveInt(String s, int def) {
203210
}
204211

205212
private static String normalizeSignature(String value) {
206-
if (value == null) return "";
213+
if (value == null)
214+
return "";
207215
String trimmed = value.trim();
208216
if (trimmed.length() <= MAX_SIGNATURE_LENGTH) {
209217
return trimmed;
@@ -212,23 +220,25 @@ private static String normalizeSignature(String value) {
212220
}
213221

214222
private boolean hasStoragePermission() {
215-
return ContextCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE)
216-
== PackageManager.PERMISSION_GRANTED;
223+
return ContextCompat.checkSelfPermission(activity,
224+
Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
217225
}
218226

219227
static File getRecordsBaseDir() {
220228
File downloads = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
221229
File dir = new File(downloads, RECORDS_DIR_NAME);
222-
//noinspection ResultOfMethodCallIgnored
230+
// noinspection ResultOfMethodCallIgnored
223231
dir.mkdirs();
224232
return dir;
225233
}
226234

227235
private File findMountedUsbRoot() {
228236
File storageDir = new File("/storage");
229-
if (!storageDir.exists()) return null;
237+
if (!storageDir.exists())
238+
return null;
230239
File[] roots = storageDir.listFiles();
231-
if (roots == null) return null;
240+
if (roots == null)
241+
return null;
232242
for (File root : roots) {
233243
if (root.isDirectory() && root.canRead() && root.canWrite()) {
234244
return root;

‎app/src/main/java/com/drivehub/kamera/MainActivity.java‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -482,6 +482,11 @@ private void renderRecordingStatus(String status, int activeCameras, int totalCa
482482
recordingStatusDot.setVisibility(View.VISIBLE);
483483
tvRecordingStatus.setText(getString(R.string.main_recording_indicator, activeCameras, totalCameras));
484484
renderDashcamDialogStatus(getString(R.string.settings_dashcam_status_recording, activeCameras, totalCameras));
485+
} else if (RecordingService.STATUS_PAUSED_OEM.equals(status)) {
486+
if (btnRecordTestClip != null) btnRecordTestClip.setEnabled(false);
487+
recordingStatusDot.setVisibility(View.GONE);
488+
tvRecordingStatus.setText(R.string.main_recording_paused_oem);
489+
renderDashcamDialogStatus(getString(R.string.settings_dashcam_status_paused_oem));
485490
} else if (RecordingService.STATUS_STARTING.equals(status)) {
486491
if (btnRecordTestClip != null) btnRecordTestClip.setEnabled(false);
487492
recordingStatusDot.setVisibility(View.VISIBLE);
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.drivehub.kamera;
2+
3+
import android.content.BroadcastReceiver;
4+
import android.content.Context;
5+
import android.content.Intent;
6+
import android.util.Log;
7+
8+
public class OemAvmReceiver extends BroadcastReceiver {
9+
private static final String TAG = "OemAvmReceiver";
10+
11+
private static final String ACTION_CAMERA_SHOW = "com.saicmotor.360camera.show";
12+
private static final String ACTION_CAMERA_CLOSE = "com.saicmotor.360camera.close";
13+
private static final String ACTION_OEM_STOP = "com.saicmotor.hmi.aroundview.mod.ACTION_STOP";
14+
15+
@Override
16+
public void onReceive(Context context, Intent intent) {
17+
if (context == null || intent == null) {
18+
return;
19+
}
20+
String action = intent.getAction();
21+
if (action == null) {
22+
return;
23+
}
24+
if (ACTION_CAMERA_SHOW.equals(action)) {
25+
Log.i(TAG, "OEM AVM show request received");
26+
RecordingService.pauseForOemRequest(context);
27+
return;
28+
}
29+
if (ACTION_CAMERA_CLOSE.equals(action) || ACTION_OEM_STOP.equals(action)) {
30+
Log.i(TAG, "OEM AVM close/stop received: " + action);
31+
RecordingService.resumeAfterOemRequest(context);
32+
}
33+
}
34+
}

0 commit comments

Comments
 (0)