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
Related
I'm trying to handle the event when a user presses "ok" or "cancel" on the automatic permission dialog presented when I connect a "known" USB device to the android phone.
I'm using the android.usb.host library and can send and receive between the android phone and the device. Futhermore do I handle the "USB_DEVICE_ATTACHED" and "USB_DEVICE_DETACHED" using a BroadcastReceiver without any problems.
I want to enable a sort of "autoconnect" feature and therefore I need to know when the user has pressed "ok" in the automatically displayed permission dialog, but I can't find anything online at all. All I find is "bypass dialog", but this is not what I want or need.
When I connect the usb device to the android phone, a permission dialog is automatically displayed because I use the "device_filter.xml" solution from androids documentation which can be seen here Android Usb Docs.
This is how I handle the USB_DEVICE_ATTATCHED and USB_DEVICE_DETACHED events:
public NativeUsbService(ReactApplicationContext reactContext) {
...
// register device attached/detached event listeners
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
reactContext.registerReceiver(usbReceiver, filter);
...
}
And then the Broadcast Receiver:
private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
synchronized (this) {
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if(device != null){
usbDevice = device;
} else {
Log.d(TAG, "onReceive: DEVICE WAS ATTACHED AND WAS NULL :(");
}
}
} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
Log.d(TAG, "onReceive: Device was detached!");
if(connection != null) {
connection.releaseInterface(usbDeviceInterface);
connection.close();
}
connection = null;
usbDevice = null;
endpointIn = null;
endpointOut = null;
}
}
};
I have tried multiple different approaches, but nothing has worked.
I have tried getting the user response in from the intent, like with a manual permission request like below:
private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
synchronized (this) {
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if(device != null){
usbDevice = device;
// THIS DOES NOT WORK ↓↓↓
if(intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
// The code never gets here...
}
} else {
Log.d(TAG, "onReceive: DEVICE WAS ATTACHED AND WAS NULL :(");
sendEvent("onDeviceAttached", false);
}
}
} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
if(connection != null) {
connection.releaseInterface(usbDeviceInterface);
connection.close();
}
connection = null;
usbDevice = null;
endpointIn = null;
endpointOut = null;
}
}
};
I have also tried by adding a usb permission listener to the broadcast receiver by first adding the action name to my class variables:
private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
Then adding the action to my intent filter like so:
public NativeUsbService(ReactApplicationContext reactContext) {
// register device attached/detached event listeners
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
filter.addAction(ACTION_USB_PERMISSION); // added action to my intent filter
reactContext.registerReceiver(usbReceiver, filter);
}
And finally reacting to the action like so:
private final BroadcastReceiver usbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(UsbManager.ACTION_USB_DEVICE_ATTACHED)) {
synchronized (this) {
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if(device != null){
usbDevice = device;
}
}
} else if (action.equals(UsbManager.ACTION_USB_DEVICE_DETACHED)) {
Log.d(TAG, "onReceive: Device was detached!");
if(connection != null) {
connection.releaseInterface(usbDeviceInterface);
connection.close();
}
connection = null;
usbDevice = null;
endpointIn = null;
endpointOut = null;
sendEvent("onDeviceDetached", true);
}
else if (action.equals(ACTION_USB_PERMISSION)) {
Log.d(TAG, "onReceive: ACTION_USB_PERMISSION");
if(intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
Log.d(TAG, "onReceive: EXTRA_PERMISSION_GRANTED = true");
} else Log.d(TAG, "onReceive: EXTRA_PERMISSION_GRANTED = false");
}
}
};
Please make me aware of any missing information.
Any help is greatly appreciated.
Answering my own question in case someone else is facing the same issue.
I though about manually requesting the permission again, after permission was granted, since it is possible to handle this manual permission request when user presses an option in the dialog. I discarded this idea, not because it wouldn't work, but because I saw it as unecessary for the user to also have to press another dialog after the initial (automatic dialog).
I must add that I have not implemented this solution, so I do not know with certainty that it would prompt the user again, but I have had trouble with the manual permission request previously. If you want to try this approach the method belongs to the UsbManager class and is invoke like so usbManger.requestPermission(usbDevice).
I ended up with a solution where I start a thread which runs a loop calling usbManager.hasPermission(usbDevice) until it has permission and then emits an event (emitting this event is my use case, implement it how you like).
The solution can be seen here:
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import com.facebook.react.bridge.ReactApplicationContext;
...
private static volatile boolean permissionThreadShouldStop = false;
private static Thread activePermissionThread = null;
...
public static void usbPermissionEventEmitter(ReactApplicationContext reactContext, UsbManager usbManager, UsbDevice usbDevice) {
if((activePermissionThread != null && activePermissionThread.isAlive())) {
activePermissionThread.interrupt();
}
permissionThreadShouldStop = false;
activePermissionThread = new Thread(new Runnable() {
#Override
public void run() {
while (!usbManager.hasPermission(usbDevice) && !permissionThreadShouldStop) {
try {
Thread.sleep(30);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
if(usbManager.hasPermission(usbDevice)) {
sendEvent(reactContext, "onUsbPermissionGranted", true);
}
}
});
activePermissionThread.start();
}
The ReactApplicationContext can be swapped with the normal android context. But this is for a react native module, so I use the reactContext.
I hope this will be helpful for someone, because i'm honestly really surpriced how scarse the android documentation is in regards to implementing Usb functionality using the android.hardware.usb library.
Also in general when searching for information online, I have often found myself lost since there is very little information on this subject.
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
Here I make a call from my app and I want to count missed or reject call
and I use a counter (counterNoiseCalling) to count missed or reject call but in node in Firebase counter still always equal 0 and I don't know why.
private class MyPhoneListener extends PhoneStateListener {
private boolean onCall = false;
String userNameCalling = UserDetails.username;
int counterNoisingCall = 0;
#Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
// phone ringing...
Toast.makeText(Call.this, incomingNumber + " calls you", Toast.LENGTH_LONG).show();
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
// one call exists that is dialing, active, or on hold
Toast.makeText(Call.this, "on call...", Toast.LENGTH_LONG).show();
if(!onCall) {
//because user answer the incoming call
Toast.makeText(Call.this, "The call is being answered", Toast.LENGTH_SHORT).show();
}else {
// reject call or missed call
Toast.makeText(Call.this, "Number Busy Or No Reply", Toast.LENGTH_SHORT).show();
counterNoisingCall++;
}
break;
case TelephonyManager.CALL_STATE_IDLE:
// in initialization of the class and at the end of phone call
// detect flag from CALL_STATE_OFFHOOK
if (onCall == true) {
Toast.makeText(Call.this, "restart app after call to Users List", Toast.LENGTH_LONG).show();
// restart our application to return to Our Contact
Intent restart = new Intent(Call.this , Users.class);
restart.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(restart);
onCall = false;
}
break;
default:
break;
}
Firebase childRef = mRootRef.child(userNameCalling);
childRef.setValue(counterNoisingCall);
}
}
If you can debug and see that counterNoisingCall variable is not 0 then the problem seem to be related to Firebase.
I guess that may be is related to security rules. Maybe the user does not have permission write on the Firebase node.
I would put a complete listener on setValue to see that the command completed with success.
Try this.
Firebase childRef = mRootRef.child(userNameCalling);
childRef.setValue(counterNoisingCall).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (!task.isSuccessful()) {
Log.d("",task.getException().getMessage());
}
}
});
I am facing this error while disabling Bluetooth. Googled it but did not find solution. Here is my broadcast receiver which invokes when bluetooth state is changed.
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
Log.e(LOG_TAG, "Broadcast receiver - onReceive");
if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
h.removeCallbacks(runnable);
final int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
switch (state) {
case BluetoothAdapter.STATE_OFF:
Log.i(LOG_TAG, "***** BLE-Bluetooth is disabled");
Toast.makeText(getApplicationContext(), "Bluetooth is disabled", Toast.LENGTH_LONG).show();
Log.i(TAG, "***** mBeaconScanner "+ mBeaconScanner);
if(null != mBeaconScanner)
mBeaconScanner.scanLeDevice(false);
break;
case BluetoothAdapter.STATE_ON:
Log.i(LOG_TAG, "***** BLE-Bluetooth is enabled");
Toast.makeText(getApplicationContext(), "Bluetooth is enabled", Toast.LENGTH_LONG).show();
if(null != mBeaconScanner)
mBeaconScanner.scanLeDevice(true);
break;
}
}
}
};
In the above code I am trying to stop ble scanning while disabling the Bluetooth. In the above code method "mBeaconScanner.scanLeDevice(false);" redirects to :
Log.i(TAG, "***** Stopping BLE Scan for Android version " + Build.VERSION.SDK_INT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (bluetoothLeScanner != null)
{
bluetoothLeScanner.stopScan(mScanCallback);
}
} else {
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
In the above snippet I am getting Exception when calling bluetoothLeScanner.stopScan(mScanCallback) method.
NOTE: this issue is not reproducible in all the devices but nexus and its not very frequently reproducible as well for me.
Please suggest me if there is any solution for this.. Thanks in advance
The only solution I can suggest here is comment this code:-
//mBeaconScanner.scanLeDevice(false);
The way BLE scan works is pretty complicated at the radio level. There are tons of factors (like power, efficiency etc..) that are taken into consideration while arriving at the right scan window sleep time etc..
For you in this particular case when the BT adapter itself is turned OFF, trying to stop scan is an invalid operation as far as the radio is concerned. Am glad that Android FW throws this exception !! Just get rid of this code and may be do some internal app specific state transition (if you have to) or else just log and be done in case BluetoothAdapter.STATE_OFF:
In my application I am copying all the Call Logs into a Database in an Async Task. But If a call comes in between my application can get terminated leading to incomplete data. So first of all I want to stop my application from getting terminated in such scenario, so what should I do? Also I want to keep track of cursor such that when application is opened again it will continue from the exact same place where it stopped and complete the job.
This is the code that helped me save my game state during incoming call.. Hope it helps you too...
TelephonyManager tm;
private PhoneStateListener mPhoneListener = new PhoneStateListener() {
public void onCallStateChanged(int state, String incomingNumber) {
try {
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
Toast.makeText(GameActivity.this, "CALL_STATE_RINGING", Toast.LENGTH_SHORT).show();
//Your function to save state right here...
stopTimer();
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
Toast.makeText(GameActivity.this, "CALL_STATE_OFFHOOK", Toast.LENGTH_SHORT).show();
break;
case TelephonyManager.CALL_STATE_IDLE:
Toast.makeText(GameActivity.this, "CALL_STATE_IDLE", Toast.LENGTH_SHORT).show();
break;
default:
Toast.makeText(GameActivity.this, "default", Toast.LENGTH_SHORT).show();
Log.i("Default", "Unknown phone state=" + state);
}
} catch (Exception e) {
Log.i("Exception", "PhoneStateListener() e = " + e);
}
}
};