I have implemented ringtone behaviour, I want it to continuously ring until someone join the channel!
Currently, when I play the custom ringtone, as soon as I join the channel the ringtone gets stopped. I am trying to call 1 to 1 calling no group calling.
What I did
PhoneStateListener phoneStateListener = new PhoneStateListener() {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
if (state == TelephonyManager.CALL_STATE_RINGING) {
//Incoming call: Pause music
PhoneCallTunes.activateRingtone(mActivity);
} else if(state == TelephonyManager.CALL_STATE_IDLE) {
//Not in call: Play music
} else if(state == TelephonyManager.CALL_STATE_OFFHOOK) {
//A call is dialing, active or on hold
}
super.onCallStateChanged(state, incomingNumber);
}
};
private void phoneStateListener(){
TelephonyManager mgr = (TelephonyManager) mActivity.getSystemService(TELEPHONY_SERVICE);
if(mgr != null) {
mgr.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
}
I have implemented the above code and things are working fine but there is a lag in the ringtone as the pattern is like
Ringtone -- > channel join detected and AGORA forcefully stop the ring tone -- > then i override the CALL_STATE_RINGING to re-enable the ringtone
Related
Hello I'm building a stream music app and when I tried to set the setNotificationListener I receive this error and the app crashes
for the record, I already can show the notification but after reinstalling the app I receive this error
this is my code
public void startToPlay(Context context){
// Global settings.
playerNotificationManagerBuilder = new PlayerNotificationManager.Builder(context,
PLAYBACK_NOTIFICATION_ID,
PLAYBACK_CHANNEL_ID);
playerNotificationManagerBuilder.setSmallIconResourceId(R.drawable.ic_image_ip);
playerNotificationManagerBuilder.setNotificationListener(new PlayerNotificationManager.NotificationListener() {
#Override
public void onNotificationCancelled(int notificationId, boolean dismissedByUser) {
PlayerNotificationManager.NotificationListener.super.onNotificationCancelled(notificationId, dismissedByUser);
stopSelf();
}
#Override
public void onNotificationPosted(int notificationId, Notification notification, boolean ongoing) {
PlayerNotificationManager.NotificationListener.super.onNotificationPosted(notificationId, notification, ongoing);
if (ongoing) {
// Here Audio is playing, so we need to make sure the service will not get destroyed by calling startForeground.
startForeground(notificationId, notification);
} else {
//Here audio has stopped playing, so we can make notification dismissible on swipe.
stopForeground(false);
}
}
});
just create a channel for the notification before calling
startForeground(notificationId, notification)
override fun onNotificationPosted(
notificationId: Int,
notification: Notification,
ongoing: Boolean
) {
// create channel for the audio player notificaiton
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val importance = NotificationManager.IMPORTANCE_LOW
val channel = NotificationChannel(
"audio_player",
"channel_name",
importance
)
channel.setSound(null, null)
notificationManager.createNotificationChannel(channel)
}
startForeground(notificationId, notification)
}
I made a music player app and for pausing the song when the phone gets called i use this code.
But this needs the permission READ_PHONE_STATE which maybe scares off some users, so i would like to know if it's possible to achieve the same thing another way without asking for this permission?
My code
private void callStateListener(){
//incomingCallPause: checkbox value if user wants to pause when there is an incoming call.
incomingCallPause = storageUtil.loadSwitchOnCall();
mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
mPhoneStateListener = new PhoneStateListener(){
#Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_OFFHOOK:
if (mediaPlayer != null){
pauseSong();
}
break;
case TelephonyManager.CALL_STATE_RINGING:
if (incomingCallPause && mediaPlayer != null) {
pauseSong();
NotificationBuilder(PlaybackStatus.PAUSED);
incomingCall = true;
}
break;
case TelephonyManager.CALL_STATE_IDLE:
if (mediaPlayer != null) {
if (incomingCall) {
incomingCall = false;
NotificationBuilder(PlaybackStatus.PLAYING);
if (!mediaPlayer.isPlaying()){
mediaPlayer.start();
}
}
}
break;
}
super.onCallStateChanged(state, incomingNumber);
}
};
As per Android official document you need to define manifest permission to use TelephonyManager.
Requires Manifest.permission.READ_PHONE_STATE
Referral link:
https://developer.android.com/reference/android/telephony/TelephonyManager
I'm currently making an app that adapt the volume depending on the bluetooth device you are connected to.
I am having issue when I want to change the volume of the music
My app detect when a bluetooth device is connected and it can change the volume but it is like it goes too quickly
Here is some of the code:
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
BluetoothDevice d = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
deviceConnected(d);
}
else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
deviceDisconnected(d);
}
}
};
private void deviceConnected(BluetoothDevice bluetoothDevice) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
boolean showToasts = prefs.getBoolean("show_toasts", false);
if (showToasts) { Toast.makeText(this, getResources().getString(R.string.connected_to) + " " + bluetoothDevice.getName(), Toast.LENGTH_SHORT).show(); }
mDeviceDAO = new DeviceDAO(this);
mDeviceDAO.open();
DeviceOptions device = mDeviceDAO.select(bluetoothDevice.getAddress());
if (device == null) { return; }
if(device.getActivated() == 0) {
return;
}
int v = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC);
saveLastVolume(v);
int volume = device.getVolume();
int flag = 0;
if(prefs.getBoolean("show_am_ui", false)) {
flag = AudioManager.FLAG_SHOW_UI;
}
mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC,
volume,
flag);
}
If add a delay of around 3000ms when I change the volume, it will work properly but it's not a clean solution. In fact the toast for the connection will be displayed before the bluetooth device is "completely" connected
I also found an app that do the same thing but it seems that I'm doing the same thing.
Here is its githublink
What's your capture is the ACL connected or ACL disconnected, yes you are right once your toast appears i.e. the ACL connected does not mean the Bluetooth profile connected(exactly what you said "completely" connected), it only means the physical connection established between two device, you might as well to receive other event, e.g. HFP/A2dp connected, that's the real profile connected.
However the ACL disconnected is the milestone that the Bluetooth connection is really disconnected.
Developing a music app. In my Music Service, I have written a custom broadcast receiver. It works with Intent.ACTION_HEADSET_PLUG but not with Intent.ACTION_MEDIA_BUTTON.
Please guide on how to control music controls from bluetooth devices (Play/Pause/Next/Previous).
Code for Intent.ACTION_HEADSET_PLUG is:
#Override
public void onReceive(Context context, Intent intent) {
// aux
if(intent.getAction().equals(Intent.ACTION_HEADSET_PLUG))
{
int state = intent.getIntExtra("state", -1);
if(state == 0)
{
// Headset is unplugged. PAUSE
pauseSong();
sendBroadcast();
}
else if(state == 1)
{
// headset is plugged
resumeSong();
sendBroadcast();
}
}
}
As explained in the Media playback the right way talk, you must be registered as the 'preferred media app'. This is much easier when you use MediaSessionCompat as explained in the MediaSessionCompat video:
ComponentName mediaButtonReceiver =
new ComponentName(context, YourBroadcastReceiver.class);
MediaSessionCompat mediaSession =
new MediaSessionCompat(context,
tag, // Debugging tag, any string
mediaButtonReceiver,
null);
mediaSession.setFlags(
MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS |
MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mediaSession.setCallback(this); // a MediaSessionCompat.Callback
// This is what enables media buttons and should be called
// Immediately after getting audio focus
mediaSession.setActive(true);
The accepted answer doesn't seem to work
I achieved it the other way with ExoPlayer
step #1
added dependencies
implementation 'com.google.android.exoplayer:exoplayer:2.13.1'
implementation 'com.google.android.exoplayer:extension-mediasession:2.13.1'
step #2
mediaSession = MediaSessionCompat(this, "Vion")
val mediaSessionConnector = MediaSessionConnector(mediaSession!!)
mediaSessionConnector.setMediaButtonEventHandler(object: MediaSessionConnector.MediaButtonEventHandler{
override fun onMediaButtonEvent(player: Player, controlDispatcher: ControlDispatcher, mediaButtonEvent: Intent): Boolean {
val event: KeyEvent = mediaButtonEvent.getParcelableExtra(Intent.EXTRA_KEY_EVENT) ?: return false
if(event.action == KeyEvent.ACTION_UP) {
when(event.keyCode) {
KeyEvent.KEYCODE_MEDIA_PREVIOUS -> {
}
KeyEvent.KEYCODE_MEDIA_PAUSE -> {
}
KeyEvent.KEYCODE_MEDIA_PLAY -> {
}
KeyEvent.KEYCODE_MEDIA_NEXT -> {
}
}
}
return true
}
})
mediaSessionConnector.setPlayer(exoPlayer)
I am developing an Android application using Android Studio 0.5.2.
My application is operating a USB host which appears to operate correctly - unless the device is connected (and permissions have not yet been granted) when the app starts.
Generally, what happens when the USB is connected (while app is running):
"onResume" is called - this detects the device and asks for permission. An intent filter is created to catch when the USB is attached, detached or permission granted
Permission request is shown, select Ok
"onResume" is called again. The first line of this funciton is "super.onResume()"
As soon as I step over super.onResume, "Unfortunately, System UI has stopped" message is displayed and the Android UI crashes
My application continues to work correctly
If I connect the device while the application is already running there is no issue - this only occurs when the USB is connected on launch.
Any insight into what could be causing this or how to further narrow down on the problem would be greatly appreciated. I have attached the notable code below. I am not normally a Java developer, so I suspect the issue is something to do with the pause/resume behaviour, receivers, intent filters or permissions.
// *************************************************************
// ************************* USB Stuff *************************
// *************************************************************
boolean resumePermissionBlocked = false;
PendingIntent pendingIntent = null;
BroadcastReceiver receiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
// Get the information about what action caused this event
try {
String action = intent.getAction();
Log.i(TAG, "$EC: action:" + action);
if ("com.android.example.USB_PERMISSION".equals(action)) {
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
if (device != null) {
if (device.getProductId() == 0xAAAA) {
if (device.getVendorId() == 0xBBBB) {
// see if we have permission
UsbManager openManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
// send a message to the worker thread to begin opening a connection to this device
ThreadMsg msg = new ThreadMsg();
msg.request = MsgRequest.openConnection;
msg.objectA = device;
msg.objectB = openManager;
sendMessageToWorker(msg);
}
}
}
} else {
if (device != null)
Log("USB Permission denied", TextFormat.StrongWarning_withTime);
}
}
if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (device == null) return;
if (device.getProductId() == 0x003C) {
if (device.getVendorId() == 0x04D8) {
// see if we have permission
UsbManager openManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
if (!openManager.hasPermission(device)) {
resumePermissionBlocked = true; // block the resume function from trying to re-ask for permission
openManager.requestPermission(device, mPermissionIntent);
return;
}
ThreadMsg msg = new ThreadMsg();
msg.request = MsgRequest.openConnection;
msg.objectA = device;
msg.objectB = openManager;
sendMessageToBootloader(msg);
}
}
} else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
// If it was a USB device detach event, then get the USB device
// that caused the event from the intent.
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
ThreadMsg msg = new ThreadMsg();
msg.request = MsgRequest.closeConnection;
msg.objectA = device;
sendMessageToBootloader(msg);
}
} catch (Exception e) {
Log.i(TAG, "onResume catch: " + e.toString());
}
}
};
boolean receiverHasBeenRegistered = false;
PendingIntent mPermissionIntent;
#Override
public void onResume(){
try {
super.onResume();
if (resumePermissionBlocked) {
// this was resumed from a permission request - don't try to connect to the device now, leave it for the USB_PERMISSION_GRANTED intent
resumePermissionBlocked = false; // clear the flag
} else {
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
if (deviceList != null) {
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
Log.i(TAG, "$EC: Begin iteration");
while (deviceIterator.hasNext()) {
// Is this the device we are after?
UsbDevice device = deviceIterator.next();
if (device == null) return;
if (device.getProductId() == 0xAAAA) {
if (device.getVendorId() == 0xBBBB) {
// see if we have permission
UsbManager openManager = (UsbManager) this.getSystemService(Context.USB_SERVICE);
if (!openManager.hasPermission(device)) {
resumePermissionBlocked = true; // block the subsequent call to this (between the application resuming and permission being granted)
openManager.requestPermission(device, mPermissionIntent);
return;
}
ThreadMsg msg = new ThreadMsg();
msg.request = MsgRequest.openConnection;
msg.objectA = device;
msg.objectB = openManager;
sendMessageToWorker(msg);
}
}
}
}
}
} catch (Exception e)
{
Log.i(TAG, "onResume catch: " + e.toString());
}
if (!receiverHasBeenRegistered) {
// this line is for permissions
mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent("com.android.example.USB_PERMISSION"), 0);
//Create a new filter to detect USB device events
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
filter.addAction("com.android.example.USB_PERMISSION");
registerReceiver(receiver, filter);
pendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(getPackageName() + ".USB_PERMISSION"), 0);
receiverHasBeenRegistered = true;
}
}
#Override
public void onPause() {
/* unregister any receivers that we have */
try {
if (receiver != null && receiverHasBeenRegistered) {
unregisterReceiver(receiver);
receiverHasBeenRegistered = false;
}
} catch (Exception e) {
// if this happens, then the receiver was probably never registered in the first place
Log.i(TAG, "onPause catch: " + e.toString());
}
super.onPause();
}
Try super.onResume at the end of onResume method
Alert user to connect the usb device on application start (main activity's
onCreate) if device is not connected, toast an alert msg and finish/close
the main activity
Add intend filter in manifest file allowing the app to start automatically on connecting the device
Try to capture exception printstacktrack when the UI stopped message is
shown and debug further from the exception.
Make sure to follow the steps from android doc
https://developer.android.com/guide/topics/connectivity/usb/host.html