Android Telephone

We implement communication capabilities:

First, we support sip fixed-line communication function, which is a complete telephone program. We support sip call out, sip call in, turn on/off the speaker, adjust the volume of your own speech, adjust the volume of the other party, and call up the dial for command output.

Second, axb double call. The process is that we call the back-end interface, and then the back-end calls the operator's interface, and then the operator calls through the intermediate number, and calls the number we want to call, and then we establish communication.

Third, support the automatic answering of axb double calls, and support the use of our program to replace the system's default phone program.

Here we mainly talk about that our program replaces the system default phone program.

First of all, we need to declare our APP as a program with a phone function, that is, let the system think that our application and the phone program that comes with the mobile phone are a type of program.

So how to declare it:

We need an Activity and a Service, let's name it PhoneActivity and PhoneService. Among them, PhoneService needs to inherit InCallService.

And we need to register in AndroidManifest.xml, the code is as follows:

        <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>

Why such a registration? The goal is to give our program the characteristics of a telephony program.

PhoneService is used to monitor the phone communication status. For example, when there is an incoming or outgoing call, the onCallAdded method will be called back.

When there is an answer or hang up, we can monitor it through onStateChanged.

Then, when the user turns on the switch in the settings, a system pop-up box will pop up to switch the system default phone program. code show as below:

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", ""));
            }
        }
    }
}

When our application is set as the system's default phone program, incoming and outgoing calls will go through our program (that is, go through the logic we wrote and pop up our call interface)

Below, we post three more important classes, which are PhoneActivity, PhoneService, and PhoneManager in turn.

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 NormalInteractionDialog.Callback() {
                    @Override
                    public void done() {
                        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                        intent.setData(Uri.fromParts("package", getPackageName(), null));
                        startActivity(intent);
                    }

                    @Override
                    public void cancel() {}
                });
            } else {
                callDeal(dialPadDialog.getInputStringList());
            }
        }
    }

    private void callDeal(List<Character> list) {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < list.size(); i++) {
            stringBuilder.append(list.get(i));
        }
        PhoneManager.call(this, stringBuilder.toString());
    }
}

PhoneService:

package com.zdj.phone.core;

import android.content.Intent;
import android.telecom.Call;
import android.telecom.InCallService;

import com.zdj.phone.Config;
import com.zdj.phone.ScreenActivity;
import com.zdj.systemfuncationlibrary.LogUtils;

/**
 * <pre>
 *     author : dejinzhang
 *     time : 2021/09/10
 *     desc : 服务
 * </pre>
 */
public class PhoneService extends InCallService {
    private static final String TAG = "PhoneService";

    @Override
    public void onCreate() {
        super.onCreate();
    }

    private Call.Callback callback = new Call.Callback() {
        @Override
        public void onStateChanged(Call call, int state) {
            super.onStateChanged(call, state);
            LogUtils.i(TAG, "state:" + state);
            if (state == Call.STATE_ACTIVE) {
                LogUtils.i(TAG, "----接听一个电话----" + call.toString());
                //在这里可以发送广播,通知通话界面变化
                Intent intent = new Intent();
                intent.setAction(ScreenActivity.CALL_NET_ING);
                sendBroadcast(intent);
            } else if (state == Call.STATE_DISCONNECTED) {
                LogUtils.i(TAG, "---挂断一个电话----" + call.toString());
                //在这里可以发送广播,通知通话界面变化
                Intent intent = new Intent();
                intent.setAction(ScreenActivity.CALL_NET_END);
                sendBroadcast(intent);
//                call.unregisterCallback(callback);  //其实这句代码也可以注释掉,因为当连接断开时,即电话挂断的时候,会在进入这个监听后,再回调onCallRemoved,我们在onCallRemoved中取消注册回调即可
            }/* else if (state == Call.STATE_CONNECTING) {
                LogUtils.i(TAG, "----呼叫,正在建立连接----" + call.toString());
                //我们也可以根据业务在这里执行相应动作。
            }*/
        }
    };

