我们实现了通信能力:
第一、我们支持sip固话通信功能,是一个完整的电话程序。我们支持sip呼出、sip呼入,打开/关闭扬声器,调整自己说话音量、调整对方音量,调起拨号盘进行指令输出。
第二、axb双呼。流程就是我们调用后端的接口,然后后端调用运营商的接口,然后运营商通过中间号打电话过来,并且打电话给我们要打的那个号码,然后我们之间建立了通信。
第三、支持axb双呼的自动接听,支持使用我们的程序替代系统默认电话程序。
这里我们先主要说下,我们的程序替代系统默认电话程序。
首先需要将我们的APP声明成一个具有电话功能的程序,即让系统觉得我们应用和手机自带的电话程序是一种类型的程序。
那么如何声明呢:
我们需要一个Activity和一个Service,我们就命名成PhoneActivity和PhoneService吧。其中PhoneService需要继承InCallService。
并且我们需要在AndroidManifest.xml中进行注册,代码如下:
<activity android:name=".PhoneActivity">
<!--region provides ongoing call UI必须有,否则调不起设置默认电话-->
<intent-filter>
<action android:name="android.intent.action.DIAL"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="tel"/>
</intent-filter>
<!--end region-->
<!--region provides dial UI-->
<intent-filter>
<action android:name="android.intent.action.DIAL"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<!--end region-->
</activity>
<service
android:name=".PhoneService"
android:permission="android.permission.BIND_INCALL_SERVICE">
<meta-data
android:name="android.telecom.IN_CALL_SERVICE_UI"
android:value="true"/>
<intent-filter>
<action android:name="android.telecom.InCallService"/>
</intent-filter>
</service>
为什么要进行这样的注册呢?目的是使我们的程序具有电话程序特征。
PhoneService用于监听电话通信状态。比如说当有来电或者去电,onCallAdded方法会被回调,
当有接听或者挂断,我们可以通过onStateChanged监听到。
然后,当用户在设置中打开开关后,会弹出切换系统默认电话程序的系统弹框。代码如下:
package com.zdj.phone;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Build;
import android.os.Bundle;
import android.telecom.TelecomManager;
import android.view.View;
import android.widget.ImageView;
import com.zdj.phone.core.PhoneManager;
import com.zdj.systemfuncationlibrary.SystemUtils;
public class PhoneMainActivity extends AppCompatActivity implements View.OnClickListener {
private ImageView switch_default_phone_application;
private SharedPreferences appInfoSP;
private SharedPreferences.Editor appInfoSPEditor;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_phone_main);
switch_default_phone_application = findViewById(R.id.switch_default_phone_application);
switch_default_phone_application.setOnClickListener(this);
appInfoSP = getApplicationContext().getSharedPreferences(getPackageName() + "_appInfo", Context.MODE_PRIVATE);
appInfoSPEditor = appInfoSP.edit();
}
@Override
protected void onResume() {
super.onResume();
if (isDefaultPhoneApplication()) {
switch_default_phone_application.setTag(true);
switch_default_phone_application.setImageResource(R.drawable.switch_on);
} else {
switch_default_phone_application.setTag(false);
switch_default_phone_application.setImageResource(R.drawable.switch_off);
}
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.switch_default_phone_application) {
switchDefaultPhoneApplication();
}
}
private boolean isDefaultPhoneApplication() {
TelecomManager telecomManager = (TelecomManager) getSystemService(TELECOM_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (telecomManager.getDefaultDialerPackage() != null) {
String defaultDialerPackage = telecomManager.getDefaultDialerPackage();
if (defaultDialerPackage.equals(getPackageName())) {
return true;
} else {
appInfoSPEditor.putString("defaultDialerPackage", defaultDialerPackage).commit();
}
}
}
return false;
}
private void switchDefaultPhoneApplication() {
if (!(boolean)switch_default_phone_application.getTag()) {
PhoneManager.setDefaultDialerSetWindow(this, getPackageName());
} else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
if (("huawei".equals(android.os.Build.BRAND.toLowerCase()) || "honor".equals(android.os.Build.BRAND.toLowerCase()))
&& !SystemUtils.isHarmonyOS()) {
Intent hwIntent = new Intent();
hwIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
hwIntent.setClassName("com.android.settings", "com.android.settings.Settings$PreferredListSettingActivity");
startActivity(hwIntent);
} else {
startActivity(new Intent("android.settings.MANAGE_DEFAULT_APPS_SETTINGS"));
}
} else {
PhoneManager.setDefaultDialerSetWindow(this, appInfoSP.getString("defaultDialerPackage", ""));
}
}
}
}
当将我们的应用设置成系统默认电话程序后,来电和去电都将是走我们的程序了(即走我们写的逻辑,弹出我们的通话界面)
下面,我们再贴出三个很重要的类,依次分别为PhoneActivity、PhoneService、PhoneManager。
PhoneActivity:
package com.zdj.phone.core;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.text.TextUtils;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import com.zdj.phone.R;
import com.zdj.phone.widget.DialPadDialog;
import com.zdj.zdjuilibrary.dialog.NormalInteractionDialog;
import java.util.List;
/**
* <pre>
* author : dejinzhang
* time : 2021/09/10
* desc : 使我们的应用成为一个电话程序必需的组件
* </pre>
*/
public class PhoneActivity extends Activity {
private static final int MY_PERMISSIONS_CALL_PHONE = 1;
private DialPadDialog dialPadDialog;
private TextView tv_input_numbers;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_phone);
tv_input_numbers = findViewById(R.id.tv_input_numbers);
dialPadDialog = new DialPadDialog(this, R.style.NotDarkenAndFirstDialogAnimationStyle);
dialPadDialog.setCanceledOnTouchOutside(false);
dialPadDialog.setCallback(new DialPadDialog.Callback() {
@Override
public void call(List<Character> list) {
if (checkSelfPermission(Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(PhoneActivity.this, new String[]{Manifest.permission.CALL_PHONE}, MY_PERMISSIONS_CALL_PHONE);
} else {
callDeal(list);
}
}
@Override
public void collapse() {}
@Override
public void refreshShow(List<Character> list) {
if (list.size() > 0) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < list.size(); i++) {
stringBuilder.append(list.get(i));
}
tv_input_numbers.setText(stringBuilder);
tv_input_numbers.setVisibility(View.VISIBLE);
} else {
tv_input_numbers.setVisibility(View.GONE);
}
}
});
Uri uri = getIntent().getData();
if (uri != null) {
String uriString = uri.toString();
if (uriString != null && uriString.startsWith("tel:")) {
String phoneNumber = uriString.substring("tel:".length());
if (!TextUtils.isEmpty(phoneNumber)) {
char[] numbers = phoneNumber.toCharArray();
for (int i = 0; i < numbers.length; i++) {
dialPadDialog.getInputStringList().add(numbers[i]);
}
tv_input_numbers.setText(phoneNumber);
tv_input_numbers.setVisibility(View.VISIBLE);
}
}
}
dialPadDialog.show();
findViewById(R.id.iv_dial_pad).setOnClickListener(v -> dialPadDialog.show());
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == MY_PERMISSIONS_CALL_PHONE) {
if (!(grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
NormalInteractionDialog normalInteractionDialog = new NormalInteractionDialog(this, R.style.DialogNoAnimationStyle);
normalInteractionDialog.init(true, false, null,
getString(R.string.call_permission_hint), getString(R.string.cancel), getString(R.string.go_open));
normalInteractionDialog.show();
normalInteractionDialog.setCallback(new NormalInteractionDia
© 版权声明
文章版权归作者所有,未经允许请勿转载。
相关文章
暂无评论...




