欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android项目:手机安全卫士(10)—— 电话号码归属地显示

程序员文章站 2022-05-19 12:32:37
...
 

Android项目:手机安全卫士(10)—— 电话号码归属地显示

1 概述

前一篇文章已经提供了电话号码的归属地查询功能,现在要做的,就是在打电话和来电显示的时候,显示一个电话归属地提示框,就像这样:

Android项目:手机安全卫士(10)—— 电话号码归属地显示

感觉很简单是不是,No,这个还费了一点功夫,首先是监听来电、去电,同时这个提示框是可以拖动的,而且这个提示框可以自定义风格,可以设置它的颜色,是否显示等,所以,并不简单。

关于项目相关文章,请访问:

项目源码地址(实时更新):https://github.com/xwdoor/MobileSafe

2 监听来电

首先创建一个服务:AddressService,然后在里面监听来电,代码如下:

//获取电话管理器
    mTM = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
    mPhoneListener = new PhoneListener(getApplicationContext());
    mTM.listen(mPhoneListener, PhoneStateListener.LISTEN_CALL_STATE);//监听来电
首先获得电话管理器,然后通过调用它的 listen() 方法开始监听,第一个参数是我们的回调方法,第二个参数是我们要监听的事件。PhoneListener 是需要自己实现的一个类,因为我们需要重写其中的 onCallStateChanged() 方法,具体代码如下:

/**
     * 电话状态监听
     *
     * Created by XWdoor on 2016/3/11 011 13:50.
     * 博客:http://blog.csdn.net/xwdoor
     */
    public class PhoneListener extends PhoneStateListener {

        private Context mContext;
        private View mView;
        private WindowManager mWM;

        public PhoneListener(Context context) {
            mContext = context;
        }

        // 电话状态发生变化
        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            super.onCallStateChanged(state, incomingNumber);
            switch (state){
                case TelephonyManager.CALL_STATE_RINGING://电话铃响
                    String address = AddressQuery.getAddress(mContext, incomingNumber);
                    showAddressBox(address);
                    break;
                case TelephonyManager.CALL_STATE_OFFHOOK://电话摘机
                    break;
                case TelephonyManager.CALL_STATE_IDLE://电话空闲
                    closeAddressBox();
                    break;
            }
        }

        /**
         * 显示电话归属地提示框
         *
         * @param address
         */
        public void showAddressBox(String address){
            Log.i(BaseActivity.TAG_LOG,"电话归属地-->"+address);
        }

        /**
         * 关闭电话归属地提示框
         */
        public void closeAddressBox(){

        }
    }

现在主要实现了代码逻辑,功能还没有做,先不用管它,可以打印一条日志。这样我们就可以监听来电了,并且在 showAddressBox() 方法中弹出提示框。

3 监听去电(打电话)

监听打电话与监听来电略有不同,需要监听广播。创建一个广播 CallReceiver,并传入 PhoneListener 对象,因为我们要用到归属地提示框的显示与关闭,代码如下:


/**
     * 打电话监听:去电监听
     *
     * Created by XWdoor on 2016/3/11 011 13:44.
     * 博客:http://blog.csdn.net/xwdoor
     */
    public class CallReceiver extends BroadcastReceiver {
        private final PhoneListener mPhoneListener;

        public CallReceiver(PhoneListener phoneListener) {
            this.mPhoneListener = phoneListener;
        }

        @Override
        public void onReceive(Context context, Intent intent) {
            String number = getResultData();//获取电话号码
            String address = AddressQuery.getAddress(context, number);

            //ToastUtils.showToast(context, "去电地址-->" + address + ",去电号码-->" + number);
            mPhoneListener.showAddressBox(address);
        }
    }

当然,去电监听是需要权限的:<uses-permission Android:name="android.permission.PROCESS_OUTGOING_CALLS" /> 
创建完广播后,因为是否显示归属地提示框是可以进行设置的,所以该广播不能在清单文件中进行注册,那样就不能停止了,我们需要在代码中进行注册,我选择在服务 AddressService 中进行注册,代码如下:

 //监听去电
    mCallReceiver = new CallReceiver(mPhoneListener);
    IntentFilter filter = new IntentFilter();
    filter.addAction(Intent.ACTION_NEW_OUTGOING_CALL);
    registerReceiver(mCallReceiver,filter);
当然,在服务停止的时候,我们需要注销监听:

  @Override
    public void onDestroy() {
        super.onDestroy();

        //取消监听来电
        mTM.listen(mPhoneListener,PhoneStateListener.LISTEN_NONE);
        //取消监听去电
        unregisterReceiver(mCallReceiver);
        mCallReceiver = null;
    }