    @Override
    public void onCallAdded(Call call) {
        super.onCallAdded(call);
        LogUtils.i(TAG, "----添加一个电话----");
        call.registerCallback(callback);  //注册回调
        Call.Details details = call.getDetails();
        String phoneNumber = "";
        int state;
        if (details != null && details.getHandle() != null) {
            phoneNumber = details.getHandle().getSchemeSpecificPart();
        }
        state = call.getState();
        LogUtils.i(TAG, "phoneNumber:" + phoneNumber);
        LogUtils.i(TAG, "callState:" + state);
        Config.call = call;

        //在这里可以跳转到通话界面
        Intent intent = new Intent();
        intent.setClass(this, ScreenActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
        intent.putExtra("phoneNum", phoneNumber);
        if (state == Call.STATE_RINGING) {
            intent.putExtra("from", ScreenActivity.NET_IN);
        } else if (state == Call.STATE_CONNECTING) {
            intent.putExtra("from", ScreenActivity.NET_OUT);
        }
        startActivity(intent);
    }

    @Override
    public void onCallRemoved(Call call) {
        super.onCallRemoved(call);
        LogUtils.i(TAG, "----移除一个电话----" + call.toString());
        call.unregisterCallback(callback);
    }
}

PhoneManager:

package com.zdj.phone.core;

import android.Manifest;
import android.app.Activity;
import android.app.role.RoleManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Build;
import android.telecom.Call;
import android.telecom.TelecomManager;
import android.telecom.VideoProfile;

import static android.content.Context.AUDIO_SERVICE;
import static android.content.Context.TELECOM_SERVICE;

/**
 * <pre>
 *     author : dejinzhang
 *     time : 2021/09/09
 *     desc : 电话管理器。该类中包含了对电话操作的各类方法:
 *            接听、挂断,以及弹出设置应用为系统默认电话程序(会弹出切换系统电话程序的系统弹框)的方法。
 * </pre>
 */
public class PhoneManager {
    /**
     * 设置应用为系统默认电话程序(会弹出切换系统默认电话程序的系统弹框)
     * 注意:
     * Android 10以上使用RoleManager
     * Android 6 ~ Android 9以上使用TelecomManager
     * Android 6以下不支持(因为Android 6以下call.answer call.disconnect等api不能用)
     * @param context  上下文
     * @param packageName  需要设置为系统默认电话程序的应用包名
     */
    public static void setDefaultDialerSetWindow(Context context, String packageName) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            RoleManager roleManager = context.getSystemService(RoleManager.class);
            if (roleManager != null) {
                Intent roleRequestIntent = roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER);
                ((Activity)context).startActivityForResult(roleRequestIntent, 999);
            }
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            TelecomManager telecomManager = (TelecomManager) context.getSystemService(TELECOM_SERVICE);
            if (telecomManager != null) {
                Intent intent = new Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER);
                intent.putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, packageName);
                context.startActivity(intent);
            }
        }
    }

    /**
     * 接听电话
     * @param call  call对象
     */
    public static void answer(Call call) {
        if (call != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                call.answer(VideoProfile.STATE_AUDIO_ONLY);
            }
        }
    }

    /**
     * 断开电话,包括来电时的拒接以及接听后的挂断
     * @param call  call对象
     */
    public static void hangup(Call call) {
        if (call != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                call.disconnect();
            }
        }
    }

    /**
     * 打电话
     * @param context  上下文
     * @param phoneNumber  所要拨打的号码
     */
    public static void call(Context context, String phoneNumber) {
        if (context.checkSelfPermission(Manifest.permission.CALL_PHONE) == PackageManager.PERMISSION_GRANTED) {
            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_CALL);
            intent.setData(Uri.parse("tel:" + phoneNumber));
            context.startActivity(intent);
        }
    }

    /**
     * 挂起
     * @param call  call对象
     */
    public static void hold(Call call) {
        if (call != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                call.hold();
            }
        }
    }

    /**
     * 取消挂起
     * @param call  call对象
     */
    public static void unHold(Call call) {
        if (call != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                call.unhold();
            }
        }
    }

    /**
     * 指示此 {@code Call} 播放双音多频信号 (DTMF) 音调。
     * @param call  call对象
     * @param digit  输入的指令数字
     */
    public static void playDtmfTone(Call call, char digit) {
        if (call != null) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                call.playDtmfTone(digit);
                call.stopDtmfTone();
            }
        }
    }

    /**
     * 打开/关闭扬声器
     * @param context  上下文
     * @param on  true 打开扬声器  false 关闭扬声器
     */
    public static void switchSpeaker(Context context, boolean on) {
        AudioManager audioManager = (AudioManager) context.getSystemService(AUDIO_SERVICE);
        if (audioManager != null) {
            audioManager.setSpeakerphoneOn(on);
            if (!on && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                audioManager.setMode(AudioManager.MODE_IN_CALL);
            }
        }
    }
}

Regarding PhoneManager, let’s briefly introduce it here. PhoneManager is a manager that includes many operations such as answering and hanging up. 

ok, next, we still need a call interface, and we will post the ScreenActivity and activity_screen.xml corresponding to our call interface.

