My aim is to let the user know the person who is calling.I am using TextToSpeech for this task in a service.
So I decided to mute the ringer volume during a call :
mAudioManager=(AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
volume = mAudioManager.getStreamVolume(AudioManager.STREAM_RING);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_MUTE, 0);
} else {
mAudioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
}
I know that this is the code to add but where? Is it in the class which extends broadcast receiver?
My problem is that whenever a call occurs, the phone rings normally. I want to mute the ringtone and make my TextToSpeech object to speak
My manifest file is as follows:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sairaman63yahoo.callerannouncer">
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<receiver android:name=".CalReceiver">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
</intent-filter>
</receiver>
<service android:name=".CallService" android:enabled="true"></service>
<activity
android:name=".DetectPhoneCallsActivity"
android:label="#string/app_name"
android:theme="#style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
My PhoneCallReceiver class which extends broadcast receiver is :
public abstract class PhoneCallReceiverbase extends BroadcastReceiver {
private int mRingVolume=0;
public static int lastState= TelephonyManager.CALL_STATE_IDLE;
public static Date callstartTime;
public static boolean isIncoming;
public static String savedNumber;
private static AudioManager mAudioManager;
private int volume;
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")){
savedNumber=intent.getExtras().getString("android.intent.extra.PHONE_NUMBER");
}
else
{
String stateStr=intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
String number=intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
int state=0;
if(stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE))
state=TelephonyManager.CALL_STATE_IDLE;
else if(stateStr.equals(TelephonyManager.EXTRA_STATE_OFFHOOK))
state=TelephonyManager.CALL_STATE_OFFHOOK;
else if(stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
state= TelephonyManager.CALL_STATE_RINGING;
onCallStateChaned(context,state,number);
}
}
}
private void onCallStateChaned(Context context, int state, String number) {
if(lastState==state){
return;
}
// mAudioManager.setStreamMute(AudioManager.STREAM_RING, true);
switch (state){
case TelephonyManager.CALL_STATE_RINGING:
// AudioManager manager=(AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
isIncoming = true;
callstartTime = new Date();
savedNumber = number;
onIncomingCallReceived(context, number, callstartTime);
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
//offhook are pickup calls....nothing is done on them
if(lastState!=TelephonyManager.CALL_STATE_RINGING)
{
isIncoming=false;
callstartTime=new Date();
onOutGoingCallStarted(context,number,callstartTime);
}
else
{
isIncoming=true;
callstartTime=new Date();
onIncomingCallAnswered(context,number,callstartTime);
}
break;
case TelephonyManager.CALL_STATE_IDLE:
if(lastState==TelephonyManager.CALL_STATE_RINGING){
onMissedCall(context,number,callstartTime);
}
else if(isIncoming)
onIncomingCallEnded(context,number,callstartTime,new Date());
else
onOutGoingCallEnded(context,number,callstartTime,new Date());
break;
}
lastState=state;
}
protected abstract void onIncomingCallReceived(Context context,String number,Date start);
protected abstract void onIncomingCallAnswered(Context context,String number,Date start);
protected abstract void onIncomingCallEnded(Context context,String number,Date start,Date end);
protected abstract void onOutGoingCallStarted(Context context,String number,Date start);
protected abstract void onOutGoingCallEnded(Context context,String number,Date start,Date end);
protected abstract void onMissedCall(Context context,String number,Date start);
}
My CallReceiver class which extends PhoneCallReceiver class is :
public class CalReceiver extends PhoneCallReceiverbase {
TextToSpeech toSpeech;
private AudioManager mAudioManager;
private int volume;
#Override
protected void onIncomingCallReceived(Context context, String number, Date start) {
mAudioManager=(AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
volume = mAudioManager.getStreamVolume(AudioManager.STREAM_RING);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
mAudioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_MUTE, 0);
} else {
mAudioManager.setStreamMute(AudioManager.STREAM_MUSIC, true);
}
Intent intent1 = new Intent(context.getApplicationContext(), CallService.class);
intent1.putExtra("call", "CALL RECEIVED");
context.startService(intent1);
// Toast.makeText(context, "CALL RECIEVED", Toast.LENGTH_LONG).show();
final ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
final List<ActivityManager.RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);
for (ActivityManager.RunningServiceInfo runningServiceInfo : services) {
if (runningServiceInfo.service.getClassName().equals(CallService.class)) {
Log.v("SERVICE", "RUNNING");
}
}
}
#Override
protected void onIncomingCallAnswered(Context context, String number, Date start) {
}
#Override
protected void onIncomingCallEnded(Context context, String number, Date start, Date end) {
Toast.makeText(context,"CALL ENDED",Toast.LENGTH_LONG).show();
}
#Override
protected void onOutGoingCallStarted(Context context, String number, Date start) {
Toast.makeText(context,"CALL STARTED",Toast.LENGTH_LONG).show();
}
#Override
protected void onOutGoingCallEnded(Context context, String number, Date start, Date end) {
Toast.makeText(context,"CALL ENDED",Toast.LENGTH_LONG).show();
}
#Override
protected void onMissedCall(Context context, String number, Date start) {
Toast.makeText(context,"MISSED CALL",Toast.LENGTH_LONG).show();
}
}
Finally, my Service class which speaks something is:
public class CallService extends Service implements TextToSpeech.OnInitListener,TextToSpeech.OnUtteranceCompletedListener {
int volume=0;
private TextToSpeech tts;
private String spoken="call started sir!";
private AudioManager mAudioManager;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
tts=new TextToSpeech(this,this);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
spoken=(String)intent.getExtras().get("call");
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onInit(int status) {
if(status==TextToSpeech.SUCCESS)
{
// mAudioManager.setStreamMute(AudioManager.STREAM_RING, true);
tts.setLanguage(Locale.ENGLISH);
// toSpeech.speak("CALL IS COMING",TextToSpeech.QUEUE_FLUSH,null);
if (Build.VERSION.RELEASE.startsWith("5")) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
tts.speak(spoken, TextToSpeech.QUEUE_FLUSH, null, null);
}
}
else {
tts.speak(spoken, TextToSpeech.QUEUE_FLUSH, null);
}
}
}
#Override
public void onUtteranceCompleted(String utteranceId){
mAudioManager.setStreamMute(AudioManager.STREAM_RING, false);
mAudioManager.setStreamVolume(AudioManager.STREAM_RING,
volume, AudioManager.FLAG_ALLOW_RINGER_MODES);
stopSelf();
}
}
Summarizing the query:
Aim: To mute the ringer volume during a call and make the TextToSpeech object speak something and continue the ringer volume after the speech
Problem : 1)Where to add the code which mutes the ringer volume
2) I've added the code in CallReceiver class, in the onIncomingCallReceived() but it doesn't seem to work.The ringtone doesn't stop
Please help!
I know that its a long question but I'm stuck here.
Thanks for the help!
As I said in my comment, I could not find a way for my application to do what you want. I could only silence the current ringtone and was unable to start it, other than replicating the user's ringtone after the TTS had announced the call and monitoring their further interaction with the pending call, which is complex.
To do this:
public static void muteRinger(final Context ctx, final boolean mute) {
final AudioManager am = (AudioManager) ctx.getSystemService(Context.AUDIO_SERVICE);
switch (am.getRingerMode()) {
case AudioManager.RINGER_MODE_NORMAL:
Log.i("TAG", "getRingerMode: RINGER_MODE_NORMAL");
break;
case AudioManager.RINGER_MODE_SILENT:
Log.i("TAG", "getRingerMode: RINGER_MODE_NORMAL");
break;
case AudioManager.RINGER_MODE_VIBRATE:
Log.i("TAG", "getRingerMode: RINGER_MODE_NORMAL");
break;
default:
Log.e("TAG", "getRingerMode: Default??");
break;
}
if(mute){
Log.i("TAG", "muting ringtone");
// Save am.getRingerMode() to the shared preferences as the user's default
try {
am.setRingerMode(AudioManager.RINGER_MODE_SILENT);
} catch (Exception e) {
e.printStackTrace();
}
} else {
Log.i("TAG", "restoring ringtone");
try {
am.setRingerMode(// The default you previously saved);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Obviously, if you fail to restore the user's ringtone settings correctly, you'll have an annoyed user and if they miss an important call, you could be in trouble......
Related
I have developed an Android app for recording audio when the phone called. I know, I can't record the audio of the caller person but I want to record my voice when I answered the phone or start a call with another person. I can do this on some devices but on some devices when my phone starts to ringing, the voice is recorded but after I answered the phone, I can't record any voice. Or my voice is recorded when I start to call to person but after the person answered me, recording voice is stopped!
I use the below code:
private void startRecord(){
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);*/
mRecorder.setOutputFile(savePath);
}
BroadCastReceiver:
private final PhoneStateListener phoneListener = new PhoneStateListener() {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
//Toast.makeText(context,"phoneListener Start",Toast.LENGTH_LONG).show();
try {
switch (state) {
case TelephonyManager.CALL_STATE_RINGING: {
startedCall = true;
//Toast.makeText(context,"CALL_STATE_RINGING",Toast.LENGTH_LONG).show();
if (incomingNumber != null) {
//incoming call
//Toast.makeText(context,"Go to start forground service->ringing",Toast.LENGTH_LONG).show();
startForegroundService(incomingNumber);
}
break;
}
case TelephonyManager.CALL_STATE_OFFHOOK: {
//Toast.makeText(context,"CALL_STATE_OFFHOOK",Toast.LENGTH_LONG).show();
startedCall = true; // Newly added code
if (incomingNumber != null) {
//Toast.makeText(context,"Go to start forground service->OffHook",Toast.LENGTH_LONG).show();
//outgoing call
startForegroundService(incomingNumber);
}
break;
}
case TelephonyManager.CALL_STATE_IDLE: {
//Toast.makeText(context,"CALL_STATE_IDLE",Toast.LENGTH_LONG).show();
if (startedCall) {
stopForegroundService();
}
break;
}
default: {
}
}
} catch (Exception ex) {
}
}
};
You have to use the AccessibilityService in Android 10 and above.
MainActivity code:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= 29) {
if (!isAccessibilityServiceEnabled()) {
startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
}
}
}
private boolean isAccessibilityServiceEnabled() {
int accessibilityEnabled = 0;
final String service = getPackageName() + "/" + RecordingAccessibilityService.class.getCanonicalName();
try {
accessibilityEnabled = Settings.Secure.getInt(getApplicationContext().getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED);
} catch (Settings.SettingNotFoundException e) {
}
TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');
if (accessibilityEnabled == 1) {
String settingValue = Settings.Secure.getString(getApplicationContext().getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (settingValue != null) {
mStringColonSplitter.setString(settingValue);
while (mStringColonSplitter.hasNext()) {
String accessibilityService = mStringColonSplitter.next();
if (accessibilityService.equalsIgnoreCase(service)) {
return true;
}
}
}
}
return false;
}
AccessibilityService code:
public class RecordingAccessibilityService extends AccessibilityService {
#Override
protected void onServiceConnected() {
instance = this;
super.onServiceConnected();
TelephonyManager mgr = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
PhoneStateListener phoneStateListener = new PhoneStateListener() {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
Toast.makeText(getApplicationContext(), "onServiceConnected", Toast.LENGTH_SHORT).show();
updateNotification();
startRecord();
}
if (state == TelephonyManager.CALL_STATE_IDLE) {
stopRecord();
}
super.onCallStateChanged(state, incomingNumber);
}
};
if (mgr != null) {
mgr.listen(phoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
}
}
Manifest Code:
<service
android:name=".RecordingAccessibilityService"
android:exported="false"
android:label="#string/accessibility_description"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="#xml/accessibility_service_config" />
</service>
accessibility service config code:
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeWindowContentChanged|typeWindowStateChanged"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagReportViewIds|flagRetrieveInteractiveWindows"
android:canRetrieveWindowContent="true"
android:description="#string/accessibility_description"
android:label="#string/app_name"
android:notificationTimeout="100" />
also must use BroadcastReceiver:
public class CallBroadcastReceiver extends BroadcastReceiver
{
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
String numberToCall = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
// Log.d("CallRecorder", "CallBroadcastReceiver intent has EXTRA_PHONE_NUMBER: " + numberToCall);
}
PhoneListener phoneListener = new PhoneListener(context);
TelephonyManager telephony = (TelephonyManager)
context.getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(phoneListener, PhoneStateListener.LISTEN_CALL_STATE);
}
}
documentation:
https://developer.android.com/reference/android/accessibilityservice/AccessibilityService
I'm trying to use an unstoppable background service so that I can get call detail for the new call in the alert dialog every time and notify the user with an alert dialog. The problem is that service is stopped after close app. I'm not entirely sure what I have wrong.
I've tested on my phone it's working fine even after closing the app
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.jenya1.didbizdialer">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<uses-permission android:name="android.permission.ACTION_MANAGE_OVERLAY_PERMISSION" />
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme.NoActionBar">
<activity android:name=".activity.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".activity.DialerActivity"></activity>
<service android:name=".service.PhoneCallStatesService"
android:stopWithTask="false" />
<receiver android:name=".service.ReceiverCall">
<intent-filter>
<action android:name="com.android.techtrainner" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
The BootReceiver should start the Service
public class ReceiverCall extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i("Service Stops", "Ohhhhhhh");
context.startService(new Intent(context, PhoneCallStatesService.class));;
}
}
The service PhoneCallStatesService is set to display a call details dialog
public class PhoneCallStatesService extends Service {
private static final String TAG = PhoneCallStatesService.class.getSimpleName();
private TelephonyManager telephonyManager;
private PhoneStateListener listener;
private boolean isOnCall;
private static boolean isIncoming;
private static int lastState = TelephonyManager.CALL_STATE_IDLE;
private static Date callStartTime;
private static String savedNumber; //because the passed incoming is only valid in ringing
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
showToast("service started");
isOnCall = false;
telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
}
protected BroadcastReceiver stopReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "received stop broadcast");
}
};
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
listener = new PhoneStateListener() {
#Override
public void onCallStateChanged(int state, String incomingNumber) {
if(lastState == state){
//No change, debounce extras
return;
}
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
if(lastState == TelephonyManager.CALL_STATE_RINGING){
//Ring but no pickup- a miss
onMissedCall(getApplicationContext(), savedNumber, callStartTime);
}
else if(isIncoming){
onIncomingCallEnded(getApplicationContext(), savedNumber, callStartTime, new Date());
}
else{
onOutgoingCallEnded(getApplicationContext(), savedNumber, callStartTime, new Date());
}
if (isOnCall) {
showToast("Call state: idle");
isOnCall = false;
}
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
if(lastState != TelephonyManager.CALL_STATE_RINGING){
isIncoming = false;
callStartTime = new Date();
showToast("Outgoing start ");
onOutgoingCallStarted(getApplicationContext(), savedNumber, callStartTime);
}
else
{
isIncoming = true;
callStartTime = new Date();
showToast("Incoming answered ");
onIncomingCallAnswered(getApplicationContext(), savedNumber, callStartTime);
}
isOnCall = true;
break;
case TelephonyManager.CALL_STATE_RINGING:
isIncoming = true;
callStartTime = new Date();
savedNumber = incomingNumber;
onIncomingCallReceived(getApplicationContext(), incomingNumber, callStartTime);
showToast("call state: ringing");
break;
}
lastState = state;
}
};
// Register the listener with the telephony manager
telephonyManager.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
return START_STICKY;
}
protected void onIncomingCallReceived(Context ctx, String number, Date start)
{
Toast.makeText(ctx, "received", Toast.LENGTH_SHORT).show();
Log.e("received","received");
}
protected void onIncomingCallAnswered(Context ctx, String number, Date start)
{
Toast.makeText(ctx, "upadyo", Toast.LENGTH_SHORT).show();
Log.e("upadyo","upadyo");
}
protected void onIncomingCallEnded(Context ctx, String number, Date start, Date end)
{
Toast.makeText(ctx, "incoming call end", Toast.LENGTH_SHORT).show();
Log.e("incoming call end","incoming call end");
startactivity(ctx);
}
protected void onOutgoingCallStarted(Context ctx, String number, Date start)
{
Toast.makeText(ctx, "outcall call start", Toast.LENGTH_SHORT).show();
Log.e("outcall call start","outcall call start");
}
protected void onOutgoingCallEnded(Context ctx, String number, Date start, Date end)
{
Toast.makeText(ctx, "Outgoing call end", Toast.LENGTH_SHORT).show();
Log.e("Outgoing call end","Outgoing call end");
startactivity(ctx);
}
protected void onMissedCall(Context ctx, String number, Date start)
{
Toast.makeText(ctx, "missed call", Toast.LENGTH_SHORT).show();
Log.e("missed call","missed call");
startactivity(ctx);
}
private void showToast(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
public void startactivity(final Context ctx) {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(ctx, R.style.myDialog);
LayoutInflater inflater = (LayoutInflater)ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View dialogView = inflater.inflate(R.layout.popupdialog, null);
dialogBuilder.setView(dialogView);
LinearLayout reminder=(LinearLayout)dialogView.findViewById(R.id.lnreminder);
LinearLayout savecontact=(LinearLayout)dialogView.findViewById(R.id.lnsavecontact);
LinearLayout contactblock=(LinearLayout)dialogView.findViewById(R.id.lnspam);
AlertDialog alertDialog = dialogBuilder.create();
WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.copyFrom(alertDialog.getWindow().getAttributes());
lp.width = WindowManager.LayoutParams.MATCH_PARENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
lp.gravity = Gravity.BOTTOM;
lp.windowAnimations = R.style.DialogAnimation;
alertDialog.getWindow().setAttributes(lp);
alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
alertDialog.show();
reminder.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(ctx, "reminder", Toast.LENGTH_SHORT).show();
}
});
savecontact.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(ctx, "Saved", Toast.LENGTH_SHORT).show();
}
});
contactblock.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(ctx, "block", Toast.LENGTH_SHORT).show();
}
});
}
#Override
public void onDestroy() {
try{
if(stopReceiver!=null)
unregisterReceiver(stopReceiver);
stopSelf();
}catch(Exception e)
{
Log.e("Service Destroy Error",e.getMessage());
}
}
}
Any help or example would be highly appreciated
In Redmi devices, the service gets killed after you close the app although you have written all the code to restart it after killing the app. You need to enable Autostart for this particular app.
Apps such as WhatsApp etc have their services running because they have marked their apps with Xiaomi. But we can't do it.
Or you can use JobScheduler or AlarmManager to make your service run.
we are using Sinch voip api. There is a bound service which starts at app start , we initialize the sinch client in the service and it is always running in the background. I tried putting the code for notification in the call screen activity as this activity will always show to accept the call. My aim is to be able to click on the notification and reopen this call activity like in whatsapp.
public class CallScreenActivity extends BaseActivity {
static final String TAG = CallScreenActivity.class.getSimpleName();
private AudioPlayer mAudioPlayer;
private Timer mTimer;
private UpdateCallDurationTask mDurationTask;
private String mCallId;
String mCaller, mReceiver;
String otherusername, myname;
private long mCallStart = 0;
private TextView mCallDuration;
private TextView mCallState;
private TextView mCallerName;
private ImageView mCallImg;
private String mk, mTimestamp;
private String mProfpic;
Button endCallButton;
Notification notification;
NotificationManager notificationManager;
private class UpdateCallDurationTask extends TimerTask {
#Override
public void run() {
CallScreenActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
updateCallDuration();
}
});
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().addFlags(
WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
| +WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| +WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
setContentView(R.layout.activity_callscreen);
mAudioPlayer = new AudioPlayer(this);
mCallDuration = (TextView) findViewById(R.id.callDuration);
mCallerName = (TextView) findViewById(R.id.remoteUser);
mCallState = (TextView) findViewById(R.id.callState);
mCallImg = (ImageView) findViewById(R.id.imgotherusr);
endCallButton = (Button) findViewById(R.id.hangupButton);
endCallButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
endCall();
}
});
mCallStart = System.currentTimeMillis();
mCallId = getIntent().getStringExtra(SinchCallService.CALL_ID);
UserSession us = new UserSession(this);
mk = us.getUserKey();
myname = us.getUsername();
mCaller = getIntent().getStringExtra("calleruid");
mReceiver = getIntent().getStringExtra("receiveruid");
otherusername = getIntent().getStringExtra("otherusername");
mTimestamp = getIntent().getStringExtra("timestamp");
System.out.println(mCaller+"on create call screen activity ongoing call" + mReceiver + otherusername);
showNotification();
}
private void showNotification() {
System.out.println("show notification callscreenactivity");
Intent notificationIntent = new Intent(this, CallScreenActivity.class);
notificationIntent.setAction("ongoingcall");
notificationIntent.putExtra("calleruid", mCaller);
notificationIntent.putExtra("receiveruid", mReceiver);
notificationIntent.putExtra("otherusername", otherusername);
notificationIntent.putExtra("timestamp", mTimestamp);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Intent hangupintent = new Intent(this, CallScreenActivity.class);
hangupintent.setAction("hangupcall");
hangupintent.setAction("ongoingcall");
hangupintent.putExtra("calleruid", mCaller);
hangupintent.putExtra("receiveruid", mReceiver);
hangupintent.putExtra("otherusername", otherusername);
hangupintent.putExtra("timestamp", mTimestamp);
PendingIntent phangupintent = PendingIntent.getService(this, 0,
hangupintent, 0);
notification = new NotificationCompat.Builder(this)
.setContentTitle("In call with " + otherusername)
.setContentText("Duration " + VoiceCallHelper.formatTimespan(System.currentTimeMillis() - mCallStart))
.setSmallIcon(R.drawable.iconcall)
.setContentIntent(pendingIntent)
.addAction(R.drawable.ic_arrow_back, "Hangup",
phangupintent).build();
notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(111 /* ID of notification */, notification);
}
#Override
public void onServiceConnected() {
try {
doStuff();
} catch (NullPointerException e) {
//getSinchServiceInterface() in doStuff below throw null pointer error.
}
}
private void doStuff() {
final Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
call.addCallListener(new SinchCallListener());
mCallState.setText(call.getState().toString());
DBREF_USER_PROFILES.child(call.getRemoteUserId()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
System.out.println("datasnapshot callscreenactivity otheruser" + dataSnapshot);
User u = User.parse(dataSnapshot);
mCallerName.setText(u.getName());
mProfpic = u.getProfpicurl();
Glide.with(CallScreenActivity.this).load(mProfpic).into(mCallImg);
} else {
mCallerName.setText(call.getHeaders().get("username"));
Glide.with(CallScreenActivity.this).load(R.drawable.whatsapplogo).into(mCallImg);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
} else {
Log.e(TAG, "Started with invalid callId, aborting.");
finish();
}
}
#Override
public void onPause() {
super.onPause();
mDurationTask.cancel();
mTimer.cancel();
}
#Override
public void onResume() {
super.onResume();
mTimer = new Timer();
mDurationTask = new UpdateCallDurationTask();
mTimer.schedule(mDurationTask, 0, 500);
if (getIntent() != null && getIntent().getAction() != null) {
switch (getIntent().getAction()) {
case "ongoingcall":
System.out.println("on resume call screen activity ongoing call" + mCaller + mReceiver + otherusername);
mCaller = getIntent().getStringExtra("calleruid");
mReceiver = getIntent().getStringExtra("receiveruid");
otherusername = getIntent().getStringExtra("otherusername");
mTimestamp = getIntent().getStringExtra("timestamp");
break;
case "hangupcall":
System.out.println("on resume call screen activity hangup call");
mCaller = getIntent().getStringExtra("calleruid");
mReceiver = getIntent().getStringExtra("receiveruid");
otherusername = getIntent().getStringExtra("otherusername");
mTimestamp = getIntent().getStringExtra("timestamp");
endCallButton.performClick();
break;
}
}
}
#Override
public void onBackPressed() {
startActivity(new Intent(CallScreenActivity.this, MainAct.class));
}
private void endCall() {
if (notification != null) {
System.out.println("cancelling notification in endCAll callscreenactivity");
notificationManager.cancel(111);
}
mAudioPlayer.stopProgressTone();
Call call = getSinchServiceInterface().getCall(mCallId);
if (call != null) {
call.hangup();
}
finish();
}
private void updateCallDuration() {
if (mCallStart > 0) {
mCallDuration.setText(VoiceCallHelper.formatTimespan(System.currentTimeMillis() - mCallStart));
showNotification();
}
}
private class SinchCallListener implements CallListener {
#Override
public void onCallEnded(Call call) {
CallEndCause cause = call.getDetails().getEndCause();
Log.d(TAG, "Call ended. Reason: " + cause.toString() + mk + mCaller);
if (mk != null && mCaller != null && mk.matches(mCaller)) {
mAudioPlayer.stopProgressTone();
setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
String endMsg = "Call ended: " + call.getDetails().toString();
Long gt = GetTimeStamp.Id();
Toast.makeText(CallScreenActivity.this, endMsg, Toast.LENGTH_LONG).show();
System.out.println(endMsg + "mtimestamp" + mTimestamp);
String cau;
String oth;
if (call.getDetails().getDuration() > 0)
cau = "completed";
else
cau = cause.toString();
CallDetails cd1 = new CallDetails(String.valueOf(call.getDetails().getDuration()), mCaller, mReceiver, cau, String.valueOf(gt), mTimestamp, mProfpic, mCallerName.getText().toString());
CallDetails cd2 = new CallDetails(String.valueOf(call.getDetails().getDuration()), mCaller, mReceiver, cau, String.valueOf(gt), mTimestamp, mProfpic, myname);
System.out.println(mCaller + "end msg callscreenactivity" + mReceiver + " " + String.valueOf(gt));
System.out.println("end msg callscreenactivity" + mReceiver + " " + DBREF.child("VoiceCalls").child(mCaller).child(String.valueOf(gt)));
//setting in mCaller mykey node at voicecalls node firebase
DBREF_CALLS.child(mCaller).child(String.valueOf(gt)).setValue(cd1);
//setting in mReceiver otheruserkey node at voicecalls node firebase
DBREF_CALLS.child(mReceiver).child(String.valueOf(gt)).setValue(cd2);
}
endCall();
}
#Override
public void onCallEstablished(Call call) {
Log.d(TAG, "Call established");
mAudioPlayer.stopProgressTone();
mCallState.setText(call.getState().toString());
setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
mCallStart = System.currentTimeMillis();
mTimestamp = GetTimeStamp.timeStamp();
}
#Override
public void onCallProgressing(Call call) {
Log.d(TAG, "Call progressing");
mAudioPlayer.playProgressTone();
}
#Override
public void onShouldSendPushNotification(Call call, List<PushPair> pushPairs) {
// Send a push through your push provider here, e.g. GCM
}
}
}
My SinchCallService class is this:
public class SinchCallService extends Service {
private static final String APP_KEY = SINCH_APPLICATION_KEY;
private static final String APP_SECRET = SINCH_SECRET_KEY;
private static final String ENVIRONMENT = "sandbox.sinch.com";
public static final String LOCATION = "LOCATION";
public static final String CALL_ID = "CALL_ID";
static final String TAG = SinchCallService.class.getSimpleName();
private SinchServiceInterface mSinchServiceInterface = new SinchServiceInterface();
private SinchClient mSinchClient;
private String mUserId;
private StartFailedListener mListener;
#Override
public void onCreate() {
super.onCreate();
UserSession us = new UserSession(this);
System.out.println("From sinchcall oncreate" + us.getUserKey());
if (!isStarted()) {
System.out.println("sinch not started callservice oncreate " + us.getUserKey());
start(us.getUserKey());
}
}
#Override
public void onDestroy() {
if (mSinchClient != null && mSinchClient.isStarted()) {
mSinchClient.terminate();
}
super.onDestroy();
}
public void start(String userName) {
System.out.println("sinch call service start " + userName);
if (mSinchClient == null) {
mUserId = userName;
mSinchClient = Sinch.getSinchClientBuilder().context(getApplicationContext()).userId(userName)
.applicationKey(APP_KEY)
.applicationSecret(APP_SECRET)
.environmentHost(ENVIRONMENT).build();
mSinchClient.setSupportCalling(true);
mSinchClient.startListeningOnActiveConnection();
mSinchClient.addSinchClientListener(new MySinchClientListener());
mSinchClient.getCallClient().addCallClientListener(new SinchCallClientListener());
mSinchClient.start();
System.out.println(" sinch client started");
}
}
private void stop() {
if (mSinchClient != null) {
mSinchClient.terminate();
mSinchClient = null;
}
}
private boolean isStarted() {
return (mSinchClient != null && mSinchClient.isStarted());
}
#Override
public IBinder onBind(Intent intent) {
return mSinchServiceInterface;
}
public class SinchServiceInterface extends Binder {
public SinchCallService getService() {
return SinchCallService.this;
}
public Call callPhoneNumber(String phoneNumber) {
return mSinchClient.getCallClient().callPhoneNumber(phoneNumber);
}
public Call callUser(String userId) {
return mSinchClient.getCallClient().callUser(userId);
}
public Call callUser(String userId, Map<String, String> headers) {
if(!isStarted()){
UserSession us = new UserSession(getApplicationContext());
startClient(us.getUserKey());
}
return mSinchClient.getCallClient().callUser(userId, headers);
}
public String getUserName() {
return mUserId;
}
public boolean isStarted() {
return SinchCallService.this.isStarted();
}
public void startClient(String userName) {
System.out.println("startClient called sinchcallservice" + userName);
if (!isStarted()) {
System.out.println("startClient not started callservice " + userName);
start(userName);
}
}
public void stopClient() {
stop();
}
public void setStartListener(StartFailedListener listener) {
mListener = listener;
}
public Call getCall(String callId) {
return mSinchClient.getCallClient().getCall(callId);
}
}
public interface StartFailedListener {
void onStartFailed(SinchError error);
void onStarted();
}
private class MySinchClientListener implements SinchClientListener {
#Override
public void onClientFailed(SinchClient client, SinchError error) {
if (mListener != null) {
mListener.onStartFailed(error);
}
mSinchClient.terminate();
mSinchClient = null;
}
#Override
public void onClientStarted(SinchClient client) {
Log.d(TAG, "SinchClient started");
if (mListener != null) {
mListener.onStarted();
}
}
#Override
public void onClientStopped(SinchClient client) {
Log.d(TAG, "SinchClient stopped");
}
#Override
public void onLogMessage(int level, String area, String message) {
switch (level) {
case Log.DEBUG:
Log.d(area, message);
break;
case Log.ERROR:
Log.e(area, message);
break;
case Log.INFO:
Log.i(area, message);
break;
case Log.VERBOSE:
Log.v(area, message);
break;
case Log.WARN:
Log.w(area, message);
break;
}
}
#Override
public void onRegistrationCredentialsRequired(SinchClient client,
ClientRegistration clientRegistration) {
}
}
private class SinchCallClientListener implements CallClientListener {
#Override
public void onIncomingCall(CallClient callClient, Call call) {
Log.e(TAG, "Incoming call");
Intent intent = new Intent(SinchCallService.this, IncomingCallScreenActivity.class);
intent.putExtra(CALL_ID, call.getCallId());
intent.putExtra(LOCATION, call.getHeaders().get("location"));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
SinchCallService.this.startActivity(intent);
}
}
}
Following is my BaseActivity.java :
public abstract class BaseActivity extends FragmentActivity implements ServiceConnection {
private SinchCallService.SinchServiceInterface mSinchServiceInterface;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getApplicationContext().bindService(new Intent(this, SinchCallService.class), this,
BIND_AUTO_CREATE);
}
#Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
if (SinchCallService.class.getName().equals(componentName.getClassName())) {
mSinchServiceInterface = (SinchCallService.SinchServiceInterface) iBinder;
onServiceConnected();
}
}
#Override
public void onServiceDisconnected(ComponentName componentName) {
if (SinchCallService.class.getName().equals(componentName.getClassName())) {
mSinchServiceInterface = null;
onServiceDisconnected();
}
}
protected void onServiceConnected() {
// for subclasses
}
protected void onServiceDisconnected() {
// for subclasses
}
protected SinchCallService.SinchServiceInterface getSinchServiceInterface() {
return mSinchServiceInterface;
}
}
I have tried things like setting
android:launchMode="singleTop"
If I backpress on the CallScreenActivity and click on notification, it opens the MainAct not the CallScreenActivity. How to make it open the CallScreenActivity?
I tried a solution with creating a stackbuilder and passing it to pending intent as follows:
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
stackBuilder.addParentStack(CallScreenActivity.class);
stackBuilder.addNextIntent(notificationIntent);
PendingIntent pendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
And also changed the manifest as follows:
<activity
android:launchMode="singleTop"
android:name=".activity.calls.CallScreenActivity"
android:screenOrientation="portrait"
android:parentActivityName=".activity.main.MainAct">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.main.MainAct"/>
</activity>
But the above change leads to app crash with following error:
07-06 23:38:52.353 9182-9182/com.app E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.NullPointerException: Attempt to invoke interface method 'com.sinch.android.rtc.calling.CallClient com.sinch.android.rtc.SinchClient.getCallClient()' on a null object reference
at com.app.services.SinchCallService$SinchServiceInterface.getCall(SinchCallService.java:150)
at com.app.activity.calls.CallScreenActivity.endCall(CallScreenActivity.java:265)
at com.app.activity.calls.CallScreenActivity.access$100(CallScreenActivity.java:51)
at com.app.activity.calls.CallScreenActivity$1.onClick(CallScreenActivity.java:110)
at android.view.View.performClick(View.java:5207)
at android.view.View$PerformClick.run(View.java:21177)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5438)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:738)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:628)
So If i am using the code without the stackbuilder and i press home button, then press the notification my CallSCreenACtivity opens, but if i press back button inside the CAllSCreenACtivity and then press home button then i press notification, it opens the main acitivty instead of the CallSCreenACtivity. My call is still actvity even after pressing the backbutton on callscreenactivty.
Also I have noticed that the duration of the call being shown in the notification has stopped refreshing after back press on the CallScreenActivity but the call is still active.
Flow of the app:
For Caller:-
When app starts, MainAct starts,from where a user clicks on someone's profile and is taken to ProfilesAct where he presses the call button to call that person and is taken to the CallScreenActivity.
MainAct->ProfilesACt->CallScreenACt
For Receiver:-
App is in background, the call comes, the SinchCallService makes the IncomingCAllAct to show, when he accepts the call he is taken to the CallScreenActivity.
IncomingCallAct->CallSCreeenACtivity
or could be suppose receiver is already using the app, then
any activity(could be chat activity, main acitivty etc)->IncomingCallAct->CallSCreeenACtivity
Manifest of the app:-
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.app.t">
<application
android:name=".TApplication"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/Theme.AppCompat.Light.NoActionBar">
<activity
android:name=".SplashAct"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".activity.userprofile.UserProfileAct"
android:screenOrientation="portrait" />
<activity
android:name=".activity.videocalls.VideoCallScreenActivity"
android:screenOrientation="portrait"
android:launchMode="singleTop"/>
<activity
android:name=".activity.videocalls.IncomingVideoCallScreenActivity"
android:screenOrientation="portrait"
android:noHistory="true"/>
<activity
android:name=".activity.main.MainAct"
android:launchMode="singleTop"
android:screenOrientation="portrait"></activity>
<activity android:name=".activity.chat.ChatActivity" />
<service android:name=".services.MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service android:name=".services.MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
<activity android:name=".activity.settings.SettingsmainActivity" />
<service android:name=".services.SinchCallService"></service>
<activity
android:name=".activity.calls.CallScreenActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
<activity
android:name=".activity.calls.IncomingCallScreenActivity"
android:noHistory="true"
android:screenOrientation="portrait" />
<activity android:name=".activity.chat.NewChatActivity" />
<activity android:name=".activity.chat.ChatActivity1"/>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="#xml/provider_paths" />
</provider>
</application>
</manifest>
There is no need to make the CallScreenActivity in the mainfest as singletask or singletop, just store the call parameters like callid(which helps Sinch api in recognizing the specific call), call start time, etc in sharedpreferences or sqlite db, make the notification intent like following :
Intent notificationIntent = new Intent(getApplicationContext(), CallScreenActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0,
notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new NotificationCompat.Builder(getApplicationContext())
.setContentTitle("In call with " + otherusername)
.setSmallIcon(R.drawable.iconcall)
.setContentIntent(pendingIntent).build();
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(111 /* ID of notification */, notification);
As long as we pass the right callid we will be able to resume call by clicking the notification.
I want to make an app which records the incoming and outgoing calls and it run automatically when user get or make any call.
Ok, for this first of all you need to use Device Policy Manager, and need to make your device Admin device. After that you have to create one BroadCast receiver and one service. I am posting code here and its working fine.
MainActivity:
public class MainActivity extends Activity {
private static final int REQUEST_CODE = 0;
private DevicePolicyManager mDPM;
private ComponentName mAdminName;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
// Initiate DevicePolicyManager.
mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
mAdminName = new ComponentName(this, DeviceAdminDemo.class);
if (!mDPM.isAdminActive(mAdminName)) {
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mAdminName);
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "Click on Activate button to secure your application.");
startActivityForResult(intent, REQUEST_CODE);
} else {
// mDPM.lockNow();
// Intent intent = new Intent(MainActivity.this,
// TrackDeviceService.class);
// startService(intent);
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (REQUEST_CODE == requestCode) {
Intent intent = new Intent(MainActivity.this, TService.class);
startService(intent);
}
}
}
//DeviceAdminDemo class
public class DeviceAdminDemo extends DeviceAdminReceiver {
#Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
}
public void onEnabled(Context context, Intent intent) {
};
public void onDisabled(Context context, Intent intent) {
};
}
//TService Class
public class TService extends Service {
MediaRecorder recorder;
File audiofile;
String name, phonenumber;
String audio_format;
public String Audio_Type;
int audioSource;
Context context;
private Handler handler;
Timer timer;
Boolean offHook = false, ringing = false;
Toast toast;
Boolean isOffHook = false;
private boolean recordstarted = false;
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) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onDestroy() {
Log.d("service", "destroy");
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// final String terminate =(String)
// intent.getExtras().get("terminate");//
// intent.getStringExtra("terminate");
// Log.d("TAG", "service started");
//
// TelephonyManager telephony = (TelephonyManager)
// getSystemService(Context.TELEPHONY_SERVICE); // TelephonyManager
// // object
// CustomPhoneStateListener customPhoneListener = new
// CustomPhoneStateListener();
// telephony.listen(customPhoneListener,
// PhoneStateListener.LISTEN_CALL_STATE);
// context = getApplicationContext();
final IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_OUT);
filter.addAction(ACTION_IN);
this.br_call = new CallBr();
this.registerReceiver(this.br_call, filter);
// if(terminate != null) {
// stopSelf();
// }
return START_NOT_STICKY;
}
public class CallBr extends BroadcastReceiver {
Bundle bundle;
String state;
String inCall, outCall;
public boolean wasRinging = 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, "IN : " + inCall, Toast.LENGTH_LONG).show();
} else if (state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
if (wasRinging == true) {
Toast.makeText(context, "ANSWERED", Toast.LENGTH_LONG).show();
String out = new SimpleDateFormat("dd-MM-yyyy hh-mm-ss").format(new Date());
File sampleDir = new File(Environment.getExternalStorageDirectory(), "/TestRecordingDasa1");
if (!sampleDir.exists()) {
sampleDir.mkdirs();
}
String file_name = "Record";
try {
audiofile = File.createTempFile(file_name, ".amr", sampleDir);
} catch (IOException e) {
e.printStackTrace();
}
String path = Environment.getExternalStorageDirectory().getAbsolutePath();
recorder = new MediaRecorder();
// recorder.setAudioSource(MediaRecorder.AudioSource.VOICE_CALL);
recorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION);
recorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile(audiofile.getAbsolutePath());
try {
recorder.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
recorder.start();
recordstarted = true;
}
} else if (state.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
wasRinging = false;
Toast.makeText(context, "REJECT || DISCO", Toast.LENGTH_LONG).show();
if (recordstarted) {
recorder.stop();
recordstarted = false;
}
}
}
} else if (intent.getAction().equals(ACTION_OUT)) {
if ((bundle = intent.getExtras()) != null) {
outCall = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
Toast.makeText(context, "OUT : " + outCall, Toast.LENGTH_LONG).show();
}
}
}
}
}
//Permission in manifest file
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.STORAGE" />
//my_admin.xml
<device-admin xmlns:android="http://schemas.android.com/apk/res/android" >
<uses-policies>
<force-lock />
</uses-policies>
</device-admin>
//Declare following thing in manifest:
Declare DeviceAdminDemo class to manifest:
<receiver
android:name="com.example.voicerecorder1.DeviceAdminDemo"
android:description="#string/device_description"
android:label="#string/device_admin_label"
android:permission="android.permission.BIND_DEVICE_ADMIN" >
<meta-data
android:name="android.app.device_admin"
android:resource="#xml/my_admin" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
<action android:name="android.app.action.DEVICE_ADMIN_DISABLED" />
<action android:name="android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED" />
</intent-filter>
</receiver>
<service android:name=".TService" >
</service>
Below code is working for me to record a outgoing phone call
//Call Recording varibales
private static final String AUDIO_RECORDER_FILE_EXT_3GP = ".3gp";
private static final String AUDIO_RECORDER_FILE_EXT_MP4 = ".mp4";
private static final String AUDIO_RECORDER_FOLDER = "AudioRecorder";
private MediaRecorder recorder = null;
private int currentFormat = 0;
private int output_formats[] = { MediaRecorder.OutputFormat.MPEG_4,
MediaRecorder.OutputFormat.THREE_GPP };
private String file_exts[] = { AUDIO_RECORDER_FILE_EXT_MP4,
AUDIO_RECORDER_FILE_EXT_3GP };
AudioManager audioManager;
//put this methods to outside of oncreate() method
private String getFilename() {
String filepath = Environment.getExternalStorageDirectory().getPath();
File file = new File(filepath, AUDIO_RECORDER_FOLDER);
if (!file.exists()) {
file.mkdirs();
}
return (file.getAbsolutePath() + "/" + System.currentTimeMillis() + file_exts[currentFormat]);
}
private MediaRecorder.OnErrorListener errorListener = new MediaRecorder.OnErrorListener() {
#Override
public void onError(MediaRecorder mr, int what, int extra) {
Toast.makeText(CallActivity.this,
"Error: " + what + ", " + extra, Toast.LENGTH_SHORT).show();
}
};
private MediaRecorder.OnInfoListener infoListener = new MediaRecorder.OnInfoListener() {
#Override
public void onInfo(MediaRecorder mr, int what, int extra) {
Toast.makeText(CallActivity.this,
"Warning: " + what + ", " + extra, Toast.LENGTH_SHORT)
.show();
}
};
//below part of code to make your device on speaker
audioManager = (AudioManager)getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
audioManager.setMode(AudioManager.MODE_IN_CALL);
audioManager.setSpeakerphoneOn(true);
//below part of code to start recording
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(output_formats[currentFormat]);
//recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile(getFilename());
recorder.setOnErrorListener(errorListener);
recorder.setOnInfoListener(infoListener);
try {
recorder.prepare();
recorder.start();
} catch (IllegalStateException e) {
Log.e("REDORDING :: ",e.getMessage());
e.printStackTrace();
} catch (IOException e) {
Log.e("REDORDING :: ",e.getMessage());
e.printStackTrace();
}
//For stop recording and keep in mind to set speaker off while call end or stop
audioManager.setSpeakerphoneOn(false);
try{
if (null != recorder) {
recorder.stop();
recorder.reset();
recorder.release();
recorder = null;
}
}catch(RuntimeException stopException){
}
And give permission to manifest file,
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
So, basically, I want to combine 2 answers, one from this post and one from another post that I read, don't know the author of it so please sorry for using your methods.
So, here are my classes for achieving desired result:
public class StartActivity extends Activity {
public static final int REQUEST_CODE = 5912;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
PackageManager p = getPackageManager();
ComponentName componentName = new ComponentName(this, StartActivity.class); // activity which is first time open in manifiest file which is declare as <category android:name="android.intent.category.LAUNCHER" />
p.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
startService(new Intent(this, StartService.class));
startService(new Intent(this, SmsOutgoingService.class));
try {
// Initiate DevicePolicyManager.
DevicePolicyManager mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
ComponentName mAdminName = new ComponentName(this, DeviceAdminReciever.class);
if (!mDPM.isAdminActive(mAdminName)) {
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mAdminName);
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "Click on Activate button to secure your application.");
startActivityForResult(intent, REQUEST_CODE);
} else {
mDPM.lockNow();
finish();
// Intent intent = new Intent(MainActivity.this,
// TrackDeviceService.class);
// startService(intent);
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (REQUEST_CODE == requestCode) {
startService(new Intent(StartActivity.this, TService.class));
finish();
}
super.onActivityResult(requestCode, resultCode, data);
}
}
And my TService class:
public class TService extends Service {
private MediaRecorder recorder;
private File audiofile;
private boolean recordstarted = false;
private static final String ACTION_IN = "android.intent.action.PHONE_STATE";
private static final String ACTION_OUT = "android.intent.action.NEW_OUTGOING_CALL";
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onDestroy() {
Log.d("service", "destroy");
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("StartService", "TService");
final IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_OUT);
filter.addAction(ACTION_IN);
this.registerReceiver(new CallReceiver(), filter);
return super.onStartCommand(intent, flags, startId);
}
private void startRecording() {
File sampleDir = new File(Environment.getExternalStorageDirectory(), "/TestRecordingDasa1");
if (!sampleDir.exists()) {
sampleDir.mkdirs();
}
String file_name = "Record";
try {
audiofile = File.createTempFile(file_name, ".amr", sampleDir);
} catch (IOException e) {
e.printStackTrace();
}
String path = Environment.getExternalStorageDirectory().getAbsolutePath();
recorder = new MediaRecorder();
// recorder.setAudioSource(MediaRecorder.AudioSource.VOICE_CALL);
recorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION);
recorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile(audiofile.getAbsolutePath());
try {
recorder.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
recorder.start();
recordstarted = true;
}
private void stopRecording() {
if (recordstarted) {
recorder.stop();
recordstarted = false;
}
}
public abstract class PhonecallReceiver extends BroadcastReceiver {
//The receiver will be recreated whenever android feels like it. We need a static variable to remember data between instantiations
private int lastState = TelephonyManager.CALL_STATE_IDLE;
private Date callStartTime;
private boolean isIncoming;
private String savedNumber; //because the passed incoming is only valid in ringing
#Override
public void onReceive(Context context, Intent intent) {
// startRecording();
//We listen to two intents. The new outgoing call only tells us of an outgoing call. We use it to get the number.
if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
savedNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER");
} else {
String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
int state = 0;
if (stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
state = TelephonyManager.CALL_STATE_IDLE;
} else if (stateStr.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
state = TelephonyManager.CALL_STATE_OFFHOOK;
} else if (stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
state = TelephonyManager.CALL_STATE_RINGING;
}
onCallStateChanged(context, state, number);
}
}
//Derived classes should override these to respond to specific events of interest
protected abstract void onIncomingCallReceived(Context ctx, String number, Date start);
protected abstract void onIncomingCallAnswered(Context ctx, String number, Date start);
protected abstract void onIncomingCallEnded(Context ctx, String number, Date start, Date end);
protected abstract void onOutgoingCallStarted(Context ctx, String number, Date start);
protected abstract void onOutgoingCallEnded(Context ctx, String number, Date start, Date end);
protected abstract void onMissedCall(Context ctx, String number, Date start);
//Deals with actual events
//Incoming call- goes from IDLE to RINGING when it rings, to OFFHOOK when it's answered, to IDLE when its hung up
//Outgoing call- goes from IDLE to OFFHOOK when it dials out, to IDLE when hung up
public void onCallStateChanged(Context context, int state, String number) {
if (lastState == state) {
//No change, debounce extras
return;
}
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
isIncoming = true;
callStartTime = new Date();
savedNumber = number;
onIncomingCallReceived(context, number, callStartTime);
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
//Transition of ringing->offhook are pickups of incoming calls. Nothing done on them
if (lastState != TelephonyManager.CALL_STATE_RINGING) {
isIncoming = false;
callStartTime = new Date();
startRecording();
onOutgoingCallStarted(context, savedNumber, callStartTime);
} else {
isIncoming = true;
callStartTime = new Date();
startRecording();
onIncomingCallAnswered(context, savedNumber, callStartTime);
}
break;
case TelephonyManager.CALL_STATE_IDLE:
//Went to idle- this is the end of a call. What type depends on previous state(s)
if (lastState == TelephonyManager.CALL_STATE_RINGING) {
//Ring but no pickup- a miss
onMissedCall(context, savedNumber, callStartTime);
} else if (isIncoming) {
stopRecording();
onIncomingCallEnded(context, savedNumber, callStartTime, new Date());
} else {
stopRecording();
onOutgoingCallEnded(context, savedNumber, callStartTime, new Date());
}
break;
}
lastState = state;
}
}
public class CallReceiver extends PhonecallReceiver {
#Override
protected void onIncomingCallReceived(Context ctx, String number, Date start) {
Log.d("onIncomingCallReceived", number + " " + start.toString());
}
#Override
protected void onIncomingCallAnswered(Context ctx, String number, Date start) {
Log.d("onIncomingCallAnswered", number + " " + start.toString());
}
#Override
protected void onIncomingCallEnded(Context ctx, String number, Date start, Date end) {
Log.d("onIncomingCallEnded", number + " " + start.toString() + "\t" + end.toString());
}
#Override
protected void onOutgoingCallStarted(Context ctx, String number, Date start) {
Log.d("onOutgoingCallStarted", number + " " + start.toString());
}
#Override
protected void onOutgoingCallEnded(Context ctx, String number, Date start, Date end) {
Log.d("onOutgoingCallEnded", number + " " + start.toString() + "\t" + end.toString());
}
#Override
protected void onMissedCall(Context ctx, String number, Date start) {
Log.d("onMissedCall", number + " " + start.toString());
// PostCallHandler postCallHandler = new PostCallHandler(number, "janskd" , "")
}
}
}
inside TService class you will find CallReceiever class that is going to handle everything you need from the call. You can add parameters as per your will, but, the main point is important.
From your MainActvitiy call Service that will start your Receiever. If you want to record media from receiever directly, you will get errors, so, you need to register Receiever from service. After that, you can call start recording and end recording wherever you like.
Calling return super.onStartCommand(intent, flags, startId); will have that Service lasting for more than one call, so keep that in mind.
Finally, AndroidManifest.xml file:
<manifest
package="your.package.name"
xmlns:android="http://schemas.android.com/apk/res/android"
android:installLocation="internalOnly">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.STORAGE" />
<application
android:name=".AppController"
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".ui.StartActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<receiver
android:name=".io.boot.DeviceAdminReciever"
android:permission="android.permission.BIND_DEVICE_ADMIN" >
<meta-data
android:name="android.app.device_admin"
android:resource="#xml/my_admin" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
<action android:name="android.app.action.DEVICE_ADMIN_DISABLED" />
<action android:name="android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED" />
</intent-filter>
</receiver>
<service android:name=".io.calls.TService" >
</service>
</application>
</manifest>
So, this is it, it's working perfectly with my Samsung Galaxy s6 Edge+, Ive tested it on Galaxy Note 4 and on Samsung J5, a big thank you to the authors of this post and the post about receiving phone calls.
The accepted answer is perfect, except it does not record outgoing calls. Note that for outgoing calls it is not possible (as near as I can tell from scouring many posts) to detect when the call is actually answered (if anybody can find a way other than scouring notifications or logs please let me know). The easiest solution is to just start recording straight away when the outgoing call is placed and stop recording when IDLE is detected. Just adding the same class as above with outgoing recording in this manner for completeness:
private void startRecord(String seed) {
String out = new SimpleDateFormat("dd-MM-yyyy hh-mm-ss").format(new Date());
File sampleDir = new File(Environment.getExternalStorageDirectory(), "/TestRecordingDasa1");
if (!sampleDir.exists()) {
sampleDir.mkdirs();
}
String file_name = "Record" + seed;
try {
audiofile = File.createTempFile(file_name, ".amr", sampleDir);
} catch (IOException e) {
e.printStackTrace();
}
String path = Environment.getExternalStorageDirectory().getAbsolutePath();
recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION);
recorder.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile(audiofile.getAbsolutePath());
try {
recorder.prepare();
} catch (IllegalStateException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
recorder.start();
recordstarted = true;
}
#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, "IN : " + inCall, Toast.LENGTH_LONG).show();
} else if (state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
if (wasRinging == true) {
Toast.makeText(context, "ANSWERED", Toast.LENGTH_LONG).show();
startRecord("incoming");
}
} else if (state.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
wasRinging = false;
Toast.makeText(context, "REJECT || DISCO", Toast.LENGTH_LONG).show();
if (recordstarted) {
recorder.stop();
recordstarted = false;
}
}
}
} else if (intent.getAction().equals(ACTION_OUT)) {
if ((bundle = intent.getExtras()) != null) {
outCall = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
Toast.makeText(context, "OUT : " + outCall, Toast.LENGTH_LONG).show();
startRecord("outgoing");
if ((bundle = intent.getExtras()) != null) {
state = bundle.getString(TelephonyManager.EXTRA_STATE);
if (state != null) {
if (state.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
wasRinging = false;
Toast.makeText(context, "REJECT || DISCO", Toast.LENGTH_LONG).show();
if (recordstarted) {
recorder.stop();
recordstarted = false;
}
}
}
}
}
}
}
The answer of pratt is bit uncomplete, because when you restart your device your app will working stop, recording stop, its become useless.
i m adding some line that copy in your project for complete working of Pratt answer.
<receiver
android:name=".DeviceAdminDemo"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data
android:name="android.app.admin"
android:resource="#xml/device_admin" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
<action android:name="android.app.action.DEVICE_ADMIN_DISABLED" />
<action android:name="android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED" />
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.HOME" />
</intent-filter>
</receiver>
put this code in onReceive of DeviceAdminDemo
#Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
context.stopService(new Intent(context, TService.class));
Intent myIntent = new Intent(context, TService.class);
context.startService(myIntent);
}
There is a simple solution to this problem using this library. I store an instance of the CallRecord class in MyService.class. When the service is first initialized, the following code is executed:
public class MyService extends Service {
public static CallRecord callRecord;
#Override
public void onCreate() {
super.onCreate();
callRecord = new CallRecord.Builder(this)
.setRecordFileName("test")
.setRecordDirName("Download")
.setRecordDirPath(Environment.getExternalStorageDirectory().getPath()) // optional & default value
.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB) // optional & default value
.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB) // optional & default value
.setAudioSource(MediaRecorder.AudioSource.VOICE_COMMUNICATION) // optional & default value
.setShowSeed(false) // optional, default=true ->Ex: RecordFileName_incoming.amr || RecordFileName_outgoing.amr
.build();
callRecord.enableSaveFile();
callRecord.startCallReceiver();
}
#Override
public void onDestroy() {
super.onDestroy();
callRecord.stopCallReceiver();
}
}
Next, do not forget to specify permissions in the manifest. (I may have some extras here, but keep in mind that some of them are necessary only for newer versions of Android)
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.PROCESS_INCOMING_CALLS" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Also it is crucial to request some permissions at the first start of the application. A guide is provided here.
If my code doesn't work, alternative code can be found here. I hope I helped you.
I am trying to display a message everytime I start a call or everytime I receive a call. I made a code which works for INCOMING call but not for OUTGOING ones.
I DID read the different posts about this subject.
Could anyone tell me why this following code shows me (of course this code does not do everything I mentionned previously) :
- "OUTGOING" when i receive a call
- "INCOMING" then "OUTGOING" when i start a call
/* From MainActivity */
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = getApplicationContext();
context.startService(new Intent(context, SvcCall.class));
}
/* From SvcCall */
public class SvcCall extends Service
{
private static final String ACTION_OUT = "android.intent.action.PHONE_STATE";
private static final String ACTION_IN = "android.intent.action.NEW_OUTGOING_CALL";
private CallBr br_call;
#Override
public void onCreate()
{
super.onCreate();
}
#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_STICKY);
}
public class CallBr extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
if (intent.getAction().equals(ACTION_IN))
Toast.makeText(context, "INCOMING", Toast.LENGTH_LONG).show();
else if (intent.getAction().equals(ACTION_OUT))
Toast.makeText(context, "OUTGOING", Toast.LENGTH_LONG).show();
}
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
}
/* From Manifest */
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
This is the class I use to react to incoming and outgoing phonecalls. Its set up so you just have to derive and override the ones you care about. It also tells you wheter the call is starting, ending, or was not picked up:
package com.gabesechan.android.reusable.receivers;
import java.util.Date;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
public abstract class PhonecallReceiver extends BroadcastReceiver {
//The receiver will be recreated whenever android feels like it. We need a static variable to remember data between instantiations
static PhonecallStartEndDetector listener;
String outgoingSavedNumber;
protected Context savedContext;
#Override
public void onReceive(Context context, Intent intent) {
savedContext = context;
if(listener == null){
listener = new PhonecallStartEndDetector();
}
//We listen to two intents. The new outgoing call only tells us of an outgoing call. We use it to get the number.
if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
listener.setOutgoingNumber(intent.getExtras().getString("android.intent.extra.PHONE_NUMBER"));
return;
}
//The other intent tells us the phone state changed. Here we set a listener to deal with it
TelephonyManager telephony = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
}
//Derived classes should override these to respond to specific events of interest
protected abstract void onIncomingCallStarted(String number, Date start);
protected abstract void onOutgoingCallStarted(String number, Date start);
protected abstract void onIncomingCallEnded(String number, Date start, Date end);
protected abstract void onOutgoingCallEnded(String number, Date start, Date end);
protected abstract void onMissedCall(String number, Date start);
//Deals with actual events
public class PhonecallStartEndDetector extends PhoneStateListener {
int lastState = TelephonyManager.CALL_STATE_IDLE;
Date callStartTime;
boolean isIncoming;
String savedNumber; //because the passed incoming is only valid in ringing
public PhonecallStartEndDetector() {}
//The outgoing number is only sent via a separate intent, so we need to store it out of band
public void setOutgoingNumber(String number){
savedNumber = number;
}
//Incoming call- goes from IDLE to RINGING when it rings, to OFFHOOK when it's answered, to IDLE when its hung up
//Outgoing call- goes from IDLE to OFFHOOK when it dials out, to IDLE when hung up
#Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
if(lastState == state){
//No change, debounce extras
return;
}
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
isIncoming = true;
callStartTime = new Date();
savedNumber = incomingNumber;
onIncomingCallStarted(incomingNumber, callStartTime);
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
//Transition of ringing->offhook are pickups of incoming calls. Nothing donw on them
if(lastState != TelephonyManager.CALL_STATE_RINGING){
isIncoming = false;
callStartTime = new Date();
onOutgoingCallStarted(savedNumber, callStartTime);
}
break;
case TelephonyManager.CALL_STATE_IDLE:
//Went to idle- this is the end of a call. What type depends on previous state(s)
if(lastState == TelephonyManager.CALL_STATE_RINGING){
//Ring but no pickup- a miss
onMissedCall(savedNumber, callStartTime);
}
else if(isIncoming){
onIncomingCallEnded(savedNumber, callStartTime, new Date());
}
else{
onOutgoingCallEnded(savedNumber, callStartTime, new Date());
}
break;
}
lastState = state;
}
}
}
Here is my update.
Actually, I tried both ways (one BR vs two BR) and both worked well thanks to your answer. Everything is not perfect at this moment. I am working on it. I'll show you how I do with one BR (because it was the object of my question).
public class SvcCall extends Service
{
Context _context;
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 void onCreate()
{
super.onCreate();
this._context = getApplicationContext();
}
#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_STICKY);
}
public class CallBr extends BroadcastReceiver
{
Bundle bundle;
String state;
String inCall, outCall;
public boolean wasRinging = 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, "IN : " + inCall, Toast.LENGTH_LONG).show();
}
else if (state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK))
{
if (wasRinging == true)
Toast.makeText(context, "ANSWERED", Toast.LENGTH_LONG).show();
}
else if (state.equals(TelephonyManager.EXTRA_STATE_IDLE))
{
wasRinging = false;
Toast.makeText(context, "REJECT || DISCO", 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, "OUT : " + outCall, Toast.LENGTH_LONG).show();
}
}
}
}
#Override
public IBinder onBind(Intent intent)
{
return null;
}
}
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import java.util.Date;
public abstract class PhoneCallReceiver2 extends BroadcastReceiver {
//The receiver will be recreated whenever android feels like it. We need a static variable to remember data between instantiations
static PhonecallStartEndDetector listener;
String outgoingSavedNumber;
protected Context savedContext;
#Override
public void onReceive(Context context, Intent intent) {
savedContext = context;
if (listener == null) {
listener = new PhonecallStartEndDetector(context);
}
//The other intent tells us the phone state changed. Here we set a listener to deal with it
TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
//We listen to two intents. The new outgoing call only tells us of an outgoing call. We use it to get the number.
if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
listener.setOutgoingNumber(intent.getExtras().getString("android.intent.extra.PHONE_NUMBER"));
return;
}
}
//Derived classes should override these to respond to specific events of interest
protected abstract void onIncomingCallStarted(Context context, String number, Date start);
protected abstract void onOutgoingCallStarted(Context context, String number, Date start);
protected abstract void onIncomingCallEnded(Context context, String number, Date start, Date end);
protected abstract void onOutgoingCallEnded(Context context, String number, Date start, Date end);
protected abstract void onMissedCall(Context context, String number, Date start);
//Deals with actual events
public class PhonecallStartEndDetector extends PhoneStateListener {
int lastState = TelephonyManager.CALL_STATE_IDLE;
Date callStartTime;
boolean isIncoming;
String savedNumber; //because the passed incoming is only valid in ringing
Context savedContext;
public PhonecallStartEndDetector(Context savedContext) {
this.savedContext = savedContext;
}
//The outgoing number is only sent via a separate intent, so we need to store it out of band
public void setOutgoingNumber(String number) {
savedNumber = number;
}
//Incoming call- goes from IDLE to RINGING when it rings, to OFFHOOK when it's answered, to IDLE when its hung up
//Outgoing call- goes from IDLE to OFFHOOK when it dials out, to IDLE when hung up
#Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
if (lastState == state) {
//No change, debounce extras
return;
}
switch (state) {
case TelephonyManager.CALL_STATE_RINGING:
isIncoming = true;
callStartTime = new Date();
savedNumber = incomingNumber;
onIncomingCallStarted(savedContext, incomingNumber, callStartTime);
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
//Transition of ringing->offhook are pickups of incoming calls. Nothing donw on them
if (lastState != TelephonyManager.CALL_STATE_RINGING) {
isIncoming = false;
callStartTime = new Date();
onOutgoingCallStarted(savedContext, savedNumber, callStartTime);
}
break;
case TelephonyManager.CALL_STATE_IDLE:
//Went to idle- this is the end of a call. What type depends on previous state(s)
if (lastState == TelephonyManager.CALL_STATE_RINGING) {
//Ring but no pickup- a miss
onMissedCall(savedContext, savedNumber, callStartTime);
} else if (isIncoming) {
onIncomingCallEnded(savedContext, savedNumber, callStartTime, new Date());
} else {
onOutgoingCallEnded(savedContext, savedNumber, callStartTime, new Date());
}
break;
}
lastState = state;
}
}
}
switch (state) {
case TelephonyManager.CALL_STATE_IDLE:
//CALL_STATE_IDLE;
Toast.makeText(getApplicationContext(), "CALL_STATE_IDLE", Toast.LENGTH_LONG).show();
break;
case TelephonyManager.CALL_STATE_OFFHOOK:
//CALL_STATE_OFFHOOK;
Toast.makeText(getApplicationContext(), "CALL_STATE_OFFHOOK", Toast.LENGTH_LONG).show();
break;
case TelephonyManager.CALL_STATE_RINGING:
//CALL_STATE_RINGING
Toast.makeText(getApplicationContext(), incomingNumber, Toast.LENGTH_LONG).show();
Toast.makeText(getApplicationContext(), "CALL_STATE_RINGING", Toast.LENGTH_LONG).show();
break;
default:
break;
}
Dont forget to make a permission..
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>