周一的时候,同事在群里问到了黑名单功能,他说网上都没有找到一个完整的,记得谁说过一句,当都没有做过的时候,这就是机会。这几天公司事比较多,只能晚上抽时间写写,直到今天才完整的做出来。

具体效果的话大家可以运行demo看看。

首先我们分析下需求

1、UI部分,毕竟这也是一个小的app,

2、需要有一个数据库来保存黑名单的号码,

3、需要用到一个服务来具体执行黑名单的功能,毕竟如果我们的app退出了,黑名单功能就失效了,这就比较坑了,

  未解决的问题,测试在L及以下版本都能够正常工作,但是在M版本PhoneStateListener会获取
  不到来电号码,这个我暂时只想到了framework层的代码来修改。待我解决了会重新上传的。

它们前面的逻辑差不多一样,但是M版在最后调用binder的时候,获取到的号码时空的。

UI部分请看底部的源码,比较简单,相信大家一看就懂。

接下来我们来设计一下黑名单的数据库。

它首先应该包括一个id这个是设计数据库时都有的,第二个应该就是要拦截的电话号码了,
最后我们在来个拦截的模式(这个可有可无,主要考虑,说不定短信也要拦截呢)。
public class BlackNumberOpenHelper extends SQLiteOpenHelper {
    public BlackNumberOpenHelper(Context context) {
        super(context, "blacknumber.db", null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        //创建数据库中表的方法
        db.execSQL("create table blacknumber " +
                "(_id integer primary key autoincrement , phone varchar(20), mode varchar(5));");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

一般有数据库后,都会提供一个操作数据库的工具类。这个大家请看底部给出的源码。

上面这些都是为我们的黑名单功能服务的,接下来看下黑名单功能的具体实现。

这里我们是用PhoneStateListener来获取电话的状态,如果是响铃状态时,通过反射来挂断电话。

来看下PhoneStateListener的使用流程。

1、获取TelephonyManager,

2、确定要监听的电话状态因为PhoneStateListener这个类不仅能监听电话的状态它还有非常多的其他状态监听。

3、重写PhoneStateListener的onCallStateChanged方法,当状态发生改变时来处理我们的流程。

  public void onCreate() {
        mDao = BlackNumberDao.getInstance(getApplicationContext());

        //拦截短信
        /*IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("android.provider.Telephony.SMS_RECEIVED");
        intentFilter.setPriority(1000);

        mInnerSmsReceiver = new InnerSmsReceiver();
        registerReceiver(mInnerSmsReceiver, intentFilter);*/

        //监听电话的状态
        //1,电话管理者对象
        mTM = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
        //2,监听电话状态
        mPhoneStateListener = new MyPhoneStateListener();
        mTM.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);

        super.onCreate();
    }

    class MyPhoneStateListener extends PhoneStateListener{
        //3,手动重写,电话状态发生改变会触发的方法
        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            switch (state) {
                case TelephonyManager.CALL_STATE_IDLE:
                    break;
                case TelephonyManager.CALL_STATE_OFFHOOK:
                    break;
                case TelephonyManager.CALL_STATE_RINGING:
                    //挂断电话  aidl文件中去了
//              mTM.endCall();
                    endCall(incomingNumber);
                    break;
            }
            super.onCallStateChanged(state, incomingNumber);
        }
    }

接下来看下挂断电话的逻辑。

android早期的版本可以通过API调用直接挂断电话,但是后期版本把它隐藏了,不过通过java的反射机制我们同样可以调用到这个方法。

try {
                //1,获取ServiceManager字节码文件
                Class<?> clazz = Class.forName("android.os.ServiceManager");
                //2,获取方法
                Method method = clazz.getMethod("getService", String.class);
                //3,反射调用此方法
                IBinder iBinder = (IBinder) method.invoke(null, Context.TELEPHONY_SERVICE);
                //4,调用获取aidl文件对象方法
                ITelephony iTelephony = ITelephony.Stub.asInterface(iBinder);
                //5,调用在aidl中隐藏的endCall方法
                iTelephony.endCall();
            } catch (Exception e) {
                e.printStackTrace();
            }

上面代码主要是通过反射获取ServiceManager类里面的getService方法,然后调用这个方法获取iBinder对象,通过iBinder我们可以得到ITelephony,最后调用ITelephony.endCall来挂断电话。

走到这里我们的黑名单功能就基本实现了,但是当我们进到通话记录里面的时候会发现,刚才手机明明没响,怎么有未接电话呢。呵呵哒。

我们来处理下这个问题,这主要是因为挂断了电话但是系统的phone却是会把这个通话记录保存下来,只要把它删掉就可以了。

这里要注意下这个删除的时机,你想啊,我们挂断电话时就把通话记录删除了,但是系统要往通话记录里面插入数据也是需要时间的呀,有时还没插入你就删除了,这不就纠结了,怎么找也找不到哪儿出问题了,这里会用到系统提供的内容观察者,只有系统往通话记录数据库中插入了数据时我们才删除。

    mContentObserver = new MyContentObserver(new Handler(),phone);
            getContentResolver().registerContentObserver(
                    Uri.parse("content://call_log/calls"), true, mContentObserver);

 class MyContentObserver extends ContentObserver {
        private String phone;
        public MyContentObserver(Handler handler, String phone) {
            super(handler);
            this.phone = phone;
        }
        //数据库中指定calls表发生改变的时候会去调用方法
        @Override
        public void onChange(boolean selfChange) {
            //插入一条数据后,再进行删除
            getContentResolver().delete(
                    Uri.parse("content://call_log/calls"), "number = ?", new String[]{phone});
            super.onChange(selfChange);
        }
    }

github好卡,暂时只传CSDN了。

源码

android 小项目------黑名单app的更多相关文章

