基于android的手机安全卫士的实现心得四

接下来我们来实现通讯卫士功能,这个功能就是为用户提供一个黑名单功能。用户把不想接听的号码添加到黑名单中,如果该号码来电就自动挂断。

因为是视图分离,所以我们先来设计界面。黑名单管理界面比较简单,只有一个标题,一个“添加”按钮,然后下面是一个ListView用来显示所有的黑名单。如果黑名单为空,就显示“无黑名单”。长按黑名单号码,会弹出来菜单选项,删除或者是修改。blacklist_layout.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">
    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <TextView
            android:id="@+id/blacklist"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:layout_marginBottom="5dp"
            android:textSize="30dp"
            android:text="黑名单管理"/>
        <Button
            android:id="@+id/add"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="5dp"
            android:layout_marginLeft="260dp"
            android:text="添加"/>
    </RelativeLayout>
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <ListView
            android:id="@+id/blacknum_list"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
        </ListView>
        <TextView
            android:id="@+id/tips"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:text="黑名单为空"/>
    </LinearLayout>
</LinearLayout>

这个listview很简单,只是简单的显示一个字符串black_item.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">
<TextView
    android:id="@+id/number"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="strings"/>
</LinearLayout>

点击添加或者修改按钮会弹出来一个对话框,用来输入号码。black_menu.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">
<EditText
    android:id="@+id/add_number_Dialog"
    android:hint="请输入号码"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>
    <LinearLayout android:layout_height="wrap_content"
        android:layout_width="match_parent">
    <Button
        android:id="@+id/ok_number_Dialog"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="确定"
        android:layout_marginLeft="1dp"/>
        <Button
            android:id="@+id/cancel_number_Dialog"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="取消"
            android:layout_marginLeft="120dp"/>
    </LinearLayout>
</LinearLayout>

接下来写实现黑名单的主要逻辑:

首先要实现黑名单功能,一个主要的前提就是建立数据库,android提供轻量级的sqlite数据库,就是一个文本文件。

我们先建一个blacknumDBHelper类,这个类继承自SQLiteOpenHelper 

package com.yangmiaoqing.example.myassitant;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

/**
 * Created by yangmiaoqing on 2018/3/29.
 */

public class blacknumDBHelper extends SQLiteOpenHelper {
    private static SQLiteOpenHelper mInstance;
    private final static String name="blacknumber.db";
    public static SQLiteOpenHelper getInstance(Context context){
        if (mInstance==null){
            mInstance=new blacknumDBHelper(context,name,null,1);
        }
        return mInstance;
    }
    public blacknumDBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory,int version){
        super(context,name,factory,version);
    }
    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        sqLiteDatabase.execSQL("create table blacknumber(_id integer primary key autoincrement,number text)");
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {

    }
}

首先得到一个SQLiteOpenHelper实例,super中写出版本号和数据库名,然后在onCreate中建立黑名单表,表中只有两个字段,既序号和number。onUpgrade是当数据库更新才用得到的。

然后是数据库操作类blacknumberDao

package com.yangmiaoqing.example.myassitant;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by yangmiaoqing on 2018/3/29.
 */

