I would like to bring up a Toast and vibrate when call status is Idle.
I added this in the manifest file:
<receiver android:name="IncomingCallInterceptor">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE"/>
</intent-filter>
</receiver>
And this is IncomingCallInterceptor
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Vibrator;
import android.telephony.TelephonyManager;
import android.widget.Toast;
import android.app.Activity;
public class IncomingCallInterceptor extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent)
{
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
String msg = "Phone state changed to " + state;
if (TelephonyManager.EXTRA_STATE_RINGING.equals(state))
{
String incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
msg += ". Incoming number is " + incomingNumber;
}
Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
if(msg == "IDLE")
{
ok() ;
}
}
public void ok()
{
Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(3000);
}
}
}
And I received this error:
Description Resource Path Location Type
The method getSystemService(String) is undefined for the type IncomingCallInterceptor IncomingCallInterceptor.java line 39 Java Problem
Make Sure You Have Added VIBRATE permission in manifest as:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="...">
<uses-permission android:name="android.permission.VIBRATE"/>
<application android:label="...">
...
</application>
</manifest>
change Your Code as:
public class IncomingCallInterceptor extends BroadcastReceiver {
private Context contextc;
#Override
public void onReceive(Context context, Intent intent)
{
this.contextc=context;
//YOUR CODE HERE
public void ok()
{
Vibrator v = (Vibrator)contextc.getSystemService(Context.VIBRATOR_SERVICE);
v.vibrate(3000);
}
Related
I have built full voice recorder application.
I would like to start recording when a voice call starts on the phone, how can I detect the Calls state? tried some code and it didn't work for me.
I just need to know hot to start recording when a voice call starts (incoming and outgoing).
Here is an example of what you need.
Declare receiver in AndroidManifest
<receiver android:name=".IncomingCall">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
</receiver>
Give read phone state permission in AndroidManifest
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
Create a class IncomingCall with extends BroadcastReceiver class
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.widget.Toast;
/**
* Created by matheszabi on Aug/20/2017 0020.
*/
public class IncomingCall extends BroadcastReceiver {
private Context context;
public void onReceive(Context context, Intent intent) {
this.context = context;
try {
// TELEPHONY MANAGER class object to register one listner
TelephonyManager tmgr = (TelephonyManager) context
.getSystemService(Context.TELEPHONY_SERVICE);
//Create Listner
MyPhoneStateListener PhoneListener = new MyPhoneStateListener();
// Register listener for LISTEN_CALL_STATE
tmgr.listen(PhoneListener, PhoneStateListener.LISTEN_CALL_STATE);
} catch (Exception e) {
Log.e("Phone Receive Error", " " + e);
}
}
private class MyPhoneStateListener extends PhoneStateListener {
public void onCallStateChanged(int state, String incomingNumber) {
Log.d("MyPhoneListener",state+" incoming no:"+incomingNumber);
if (state == 1) {
String msg = "New Phone Call Event. Incomming Number : "+incomingNumber;
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(context, msg, duration);
toast.show();
}
}
}
}
Above Android 6.0 you need to handle a bit different the permissions:
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
private static final int MY_REQUEST_CODE = 1234;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
protected void onResume() {
super.onResume();
if (checkSelfPermission(Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.READ_PHONE_STATE},
MY_REQUEST_CODE);
}
}
public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == MY_REQUEST_CODE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Now user should be able to use camera
Toast.makeText(this, "I have access", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "I DON'T have access", Toast.LENGTH_SHORT).show();
}
}
}
}
You must allow the permissions at the first time run:
Here is the screenshot of the working code:
I found how to do so:
package com.example.tsuryohananov.mycallrecorder;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.widget.Toast;
/**
* Created by tsuryohananov on 20/08/2017.
*/
public class MyPhoneReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Bundle extras = intent.getExtras();
if (extras != null) {
String state = extras.getString(TelephonyManager.EXTRA_STATE);
Log.d("MY_DEBUG_TAG", state);
if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
String phoneNumber = extras.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
Log.d("MY_DEBUG_TAG", phoneNumber);
// here i need to save the number for the listview.
}
if ((state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK))){
String phoneNumber = extras.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
Toast.makeText(context,"Answered" + phoneNumber, Toast.LENGTH_SHORT).show();
MainActivity.recordStart();
}
if (state.equals(TelephonyManager.EXTRA_STATE_IDLE)){
Toast.makeText(context,"Idle State", Toast.LENGTH_SHORT).show();
MainActivity.stopRecord();
}
}
}
}
How to use ussdinterceptor service in android is question which is asked a lot. and the responses for it are poor.
this is my response.
first the service it self.
classes for the package com.example.ussdcodes
BootReceiver.java
package com.example.ussdcodes;
//import com.example.ussdcodes.R;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Log.d("USSDService", context.getString(R.string.service_started));
context.startService(new Intent(context,USSDDumbExtendedNetworkService.class));
}
}
USSDDumbExtendedNetworkService.java
package com.example.ussdcodes;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.IBinder;
import android.os.PatternMatcher;
import android.os.RemoteException;
import android.util.Log;
import com.android.internal.telephony.IExtendedNetworkService;
//import com.android.internal.telephony.IExtendedNetworkService;
import com.example.ussdcodes.R;
/**
* Service implements IExtendedNetworkService interface.
* USSDDumbExtendedNetworkService
* Service must have name "com.android.ussd.IExtendedNetworkService" of the intent declared
* in the Android manifest file so com.android.phone.PhoneUtils class bind
* to this service after system rebooted.
* Please note service is loaded after system reboot!
* Your application must check is system rebooted.
* #see Util#syslogHasLine(String, String, String, boolean)
*/
public class USSDDumbExtendedNetworkService extends Service {
public static final String TAG = "CommandusUSSDExtNetSvc";
public static final String LOG_STAMP = "*USSDTestExtendedNetworkService bind successfully*";
public static final String URI_SCHEME = "ussdcodes";
public static final String URI_AUTHORITY = "android.com";
public static final String URI_PATH = "/";
public static final String URI_PAR = "return";
public static final String URI_PARON = "on";
public static final String URI_PAROFF = "off";
public static final String MAGIC_ON = ":ON;)";
public static final String MAGIC_OFF = ":OFF;(";
public static final String MAGIC_RETVAL = ":RETVAL;(";
public static final String RESULT = "result";
private static boolean mActive = true;
private static CharSequence mRetVal = null;
private Context mContext = null;
private String msgUssdRunning = "USSD running...";
final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_INSERT.equals(intent.getAction())) {
mContext = context;
if (mContext != null) {
msgUssdRunning = mContext.getString(R.string.USSD_run);
mActive = true;
Log.d(TAG, "activate");
}
} else if (Intent.ACTION_DELETE.equals(intent.getAction())) {
mContext = null;
mActive = false;
Log.d(TAG, "deactivate");
}
}
};
private final IExtendedNetworkService.Stub mBinder = new IExtendedNetworkService.Stub() {
#Override
public void setMmiString(String number) throws RemoteException {
Log.d(TAG, "setMmiString: " + number);
Intent intent =new Intent("com.example.ussdcodes.setmmistring") ;
intent.putExtra("ussdCode", number);
sendBroadcast(intent);
}
#Override
public CharSequence getMmiRunningText() throws RemoteException {
Log.d(TAG, "getMmiRunningText: " + msgUssdRunning);
return msgUssdRunning;
}
#Override
public CharSequence getUserMessage(CharSequence text)
throws RemoteException {
/* if (MAGIC_ON.contentEquals(text)) {
mActive = true;
Log.d(TAG, "control: ON");
return text;
} else {
if (MAGIC_OFF.contentEquals(text)) {
mActive = false;
Log.d(TAG, "control: OFF");
return text;
} else {
if (MAGIC_RETVAL.contentEquals(text)) {
mActive = false;
Log.d(TAG, "control: return");
return mRetVal;
}
}
}
if (!mActive) {
Log.d(TAG, "getUserMessage deactivated: " + text);
return text;
} */
String s = text.toString();
// store s to the !
Uri uri = new Uri.Builder()
.scheme(URI_SCHEME)
.authority(URI_AUTHORITY)
.path(URI_PATH)
.appendQueryParameter(URI_PAR, text.toString())
.build();
// Intent intent =new Intent(Intent.ACTION_GET_CONTENT, uri) ;
// Intent intent =new Intent("com.example.ussdcodes", uri) ;
Intent intent =new Intent("com.example.ussdcodes.getusermsg") ;
intent.putExtra("result", s);
sendBroadcast(intent);
mActive = false;
mRetVal = text;
Log.d(TAG, "getUserMessage: " + text + "=" + s);
return null;
}
#Override
public void clearMmiString() throws RemoteException {
Log.d(TAG, "clearMmiString");
}
};
/**
* Put stamp to the system log when PhoneUtils bind to the service
* after Android has rebooted. Application must call {#link Util#syslogHasLine(String, String, String, boolean)} to
* check is phone rebooted or no. Without reboot phone application does not bind tom this service!
*/
#Override
public IBinder onBind(Intent intent) {
// Do not localize!
Log.i(TAG, LOG_STAMP);
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_INSERT);
filter.addAction(Intent.ACTION_DELETE);
filter.addDataScheme(URI_SCHEME);
filter.addDataAuthority(URI_AUTHORITY, null);
filter.addDataPath(URI_PATH, PatternMatcher.PATTERN_LITERAL);
registerReceiver(mReceiver, filter);
return mBinder;
}
public IBinder asBinder() {
Log.d(TAG, "asBinder");
return mBinder;
}
}
the aidl for com.android.internal.telephony
IExtendedNetworkService.aidl
package com.android.internal.telephony;
/**
* Interface used to interact with extended MMI/USSD network service.
*/
interface IExtendedNetworkService {
/**
* Set a MMI/USSD command to ExtendedNetworkService for further process.
* This should be called when a MMI command is placed from panel.
* #param number the dialed MMI/USSD number.
*/
void setMmiString(String number);
/**
* return the specific string which is used to prompt MMI/USSD is running
*/
CharSequence getMmiRunningText();
/**
* Get specific message which should be displayed on pop-up dialog.
* #param text original MMI/USSD message response from framework
* #return specific user message correspond to text. null stands for no pop-up dialog need to show.
*/
CharSequence getUserMessage(CharSequence text);
/**
* Clear pre-set MMI/USSD command.
* This should be called when user cancel a pre-dialed MMI command.
*/
void clearMmiString();
}
the manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.ussdcodes"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="21" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<receiver android:name="com.example.ussdcodes.BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service
android:name=".USSDDumbExtendedNetworkService" >
<intent-filter android:icon="#drawable/ic_launcher">
<action android:name="com.android.ussd.IExtendedNetworkService" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
</application>
</manifest>
now the activity that uses the service (don't forgot to reboot the phone after installing the service)
MainActivity.java
package com.example.usingussdservice;
import android.app.Activity;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
public class MainActivity extends Activity {
UssdMsg ussdMsg = new UssdMsg();
UssdsetMmi ussdsetMmi = new UssdsetMmi();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ussdMsg.setMainActivityHandler(this);
ussdsetMmi.setMainActivityHandler(this);
// com.example.ussdcodes
IntentFilter fltr_ussdmsg = new IntentFilter("com.example.ussdcodes.getusermsg");
registerReceiver(ussdMsg,fltr_ussdmsg);
IntentFilter fltr_setmmistring = new IntentFilter("com.example.ussdcodes.setmmistring");
registerReceiver(ussdsetMmi,fltr_setmmistring);
}
public void onClick(View v) {
sendUssd( ) ;
}
public void sendUssd( )
{
//codeToSend=*600*1*#
String codeToSend ;
codeToSend = "tel:" + Uri.encode("*") + "600"+ Uri.encode("*")+"1"+ Uri.encode("#");
// codeToSend = "tel:" + codeUssd ;
startActivityForResult(new Intent("android.intent.action.CALL",
Uri.parse(codeToSend)), 1);
}
}
UssdMsg.java
package com.example.usingussdservice;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.util.Log;
import android.widget.Toast;
public class UssdMsg extends BroadcastReceiver {
// Get the object of SmsManager
MainActivity mainActivity = null;
void setMainActivityHandler(MainActivity mainActivity ){
this.mainActivity=mainActivity;
}
public void onReceive(Context context, Intent intent) {
setMainActivityHandler(mainActivity);
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(context,
"inside UssdMsg.onReceive", duration);
toast.show();
Bundle bundle = intent.getExtras();
if (bundle != null) {
String string = bundle.getString("result");
Toast toast2 = Toast.makeText(context,
"result="+string, duration);
toast2.show();
}
}
}
UssdsetMmi.java
package com.example.usingussdservice;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.util.Log;
import android.widget.Toast;
public class UssdsetMmi extends BroadcastReceiver {
// Get the object of SmsManager
MainActivity mainActivity = null;
void setMainActivityHandler(MainActivity mainActivity ){
this.mainActivity=mainActivity;
}
final SmsManager sms = SmsManager.getDefault();
public void onReceive(Context context, Intent intent) {
setMainActivityHandler(mainActivity);
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(context,
"inside UssdsetMmi.onReceive", duration);
toast.show();
Bundle bundle = intent.getExtras();
if (bundle != null) {
String string = bundle.getString("ussdCode");
Toast toast2 = Toast.makeText(context,
"ussdCode="+string, duration);
toast2.show();
}
}
}
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.usingussdservice"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="21" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.CALL_PHONE"/>
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="${relativePackage}.${activityClass}" >
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginLeft="35dp"
android:layout_marginTop="34dp"
android:onClick="onClick"
android:text="send ussd code" />
</RelativeLayout>
I Could not figure out what is wrong with below code. I also checked about registering receiver twice. But that's also not the case. or may be I am missing something.
Could any please help. I really need it. :(
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.IBinder;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.widget.Toast;
/**
*
* #author Bharat
*
*/
public class CallNotifierService extends Service
{
private static final String ACTION_IN = "android.intent.action.PHONE_STATE";
private static final String ACTION_OUT = "android.intent.action.NEW_OUTGOING_CALL";
private CallBr br_call;
#Override
public IBinder onBind(Intent arg0)
{
return null;
}
#Override
public void onDestroy()
{
Log.d("service", "destroy");
this.unregisterReceiver(this.br_call);
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
{
final IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_OUT);
filter.addAction(ACTION_IN);
this.br_call = new CallBr();
this.registerReceiver(this.br_call, filter);
return START_NOT_STICKY;
}
public class CallBr extends BroadcastReceiver
{
Bundle bundle;
String state;
String inCall, outCall;
public boolean wasRinging = false;
public boolean answered = false;
public boolean outgoing = false;
#Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction().equals(ACTION_IN))
{
if ((bundle = intent.getExtras()) != null)
{
state = bundle.getString(TelephonyManager.EXTRA_STATE);
if (state.equals(TelephonyManager.EXTRA_STATE_RINGING))
{
inCall = bundle.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
wasRinging = true;
Toast.makeText(context, "Incoming Call : " + inCall, Toast.LENGTH_LONG).show();
}
else if (state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK))
{
if (wasRinging == true)
{
answered = true;
Toast.makeText(context, "Answered", Toast.LENGTH_LONG).show();
}
}
else if (state.equals(TelephonyManager.EXTRA_STATE_IDLE))
{
wasRinging = false;
Toast.makeText(context, "Disconnected", Toast.LENGTH_LONG).show();
}
}
}
else if (intent.getAction().equals(ACTION_OUT))
{
if ((bundle = intent.getExtras()) != null)
{
outCall = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
Toast.makeText(context, "Outgoing Call : " + outCall, Toast.LENGTH_LONG).show();
outgoing = true;
}
}
}
}
}
Below is the activity I am calling it from
public class MyActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.roaming);
Intent intent = new Intent(this, CallNotifierService.class);
startService(intent);
}
.
.
.
}
Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ind.cosmos.main"
android:versionCode="1"
android:versionName="1.0" >
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-sdk
android:minSdkVersion="22"
android:targetSdkVersion="22" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name" >
<!-- android:theme="#style/AppTheme"> -->
<activity
android:name="ind.cosmos.main.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name="ind.cosmos.callRecord.CallNotifierService" />
</application>
As Bharat said it's better so handle it in code. Below is a trick to do so, found here, thanks to Michael Marvick who has given detail description as well.
public class PhoneStateBroadcastReceiver extends BroadcastReceiver {
public static final String TAG = "PHONE STATE";
private static String mLastState;
#Override
public void onReceive(Context context, Intent intent) {
String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
if (!state.equals(mLastState)) {
mLastState = state;
Log.e(TAG, state);
}
}
}
Actually .. there is nothing wrong with the code. its System who triggers this. some time its 2 times.. other times it touches 4 times.
So the best we can do it handle it in the code itself.
I have the same behavior, and there is no issue in your code. android.intent.action.PHONE_STATE can be received few times, but Intent's bundle is always different, for example:
D/App(2001): Bundle{ state => OFFHOOK; subscription => 1; }Bundle
D/App(2001): Bundle{ incoming_number => 555555555; state => OFFHOOK; subscription => 9223372036854775807; }Bundle
I wrote a simple class that extends BroadcastReceiver to handle an incoming call and get the caller's number. Now I want to display that number in a popup window or something similar. I've tried to display an alert dialog or open an activity but my app crashed. Then I read that it's not a good idea to start dialog from a BroadcastReceiver class. I need you to advice me how to correctly display the caller's number on the screen when there's an incoming call. This is my Broadcast Receiver class:
package com.example.nrsearch;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
public class CallReceiver extends BroadcastReceiver {
public CallReceiver() {
}
#Override
public void onReceive(Context context, Intent intent) {
Log.i("CallReceiverBroadcast", "onReceive() is called. ");
TelephonyManager teleMgr = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
PhoneStateListener psl = new PhoneStateListener() {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
Log.i("CallReceiverBroadcast", "onCallStateChanged() is called. ");
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
Log.i("CallReceiverBroadcast", "Incoming call caught. Caller's number is " + incomingNumber + ".");
}
}
};
teleMgr.listen(psl, PhoneStateListener.LISTEN_CALL_STATE);
teleMgr.listen(psl, PhoneStateListener.LISTEN_NONE);
}
}
I created a IncomingCallService service that extends from BroadcastReceiver to handle an incoming call and get the caller's number. Now I displayed that Incoming caller number in a popup window like TrueCaller. Its working fine me. Hope it should helpful for you. Please try and let me know. Thanks
CallReceiver.java
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
public class CallReceiver extends BroadcastReceiver {
static String IncomingNumber;
#Override
public void onReceive(final Context context, Intent intent) {
TelephonyManager teleMgr = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
PhoneStateListener psl = new PhoneStateListener() {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
IncomingNumber = incomingNumber;
Log.i("CallReceiverBroadcast", "Incoming call caught. Caller's number is " + incomingNumber + ".");
Intent i = new Intent(context, IncomingCallService.class);
context.startService(i);
case TelephonyManager.CALL_STATE_IDLE:
Log.i("CallReceiverBroadcast", "CALL_STATE_IDLE");
IncomingCallService.clearView(context);
// Call Disconnected
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
Log.i("CallReceiverBroadcast", "CALL_STATE_OFFHOOK");
IncomingCallService.clearView(context);
// Call Answer Mode
break;
}
}
};
teleMgr.listen(psl, PhoneStateListener.LISTEN_CALL_STATE);
teleMgr.listen(psl, PhoneStateListener.LISTEN_NONE);
}
}
IncomingCallService.java:
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
public class IncomingCallService extends Service {
static View view = null;
static WindowManager wm;
static LayoutInflater inflater;
static WindowManager.LayoutParams params;
private static TextView txtIncomingnumber;
static boolean processingAction = false;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onCreate();
params = new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT, 350,
WindowManager.LayoutParams.TYPE_PHONE, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);
params.gravity = Gravity.LEFT | Gravity.TOP;
params.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
wm = (WindowManager) getSystemService(WINDOW_SERVICE);
inflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.activity_main,null);
wm.addView(view, params);
txtIncomingnumber = (TextView)view.findViewById(R.id.txtIncomingnumber);
txtIncomingnumber.setText("You have Incoming Call from " + CallReceiver.IncomingNumber);
return START_STICKY;
}
public static void clearView(Context context) {
wm = (WindowManager) context.getSystemService(WINDOW_SERVICE);
if(view != null) {
if(view.isEnabled()){
wm.removeView(view);
view = null;
}
}
}
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentLeft="true"
android:background="#0090FF"
android:padding="10dp"
android:keepScreenOn="true" >
<TextView
android:id="#+id/txtIncomingnumber"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="30dp"
android:text="Incoming Call Number"
android:textColor="#fff" />
</RelativeLayout>
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.stackoverflow"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" ></uses-permission>
<uses-permission android:name="android.permission.VIBRATE"></uses-permission>
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<receiver android:name=".CallReceiver" >
<intent-filter android:priority="99999">
<action android:name="android.intent.action.PHONE_STATE"/>
</intent-filter>
</receiver>
<activity
android:name="com.stackoverflow.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".IncomingCallService">
</service>
</application>
</manifest>
If you want to show a dialog from inside your onReceive of the BroadcastReceiver, you have to start a transparent activity with an alert dialog and NEVER called setContentView(). The activity will have an transparent view and only the alert dialog will show..
Your Broadcast Receiver onReceive
#Override
public void onReceive(Context context, Intent intent) {
Log.i("CallReceiverBroadcast", "onReceive() is called. ");
TelephonyManager teleMgr =(TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
PhoneStateListener psl = new PhoneStateListener() {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
Log.i("CallReceiverBroadcast", "onCallStateChanged() is called. ");
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
Log.i("CallReceiverBroadcast", "Incoming call caught. Caller's number is " +incomingNumber + ".");
//start activity which has dialog
Intent i=new Intent(context,DialogActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
}
}
};
teleMgr.listen(psl, PhoneStateListener.LISTEN_CALL_STATE);
teleMgr.listen(psl, PhoneStateListener.LISTEN_NONE);
}
And your DiaLogActivity
public class DialogActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//dont call setcontent view
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Are you sure you want to exit?").setCancelable(
false).setPositiveButton("Yes",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
}).setNegativeButton("No",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
AlertDialog alert = builder.create();
alert.show();
}
}
I have an Activity class, in which I have a static flag, let's say
public static volatile flag = false;
Then in the class, I start a thread, which checks the flag and do different things.
I also have a broadcastreceiver, which sets the flag to true or false.
I though volatile will force the flag to the most recent value. But I can see my broadcastreceiver sets the static flag to true, but my thread is still getting it as false.
Am I missing something basic here? Any help would be appreciated!
Simplified Code (Updated) - So the flag is supposed to change to true after one minute. But it never did. But message from broadcast receiver shows it has been change to true
TestappActivity.java:
package com.test;
import java.util.Calendar;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
public class TestappActivity extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Intent intent0 = new Intent(this, TestService.class);
this.startService(intent0);
Intent intent = new Intent(this, TestReceiver.class);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
PendingIntent sender = PendingIntent.getBroadcast(this,
1, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
Calendar slot = Calendar.getInstance();
int min = slot.get(Calendar.MINUTE);
slot.set(Calendar.MINUTE, min+1);
am.set(AlarmManager.RTC_WAKEUP, slot.getTimeInMillis(), sender);
}
}
TestService.java:
package com.test;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class TestService extends Service {
private static final String TAG = "TestService";
public static volatile boolean flag = false;
private MyTopThread mTopThread;
public TestService() {
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
}
#Override
public void onDestroy() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
protect();
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
/**
* Run protection
*
*/
private void protect() {
mTopThread = new MyTopThread();
mTopThread.start();
}
private class MyTopThread extends Thread {
#Override
public void run() {
while (true) {
try {
Thread.sleep(150);
Log.d(TAG, "Flag is " + TestService.flag);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
TestReceiver.java:
package com.test;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class TestReceiver extends BroadcastReceiver {
final static private String TAG = "TestReceiver";
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive is triggered ...");
TestService.flag = true;
Log.d(TAG, "flag is changed to " + TestService.flag);
}
}
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="8" />
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name" >
<activity
android:name=".TestappActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service android:name=".TestService" />
<receiver
android:name=".TestReceiver"
android:process=":remote" >
</receiver>
</application>
</manifest>
I think the problem is that you are running the receiver in its own process. From the docs for the android:process attribute of <receiver>:
If the name assigned to this attribute begins with a colon (':'), a new process, private to the application, is created when it's needed and the broadcast receiver runs in that process.
I think the receiver is modifying a process-local version of TestService.flag, not the one being used by TestService. Try removing the android:process attribute from the <receiver> tag in your manifest.
From this link
http://www.javamex.com/tutorials/synchronization_volatile.shtml
Essentially, volatile is used to indicate that a variable's value will
be modified by different threads.
I really hope your service thread is not this one (I don't see any other one):
private class MyTopThread extends Thread {
#Override
public void run() {
while (true) {
try {
Thread.sleep(150);
Log.d(TAG, "Flag is " + TestService.flag);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Because you have while(true) here, not while(!flag) as it should be.