我正在构建一个默认的来电显示和垃圾邮件发送者应用程序,它将检索一个号码的来电详细信息并拦截呼叫的详细信息。我在 Android 10.0 上,不知道这段代码将如何在其他版本的 Android 上运行。安装应用程序后,我无法拨打任何拨出号码。我收到此错误消息:
Call can't be placed by <appname>. Try using a different call redirecting app or contacting the developer for help.
谁能告诉我如何解决这个问题?
This is the manifest file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="xxxx.xxxxxxxx.xxxxxxxxxxx">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" tools:ignore="ProtectedPermissions"/>
<uses-permission android:name="android.permission.ACTION_MANAGE_OVERLAY_PERMISSION" />
<uses-feature
android:name="android.hardware.telephony"
android:required="true" />
<uses-feature
android:name="android.hardware.microphone"
android:required="true" />
<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/Theme.GoDial">
<activity android:name="xxxx.xxxx.xxxx.GetCallerInfoActivity" android:theme="@style/Theme.AppCompat.Transparent.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<action android:name="android.intent.action.DIAL" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="tel" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.DIAL" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<receiver android:name="xxxxxxxx.xxxxxxxx.xxxxxxxx.AnsCallBroadcastReceiver"
android:permission="android.permission.BIND_SCREENING_SERVICE" >
<intent-filter>
<action android:name="android.telecom.CallScreeningService" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
<service
android:name="xxxxxxxx.xxxxxxxxx.xxxxxxx.CallMonitorService"
android:permission="android.permission.BIND_CALL_REDIRECTION_SERVICE"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.telecom.CallRedirectionService"/>
</intent-filter>
</service>
</application>
</manifest>
GetCallerInfoActivity.java
public class GetCallerInfoActivity extends Activity{
private String number;
public static int READ_PHONE_STATE=101;
public static int REQUEST_CALL_LOG=102;
public static int PERMISSION_DRAW_OVER_OTHER_APP=103;
private RoleManager roleManager = null;
private static final String[] PERMS = {Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_CALL_LOG};
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
synchronized (this) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(GetCallerInfoActivity.this)) {
//If the draw over permission is not available open the settings screen to grant the permission.
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, PERMISSION_DRAW_OVER_OTHER_APP);
}else
checkCallStates();
}
try {
if (getApplicationContext().checkSelfPermission(Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
//Permission has not been granted, therefore prompt the user to grant permission
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_PHONE_STATE,
Manifest.permission.READ_CALL_LOG}, READ_PHONE_STATE);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@RequiresApi(api = Build.VERSION_CODES.M)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PERMISSION_DRAW_OVER_OTHER_APP) {
//Check if the permission is granted or not.
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
roleManager = (RoleManager) getSystemService(Context.ROLE_SERVICE);
Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_CALL_REDIRECTION);
startActivityForResult(intent, 1);
}
} else if (requestCode == 1){
finish();
}else
super.onActivityResult(requestCode, resultCode, data);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == READ_PHONE_STATE && grantResults.length > 0) {
if (grantResults[0] != PackageManager.PERMISSION_GRANTED|| grantResults[1] != PackageManager.PERMISSION_GRANTED) {
if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(getApplicationContext(), "Please grant permission to read contacts", Toast.LENGTH_SHORT).show();
} else if (grantResults[1] != PackageManager.PERMISSION_GRANTED) {
Toast.makeText(getApplicationContext(), "Please grant permission to get your contact number", Toast.LENGTH_SHORT).show();
}
}
}
if (requestCode==PERMISSION_DRAW_OVER_OTHER_APP&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
//If the draw over permission is not available open the settings screen to grant the permission.
checkCallStates();
}
}
private void checkCallStates(){
PhoneCallListener phoneListener = new PhoneCallListener();
TelephonyManager telephonyManager = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
telephonyManager.listen(phoneListener, PhoneStateListener.LISTEN_CALL_STATE);
}
class PhoneCallListener extends PhoneStateListener {
private boolean isPhoneCalling = false;
@Override
public void onCallStateChanged(int state, String incomingNumber) {
if (TelephonyManager.CALL_STATE_RINGING == state) {
// phone ringing
Log.i("LOG_TAG", "RINGING, number: " + incomingNumber);
}
if (TelephonyManager.CALL_STATE_OFFHOOK == state) {
// active
Log.i("LOG_TAG", "OFFHOOK");
isPhoneCalling = true;
}
if (TelephonyManager.CALL_STATE_IDLE == state) {
// run when class initial and phone call ended, need detect flag
// from CALL_STATE_OFFHOOK
Log.i("LOG_TAG", "IDLE number");
if (isPhoneCalling) {
Handler handler = new Handler();
//Put in delay because call log is not updated immediately when state changed
// The dialler takes a little bit of time to write to it 500ms seems to be enough
handler.postDelayed(new Runnable() {
@Override
public void run() {
// get start of cursor
Log.i("CallLogDetailsActivity", "Getting Log activity...");
String[] projection = new String[]{CallLog.Calls.NUMBER};
Cursor cur = getContentResolver().query(CallLog.Calls.CONTENT_URI, projection, null, null, CallLog.Calls.DATE + " desc");
cur.moveToFirst();
String lastCallnumber = cur.getString(0);
}
}, 500);
isPhoneCalling = false;
}
}
}
}
}
AnsCallBroadcastReceiver.java
public class AnsCallBroadcastReceiver extends BroadcastReceiver {
private boolean isPhoneCalling = false;
@Override
public void onReceive(Context arg0, Intent arg1) {
TelephonyManager telephony = (TelephonyManager)arg0.getSystemService(Context.TELEPHONY_SERVICE);
telephony.listen(new PhoneStateListener(){
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
System.out.println("incomingNumber : "+incomingNumber);
if (TelephonyManager.CALL_STATE_RINGING == state) {
// phone ringing
Log.i("LOG_TAG", "RINGING, number: " + incomingNumber);
Toast.makeText(arg0,"CALL_STATE_RINGING: "+incomingNumber,Toast.LENGTH_LONG).show();
Intent intent = new Intent(arg0, PopUpDialog.class);
// To pass any data to next activity
intent.putExtra("message", incomingNumber);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// start your next activity
arg0.startActivity(intent);
}
if (TelephonyManager.CALL_STATE_OFFHOOK == state) {
// active
Log.i("LOG_TAG", "OFFHOOK");
isPhoneCalling = true;
Toast.makeText(arg0,"CALL_STATE_OFFHOOK: " +incomingNumber,Toast.LENGTH_LONG).show();
Intent intent = new Intent(arg0, PopUpDialog.class);
// To pass any data to next activity
intent.putExtra("message", incomingNumber);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// start your next activity
arg0.startActivity(intent);
}
if (TelephonyManager.CALL_STATE_IDLE == state) {
// run when class initial and phone call ended, need detect flag
// from CALL_STATE_OFFHOOK
Log.i("LOG_TAG", "IDLE number");
if (isPhoneCalling) {
Handler handler = new Handler();
//Put in delay because call log is not updated immediately when state changed
// The dialler takes a little bit of time to write to it 500ms seems to be enough
handler.postDelayed(new Runnable() {
@Override
public void run() {
// get start of cursor
Log.i("CallLogDetailsActivity", "Getting Log activity...");
String[] projection = new String[]{CallLog.Calls.NUMBER};
Cursor cur = arg0.getContentResolver().query(CallLog.Calls.CONTENT_URI, projection, null, null, CallLog.Calls.DATE + " desc");
cur.moveToFirst();
String lastCallnumber = cur.getString(0);
Toast.makeText(arg0,"CALL_STATE_OFFHOOK: " +lastCallnumber,Toast.LENGTH_LONG).show();
}
}, 500);
isPhoneCalling = false;
}
}
}
}, PhoneStateListener.LISTEN_CALL_STATE);
}
}
CallMonitorService.java
public class CallMonitorService extends CallRedirectionService {
private static final String TAG = "CallMonitorService";
public CallMonitorService() {
}
@Override
public void onPlaceCall(Uri uri, PhoneAccountHandle phoneAccountHandle, boolean b) {
Log.e(TAG, "onPlaceCall:### ");
}
}