4 开启服务

在 activity_setting.xml 文件中增加一个 item,用于设置归属地服务是否开启:

 <net.xwdoor.mobilesafe.view.SettingItemView
        android:id="@+id/siv_address"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        xwdoor:stitle="电话归属地显示设置"
        xwdoor:desc_on="归属地显示已开启"
        xwdoor:desc_off="归属地显示已关闭"/>
后台代码如下:

/**
     * 初始化归属地设置
     */
    private void initAddress() {
        final SettingItemView sivAddress = (SettingItemView) findViewById(R.id.siv_address);
        // 根据服务是否运行来更新checkbox
        boolean serviceRunning = ServiceStatusUtils.isServiceRunning(this,
                "net.xwdoor.mobilesafe.service.AddressService");
        sivAddress.setChecked(serviceRunning);

        sivAddress.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                sivAddress.setChecked(!sivAddress.isChecked());
                Intent service = new Intent(SettingActivity.this, AddressService.class);
                if(sivAddress.isChecked()){
                    startService(service);//开启电话归属地显示服务
                }else {
                    stopService(service);//关闭电话归属地服务
                }
            }
        });
    }
首先判断该服务是否已经运行,然后设置 item 的状态,判断服务是否运行的代码如下所示。然后设置 item 的点击事件,根据用户的选择来动态启动或关闭归属地显示服务。

/**
     * 判断服务是否正在运行
     * @param context
     * @param serviceName
     * @return
     */
    public static boolean isServiceRunning(Context context, String serviceName) {
        //获取活动管理器
        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        //获取当前正在运行的服务,最多返回100条记录
        List<ActivityManager.RunningServiceInfo> runningServices = am.getRunningServices(100);
        for(ActivityManager.RunningServiceInfo service : runningServices){
            // 获取服务名称,并判断
            if(serviceName.equals(service.service.getClassName())){
                return true;
            }
        }
        return false;
    }

5 显示归属地提示框

以上的工作就可以正常打印日志了,但是还没有具体的提示框出现,现在就来实现我们的提示框,实现原理参照系统的 Toast,因为只有它可以在另一个应用运行时进行显示,额,可以这么说,Toast 显示的时候,可以运行其他任何 app。首先创建一个布局文件,里面就只有一个 TextView,这里就不写了,相信写了那么多的 UI 布局,这个可以轻松搞定。

然后,我么就可以实现 PhoneListener 中的两个方法了:

/**
     * 显示电话归属地提示框
     * 需要权限:android.permission.SYSTEM_ALERT_WINDOW
     *
     * @param address
     */
    public void showAddressBox(String address){
        Log.i(BaseActivity.TAG_LOG,"电话归属地-->"+address);

        //窗口管理器, 系统最*的界面布局, 所有东西都展示在窗口上,activity,状态栏
        mWM = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);

        //初始化布局参数
        final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        params.width = WindowManager.LayoutParams.WRAP_CONTENT;
        params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                // | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
        params.format = PixelFormat.TRANSLUCENT;
        params.type = WindowManager.LayoutParams.TYPE_PHONE;// 提高类型级别,保证可以触摸移动
        params.gravity = Gravity.CENTER;// 将重心设置在左上方位置,和屏幕坐标体系重合,方便修改窗口的位置

        // 初始化布局
        mView = View.inflate(mContext, R.layout.dialog_address_box, null);
        TextView tvAddress = (TextView) mView.findViewById(R.id.tv_address);
        tvAddress.setText(address);

        // 给窗口添加布局
        mWM.addView(mView, params);
    }

    /**
     * 关闭电话归属地提示框
     */
    public void closeAddressBox(){
        if (mWM != null && mView != null) {
            mWM.removeView(mView);// 电话挂断后,移除窗口布局
        }
    }

别看这么复杂,其中初始化布局参数那一段代码都是从系统 Toast 的源码中复制过来的,所以,呵呵。有了以上的代码,运行效果就跟文章图片中展示的一样了。

6 总结

我觉得这篇文章也是属于干货,学到的知识点有:

  • 监听来电,获取来电号码,实现电话拦截
  • 监听去电,记录行为
  • WindowManager、ActivityManager、TelephonyManager 的使用

由于篇幅的限制,还有一部分功能没有实现,就是我们的归属地的自定义风格,可拖拽等效果,没办法,只能放到下篇了。

关于项目相关文章,请访问:

项目源码地址(实时更新):https://github.com/xwdoor/MobileSafe


转发自:http://blog.csdn.net/xwdoor/article/details/50857147