ScreenActivity:

package com.zdj.phone;

import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.media.AudioManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import com.zdj.phone.adapter.DialPadAdapter;
import com.zdj.phone.core.PhoneManager;
import com.zdj.systemfuncationlibrary.LogUtils;
import com.zdj.systemfuncationlibrary.MatchUtils;
import com.zdj.systemfuncationlibrary.SystemUtils;
import com.zdj.systemfuncationlibrary.UiUtils;
import com.zdj.zdjuilibrary.popup_window.VolumePopupWindow;


/**
 * <pre>
 *     author : dejinzhang
 *     time : 2021/09/12
 *     desc : 通话界面
 * </pre>
 */
public class ScreenActivity extends Activity implements SensorEventListener {
    private static final String TAG = "ScreenActivity";
    public static final String CALL_NET_END = "call_net_end";
    public static final String CALL_NET_ING = "call_net_ing";
    public static final String NET_IN = "net_in";
    public static final String NET_OUT = "net_out";

    private TextView phoneNumTxt, phoneInfo, inputTxt, statusTxt,
            tv_tel_microphone, tv_tel_receiver, tv_on_speakerphone, tv_dial;
    private LinearLayout ll_no, ll_ok, ll_relevant_operate;
    private GridView gv_dial_pad;

    private VolumePopupWindow volumePopupWindow;  //调节音量的popupWindow

    private String phoneNum = "";
    private String phoneFrom = "";  //标记是呼出,还是呼入,即打电话还是接电话。"net_in"---呼入;"net_out"---呼出。
    private boolean isPhoneHide;  //是否对号码进行脱敏处理
    private boolean isSip;  //是否是sip固话
    private boolean isAxbTwoAutoAnswer;  //是否是axb双呼自动接听
    private boolean isHeadset;  //是否有耳机
    private boolean isRing;  //是否正在响铃
    private boolean isNetting;

    private TelephonyManager telephonyManager;  //电话管理器
    private AudioManager audioManager;  //声音管理器
//    private SensorManager sensorManager;  //传感器管理器
//    private Sensor mProximity;  //传感器实例
    private AudioManager.OnAudioFocusChangeListener mAudioFocusChangeListener;  //更新系统对音频焦点时要调用的回调的接口实例

    private static final int MY_PERMISSIONS_RECORD_AUDIO = 1;

    private final BroadcastReceiver mEndCallReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent == null) {
                return;
            }
            LogUtils.i(TAG, "intent.getAction()===" + intent.getAction());
            if (intent.getAction().equals("android.intent.action.PHONE_STATE")) {
                telephonyManager = (TelephonyManager) context.getSystemService(Service.TELEPHONY_SERVICE);
                switch (telephonyManager.getCallState()) {
                    case TelephonyManager.CALL_STATE_RINGING:  //来电响铃
                        LogUtils.i(TAG, "---来电话了---");
                        break;
                    case TelephonyManager.CALL_STATE_OFFHOOK:
                        LogUtils.i(TAG, "---接听电话---");  //接听电话
                        Message message = Message.obtain();
                        message.what = 1;
                        message.obj = intent.getAction();
                        callbackHandler.sendMessage(message);
                        break;
                    case TelephonyManager.CALL_STATE_IDLE:  //挂断电话
                        LogUtils.i(TAG, "---挂断电话---");
                        Toast.makeText(ScreenActivity.this, getString(R.string.call_ended), Toast.LENGTH_SHORT).show();
                        break;
                }
            } else if (intent.getAction().equals("android.intent.action.HEADSET_PLUG")) {
                if (intent.hasExtra("state")) {
                    if (intent.getIntExtra("state", 0) == 0) {
                        isHeadset = false;
                    } else if (intent.getIntExtra("state", 0) == 1) {
                        isHeadset = true;
                        if (isRing) {
                            return;
                        }
                        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                            audioManager.setSpeakerphoneOn(false);
                            audioManager.setMode(AudioManager.MODE_IN_CALL);
                        } else {
                            audioManager.setSpeakerphoneOn(false);
                        }
                    }
                }
            } else {
                Message msg = Message.obtain();
                msg.what = 1;
                msg.obj = intent.getAction();
                callbackHandler.sendMessage(msg);
            }
        }
    };

    private final Handler callbackHandler = new CallbackHandler();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