public class blacknumberDao {
    private SQLiteOpenHelper mInstance;
    public blacknumberDao(Context context){
        mInstance=blacknumDBHelper.getInstance(context);
    }
    public void add(String number){
        SQLiteDatabase db=mInstance.getWritableDatabase();
        if(db.isOpen()){
            ContentValues values=new ContentValues();
            values.put("number",number);
            db.insert("blacknumber",null,values);
            db.close();
            Log.e("TAG","添加成功");
        }
    }
    public boolean isBlackNumber(String number){
        boolean isExist=false;
        SQLiteDatabase db=mInstance.getReadableDatabase();
        if(db.isOpen()){
            Cursor c=db.query("blacknumber",null,"number =?",new String[]{number},null,null,null);
            if(c.moveToFirst()){
                isExist=true;
            }
            c.close();
            db.close();
        }
        return isExist;
    }
    public void delete(String number){
        SQLiteDatabase db=mInstance.getWritableDatabase();
        if(db.isOpen()){
            db.delete("blacknumber","number=?",new String[]{number});
            db.close();
        }
    }
    public List<String> findAll(){
        List<String>blacknumbers=new ArrayList<String>();
        SQLiteDatabase db=mInstance.getReadableDatabase();
        if(db.isOpen()){
            Cursor c=db.query("blacknumber",new String[]{"number"},null,null,null,null,null);
            while (c.moveToNext()){
                String number=c.getString(0);
                blacknumbers.add(number);
            }
            c.close();
            db.close();
        }
        return blacknumbers;
    }
    public int queryId(String number){
        int _id=0;
        SQLiteDatabase db=mInstance.getReadableDatabase();
        if(db.isOpen()){
            Cursor c=db.query("blacknumber",new String[]{"_id"},"number=?",new String[]{number},null,null,null);
            if(c.moveToFirst()){
                _id=c.getInt(0);
            }
            c.close();
            db.close();
        }
        return _id;
    }
    public void update(int id,String number){
        SQLiteDatabase db=mInstance.getWritableDatabase();
        if(db.isOpen()){
            ContentValues values=new ContentValues();
            values.put("number",number);
            db.update("blacknumber",values,"_id=?",new String[]{id+""});
            db.close();
        }
    }
}

主要需要的数据库操作只有这么几种,即插入,查询是否是黑名单,删除,修改,找出所有的黑名单号码以List的形式返回。

所有的黑名单要显示出来,前面我们说了,内容是包含在一个ListView中的。先收一下这个LIstView的适配器。blacknumAdapter这次继承自BaseAdapter。因为比较简单,只需要重写下getView即可。

package com.yangmiaoqing.example.myassitant;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import java.util.List;

/**
 * Created by yangmiaoqing on 2018/3/29.
 */

public class blacknumberAdapter extends BaseAdapter {
    private List<String> list=null;
    private Context context=null;
    private LayoutInflater inflater=null;
    public blacknumberAdapter(Context context,List<String> list){
        this.context=context;
        this.list=list;
        inflater=LayoutInflater.from(context);
    }
    public void setBlackNumbers(List<String> list){
       this.list=list;
    }
    @Override
    public int getCount() {
        return list.size();
    }

    @Override
    public Object getItem(int i) {
        return list.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        String s=list.get(i);
        view=inflater.inflate(R.layout.black_item,null);
        TextView textView=(TextView)view.findViewById(R.id.number);
        textView.setText(s);
        return view;

    }
}

接下来是黑名单的操作逻辑blacknumber_activity.java

package com.yangmiaoqing.example.myassitant;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.support.v7.app.AlertDialog;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;

/**
 * Created by yangmiaoqing on 2018/3/29.
 */

public class blacklist_Activity extends Activity implements View.OnClickListener {
    private  static final int MENU_UPDATE_ID=0;
    private static final int MENU_DELETE_ID=1;
    private TextView tips;
    private Button add_blacknum;
    private ListView list_blacknumber;
    private LayoutInflater mInflater;
    private EditText add_number_Dialog;
    private Button ok_number_Dialog;
    private Button cancle_number_Dialog;
    private AlertDialog dialog;
    private String blacknumber;
    private View view;
    private blacknumberDao blacknumberDao;
    private blacknumberAdapter blacknumberAdapter;
    private int flag = 0;
    private final static int ADD = 1;
    private final static int UPDATE = 2;

