I'm totally new to mobile programming and to Xamarin or Xamarin.Forms. So I thought about starting small and trying a first app that is showing the phone number of an incoming call (just to know how to get this information into my app).
After a lot of trying and searching the net and not finding appropriate answers, I managed to at least be able to hit break points when there's an incoming call.
For that I created a class called StateListener in the Android specific project of my Xamarin.Forms solution. This class looks like that:
public class StateListener : PhoneStateListener
{
public override void OnCallStateChanged(CallState state, string incomingNumber)
{
base.OnCallStateChanged(state, incomingNumber);
switch (state)
{
case CallState.Ringing:
break; // <== set break point here
case CallState.Offhook:
break;
case CallState.Idle:
break;
}
}
}
And I instantiated this class in my MainActivity's OnCreate method like this:
StateListener phoneStateListener = new StateListener();
TelephonyManager telephonyManager = (TelephonyManager)GetSystemService(Context.TelephonyService);
telephonyManager.Listen(phoneStateListener, PhoneStateListenerFlags.CallState);
Now, when I run my little solution then the break point (see comment in code) is hit, but unfortunately the incomingNumber of the OnCallStateChanged method is always empty.
Following the unsatisfying documentation about the OnCallStateChanged method I set the needed Permission.ReadPhoneState permission in the manifest file, but that didn't help.
Maybe you can?
As doc says:
If application does not have READ_CALL_LOG permission or carrier
privileges (see TelephonyManager.hasCarrierPrivileges()), an empty
string will be passed as an argument.
so please check your application permissions.
It seems that once you emulated your app you have to set any new permissions manually inside the emulated Android, your app won't ask for it, since it's already installed.
After I gave my app the "Phone" permissions inside the emulated Android and restarted my app, I got the incomingNumber.
Related
I am writing some code for a Xamarin Forms Android app which dials a phone number but I don't want to dial the number if the user is currently on a call(whether incoming or outgoing, it doesn't make a difference). I have researched a lot about the phone state but I can't find what I am looking for, unless I am applying it incorrectly. What I need is something like this:
if (NoCurrentCallIsTakingPlace)
{
var uri = Android.Net.Uri.Parse(string.Format("tel:{0}", PhoneNumber));
var intent = new Intent(Intent.ActionCall, uri);
Xamarin.Forms.Forms.Context.StartActivity(CurrentIntent);
}
The code to dial the number works but it's the the conditional statement/code to check the phone state that I am having a problem with. Please could someone help. Apologies if your need more info. Please let me know and I will provide it. Thank you.
Well in native android you have the telephony manager to check what is the state of your device:
It has three states:
Idle: when it's idle there is no call
Offhook: when Off-hook it is in call
Ringing: when Ringing
var telephonyManagerService = (TelephonyManager)Xamarin.Forms.Forms.Context.GetSystemService(TelephonyService);
var getCurrentState = telephonyManagerService?.CallState;
switch (getCurrentState)
{
case CallState.Idle:
//No call
break;
case CallState.Ringing:
//Ringing
break;
case CallState.Offhook:
//On call
break;
default:
break;
}
Revert in case of queries
I've got an app which connect itself programatically to a wifi connection. My problem is, I want to handle the case, that the password is wrong. I want to detect that the password is not correct in runtime. To be precise I've got a progressdialog running while the connection is established, so if the password is wrong the progressdialog is just shown all the time and can't be skipped. A further note: I handled a password which is less than 8 characters by using this code:
if(!m_wifiManager.enableNetwork(netId, true)) {
progressDialogConnecting.dismiss();
createInfoMessageDialog(CONST.WIFI_CON_FAILED_TITLE, CONST.WIFI_CON_FAILED_MSG_CONFAILURE);
m_wifiManager.reconnect();
return;
}
If the key for the wifi connection is less than 8 characters, this if-case gets triggered. But if it is longer than 8 characters and wrong I get an endless state of showing the progress dialog.
What I exactly want to ask: how do I handle 1. wrong password 2. connection states (just like Android system showing me the toasts "Connected to Wifi xyz") ? AND is it even possible to handel the first one (wrong password)?
Here is the code, that did not work for handling connection established event (this is just the wifirecevier, I also registered it in the activity):
public class WifiReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (action.equals(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION)) {
if (intent.getBooleanExtra(WifiManager.EXTRA_SUPPLICANT_CONNECTED, false)){
if(wrongNetworkConnected)
progressDialogConnecting.dismiss();
}
}
} else {
}
}
}
}
Edit: What I am currently doing, is that I have a Handler which tells me to whom I am connected. That's useful because I can say that after the reconnect() I am reconnected to the old network (current network) and not the new one - so apparently the password could be wrong (or something else), because I could not connect to the new network.
The problem about this method is that first of all it takes too much time and secondly it is not reliable. I can lie and say that if you will get reconnected to your current network it is the fault of a wrong password, but actually it is not 100% sure that you cannot reconnect because of this - it may also have other reasons. So I am still searching for a simple feedback/handle from the suplicant that the password is wrong, just like the android api does in the wifi settings of each android device...
My problem is, I want to handle the case, that the password is wrong.
After some research I found this post which is not marked as answered but it still worked for me very well.
Here is the if-case in which the program jumps (already tested several times by me) if there is an authentication error --> e.g. wrong password:
int supl_error=intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, -1);
if(supl_error==WifiManager.ERROR_AUTHENTICATING){
// DO SOMETHING
}
NOTE: As seen in the linked post above this if-case should appear in a BroadcastReceiver adding the intent WifiManager.SUPPLICANT_STATE_CHANGED_ACTIONto the receiver-registration in your activity-class.
I'm using the >=4.3 NotificationListenerService to access notifications. On the first start, my app takes the user to the "Access Notifications" system panel, but I'd like to take the user there whenever the checkbox for my app in "Access Notifications" is disabled. I haven't found a isNotificationAccessEnabled()-method anywhere, but I definitely know that it's possible because apps like Krome do this, too.
Edit June 15th, 2016
I'm not sure which version of the support library this was added to, but it looks like this functionality is now built in. Simply use:
NotificationManagerCompat.getEnabledListenerPackages(context); (link to docs)
This returns a Set<String> that you can iterate through to find your package name. Note however that I haven't personally tested this. But it looks like it's probably preferred to use this in place of my old solution below.
Old Solution
This code is working for my app:
ContentResolver contentResolver = context.getContentResolver();
String enabledNotificationListeners = Settings.Secure.getString(contentResolver, "enabled_notification_listeners");
String packageName = context.getPackageName();
// check to see if the enabledNotificationListeners String contains our package name
if (enabledNotificationListeners == null || !enabledNotificationListeners.contains(packageName))
{
// in this situation we know that the user has not granted the app the Notification access permission
throw new Exception();
}
else
{
doSomethingThatRequiresNotificationAccessPermission();
}
Typical values that I've seen for the enabledNotificationsListeners String look like this:
User has given none of their apps Notification access permission
null or ""
User has given one app Notification access permission
"com.woodblockwithoutco.remotecontrollerexample/com.woodblockwithoutco.remotecontrollerexample.RemoteControlService"
User has given two apps Notification access permission
"com.scootrnova.android/com.scootrnova.android.ListenerService:com.woodblockwithoutco.remotecontrollerexample/com.woodblockwithoutco.remotecontrollerexample.RemoteControlService"
This implementation is very straightforward and works great :)
P.S. I got the idea to use the hardcoded "enabled_notification_listeners" String from this answer.
Starting with Android 8.1 (SDK 27) you can call isNotificationListenerAccessGranted on the NotificationManager. This is the correct API to use. Older Android versions should use getEnabledListenerPackages as a second best option. Relying on your listener callbacks can give incorrect results. See explanation below.
Im developer of Krome. What have I done to check if service is enabled is add public static variable that changes to true in onBind method and to false in unbind. That is how this service work.
Edit:
public static boolean isNotificationAccessEnabled = false;
#Override
public void onListenerConnected() {
isNotificationAccessEnabled = true;
}
#Override
public void onListenerDisconnected() {
isNotificationAccessEnabled = false;
}
Works well with slightly modified #Damians answer
public class NotifyListener extends NotificationListenerService{
public static boolean listnerConnected = false;
#Override
public IBinder onBind(Intent intent) {
Log.d(name,"onBind Called");
listnerConnected = true;
return super.onBind(intent);
}
#Override
public void onDestroy()
{
super.onDestroy();
Log.e("destroy", "called");
listnerConnected = false;
}
}
Starting with Android 8.1 (SDK 27) you can call isNotificationListenerAccessGranted on the NotificationManager. This is the correct API to use, not the one of the accepted answer. See explanation below.
Like shai tibber also already said the accepted answer is incorrect.
onListenerConnected() and onListenerDisconnect() can get called even when there is no NotificationListener access granted. So relying on this callbacks to set a boolean will give wrong results. And getEnabledListenerPackages(context) will just return all the packages that have an enabled notification listener defined in there AndroidManifest (android:enabled=true). It's NOT directly related to the user access. The documentation states exactly that:
Get the set of packages that have an enabled notification listener component within them.
Just a quick background I'm Running CM7 on a rooted Nexus one.
I am trying to detect when an outgoing call is actually connected: has stopped ringing and the person you are calling has answered. Looking through the forums this seems to be a tough and perhaps unanswered question. I'd really appreciate any insight into this.
In my searching the best I could find was in:
Android : How to get a state that the outgoing call has been answered?
#PattabiRaman said: "instead of detecting the outgoing call connection state, it is easy to get the duration of the last dialed call."
Does he mean that one should get the duration of the last dialed call as the call is in progress? And when that duration goes over 0 then you know?
The class com.android.internal.telephony.CallManager should have information about when the call actually is answered. It has a public static method getInstance() which returns the CallManager instance, and a public method getActiveFgCallState() which returns the current call state as a Call.State enum.
So in theory something like this might work:
Method getFgState = null;
Object cm = null;
try {
Class cmDesc = Class.forName("com.android.internal.telephony.CallManager");
Method getCM = cmDesc.getMethod("getInstance");
getFgState = cmDesc.getMethod("getActiveFgCallState");
cm = getCM.invoke(null);
} catch (Exception e) {
e.printStackTrace();
}
And then repeatedly poll the state:
Object state = getFgState.invoke(cm);
if (state.toString().equals("IDLE")) {
...
} else if (state.toString().equals("ACTIVE")) {
// If the previous state wasn't "ACTIVE" then the
// call has been established.
}
I haven't verified that this actually works. And even if it does you'll have to keep in mind that the API could change, since this isn't something that app developers are supposed to rely on.
I have looked into the code.
It will always give null unless you instantiate a Phone object and set it as default Phone.
But instantiating it needs some System permissions allowed only to system aps.
By using this method:
com.android.internal.telephony.PhoneFactory# public static void makeDefaultPhones(Context context) {
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.4_r1.2/com/android/internal/telephony/PhoneFactory.java
This question is more of a "is it possible" question.
Is it possible for Android to use a different contact list from an application when displaying the identity of an incoming caller?
So for example, if a person calls and their identity is in the apps contact list, but NOT in the internal devices contact list, i want to make the id found in the apps contact list show up on the caller id of the incoming call.
Any insight would be appreciated!
I found the solution I was looking for. I ended up just using a notification to display who was calling. I hope this helps someone else out there looking for a nice solution!
StateListener yourListener = new StateListener();
TelephonyManager yourmanager =(TelephonyManager)getSystemService(TELEPHONY_SERVICE);
yourmanager.listen(yourListener, PhoneStateListener.LISTEN_CALL_STATE);
class StateListener extends PhoneStateListener{
#Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
switch(state){
case TelephonyManager.CALL_STATE_RINGING:
//do what you want with the incoming number here:
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
break;
case TelephonyManager.CALL_STATE_IDLE:
break;
}
};
public void onDestroy() {
}
}
I think it's possible with broadcast receiver. Here some similar question Call block, Taking complete control of phone, is it possible and another open source app called Intent Intercept. I give you call blocking link so you can catch number, direct to your app and block normal dialer