Android Telephone

内容分享5天前发布
1 0 0

我们实现了通信能力:

第一、我们支持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
© 版权声明

相关文章

暂无评论

none
暂无评论...