    @Override
    protected void onCreate(Bundle savedInstance){
        super.onCreate(savedInstance);
       // blacknumber=getIntent().getStringExtra("number");
        setContentView(R.layout.blacklist_layout);



        add_blacknum=(Button)findViewById(R.id.add);
        list_blacknumber=(ListView)findViewById(R.id.blacknum_list);
        tips=(TextView)findViewById(R.id.tips);
        mInflater=(LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view=mInflater.inflate(R.layout.black_menu,null);  //为了加载上下文菜单布局
        add_number_Dialog=(EditText)view.findViewById(R.id.add_number_Dialog);
        ok_number_Dialog=(Button)view.findViewById(R.id.ok_number_Dialog);
        cancle_number_Dialog=(Button)view.findViewById(R.id.cancel_number_Dialog);
        ok_number_Dialog.setOnClickListener(this);
        cancle_number_Dialog.setOnClickListener(this);

        list_blacknumber.setEmptyView(tips);    //当listview为空时,显示信息
        blacknumberDao=new blacknumberDao(this);
        List<String>blacknumbers=blacknumberDao.findAll();
        blacknumberAdapter=new blacknumberAdapter(this,blacknumbers);
        list_blacknumber.setAdapter(blacknumberAdapter);
        add_blacknum.setOnClickListener(this);

        registerForContextMenu(list_blacknumber);     //为listview注册上下文菜单

       
    }


    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo){
        super.onCreateContextMenu(menu,v,menuInfo);
        menu.add(0,MENU_UPDATE_ID,0,"更新");                  //四个参数的含义:1,分组 2,ID 3,显示顺序 4,显示信息
        menu.add(0,MENU_DELETE_ID,0,"删除");
    }
    @Override
    public boolean onContextItemSelected(MenuItem item){
        AdapterView.AdapterContextMenuInfo acmi=(AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
        int position=acmi.position;
        blacknumber=(String)blacknumberAdapter.getItem(position);
        int id=item.getItemId();
        switch(id){
            case MENU_UPDATE_ID:
                ViewGroup parent=(ViewGroup)view.getParent();
                if(parent!=null)parent.removeAllViews();         
               // add_number_Dialog.setText(blacknumber);
                AlertDialog.Builder builder=new AlertDialog.Builder(this);
                builder.setTitle("更新黑名单");
                builder.setView(view);
                dialog=builder.create();
                dialog.show();
                flag=UPDATE;
                break;
            case MENU_DELETE_ID:
                blacknumberDao.delete(blacknumber);
                blacknumberAdapter.setBlackNumbers(blacknumberDao.findAll());
                blacknumberAdapter.notifyDataSetChanged();
                break;
            default:
                break;
        }
        return super.onContextItemSelected(item);
        }

    public void onClick(View v){
        int id=v.getId();
        switch (id){
            case R.id.add:
                ViewGroup parent = (ViewGroup) view.getParent();
                if(parent != null){
                    parent.removeAllViews();
                }
                AlertDialog.Builder builder = new AlertDialog.Builder(this);
                add_number_Dialog.setText("");
                builder.setTitle("添加黑名单");
                builder.setView(view);
                dialog = builder.create();
                dialog.show();
                flag = ADD;
                break;
            case R.id.ok_number_Dialog:
                String number=add_number_Dialog.getText().toString();
                if("".equals(number))
                    Toast.makeText(this,"黑名单号码不能为空",Toast.LENGTH_LONG).show();
                else{
                    boolean isBlackNumber=blacknumberDao.isBlackNumber(number);
                    if(isBlackNumber)Toast.makeText(this,"已存在此号码",Toast.LENGTH_LONG).show();
                    else {
                        if(flag==ADD){
                            blacknumberDao.add(number);
                            Toast.makeText(this,"添加成功",Toast.LENGTH_LONG).show();
                        }else if(flag==UPDATE){
                           int _id=blacknumberDao.queryId(blacknumber);
                            String number1=add_number_Dialog.getText().toString();
                            blacknumberDao.update(_id,number1);
                            Toast.makeText(this,"修改成功",Toast.LENGTH_LONG).show();
                        }
                        dialog.dismiss();
                        List<String> blacknumbers=blacknumberDao.findAll();
                        blacknumberAdapter.setBlackNumbers(blacknumbers);
                        blacknumberAdapter.notifyDataSetChanged();
                    }
                }
                break;
            case R.id.cancel_number_Dialog:
                dialog.dismiss();
                break;
        }
    }

    @Override
    public void onPointerCaptureChanged(boolean hasCapture) {

    }
}

以上都是本地的操作黑名单的动作实现,接下来是最重要的:监听来电,判断是否存在于黑名单中,如果存在,调用endcall,自动挂断电话。

值得一提的是,android并没有暴漏这个方法,也就是说并不直接提供我们使用,但是的确存在。如何使用呢?反射

 

这里需要补充一点的是:由于我们是直接拦截的来电,相当于是跨进程间的通信,这个时候aidl就登场了,好在Android有这些接口: 
因为下面的代码会调用这些接口,所以我们提交准备好:

1.在根目录新建一个android.telephony的包,在里面新建一个aidl文件,命名为NeighboringCellInfo;

2.在根目录下新建一个com.android.internal.telephony的包,在里面新建一个aidl文件,命名为ITelephony;

这里一定要弄好,不然无法调用方法。

package com.yangmiaoqing.example.myassitant;

import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;


import com.android.internal.telephony.ITelephony;

import java.lang.reflect.Method;

/**
 * Created by yangmiaoqing on 2018/3/30.
 */

public class blackNumService extends Service {
  private boolean isblacknum;
  private blacknumberDao blacknumber=new blacknumberDao(this);
  private TelephonyManager tm;
  private MyPhoneStateListener listener;
  private NotificationManager nm;
@Override
public void onCreate(){
  super.onCreate();
  tm= (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
  listener=new MyPhoneStateListener();
  tm.listen(listener,PhoneStateListener.LISTEN_CALL_STATE);
}


  private final class MyPhoneStateListener extends PhoneStateListener{
    @Override
    public void onCallStateChanged(int state,String incomingnumber){
        super.onCallStateChanged(state,incomingnumber);
        //isblacknum=true;
        switch (state){
          case TelephonyManager.CALL_STATE_RINGING:
            isblacknum=blacknumber.isBlackNumber(incomingnumber);
            Log.e("blackNumService",incomingnumber+"来电");
            if(isblacknum){
              endCall();
            }
            break;
          case TelephonyManager.CALL_STATE_IDLE:
            break;
          case TelephonyManager.CALL_STATE_OFFHOOK:
            break;
          default:
            break;
        }
    }
  }

  private void endCall(){
    try {
      Class<?> clazz = Class.forName("android.os.ServiceManager");
      Method method = clazz.getMethod("getService", String.class);
      IBinder ibinder = (IBinder) method.invoke(null, Context.TELEPHONY_SERVICE);
      ITelephony iTelephony = ITelephony.Stub.asInterface(ibinder);
      iTelephony.endCall();

    } catch (Exception e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }

  @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

解释下,这个监听功能肯定是要在后台服务的,所以继承自Service,在MyPhoneStateListener中监听来电状态,如果是响铃,则判断是否来电号码在黑名单中,如果是则调用endcall自动挂断。endcall函数利用反射机制拿到通过IBinder拿到ITelephony的方法endcall。

最后需要说明的是,在android6.0之后要得到监听电话的权限是要动态实现的。前面已经说过,这个权限实在Mainactivity中申请的。至此,通讯卫士功能就算基本完成了,等所有功能完成之后再来完善。

(后续)因为要在设置中添加黑名单功能的开关,其实很简单,但是做的时候遇到了问题。理论上来说只需要停止我们的service即可,但是试了半天都不可行,很名单功能死活都停止不了。最后发现,Service停止的时候不是立即停止,而是等所有功能完成之后停止,我们PhoneStateListenter还在监听状态,只需要在onDestroy中添加

tm.listen(listener,PhoneStateListener.LISTEN_NONE);

即可

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值