  1. Android小项目之十一 应用程序的主界面

    ------- 源自梦想.永远是你IT事业的好友.只是勇敢地说出我学到! ---------- 按惯例,写在前面的:可能在学习Android的过程中,大家会和我一样,学习过大量的基础知识,很多的知识点 ...

  2. Android小项目练习之一 项目简介

    ------- 源自梦想.永远是你IT事业的好友.只是勇敢地说出我学到! ---------- 按惯例,写在前面的:可能在学习Android的过程中,大家会和我一样,学习过大量的基础知识,很多的知识点 ...

  3. Android小项目之十二 设置中心的界面

    ------- 源自梦想.永远是你IT事业的好友.只是勇敢地说出我学到! ---------- 按惯例,写在前面的:可能在学习Android的过程中,大家会和我一样,学习过大量的基础知识,很多的知识点 ...

  4. Android小项目之十 应用程序更新的签名问题

    ------- 源自梦想.永远是你IT事业的好友.只是勇敢地说出我学到! ---------- 按惯例,写在前面的:可能在学习Android的过程中,大家会和我一样,学习过大量的基础知识,很多的知识点 ...

  5. Android小项目之八 界面细节

    ------- 源自梦想.永远是你IT事业的好友.只是勇敢地说出我学到! ---------- 按惯例,写在前面的:可能在学习Android的过程中,大家会和我一样,学习过大量的基础知识,很多的知识点 ...

  6. Android小项目之五 splash动画效果

    ------- 源自梦想.永远是你IT事业的好友.只是勇敢地说出我学到! ---------- 按惯例,写在前面的:可能在学习Android的过程中,大家会和我一样,学习过大量的基础知识,很多的知识点 ...

  7. 【Android小项目】找不同,改编自&quot;寻找房祖名&quot;的一款开源小应用。

    近期在微信朋友圈"寻找房祖名"和"万里寻刀"这类小游戏比較火.我试着写了一个android版本号的,里面全是一系列的形近字,实现原理非常easy:用一个Grid ...

  8. Android小项目之九 两种上下文的区别

    ------- 源自梦想.永远是你IT事业的好友.只是勇敢地说出我学到! ---------- 按惯例,写在前面的:可能在学习Android的过程中,大家会和我一样,学习过大量的基础知识,很多的知识点 ...

  9. Android小项目之七 应用程序的更新安装

    ------- 源自梦想.永远是你IT事业的好友.只是勇敢地说出我学到! ---------- 按惯例,写在前面的:可能在学习Android的过程中,大家会和我一样,学习过大量的基础知识,很多的知识点 ...

随机推荐

  1. #定义一个方法get_num(num),num参数是列表类型,判断列表里面的元素为数字类型。其他类型则报错,并且返回一个偶数列表:(注:列表里面的元素为偶数)。

    #定义一个方法get_num(num),num参数是列表类型,判断列表里面的元素为数字类型.其他类型则报错,并且返回一个偶数列表:(注:列表里面的元素为偶数). def get_num(num): i ...

  2. Java基础之关键字,标识符,变量

    Java基础 首先,来看一下Java基础知识图解,以下便是在java学习中我们需要学习设计到的一些知识(当然不是很完全). 这些都是接下来在以后的学习中我们会学到的一些知识. 1 关键字 首次先来学习 ...

  3. linux如何安装django

    首先在命令行下输入python 进入界面后先importdjango 如果这一步有错,说明没有django 需要我们安装 源码安装方法: 下载源码包 https://www.djangoproject ...

  4. [LeetCode] Boundary of Binary Tree 二叉树的边界

    Given a binary tree, return the values of its boundary in anti-clockwise direction starting from roo ...

  5. [NOI 2015]荷马史诗

    Description 追逐影子的人,自己就是影子. ——荷马 Allison 最近迷上了文学.她喜欢在一个慵懒的午后,细细地品上一杯卡布奇诺,静静地阅读她爱不释手的<荷马史诗>.但是由& ...

  6. [UOJ UNR #2]积劳成疾

    来自FallDream的博客,未经允许,请勿转载,谢谢. 传送门 区间最大值的题emmmm 想到构建笛卡尔树,这样自然就想到了一种dp f[i][j]表示大小为i的笛卡尔树,根的权值是j的答案. 转移 ...

  7. 伸展树Splay【非指针版】

    ·伸展树有以下基本操作(基于一道强大模板题:codevs维护队列): a[]读入的数组;id[]表示当前数组中的元素在树中节点的临时标号;fa[]当前节点的父节点的编号;c[][]类似于Trie,就是 ...

  8. hdu5631 BestCoder Round #73 (div.2)

    Rikka with Graph  Accepts: 123  Submissions: 525  Time Limit: 2000/1000 MS (Java/Others)  Memory Lim ...

  9. poj 1845 (逆元 + 约数和)

    题意: 求A^B的所有约数(即因子)之和,并对其取模 9901再输出. 思路: A可以表示为A=(p1^k1)*(p2^k2)*(p3^k3)*....*(pn^kn)   其中pi均为素数 那么A的 ...

  10. [bzoj4850][Jsoi2016]灯塔

    来自FallDream的博客,未经允许,请勿转载,谢谢. JSOI的国境线上有N一座连续的山峰,其中第ii座的高度是hi??.为了简单起见,我们认为这N座山峰排成了连续一条 直线.如果在第ii座山峰上 ...