//        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
//        mProximity = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY);
        mAudioFocusChangeListener = focusChange -> {
            if (focusChange == AudioManager.AUDIOFOCUS_LOSS) {
                LogUtils.i(TAG, "---失去焦点---");
            } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
                LogUtils.i(TAG, "---获得焦点---");
            }
        };
        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_screen);

        if (getIntent().hasExtra("isPhoneHide")) {
            isPhoneHide = getIntent().getBooleanExtra("isPhoneHide", false);
        }

        if (getIntent().hasExtra("isAxbTwoAutoAnswer")) {
            isAxbTwoAutoAnswer = getIntent().getBooleanExtra("isAxbTwoAutoAnswer", false);
        }

        if (getIntent().hasExtra("from")) {
            phoneFrom = getIntent().getStringExtra("from");
        }

        initView();

        if (getIntent().hasExtra("phoneNum")) {
            phoneNum = getIntent().getStringExtra("phoneNum");
            if (!TextUtils.isEmpty(phoneNum)) {
                if (phoneNum.contains("sip:") && phoneNum.contains("@")) {
                    isSip = true;
                    phoneNum = phoneNum.substring(phoneNum.indexOf(":") + 1, phoneNum.indexOf("@"));
                    if (phoneFrom.equals(NET_IN)) {
                        //调用后端接口获取客户信息并显示在界面上。如果获取不到,表示来电不在库中,此时不显示即可。
                    }
                } else {
                    isSip = false;
                }
            } else {
                isSip = true;  //默认按sip来处理
                phoneNum = "未知号码";
            }
        }

        if (phoneFrom.equals(NET_IN)) {  //呼入
            isRing = true;
            audioManager.requestAudioFocus(mAudioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
            if (!isAxbTwoAutoAnswer) {
                ll_ok.setVisibility(View.VISIBLE);
                ll_no.setVisibility(View.VISIBLE);
                SystemUtils.playSound(this, R.raw.ring, true);  //播放铃声
                callStatus = 2;
            } else {
                ll_no.setVisibility(View.VISIBLE);
                PhoneManager.answer(Config.call);
                callStatus = 3;
                registerReceiver(mEndCallReceiver, new IntentFilter(CALL_NET_END));
                animationHandler.postDelayed(timeRunnable, 1000);
                ll_relevant_operate.setVisibility(View.VISIBLE);
                tv_tel_receiver.setVisibility(View.GONE);
                tv_tel_microphone.setVisibility(View.GONE);
            }
        } else if (phoneFrom.equals(NET_OUT)) {  //呼出
            audioManager.requestAudioFocus(mAudioFocusChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
            ll_no.setVisibility(View.VISIBLE);
            ll_relevant_operate.setVisibility(View.VISIBLE);
            callStatus = CONNECTING;
            if (!isSip) {
                tv_tel_receiver.setVisibility(View.GONE);
                tv_tel_microphone.setVisibility(View.GONE);
            }
        }
        StringBuilder stringBuilder = new StringBuilder();
        if (isPhoneHide) {
            if (!phoneNum.equals("未知号码")) {
                stringBuilder.append(UiUtils.desensitizePhoneNum(phoneNum));
            } else {
                stringBuilder.append(phoneNum);
            }
        } else {
            if (MatchUtils.isLegalMobilePhoneNum(phoneNum)) {
                stringBuilder.append(phoneNum.substring(0, 3)).append("-").append(phoneNum.substring(3, 7)).append("-").append(phoneNum.substring(7));
            } else if (phoneNum.startsWith("400") && phoneNum.length() == 10) {
                stringBuilder.append(phoneNum.substring(0, 3)).append("-").append(phoneNum.substring(3, 6)).append("-").append(phoneNum.substring(6));
            } else if (phoneNum.length() > 5) {
                if (phoneNum.startsWith("01") || phoneNum.startsWith("02")) {
                    stringBuilder.append(phoneNum.substring(0, 3)).append("-").append(phoneNum.substring(3));
                } else {
                    stringBuilder.append(phoneNum.substring(0, 4)).append("-").append(phoneNum.substring(4));
                }
            } else {
                stringBuilder.append(phoneNum);
            }
        }
        phoneNumTxt.setText(stringBuilder);

        if (!isAxbTwoAutoAnswer) {
            registerReceiver(mEndCallReceiver, new IntentFilter(CALL_NET_END));
            registerReceiver(mEndCallReceiver, new IntentFilter(CALL_NET_ING));
            if (isSip) {
                registerReceiver(mEndCallReceiver, new IntentFilter("android.intent.action.PHONE_STATE"));
                registerReceiver(mEndCallReceiver, new IntentFilter("android.intent.action.HEADSET_PLUG"));
            }
            animationHandler.postDelayed(timeRunnable, 1000);
        }

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, MY_PERMISSIONS_RECORD_AUDIO);
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        /*//注册传感器
        if (sensorManager != null) {
            sensorManager.registerListener(this, mProximity, SensorManager.SENSOR_DELAY_NORMAL);
        }*/
        //如果是sip,这里要发送CALL_NET_END的广播
    }

    @Override
    protected void onPause() {
        super.onPause();
        /*//取消注册传感器
        if (sensorManager != null) {
            sensorManager.unregisterListener(this);
        }*/
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    protected void onDestroy() {
        try {
            unregisterReceiver(mEndCallReceiver);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (!isAxbTwoAutoAnswer) {
            animationHandler.removeCallbacks(timeRunnable);
        }
        currentTime = 0;
        audioManager.abandonAudioFocus(mAudioFocusChangeListener);  //释放焦点
        //如果是sip的话,这里要进行一些恢复操作
        super.onDestroy();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case MY_PERMISSIONS_RECORD_AUDIO:
                if (!(grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                    AlertDialog.Builder builder = new AlertDialog.Builder(this)
                            .setMessage(getString(R.string.microphone_permission_request_hint_text))
                            .setPositiveButton(getString(R.string.ok), (dialog, which) -> {
                                ActivityCompat.requestPermissions(ScreenActivity.this, new String[]{Manifest.permission.RECORD_AUDIO}, MY_PERMISSIONS_RECORD_AUDIO);
                            })
                            .setNegativeButton(getString(R.string.cancel), (dialog, which) -> {
                                if (isSip) {
                                    //调用sip的挂断接口
                                } else {
                                    PhoneManager.hangup(Config.call);
                                }
                            });
                    builder.create().show();
                }
                break;
        }
    }

    private void initView() {
        ll_no = findViewById(R.id.ll_no);
        ll_ok = findViewById(R.id.ll_ok);
        ImageView iv_no = findViewById(R.id.iv_no);
        ImageView iv_ok = findViewById(R.id.iv_ok);
        phoneNumTxt = findViewById(R.id.call_phone_num);
        phoneInfo = findViewById(R.id.phone_info);
        inputTxt = findViewById(R.id.input_text);
        statusTxt = findViewById(R.id.call_status);
        ll_relevant_operate = findViewById(R.id.ll_relevant_operate);
        tv_tel_microphone = findViewById(R.id.tv_tel_microphone);
        tv_tel_receiver = findViewById(R.id.tv_tel_receiver);
        tv_on_speakerphone = findViewById(R.id.tv_on_speakerphone);
        tv_dial = findViewById(R.id.tv_dial);
        gv_dial_pad = findViewById(R.id.gv_dial_pad);
        final DialPadAdapter dialPadAdapter = new DialPadAdapter(this, DialPadAdapter.SCREEN_UI);
        gv_dial_pad.setAdapter(dialPadAdapter);
        gv_dial_pad.setSelector(new ColorDrawable(Color.TRANSPARENT));
        gv_dial_pad.setOnItemClickListener((parent, view, position, id) -> {
            String str = dialPadAdapter.getList().get(position).getMainText();
            String existsText = inputTxt.getText().toString();
            StringBuilder stringBuilder = new StringBuilder();
            if (!TextUtils.isEmpty(existsText)) {
                stringBuilder.append(existsText);
            }
            stringBuilder.append(str);
            inputTxt.setText(stringBuilder);
            inputTxt.setVisibility(View.VISIBLE);
            if (isSip) {
                //调用sip的输入指令的接口
            } else {
                PhoneManager.playDtmfTone(Config.call, str.charAt(0));
            }
        });

        View.OnClickListener clickListener = v -> {
            if (v.getId() == R.id.tv_tel_microphone) {
                tv_tel_microphone.setTag(!((boolean) tv_tel_microphone.getTag()));
                tv_tel_microphone.setTextColor(((boolean) tv_tel_microphone.getTag()) ? Color.parseColor("#D6E3F4") : Color.parseColor("#62696C"));
                tv_tel_microphone.setCompoundDrawablesRelativeWithIntrinsicBounds(0, ((boolean) tv_tel_microphone.getTag()) ? R.drawable.tel_microphone_highlight : R.drawable.tel_microphone, 0, 0);
                if ((boolean)tv_tel_receiver.getTag()) {
                    tv_tel_receiver.setTag(false);
                    tv_tel_receiver.setTextColor(Color.parseColor("#62696C"));
                    tv_tel_receiver.setCompoundDrawablesRelativeWithIntrinsicBounds(0, R.drawable.tel_receiver, 0, 0);
                    if (volumePopupWindow != null) {
                        volumePopupWindow.dismiss();
                    }
                }
                if ((boolean) tv_tel_microphone.getTag()) {
                    showVolumePopupWindow(tv_tel_microphone);
                } else {
                    if (volumePopupWindow != null) {
                        volumePopupWindow.dismiss();
                    }
                }
            } else if (v.getId() == R.id.tv_tel_receiver) {
                tv_tel_receiver.setTag(!((boolean) tv_tel_receiver.getTag()));
                tv_tel_receiver.setTextColor(((boolean) tv_tel_receiver.getTag()) ? Color.parseColor("#D6E3F4") : Color.parseColor("#62696C"));
                tv_tel_receiver.setCompoundDrawablesRelativeWithIntrinsicBounds(0, ((boolean) tv_tel_receiver.getTag()) ? R.drawable.tel_receiver_highlight : R.drawable.tel_receiver, 0, 0);
                if ((boolean)tv_tel_microphone.getTag()) {
                    tv_tel_microphone.setTag(false);
                    tv_tel_microphone.setTextColor(Color.parseColor("#62696C"));
                    tv_tel_microphone.setCompoundDrawablesRelativeWithIntrinsicBounds(0, R.drawable.tel_microphone, 0, 0);
                    if (volumePopupWindow != null) {
                        volumePopupWindow.dismiss();
                    }
                }
                if ((boolean)tv_tel_receiver.getTag()) {
                    showVolumePopupWindow(tv_tel_receiver);
                } else {
                    if (volumePopupWindow != null) {
                        volumePopupWindow.dismiss();
                    }
                }
            } else if (v.getId() == R.id.tv_on_speakerphone) {
                tv_on_speakerphone.setTag(!((boolean) tv_on_speakerphone.getTag()));
                tv_on_speakerphone.setTextColor(((boolean) tv_on_speakerphone.getTag()) ? Color.parseColor("#D6E3F4") : Color.parseColor("#62696C"));
                tv_on_speakerphone.setCompoundDrawablesRelativeWithIntrinsicBounds(0, ((boolean) tv_on_speakerphone.getTag()) ? R.drawable.on_speakerphone_highlight : R.drawable.on_speakerphone, 0, 0);
                PhoneManager.switchSpeaker(ScreenActivity.this, (boolean)tv_on_speakerphone.getTag());
            } else if (v.getId() == R.id.tv_dial) {
                tv_dial.setTag(!((boolean)tv_dial.getTag()));
                if ((boolean)tv_dial.getTag()) {
                    gv_dial_pad.setVisibility(View.VISIBLE);
                } else {
                    gv_dial_pad.setVisibility(View.GONE);
                }
            }
        };
        tv_tel_microphone.setOnClickListener(clickListener);
        tv_tel_microphone.setTag(false);
        tv_tel_receiver.setOnClickListener(clickListener);
        tv_tel_receiver.setTag(false);
        tv_on_speakerphone.setOnClickListener(clickListener);
        tv_on_speakerphone.setTag(false);
        tv_dial.setOnClickListener(clickListener);
        tv_dial.setTag(false);

        //接听电话
        iv_ok.setOnClickListener(v -> {
            if (phoneFrom.equals(NET_IN)) {
                if (isSip) {
                    //调用sip的接听电话的接口
                    Intent intent = new Intent();
                    intent.setAction(ScreenActivity.CALL_NET_ING);
                    sendBroadcast(intent);
                    ll_relevant_operate.setVisibility(View.VISIBLE);
                } else {
                    PhoneManager.answer(Config.call);
                    ll_relevant_operate.setVisibility(View.VISIBLE);
                    tv_tel_receiver.setVisibility(View.GONE);
                    tv_tel_microphone.setVisibility(View.GONE);
                }
            }
        });
        //挂断电话
        iv_no.setOnClickListener(v -> {
            if (phoneFrom.equals(NET_IN) || phoneFrom.equals(NET_OUT)) {
                if (isSip) {
                    //调用sip的挂断电话的接口
                } else {
                    PhoneManager.hangup(Config.call);
                }
            }
        });
    }

    //未知
    private static final int UNKNOWN = 0;
    //正在呼叫
    private static final int CONNECTING = 1;
    //正在响铃(等待接听)
    private static final int RING = 2;
    //通话中
    private static final int CONNECTED = 3;
    private int callStatus = UNKNOWN;
    private int currentTime;
    private Runnable timeRunnable = new Runnable() {
        @Override
        public void run() {
            currentTime++;
            switch (callStatus) {
                case CONNECTING:
                    if (currentTime % 4 == 0) {
                        statusTxt.setText("正在呼叫");
                    } else if (currentTime % 4 == 1) {
                        statusTxt.setText(" 正在呼叫.");
                    } else if (currentTime % 4 == 2) {
                        statusTxt.setText("  正在呼叫..");
                    } else if (currentTime % 4 == 3) {
                        statusTxt.setText("   正在呼叫...");
                    }
                    break;
                case RING:
                    break;
                case CONNECTED:
                    statusTxt.setText(new StringBuilder().append(UiUtils.formatAA(currentTime / 60)).append(":").append(UiUtils.formatAA(currentTime % 60)));
                    break;
            }
            animationHandler.postDelayed(timeRunnable, 1000);
        }
    };
    private final Handler animationHandler = new Handler();

    /**
     * 注意:该方法目前不会被回调,因为通过传感器判断距离,进一步自动打开/关闭扬声器的功能,目前禁掉了,但是代码保留,需要用开放即可。
     * @param event
     */
    @Override
    public void onSensorChanged(SensorEvent event) {
        if (isHeadset) {
            return;
        }
        float[] its = event.values;
        LogUtils.i(TAG, "its[0]:" + its[0]);
        if (!(boolean) tv_on_speakerphone.getTag()) {  //表示未手动打开扬声器
            if (its[0] == 0.0) {  //贴近手机
                PhoneManager.switchSpeaker(this, false);
                tv_on_speakerphone.setTextColor(Color.parseColor("#62696C"));
                tv_on_speakerphone.setCompoundDrawablesRelativeWithIntrinsicBounds(0, R.drawable.on_speakerphone, 0, 0);
                tv_on_speakerphone.setTag(false);
            } else {  //远离手机
                PhoneManager.switchSpeaker(this, true);
                tv_on_speakerphone.setTextColor(Color.parseColor("#D6E3F4"));
                tv_on_speakerphone.setCompoundDrawablesRelativeWithIntrinsicBounds(0, R.drawable.on_speakerphone_highlight, 0, 0);
                tv_on_speakerphone.setTag(true);
            }
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {}

    private void showVolumePopupWindow(final View targetView) {
        if (volumePopupWindow == null) {
            volumePopupWindow = new VolumePopupWindow(LayoutInflater.from(this).inflate(R.layout.popup_window_volume, null),
                    ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        }
        volumePopupWindow.setCallback(progress -> {
            if (targetView.getId() == R.id.tv_tel_microphone) {
                //调用sip的接口调节话筒的音量
            } else if (targetView.getId() == R.id.tv_tel_receiver) {
                //调用sip的接口调节听筒的音量
            }
        });
        if (targetView.getId() == R.id.tv_tel_microphone) {
//            volumePopupWindow.initProgress();  //initProgress方法中的参数使用sip的接口获取
        } else if (targetView.getId() == R.id.tv_tel_receiver) {
//            volumePopupWindow.initProgress();  //initProgress方法中的参数使用sip的接口获取
        }
        int[] location = new int[2];
        targetView.getLocationOnScreen(location);
        int temp_width = (UiUtils.dpToPx(this, 80) - targetView.getWidth()) / 2;
        volumePopupWindow.showAtLocation(targetView, Gravity.NO_GRAVITY, location[0] - temp_width, location[1] - UiUtils.dpToPx(this, 90));
    }

    class CallbackHandler extends Handler {
        @Override
        public void handleMessage(@NonNull Message msg) {
            if (msg.what == 1) {
                String action = String.valueOf(msg.obj);
                if (action.equals(CALL_NET_ING)) {  //正在通话
                    if (isNetting) {
                        return;
                    }
                    isNetting = true;
                    isRing = false;
                    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                        audioManager.setSpeakerphoneOn(false);
                        audioManager.setMode(AudioManager.MODE_IN_CALL);
                    } else {
                        audioManager.setSpeakerphoneOn(false);
                    }
                    currentTime = 0;
                    callStatus = CONNECTED;
                    if (ll_ok != null) {
                        ll_ok.setVisibility(View.GONE);
                    }
                    if (ll_no != null) {
                        ll_no.setVisibility(View.VISIBLE);
                    }
                    SystemUtils.stopSound();  //停止播放铃声
                } else if (action.equals(CALL_NET_END)) {
                    if (ll_ok != null) {
                        ll_ok.setVisibility(View.GONE);
                    }
                    if (ll_no != null) {
                        ll_no.setVisibility(View.GONE);
                    }
                    isRing = false;
                    LogUtils.i(TAG, "---call_net_end---");
                    SystemUtils.stopSound();  //停止播放铃声
                    statusTxt.setText(getString(R.string.call_ended));
                    Toast.makeText(ScreenActivity.this, getString(R.string.call_ended), Toast.LENGTH_SHORT).show();
                    finish();
                } else if (action.equals("android.intent.action.PHONE_STATE")) {
                    //调用sip的挂断电话的接口
                }
            }
        }
    }
}

activity_screen.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#2f343a">

    <!--号码-->
    <TextView
        android:id="@+id/call_phone_num"
        android:layout_width="match_parent"
        android:layout_height="90dp"
        android:gravity="bottom|center_horizontal"
        android:text="@string/unknown_call"
        android:textColor="@color/white"
        android:textSize="32dp"/>

    <!--电话归属信息:比如客户名称、联系人姓名-->
    <TextView
        android:id="@+id/phone_info"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="16dp"
        android:textColor="@color/white"
        android:layout_marginTop="10dp"
        android:lineSpacingExtra="5dp"
        android:gravity="center"
        android:visibility="gone"/>

    <!--用户输入的指令,比如拨打10086的时候调起软键盘输入1-->
    <TextView
        android:id="@+id/input_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textColor="@color/white"
        android:textSize="18dp"
        android:layout_marginTop="10dp"
        android:visibility="gone"/>

    <!--通话状态,比如显示"正在呼叫"或者当前的通话时长等等-->
    <TextView
        android:id="@+id/call_status"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:gravity="center"
        android:textColor="@color/white"
        android:textSize="16dp"/>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1">

        <com.zdj.zdjuilibrary.view.RippleView
            android:id="@+id/layout_ripple"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#ff2f343a"
            android:fitsSystemWindows="true"
            android:gravity="center">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/call_icon"/>
        </com.zdj.zdjuilibrary.view.RippleView>

        <GridView
            android:id="@+id/gv_dial_pad"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:verticalSpacing="10dp"
            android:horizontalSpacing="40dp"
            android:numColumns="3"
            android:layout_gravity="center"
            android:background="#ff2f343a"
            android:layout_marginLeft="30dp"
            android:layout_marginRight="30dp"
            android:visibility="gone"/>
    </FrameLayout>

    <LinearLayout
        android:id="@+id/ll_relevant_operate"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center"
        android:paddingTop="15dp"
        android:paddingBottom="15dp"
        android:background="#272e34"
        android:visibility="gone">

        <TextView
            android:id="@+id/tv_dial"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/dial"
            android:textSize="14dp"
            android:textColor="#62696c"
            android:drawablePadding="10dp"
            android:drawableTop="@drawable/dial_pad"
            android:gravity="center"/>

        <TextView
            android:id="@+id/tv_tel_microphone"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/microphone"
            android:textSize="14dp"
            android:textColor="#62696C"
            android:drawablePadding="10dp"
            android:drawableTop="@drawable/tel_microphone"
            android:gravity="center"
            android:layout_marginLeft="70dp"/>

        <TextView
            android:id="@+id/tv_tel_receiver"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/earpiece"
            android:textSize="14dp"
            android:textColor="#62696c"
            android:drawablePadding="10dp"
            android:drawableTop="@drawable/tel_receiver"
            android:gravity="center"
            android:layout_marginLeft="70dp"/>

        <TextView
            android:id="@+id/tv_on_speakerphone"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/speaker"
            android:textSize="14dp"
            android:textColor="#62696C"
            android:drawablePadding="10dp"
            android:drawableTop="@drawable/on_speakerphone"
            android:gravity="center"
            android:layout_marginLeft="70dp"/>
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#272c32"
        android:orientation="horizontal">

        <LinearLayout
            android:id="@+id/ll_no"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="25dp"
            android:layout_marginBottom="25dp"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="horizontal"
            android:visibility="gone">

            <ImageView
                android:id="@+id/iv_no"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/hang_up"/>
        </LinearLayout>

        <LinearLayout
            android:id="@+id/ll_ok"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginTop="25dp"
            android:layout_marginBottom="25dp"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="horizontal"
            android:visibility="gone">

            <ImageView
                android:id="@+id/iv_ok"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:src="@drawable/answer"/>
        </LinearLayout>
    </LinearLayout>
</LinearLayout>

ok, so far, we are done.

The complete code can be moved to my github:

https://github.com/ZhangDeJin/UiDemo/tree/master/phone

Guess you like

Origin blog.csdn.net/zdj_Develop/article/details/120288057