Java 在拨号器应用程序上检测来电并在后台打开服务
在检测到来电后,我会在来电时打开一个类似messenger的聊天图标。但我面临两个问题:Java 在拨号器应用程序上检测来电并在后台打开服务,java,android,service,broadcastreceiver,Java,Android,Service,Broadcastreceiver,在检测到来电后,我会在来电时打开一个类似messenger的聊天图标。但我面临两个问题: 1.我的应用程序关闭时未检测到来电(即使在后台也未运行)。 2.当我的手机被锁定时,聊天图标不会出现。来电时,聊天图标隐藏在拨号器应用程序后面。 我正在使用Broadcast Receiver接收来电,使用PhoneCallReceiver类调用CallReceiver类下定义的方法,并在检测来电时启动服务ChatHeadService,该服务会打开一个类似聊天的图标。我附上了聊天图标的屏幕截图。在过去的
1.我的应用程序关闭时未检测到来电(即使在后台也未运行)。
2.当我的手机被锁定时,聊天图标不会出现。来电时,聊天图标隐藏在拨号器应用程序后面。
我正在使用
Broadcast Receiver
接收来电,使用PhoneCallReceiver
类调用CallReceiver
类下定义的方法,并在检测来电时启动服务ChatHeadService
,该服务会打开一个类似聊天的图标。我附上了聊天图标的屏幕截图。在过去的6个月里,我一直面临着这个问题,但一直无法解决。任何帮助都将不胜感激。
编译DK23版
buildToolsVersion“27.0.3”
targetSdkVersion 23
我在两台API级别为18和26的设备上测试了该应用程序。在API级别18中,我的应用程序运行良好,上述两个问题都已修复。但在API级别26中,我的应用程序工作了,但没有工作,聊天图标隐藏在拨号器应用程序后面。
我在Oreo API 26的传入调用中遇到以下错误。
06-13 16:22:23.969 1238-4375/?W/BroadcastQueue:Permission Denial:receiving Intent{act=android.Intent.action.PHONE_STATE flg=0x1000010(有附加项)}到com.skype.m2/com.skype.nativephone.connector.NativePhoneCallReceiver需要android.Permission.READ_PHONE_STATE,因为发送方android(uid 1000)
API等级26
API等级18
AndroidManifest.xml
CallReceiver.java
ChatHeadService.java
在调用结束后调用此方法
private void alert(Context ctx) {
StringBuffer sb = new StringBuffer();
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
Cursor cur = getContentResolver().query(CallLog.Calls.CONTENT_URI,
null, null, null, CallLog.Calls.DATE + " DESC limit 1;");
//Cursor cur = getContentResolver().query( CallLog.Calls.CONTENT_URI,null, null,null, android.provider.CallLog.Calls.DATE + " DESC");
int number = cur.getColumnIndex( CallLog.Calls.NUMBER );
int duration = cur.getColumnIndex( CallLog.Calls.DURATION);
int type = cur.getColumnIndex(CallLog.Calls.TYPE);
int date = cur.getColumnIndex(CallLog.Calls.DATE);
sb.append( "Call Details : \n");
phNumber = null;
callDuration = null;
callType = null;
callDate = null;
String dir = null;
String callDayTime = null;
while ( cur.moveToNext() ) {
phNumber = cur.getString( number );
callDuration = cur.getString( duration );
callType = cur.getString( type );
callDate = cur.getString( date );
callDayTime = new Date(Long.valueOf(callDate)).toString();
int dircode = Integer.parseInt(callType);
switch (dircode) {
case CallLog.Calls.OUTGOING_TYPE:
dir = "OUTGOING";
break;
case CallLog.Calls.INCOMING_TYPE:
dir = "INCOMING";
break;
case CallLog.Calls.MISSED_TYPE:
dir = "MISSED";
break;
}
// sb.append( "\nPhone Number:--- "+phNumber +" \nCall duration in sec :--- "+callDuration );
sb.append("\nPhone Number:--- " + phNumber + " \nCall Type:--- " + dir + " \nCall Date:--- " + callDayTime + " \nCall duration in sec :--- " + callDuration);
sb.append("\n----------------------------------");
Log.e("dir",dir);
}
cur.close();
callType=dir;
callDate=callDayTime;
Log.e("call ",phNumber+" duration"+callDuration+" type "+callType+" date "+callDate);
startactivity(ctx);
}
它将为您提供上次通话的详细信息不久前我发现了这个示例。我只加了一句,电话是打进来的还是打出去的。通过意图将数据传递给服务,并使用它执行服务。应在api 23中工作。在最新版本中,我不能保证这一点
public class CallReceiver extends BroadcastReceiver {
private final static String TAG = "CallReceiver";
private static PhoneCallStartEndDetector listener;
private String outgoingSavedNumber;
protected Context savedContext;
@Override
public void onReceive(Context context, Intent intent) {
this.savedContext = context;
if (listener == null) {
listener = new PhoneCallStartEndDetector();
}
String phoneState = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
if (phoneState == null) {
listener.setOutgoingNumber(intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER));
} else if (phoneState.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
listener.setOutgoingNumber(intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER));
}
TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
}
//Deals with actual events
private class PhoneCallStartEndDetector extends PhoneStateListener {
int lastState = TelephonyManager.CALL_STATE_IDLE;
boolean isIncoming;
boolean isOutgoing;
String savedNumber; //because the passed incoming is only valid in ringing
private PhoneCallStartEndDetector() {
}
//The outgoing number is only sent via a separate intent, so we need to store it out of band
private void setOutgoingNumber(String number) {
savedNumber = number;
}
Intent serviceIntent = new Intent(savedContext, YourService.class);
//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;
savedNumber = incomingNumber;
serviceIntent.putExtra("label", value);
savedContext.startService(serviceIntent);
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) {
if (!isOutgoing) {
isOutgoing = true;
}
if (!savedNumber.equals("")) {
serviceIntent.putExtra("label", value);
savedContext.startService(serviceIntent);
}
}
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
savedContext.stopService(serviceIntent);
} else if (isIncoming) {
savedContext.stopService(serviceIntent);
} else {
if (isOutgoing) {
savedContext.stopService(serviceIntent);
isOutgoing = false;
}
}
break;
}
lastState = state;
}
}
}
在清单中注册此接收者,这应适用于api 25:
<receiver
android:name=".calls.CallReceiver"
android:enabled="true">
<intent-filter android:priority="-1">
<action android:name="android.intent.action.PHONE_STATE" />
<action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
</intent-filter>
</receiver>
当然,要使用此代码,您需要授予权限。在api级别低于23的清单中:
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
若你们不想在后台运行应用程序,那个么试着看看如何在其他应用程序上运行和显示标签。@Jarvis我希望我的应用程序在后台运行。我希望即使我的应用程序关闭,广播接收器也能正常工作。使用startForeground()方法在后台运行服务public void onCallStateChanged(上下文上下文、int状态、字符串编号)
这不是重写方法,您应该使用侦听器通知入/出呼叫开始。让我们搜索一个示例,其中扩展了PhoneStateListener
。@grabarz121您能给我一个示例或一些链接吗?还记得关于读取呼叫日志的权限吗android.permission.read\u call\u LOG
我不想要这个方法。我已经知道来电的详细情况了。我想在我遇到问题的来电中打开聊天图标。你在哪个设备上测试应用程序?@miteshmakwana Api 26 Orei在不同的设备上测试我在Api级别为17的不同设备上使用我的应用程序,我的应用程序在该设备上工作。因此,我认为这个问题在某种程度上与当前api级别的权限有关。我理解您的应用程序完全无法工作。签入oreo设备应用程序管理器,您的应用程序是否已授予权限。根据api 23,清单中的权限不会自动授予,您必须询问用户(启动活动中最好的)。如果已授予权限,请尝试以编程方式注册您的broadcastreceiver。在api 26中,接收器和服务存在限制。在代码中添加一些日志,并尝试在启动活动中注册此日志,然后进行呼叫。我已在oreo设备上手动授予应用程序所需的所有权限。我不知道如何以编程方式注册广播接收器。registerReceiver()
或context.registerReceiver()
我应该在哪个类中调用registerReceiver
或context.registerReceiver
?
private void alert(Context ctx) {
StringBuffer sb = new StringBuffer();
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
Cursor cur = getContentResolver().query(CallLog.Calls.CONTENT_URI,
null, null, null, CallLog.Calls.DATE + " DESC limit 1;");
//Cursor cur = getContentResolver().query( CallLog.Calls.CONTENT_URI,null, null,null, android.provider.CallLog.Calls.DATE + " DESC");
int number = cur.getColumnIndex( CallLog.Calls.NUMBER );
int duration = cur.getColumnIndex( CallLog.Calls.DURATION);
int type = cur.getColumnIndex(CallLog.Calls.TYPE);
int date = cur.getColumnIndex(CallLog.Calls.DATE);
sb.append( "Call Details : \n");
phNumber = null;
callDuration = null;
callType = null;
callDate = null;
String dir = null;
String callDayTime = null;
while ( cur.moveToNext() ) {
phNumber = cur.getString( number );
callDuration = cur.getString( duration );
callType = cur.getString( type );
callDate = cur.getString( date );
callDayTime = new Date(Long.valueOf(callDate)).toString();
int dircode = Integer.parseInt(callType);
switch (dircode) {
case CallLog.Calls.OUTGOING_TYPE:
dir = "OUTGOING";
break;
case CallLog.Calls.INCOMING_TYPE:
dir = "INCOMING";
break;
case CallLog.Calls.MISSED_TYPE:
dir = "MISSED";
break;
}
// sb.append( "\nPhone Number:--- "+phNumber +" \nCall duration in sec :--- "+callDuration );
sb.append("\nPhone Number:--- " + phNumber + " \nCall Type:--- " + dir + " \nCall Date:--- " + callDayTime + " \nCall duration in sec :--- " + callDuration);
sb.append("\n----------------------------------");
Log.e("dir",dir);
}
cur.close();
callType=dir;
callDate=callDayTime;
Log.e("call ",phNumber+" duration"+callDuration+" type "+callType+" date "+callDate);
startactivity(ctx);
}
public class CallReceiver extends BroadcastReceiver {
private final static String TAG = "CallReceiver";
private static PhoneCallStartEndDetector listener;
private String outgoingSavedNumber;
protected Context savedContext;
@Override
public void onReceive(Context context, Intent intent) {
this.savedContext = context;
if (listener == null) {
listener = new PhoneCallStartEndDetector();
}
String phoneState = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
if (phoneState == null) {
listener.setOutgoingNumber(intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER));
} else if (phoneState.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
listener.setOutgoingNumber(intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER));
}
TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
}
//Deals with actual events
private class PhoneCallStartEndDetector extends PhoneStateListener {
int lastState = TelephonyManager.CALL_STATE_IDLE;
boolean isIncoming;
boolean isOutgoing;
String savedNumber; //because the passed incoming is only valid in ringing
private PhoneCallStartEndDetector() {
}
//The outgoing number is only sent via a separate intent, so we need to store it out of band
private void setOutgoingNumber(String number) {
savedNumber = number;
}
Intent serviceIntent = new Intent(savedContext, YourService.class);
//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;
savedNumber = incomingNumber;
serviceIntent.putExtra("label", value);
savedContext.startService(serviceIntent);
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) {
if (!isOutgoing) {
isOutgoing = true;
}
if (!savedNumber.equals("")) {
serviceIntent.putExtra("label", value);
savedContext.startService(serviceIntent);
}
}
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
savedContext.stopService(serviceIntent);
} else if (isIncoming) {
savedContext.stopService(serviceIntent);
} else {
if (isOutgoing) {
savedContext.stopService(serviceIntent);
isOutgoing = false;
}
}
break;
}
lastState = state;
}
}
<receiver
android:name=".calls.CallReceiver"
android:enabled="true">
<intent-filter android:priority="-1">
<action android:name="android.intent.action.PHONE_STATE" />
<action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
</intent-filter>
</receiver>
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("android.intent.action.PHONE_STATE");
CallReceiver receiver = new CallReceiver();
registerReceiver(receiver, intentFilter);
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
Manifest.permission.READ_PHONE_STATE