-
-
Save dragonlord2025/86b05ae10dbd6456aa53 to your computer and use it in GitHub Desktop.
| package com.example.audiogain; | |
| import android.app.Activity; | |
| import android.bluetooth.BluetoothAdapter; | |
| import android.content.BroadcastReceiver; | |
| import android.content.Context; | |
| import android.content.Intent; | |
| import android.content.IntentFilter; | |
| import android.content.SharedPreferences; | |
| import android.media.AudioManager; | |
| import android.os.CountDownTimer; | |
| import android.widget.Toast; | |
| public class Recording { | |
| static int count = 0; | |
| static String Shared; | |
| static String bFlag; | |
| public static int TIMEOUT = 5000; | |
| public static int COUNTDOWN_INTERVAL = 1000; | |
| static Context context; | |
| public static void checkAndRecord(Context context, | |
| OnBluetoothRecording BluetoothRecording, boolean resume) { | |
| // Check bluetooth flag And Bluetooth is ON or OFF | |
| if (getBluetoothFlag(context) && isBluetoothON()) { | |
| // Check for bluetooth and Record | |
| startBluetoothRecording(BluetoothRecording, resume, context); | |
| } else { | |
| // If Bluetooth is OFF Show Toast else Dont Show | |
| if (getBluetoothFlag(context) && !isBluetoothON()) { | |
| // false because recording not started | |
| Toast.makeText(context, | |
| "Bluetooth is OFF. Recording from Phone MIC.", | |
| Toast.LENGTH_SHORT).show(); | |
| BluetoothRecording.onStartRecording(resume, false); | |
| } else { | |
| // false because recording not started | |
| BluetoothRecording.onStartRecording(resume, false); | |
| } | |
| } | |
| } | |
| private static void startBluetoothRecording( | |
| final OnBluetoothRecording BluetoothRecording, | |
| final boolean resume, Context context) { | |
| // TODO Auto-generated method stub | |
| final int MAX_ATTEPTS_TO_CONNECT = 3; | |
| final AudioManager audioManager = (AudioManager) context | |
| .getSystemService(Context.AUDIO_SERVICE); | |
| final CountDownTimer timer = getTimer(BluetoothRecording, audioManager, | |
| resume); | |
| context.registerReceiver(new BroadcastReceiver() { | |
| @Override | |
| public void onReceive(Context context, Intent intent) { | |
| int state = intent.getIntExtra( | |
| AudioManager.EXTRA_SCO_AUDIO_STATE, -1); | |
| if (AudioManager.SCO_AUDIO_STATE_CONNECTED == state) { | |
| // cancel Timer | |
| timer.cancel(); | |
| context.unregisterReceiver(this); | |
| // pass through and true because | |
| // recording from bluetooth so set 8000kHz | |
| BluetoothRecording.onStartRecording(resume, true); | |
| } else if (AudioManager.SCO_AUDIO_STATE_DISCONNECTED == state) { | |
| if (count > MAX_ATTEPTS_TO_CONNECT) { | |
| context.unregisterReceiver(this); | |
| // Stop BluetoothSCO | |
| audioManager.stopBluetoothSco(); | |
| // reset Counter | |
| count = 0; | |
| // stop timer | |
| timer.cancel(); | |
| // false because still recording not started | |
| BluetoothRecording.onStartRecording(resume, false); | |
| } else { | |
| // Increment Disconnect state Count | |
| count++; | |
| } | |
| } | |
| } | |
| }, new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED)); | |
| // Start the timer | |
| timer.start(); | |
| audioManager.startBluetoothSco(); | |
| } | |
| // set the Timeout | |
| private static CountDownTimer getTimer( | |
| final OnBluetoothRecording BluetoothRecording, | |
| final AudioManager audioManager, final boolean resume) { | |
| // TODO Auto-generated method stub | |
| return new CountDownTimer(TIMEOUT, COUNTDOWN_INTERVAL) { | |
| @Override | |
| public void onTick(long millisUntilFinished) { | |
| // Do Nothing | |
| } | |
| @Override | |
| public void onFinish() { | |
| // stopBluetoothSCO() and start Normal Recording | |
| audioManager.stopBluetoothSco(); | |
| // false because recording button is already clicked but still | |
| // not recording. | |
| BluetoothRecording.onStartRecording(resume, false); | |
| } | |
| }; | |
| } | |
| // Return's the bluetooth state | |
| private static boolean isBluetoothON() { | |
| BluetoothAdapter bluetoothAdapter = BluetoothAdapter | |
| .getDefaultAdapter(); | |
| return bluetoothAdapter.isEnabled(); | |
| } | |
| // Return's the bluetoothFlag state | |
| private static boolean getBluetoothFlag(Context context) { | |
| // shared pref | |
| SharedPreferences sp = context.getSharedPreferences(Shared, | |
| Context.MODE_PRIVATE); | |
| return sp.getBoolean(bFlag, false); | |
| } | |
| } |
dear shivarajp....what is OnBluetoothRecording ??
I can read this type/class in several posts in internet....but none of them defines it
thanks in advance
Alberto
Hey mate i have the tried the same code but it keep going to Disconnected bloc , any reason why !! bluetooth is on , device is connected bu when i execute the code , it never goes to connected ..
Great working for me be low is my code
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.os.Binder;
import android.os.Build;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.util.Log;
import androidx.annotation.RequiresApi;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import androidx.core.app.ServiceCompat;
import androidx.lifecycle.MutableLiveData;
import com.dev.auditiontech.R;
import com.dev.auditiontech.activity.MainActivity;
public class MonitorServiceMic extends Service {
private static final String TAG = "MonitorServiceMic";
public static MonitorServiceMic service;
private NotificationManager nm;
private NotificationCompat.Builder nCB;
private static final String CHANNEL_ID = "MonitoringChannel";
private static final int NOTIFICATION_ID = 1001;
private AudioRecord audioRecord;
private AudioManager audioManager;
private BluetoothAdapter bluetoothAdapter;
private BluetoothHeadset bluetoothHeadset;
private String micSourceName = "";
private boolean isBluetoothMic = false;
private BluetoothDevice connectedBluetoothDevice = null;
private double lastDecibel = 0.0;
private String lastSafeFor = "N/A";
private double lastNotifiedDecibel = -1.0;
private String lastNotifiedSafeFor = "";
private final IBinder binderMic = new LocalBinder();
private Handler micUpdateHandler;
private MutableLiveData volume = new MutableLiveData<>();
private boolean isRecording = false;
private boolean isInitializing = false;
private static final int SAMPLE_RATE = 8000; // Optimized for Bluetooth
private static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;
private static final int AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
private static final int BUFFER_SIZE = AudioRecord.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT) * 2;
private static final double BASE = 32767.0; // Max amplitude for 16-bit PCM
private static final long RECONNECT_DELAY_MS = 5000;
private static final int SCO_TIMEOUT_MS = 5000;
private static final int SCO_COUNTDOWN_INTERVAL_MS = 1000;
private static final int MAX_SCO_ATTEMPTS = 3;
private Runnable micUpdateRunnable = new Runnable() {
@Override
public void run() {
updateMicStatus();
micUpdateHandler.postDelayed(this, 200);
}
};
private final BroadcastReceiver bluetoothReceiver = new BroadcastReceiver() {
@SuppressLint("MissingPermission")
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
String deviceName = device != null && device.getName() != null ? device.getName() : "Unknown Device";
if (state == BluetoothProfile.STATE_CONNECTED) {
Log.d(TAG, "HFP connected: " + deviceName);
connectedBluetoothDevice = device;
micSourceName = deviceName;
isBluetoothMic = true;
handleBluetoothReconnect();
} else if (state == BluetoothProfile.STATE_DISCONNECTED) {
Log.d(TAG, "HFP disconnected: " + deviceName);
connectedBluetoothDevice = null;
isBluetoothMic = false;
micSourceName = getDeviceName();
reinitializeAudioRecord();
}
} else if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
if (state == BluetoothAdapter.STATE_OFF) {
Log.d(TAG, "Bluetooth disabled");
connectedBluetoothDevice = null;
isBluetoothMic = false;
micSourceName = getDeviceName();
reinitializeAudioRecord();
} else if (state == BluetoothAdapter.STATE_ON) {
Log.d(TAG, "Bluetooth enabled");
initializeBluetoothProfile();
}
}
}
};
private BroadcastReceiver scoReceiver;
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "Service created");
nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
micUpdateHandler = new Handler(Looper.getMainLooper());
micSourceName = getDeviceName();
createNotificationChannel();
nCB = new NotificationCompat.Builder(this, CHANNEL_ID);
initNotification();
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
registerReceiver(bluetoothReceiver, filter);
initializeBluetoothProfile();
initAudioRecord();
service = this;
}
@RequiresApi(api = Build.VERSION_CODES.O)
private void initNotification() {
Intent meterIntent = new Intent(this, MainActivity.class);
meterIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingStartMeterIntent = PendingIntent.getActivity(this, 0, meterIntent, PendingIntent.FLAG_IMMUTABLE);
Intent stopIntent = new Intent(this, MonitorServiceMic.class);
stopIntent.setAction("ACTION_STOP_MONITOR");
PendingIntent pendingStopIntent = PendingIntent.getService(this, 1, stopIntent, PendingIntent.FLAG_IMMUTABLE);
String initialText = "Using the device mic: " + micSourceName +
"\nLevel: N/A | Safe For: N/A";
nCB.setSmallIcon(R.drawable.ic_hearing_24px)
.setContentTitle("Atlas Safe Listen - Mic Monitoring")
.setContentText(initialText)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingStartMeterIntent)
.setAutoCancel(false)
.addAction(R.drawable.ic_mic_off_24px, "Stop", pendingStopIntent)
.setOngoing(true)
.setOnlyAlertOnce(true);
int serviceType = Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q ?
ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE : 0;
try {
ServiceCompat.startForeground(this, NOTIFICATION_ID, nCB.build(), serviceType);
} catch (Exception e) {
Log.e(TAG, "Failed to start foreground service: " + e.getMessage(), e);
stopSelf();
}
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(
CHANNEL_ID,
"Mic Monitoring",
NotificationManager.IMPORTANCE_DEFAULT
);
channel.setDescription("Notifications for mic monitoring service");
channel.enableVibration(true);
channel.setSound(null, null);
nm.createNotificationChannel(channel);
}
}
@SuppressLint("MissingPermission")
private void initializeBluetoothProfile() {
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
Log.d(TAG, "Bluetooth not enabled or adapter unavailable, using device mic: " + micSourceName);
isBluetoothMic = false;
connectedBluetoothDevice = null;
return;
}
bluetoothAdapter.getProfileProxy(this, new BluetoothProfile.ServiceListener() {
@Override
public void onServiceConnected(int profile, BluetoothProfile proxy) {
if (profile == BluetoothProfile.HEADSET) {
bluetoothHeadset = (BluetoothHeadset) proxy;
checkBluetoothMic();
}
}
@Override
public void onServiceDisconnected(int profile) {
if (profile == BluetoothProfile.HEADSET) {
bluetoothHeadset = null;
Log.d(TAG, "HFP profile disconnected");
isBluetoothMic = false;
micSourceName = getDeviceName();
connectedBluetoothDevice = null;
reinitializeAudioRecord();
}
}
}, BluetoothProfile.HEADSET);
}
@SuppressLint("MissingPermission")
private void checkBluetoothMic() {
if (isInitializing) {
Log.d(TAG, "AudioRecord initialization in progress, skipping Bluetooth mic check");
return;
}
if (bluetoothHeadset == null || !bluetoothAdapter.isEnabled()) {
Log.d(TAG, "No Bluetooth headset or Bluetooth disabled, using device mic: " + micSourceName);
if (isBluetoothMic || connectedBluetoothDevice != null) {
isBluetoothMic = false;
connectedBluetoothDevice = null;
micSourceName = getDeviceName();
reinitializeAudioRecord();
}
return;
}
boolean foundConnectedDevice = false;
for (BluetoothDevice device : bluetoothHeadset.getConnectedDevices()) {
if (device != null) {
connectedBluetoothDevice = device;
micSourceName = device.getName() != null ? device.getName() : "Bluetooth Device";
isBluetoothMic = true;
foundConnectedDevice = true;
Log.d(TAG, "Detected connected HFP device: " + micSourceName);
reinitializeAudioRecord();
break;
}
}
if (!foundConnectedDevice && (isBluetoothMic || connectedBluetoothDevice != null)) {
Log.d(TAG, "No HFP devices connected, switching to device mic: " + micSourceName);
isBluetoothMic = false;
connectedBluetoothDevice = null;
micSourceName = getDeviceName();
reinitializeAudioRecord();
}
}
private void initAudioRecord() {
if (isInitializing) {
Log.d(TAG, "AudioRecord initialization in progress, skipping");
return;
}
isInitializing = true;
Log.d(TAG, "Initializing AudioRecord...");
releaseAudioRecord();
try {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
Log.e(TAG, "RECORD_AUDIO permission not granted");
isInitializing = false;
stopSelf();
return;
}
if (isBluetoothMic && audioManager != null && connectedBluetoothDevice != null && bluetoothAdapter.isEnabled()) {
Log.d(TAG, "Attempting Bluetooth mic setup: " + micSourceName);
startBluetoothRecording();
return;
}
setupAudioRecord(android.media.MediaRecorder.AudioSource.MIC);
isInitializing = false;
} catch (Exception e) {
Log.e(TAG, "AudioRecord initialization failed: " + e.getMessage(), e);
isRecording = false;
isInitializing = false;
updateNotificationWithWarning("AudioRecord initialization failed: " + micSourceName);
stopSelf();
}
}
private void startBluetoothRecording() {
final int[] count = {0};
audioManager.setMode(AudioManager.MODE_NORMAL);
Log.d(TAG, "AudioManager mode before SCO: " + audioManager.getMode() +
", isBluetoothScoAvailable: " + audioManager.isBluetoothScoAvailableOffCall());
if (!audioManager.isBluetoothScoAvailableOffCall()) {
Log.w(TAG, "Bluetooth SCO not available, falling back to device mic");
isBluetoothMic = false;
micSourceName = getDeviceName();
connectedBluetoothDevice = null;
setupAudioRecord(android.media.MediaRecorder.AudioSource.MIC);
isInitializing = false;
return;
}
final CountDownTimer timer = new CountDownTimer(SCO_TIMEOUT_MS, SCO_COUNTDOWN_INTERVAL_MS) {
@Override
public void onTick(long millisUntilFinished) {
// Do nothing
}
@Override
public void onFinish() {
Log.w(TAG, "SCO connection timed out, falling back to device mic");
if (scoReceiver != null) {
try {
unregisterReceiver(scoReceiver);
} catch (IllegalArgumentException e) {
Log.e(TAG, "SCO receiver not registered: " + e.getMessage());
}
}
audioManager.stopBluetoothSco();
isBluetoothMic = false;
micSourceName = getDeviceName();
connectedBluetoothDevice = null;
setupAudioRecord(android.media.MediaRecorder.AudioSource.MIC);
isInitializing = false;
}
};
scoReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, -1);
if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) {
Log.d(TAG, "SCO connected, starting Bluetooth recording");
timer.cancel();
try {
unregisterReceiver(this);
} catch (IllegalArgumentException e) {
Log.e(TAG, "SCO receiver not registered: " + e.getMessage());
}
audioManager.setBluetoothScoOn(true);
setupAudioRecord(android.media.MediaRecorder.AudioSource.VOICE_COMMUNICATION);
isInitializing = false;
} else if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED) {
count[0]++;
if (count[0] >= MAX_SCO_ATTEMPTS) {
Log.w(TAG, "SCO failed after " + MAX_SCO_ATTEMPTS + " attempts, falling back to device mic");
timer.cancel();
try {
unregisterReceiver(this);
} catch (IllegalArgumentException e) {
Log.e(TAG, "SCO receiver not registered: " + e.getMessage());
}
audioManager.stopBluetoothSco();
isBluetoothMic = false;
micSourceName = getDeviceName();
connectedBluetoothDevice = null;
setupAudioRecord(android.media.MediaRecorder.AudioSource.MIC);
isInitializing = false;
} else {
Log.d(TAG, "SCO disconnected, retrying (" + count[0] + "/" + MAX_SCO_ATTEMPTS + ")");
audioManager.startBluetoothSco();
}
}
}
};
IntentFilter scoFilter = new IntentFilter(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
registerReceiver(scoReceiver, scoFilter);
timer.start();
audioManager.startBluetoothSco();
}
private void setupAudioRecord(int audioSource) {
try {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
audioRecord = new AudioRecord(audioSource, SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT, BUFFER_SIZE);
if (audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
Log.e(TAG, "AudioRecord not initialized");
releaseAudioRecord();
if (isBluetoothMic) {
isBluetoothMic = false;
micSourceName = getDeviceName();
connectedBluetoothDevice = null;
updateNotificationWithWarning("Bluetooth mic not supported, falling back to device mic: " + micSourceName);
setupAudioRecord(android.media.MediaRecorder.AudioSource.MIC);
} else {
stopSelf();
}
return;
}
audioRecord.startRecording();
isRecording = true;
Log.d(TAG, "AudioRecord started successfully with " +
(isBluetoothMic ? "Bluetooth mic: " + micSourceName : "device mic: " + micSourceName));
updateNotification();
startMonitoring();
} catch (Exception e) {
Log.e(TAG, "AudioRecord setup failed: " + e.getMessage(), e);
releaseAudioRecord();
if (isBluetoothMic) {
isBluetoothMic = false;
micSourceName = getDeviceName();
connectedBluetoothDevice = null;
updateNotificationWithWarning("Bluetooth mic not supported, falling back to device mic: " + micSourceName);
setupAudioRecord(android.media.MediaRecorder.AudioSource.MIC);
} else {
stopSelf();
}
}
}
private void handleBluetoothReconnect() {
if (isRecording) {
stopMonitoring();
}
micUpdateHandler.postDelayed(() -> {
if (isBluetoothMic && connectedBluetoothDevice != null && bluetoothHeadset != null &&
bluetoothHeadset.getConnectedDevices().contains(connectedBluetoothDevice)) {
Log.d(TAG, "Reinitializing AudioRecord after reconnect delay");
reinitializeAudioRecord();
} else {
Log.w(TAG, "Bluetooth device no longer connected after delay, staying with device mic");
isBluetoothMic = false;
micSourceName = getDeviceName();
connectedBluetoothDevice = null;
reinitializeAudioRecord();
}
}, RECONNECT_DELAY_MS);
}
private void reinitializeAudioRecord() {
if (isInitializing) {
Log.d(TAG, "AudioRecord initialization in progress, skipping reinitialization");
return;
}
Log.d(TAG, "Reinitializing AudioRecord due to mic source change");
stopMonitoring();
initAudioRecord();
}
private void updateNotification() {
int roundedDecibel = (lastDecibel > 0) ? Math.round((float) lastDecibel) : 0;
String text = (isBluetoothMic ?
"Using Bluetooth Mic: " + micSourceName :
"Using the device mic: " + micSourceName) +
"\nLevel: " + (lastDecibel > 0 ? roundedDecibel + " dB" : "N/A") +
" | Safe For: " + lastSafeFor;
nCB.setContentText(text)
.setOnlyAlertOnce(true);
nm.notify(NOTIFICATION_ID, nCB.build());
Log.d(TAG, "Notification updated: " + text);
}
private void updateNotificationWithWarning(String warning) {
int roundedDecibel = (lastDecibel > 0) ? Math.round((float) lastDecibel) : 0;
String text = warning +
"\nLevel: " + (lastDecibel > 0 ? roundedDecibel + " dB" : "N/A") +
" | Safe For: " + lastSafeFor;
nCB.setContentText(text)
.setOnlyAlertOnce(true);
nm.notify(NOTIFICATION_ID, nCB.build());
Log.d(TAG, "Notification updated with warning: " + text);
}
private void startMonitoring() {
Log.d(TAG, "Starting monitoring...");
if (isRecording && audioRecord != null) {
micUpdateHandler.post(micUpdateRunnable);
} else {
Log.w(TAG, "Cannot start monitoring: AudioRecord not initialized");
if (!isInitializing) {
initAudioRecord();
}
}
}
private void stopMonitoring() {
if (micUpdateHandler != null) {
micUpdateHandler.removeCallbacks(micUpdateRunnable);
}
if (isRecording && audioRecord != null) {
try {
audioRecord.stop();
isRecording = false;
Log.d(TAG, "AudioRecord stopped successfully");
} catch (IllegalStateException e) {
Log.e(TAG, "Error stopping AudioRecord: " + e.getMessage());
isRecording = false;
}
}
}
public void updateMicStatus() {
if (isRecording && audioRecord != null) {
try {
short[] buffer = new short[BUFFER_SIZE / 2];
int read = audioRecord.read(buffer, 0, buffer.length);
if (read > 0) {
// Find max amplitude, similar to MediaRecorder.getMaxAmplitude()
double maxAmplitude = 0;
for (short sample : buffer) {
double absSample = Math.abs(sample);
if (absSample > maxAmplitude) {
maxAmplitude = absSample;
}
}
// Calculate dB with calibration, inspired by MediaRecorder logic
double decibel = maxAmplitude > 0 ? 20 * Math.log10(maxAmplitude / BASE) + 90 : 0;
if (decibel > 30 && decibel < 120 && Double.isFinite(decibel)) { // Realistic range
lastDecibel = decibel;
volume.postValue((float) decibel);
lastSafeFor = getSafeFor(decibel);
int roundedDecibel = Math.round((float) decibel);
boolean shouldUpdateNotification = Math.abs(roundedDecibel - lastNotifiedDecibel) >= 1 ||
!lastSafeFor.equals(lastNotifiedSafeFor);
if (shouldUpdateNotification) {
lastNotifiedDecibel = roundedDecibel;
lastNotifiedSafeFor = lastSafeFor;
updateNotification();
Log.d(TAG, "Mic Volume Updated: " + decibel + " dB, Safe For: " + lastSafeFor +
", Source: " + (isBluetoothMic ? "Bluetooth (" + micSourceName + ")" : "Device (" + micSourceName + ")"));
}
} else {
Log.w(TAG, "Invalid decibel value: " + decibel + ", maxAmplitude: " + maxAmplitude);
}
} else {
Log.w(TAG, "No audio data read: " + read);
}
} catch (Exception e) {
Log.e(TAG, "Error reading audio data: " + e.getMessage(), e);
isRecording = false;
if (!isInitializing) {
reinitializeAudioRecord();
}
}
}
}
private String getSafeFor(double vol) {
if (vol <= 85) {
return "24h"; // Safe for extended periods
}
// NIOSH formula: Time (hours) = 8 / 2^((dB - 85)/3)
double hours = 8.0 / Math.pow(2.0, (vol - 85.0) / 3.0);
if (hours < 0.01) {
return "<1m"; // Less than a minute
}
if (hours < 1) {
int minutes = (int) Math.round(hours * 60);
return minutes + "m";
}
int roundedHours = (int) Math.round(hours);
return roundedHours + "h";
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "Destroying service...");
stopMonitoring();
releaseAudioRecord();
if (audioManager != null) {
audioManager.stopBluetoothSco();
audioManager.setBluetoothScoOn(false);
}
if (bluetoothHeadset != null && bluetoothAdapter != null) {
bluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, bluetoothHeadset);
}
if (scoReceiver != null) {
try {
unregisterReceiver(scoReceiver);
} catch (IllegalArgumentException e) {
Log.e(TAG, "SCO receiver not registered: " + e.getMessage());
}
}
try {
unregisterReceiver(bluetoothReceiver);
} catch (IllegalArgumentException e) {
Log.e(TAG, "Receiver not registered: " + e.getMessage());
}
if (micUpdateHandler != null) {
micUpdateHandler.removeCallbacksAndMessages(null);
}
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE);
service = null;
Log.d(TAG, "Service destroyed");
}
private void releaseAudioRecord() {
if (audioRecord != null) {
try {
if (isRecording) {
audioRecord.stop();
isRecording = false;
}
} catch (IllegalStateException e) {
Log.e(TAG, "Error stopping AudioRecord: " + e.getMessage());
} finally {
audioRecord.release();
audioRecord = null;
isRecording = false;
Log.d(TAG, "AudioRecord released");
}
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null && "ACTION_STOP_MONITOR".equals(intent.getAction())) {
Log.d(TAG, "Received stop action, stopping service");
stopForeground(true);
stopSelf();
return START_NOT_STICKY;
}
Log.d(TAG, "Service started or restarted");
if (!isRecording && !isInitializing) {
initAudioRecord();
}
return START_STICKY;
}
public MutableLiveData<Float> getVolume() {
return volume;
}
private String getDeviceName() {
String manufacturer = Build.MANUFACTURER;
String model = Build.MODEL;
if (model.startsWith(manufacturer)) {
return capitalize(model);
} else {
return capitalize(manufacturer) + " " + model;
}
}
private String capitalize(String s) {
if (s == null || s.isEmpty()) {
return s;
}
return s.substring(0, 1).toUpperCase() + s.substring(1);
}
@Override
public IBinder onBind(Intent intent) {
return binderMic;
}
public class LocalBinder extends Binder {
public MonitorServiceMic getService() {
return MonitorServiceMic.this;
}
}
}
Help mate. I need usage example