手机卫士-05

手机卫士-05

课1

模仿网易新闻下拉加载分页数据listView

在activtiycallsafe.xml里重新修改(去掉原来的button)

activtiycallsafe.xml

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

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <TextView
            style="@style/textview_title_style"
            android:layout_height="60dp"
            android:gravity="center"
            android:text="通讯卫士" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:onClick="addBlackNumber"
            android:text="添加" />
    </RelativeLayout>
    <!-- android:fastScrollEnabled="true" 设置快速滑动 -->

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

        <LinearLayout
            android:id="@+id/ll_loading"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:orientation="vertical"
            android:visibility="invisible" >

            <ProgressBar
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="玩命加载中...." />
        </LinearLayout>

        <ListView
            android:id="@+id/list_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:divider="@drawable/list_devider"
            android:fastScrollEnabled="true" />
    </FrameLayout>

</LinearLayout>

在CallSafeActivity.class里重新修改

list_view.setOnScrollListener//初始化listview的滚动监听 在方法里有分别识别(惯性滑动、当滚动然后停下来闲置的时候、在触摸屏幕的时候调用方法)的操作 现在目的是下拉滑动的数据到20条截止就再请求数据库获取数据 在case OnScrollListener.SCROLLSTATEIDLE里进行操作

CallSafeActivity.class

list_view.setOnScrollListener(new OnScrollListener() {

            @Override
            public void onScrollStateChanged(AbsListView view, int scrollState) {
                switch (scrollState) {
                // 惯性滑动
                case OnScrollListener.SCROLL_STATE_FLING:

                    break;
                // 当滚动然后停下来闲置的时候
                case OnScrollListener.SCROLL_STATE_IDLE:
                    // 最后一个显示可见的位置
                    int lastVisiblePosition = list_view
                            .getLastVisiblePosition();
                    System.out.println("lastVisiblePosition----->"
                            + lastVisiblePosition);

                    if (lastVisiblePosition == lists.size() - 1) {

                        startIndex += pageCount;

                        if (startIndex > countTotal) {
                            Toast.makeText(CallSafeActivity.this,
                                    "没有更多的数据进行加载了", 0).show();
                            return;
                        }


                    }
                    initData();
                    break;
                // 在触摸屏幕的时候调用的方法
                case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:

                    break;
                }

            }

            @Override
            public void onScroll(AbsListView view, int firstVisibleItem,
                    int visibleItemCount, int totalItemCount) {
                // TODO Auto-generated method stub

            }
        });

在BlackNumberDao里继续加功能(分批加载数据)

CallSafeActivity.class里使用分批加载数据的方法(首先定义好startIndex、pageCount初始化了)

CallSafeActivity.class

/**
 * 从第0条数据进行加载
 */
private int startIndex = 0;
/**
 * 每页最多的加载数据
 */
private int pageCount = 20;
调用lists = dao.findPage2()
BlackNumberDao.java

/**
 * 分批加载数据
 * 
 * @param startIndex
 *           开始数据的条目
 * @param pageCount
 *            每页最多加载多少条数据
 * @return 返回一个黑名单的集合数据
 */
public List<BlackNumberInfo> findPage2(int startIndex, int pageCount) {

    SQLiteDatabase db = helper.getReadableDatabase();

    Cursor cursor = db.rawQuery(
            "select number,mode from blackinfo order by _id desc  limit ? offset ?",
            new String[] { String.valueOf(pageCount),
                    String.valueOf(startIndex) });
    // 初始化黑名单的集合
    ArrayList<BlackNumberInfo> lists = new ArrayList<BlackNumberInfo>();

    while (cursor.moveToNext()) {
        BlackNumberInfo info = new BlackNumberInfo();
        info.setMode(cursor.getString(1));
        info.setNumber(cursor.getString(0));
        lists.add(info);
    }
    cursor.close();
    db.close();
    return lists;
}

但是这样做加载后就把之前的给覆盖了 解决小bug,在使用lists = dao.findPage2()的时候加个判断,如果lists!=null,就再lists里追加加载后的数据。

CallSafeActivity.class

// 当滚动然后停下来闲置的时候
case OnScrollListener.SCROLL_STATE_IDLE:
    // 最后一个显示可见的位置
    int lastVisiblePosition = list_view
            .getLastVisiblePosition();
    System.out.println("lastVisiblePosition----->"
            + lastVisiblePosition);

    if (lastVisiblePosition == lists.size() - 1) {

        startIndex += pageCount;

        if (startIndex > countTotal) {
            Toast.makeText(CallSafeActivity.this,
                    "没有更多的数据进行加载了", 0).show();
            return;
        }


    }
    initData();
    break;

继续解决小bug,由于每次都追加后都new一个新的adapter,所以在new一个适配器时需要进行判断,如果已经有适配器时,就使用刷新界面的适配器方法notifyDataSetChanged

CallSafeActivity.class

//判断当前的结果值是否添加成功
boolean result = dao.add(phone, mode);

if(result){
    if(adapter == null){

        CallSafeAdapter adapter = new CallSafeAdapter(CallSafeActivity.this, lists);
        list_view.setAdapter(adapter);
    }else{
        adapter.notifyDataSetChanged();
    }

}

继续实现黑名单里的删除图片的删除功能 在getView里添加图片控件,并让holder去管理

itemcallsafe.xml

<ImageView 
    android:id="@+id/iv_delete"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/delet_selector"
    android:layout_centerVertical="true"
    android:layout_alignParentRight="true"
    android:layout_marginRight="10dp"
    />

ViewHolder的详解(减低ListView里工作)

holder用来管理ListView里的GetView所绑定的xml文件里的控件 简单来说就是通过在下面 private class ViewHolder { TextView tvnumber; TextView tvmode; ImageView iv_delete; } ViewHolder里管理的控件变量,当在获取getView要绑定的xml资源到View view时,通过在绑定后把holder里管理的变量通过该view去findViewById获取对象并添加一个标记view.setTag(holder);,那么每次getView时就可以实现不用重复再重复的去findViewById获取对象,这就可以节省手机的cpu处理资源,大大减低ListView的效率

在设置的点击事件里进行操作集合移除的操作

但是如果不使用adapter的刷新方法,那么也看不出马上被删除的效果,解决这个小bug

CallSafeActivity.class

private class ViewHolder {
    TextView tv_number;
    TextView tv_mode;
    ImageView iv_delete;
}


private class CallSafeAdapter extends
        MyBaseAdapter<BlackNumberInfo, ListView> {

    private View view;
    private ViewHolder holder;
    //private BlackNumberInfo info;

    public CallSafeAdapter(CallSafeActivity callSafeActivity,
            List<BlackNumberInfo> lists) {
        super(lists, callSafeActivity);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if (convertView == null) {
            view = View.inflate(context, R.layout.item_call_safe, null);

            holder = new ViewHolder();

            holder.tv_number = (TextView) view.findViewById(R.id.tv_number);

            holder.tv_mode = (TextView) view.findViewById(R.id.tv_mode);

            holder.iv_delete = (ImageView) view.findViewById(R.id.iv_delete);


            // 添加一个标记
            view.setTag(holder);

        } else {
            view = convertView;
            holder = (ViewHolder) view.getTag();
        }

        final BlackNumberInfo info = lists.get(position);
        System.out.println("---------------------"+info.getNumber());
        //给imageview设置删除点击事件
        holder.iv_delete.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                String blackNumber = info.getNumber();
                //删除黑名单电话号码
                boolean result = dao.delete(blackNumber);
                //从集合里面移除黑名单电话号码
                lists.remove(info);

                if(result){
                    adapter.notifyDataSetChanged();
                }
            }
        });


课2

继续在黑名单里加上添加黑名单按钮的功能 在activitycallsafe.xml增加按钮控件

activitycallsafe.xml

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentRight="true"
    android:layout_centerVertical="true"
    android:onClick="addBlackNumber"
    android:text="添加" />

在CallSafeActivity.java里为按钮方法添加一个对话框

CallSafeActivity.java

/**
 * 添加黑名单电话号码
 * @param view
 */
public void addBlackNumber(View view){
    AlertDialog.Builder  builder = new Builder(this);
    View dialogview = View.inflate(CallSafeActivity.this, R.layout.dialog_add_black_number, null);

    final EditText et_phone = (EditText) dialogview.findViewById(R.id.et_phone);

    final CheckBox cb_phone = (CheckBox) dialogview.findViewById(R.id.cb_phone);

    final CheckBox cb_sms = (CheckBox) dialogview.findViewById(R.id.cb_sms);
    //取消
    dialogview.findViewById(R.id.bt_cancel).setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            // TODO Auto-generated method stub
            dialog.dismiss();
        }
    });

    //确定
    dialogview.findViewById(R.id.bt_ok).setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            String mode = "0";
            String phone = et_phone.getText().toString().trim();
            //判断当前的电话号码是否有值
            if(TextUtils.isEmpty(phone)){
                Toast.makeText(CallSafeActivity.this, "请输入电话号码", 0).show();
                return ;
            }
            /**
             * 拦截模式 1 全部拦截 2 短信拦截 3 电话拦截
             */
            if(cb_phone.isChecked() && cb_sms.isChecked()){
                mode = "1";
            }else if(cb_phone.isChecked()){
                mode = "3";
            }else if(cb_sms.isChecked()){
                mode = "2";
            }else{
                Toast.makeText(CallSafeActivity.this, "请勾选拦截模式", 0).show();
                return;
            }
            BlackNumberInfo info = new BlackNumberInfo();
            info.setMode(mode);
            info.setNumber(phone);
            lists.add(0,info);
            //判断当前的结果值是否添加成功
            boolean result = dao.add(phone, mode);

            if(result){
                if(adapter == null){

                    CallSafeAdapter adapter = new CallSafeAdapter(CallSafeActivity.this, lists);
                    list_view.setAdapter(adapter);
                }else{
                    adapter.notifyDataSetChanged();
                }

            }
            dialog.dismiss();
        }
    });

    builder.setView(dialogview);
    dialog = builder.show();
}

实现对话框的布局dialogaddblack_number.xml

dialogaddblack_number.xml

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

    <TextView
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:background="#5500ff00"
        android:gravity="center"
        android:text="黑名单电话号码添加"
        android:textSize="24sp" />

    <EditText
        android:id="@+id/et_phone"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入电话号码"
        android:inputType="phone" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <CheckBox
            android:id="@+id/cb_phone"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="电话拦截" />

        <CheckBox
            android:id="@+id/cb_sms"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="短信拦截" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/bt_ok"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="@drawable/btn_selector"
            android:text="确定" />

        <Button
            android:id="@+id/bt_cancel"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:background="@drawable/btn_selector"
            android:text="取消" />
    </LinearLayout>

</LinearLayout>


设计完毕后就继续注入dialogView去使用。

CallSafeActivity.java

AlertDialog.Builder  builder = new Builder(this);
        View dialogview = View.inflate(CallSafeActivity.this, R.layout.dialog_add_black_number, null);

        final EditText et_phone = (EditText) dialogview.findViewById(R.id.et_phone);

        final CheckBox cb_phone = (CheckBox) dialogview.findViewById(R.id.cb_phone);

        final CheckBox cb_sms = (CheckBox) dialogview.findViewById(R.id.cb_sms);

(技巧)我们可以设置dialog里面的一个方法setCanceledOnTouchOutside(true);的一个方法去锁定提示框

AlertDialog dialog = builder.show();
dialog.setCanceledOnTouchOutside(true);

主要逻辑是对话框里的确定按钮响应事件

1、拿到EditText里的内容    
2、判断checkBox是否有勾上
3、操作赋值
4、更新适配器

CallSafeActivity.java

//确定
dialogview.findViewById(R.id.bt_ok).setOnClickListener(new OnClickListener() {

    @Override
    public void onClick(View v) {
        String mode = "0";
        String phone = et_phone.getText().toString().trim();
        //判断当前的电话号码是否有值
        if(TextUtils.isEmpty(phone)){
            Toast.makeText(CallSafeActivity.this, "请输入电话号码", 0).show();
            return ;
        }
        /**
         * 拦截模式 1 全部拦截 2 短信拦截 3 电话拦截
         */
        if(cb_phone.isChecked() && cb_sms.isChecked()){
            mode = "1";
        }else if(cb_phone.isChecked()){
            mode = "3";
        }else if(cb_sms.isChecked()){
            mode = "2";
        }else{
            Toast.makeText(CallSafeActivity.this, "请勾选拦截模式", 0).show();
            return;
        }
        BlackNumberInfo info = new BlackNumberInfo();
        info.setMode(mode);
        info.setNumber(phone);
        lists.add(0,info);
        //判断当前的结果值是否添加成功
        boolean result = dao.add(phone, mode);

        if(result){
            if(adapter == null){

                CallSafeAdapter adapter = new CallSafeAdapter(CallSafeActivity.this, lists);
                list_view.setAdapter(adapter);
            }else{
                adapter.notifyDataSetChanged();
            }

        }
        dialog.dismiss();
    }
});

bug,如果添加的话,它会加载到lists的最后,解决lists.add(position,xxx); 解决了上一个bug之后,发现关掉该页面后再打开,lists还是按顺序展示,所以我们需要修改dao层里的数据库的sql

BlackNumberDao.java

public List<BlackNumberInfo> findPage2(int startIndex, int pageCount) {

        SQLiteDatabase db = helper.getReadableDatabase();

        Cursor cursor = db.rawQuery(
                "select number,mode from blackinfo order by _id desc  limit ? offset ?",
                new String[] { String.valueOf(pageCount),
                        String.valueOf(startIndex) });


真正实现电话和信息拦截的功能 首先实现电话拦截 短信拦截:在清单文件里已经实现过了(静态注册过了) 今天实现短信拦截的动态注册

在之前学过activitysettingcenter.xml里添加二维码的布局

activitysettingcenter.xml

<TextView
    android:layout_width="match_parent"
    android:layout_height="40dp"
    android:background="@drawable/list_selector"
    android:clickable="true"
    android:enabled="true"
    android:focusable="true"
    android:onClick="about"
    android:text="关于我们"
    android:textSize="24sp" />

<View
    android:layout_width="match_parent"
    android:layout_height="1dp"
    android:background="@drawable/list_devider" />

<TextView
    android:layout_width="match_parent"
    android:layout_height="40dp"
    android:background="@drawable/list_selector"
    android:clickable="true"
    android:enabled="true"
    android:focusable="true"
    android:text="扫一扫"
    android:textSize="24sp" />

<View
    android:layout_width="match_parent"
    android:layout_height="1dp"
    android:background="@drawable/list_devider" />


二维码实现 颜色选择器添加:在value里新建color.xml--->然后在新建的listselector.xml来调用,然后再在关于的TextView里调用 activityabount.xml--->ImageView 里加上一个网上下载的二维码图片在abountActivity.class注入,然后通过SettingCenterActivity.java里的关于(点击时加上选择器)跳到abountActivity.class里 并在SettingCenterActivity.java里实现二维码功能--->调到处理二维码的类:abountActivity.class

AboutActivity.java

public class AboutActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_about);
    }
}

清单文件加上activity节点

<!-- 关于 -->
<activity
    android:name="com.itheima.mobile47.AboutActivity"
    android:screenOrientation="portrait" >
</activity>

布置布局文件activity_abount.xml--->ImageView

activity_about.xml

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

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:textSize="24sp"
        android:background="#ff0000"
        android:text="关于" />

    <ImageView
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true"
        android:background="@drawable/qrcode" />

</RelativeLayout>


课3

继续在xx实现扫一扫的功能 zxing的githut里下载二维码的开源代码 移植到项目中 然后通过顺藤摸瓜去找到项目如何把链接显示在照片上,然后找到首页面,首页面所支持的TextView,然后在TextView要setText时找到大致的地方去截取要set的地址值,然后进行处理

继续在activitysettingcenter.xml设置中心布局里实现黑名单拦截功能打开


在SettingCenterActivity.java里加上响应事件里增加功能 使用服务实现功能 新建黑名单拦截服务:CallSafeService.java

四大组件配置清单文件(服务)

true:开启服务、false:关闭服务,在手机本地的应用服务里查看没加功能的服务开启没

SettingCenterActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_setting_center);
    intent = new Intent(this,CallSafeService.class);
    //黑名单设置
    setting_view_black.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            if(setting_view_black.isChecked()){
                setting_view_black.setChecked(false);
                stopService(intent);
            }else{
                setting_view_black.setChecked(true);
                startService(intent);
            }

        }
    });

继续实现服务功能CallSafeService.java 过滤器是广播的一种实现receiver节点里的action节点 在CallSafeService.java动态注册短信的广播,然后在filter里设置setPriority,在然后就registerReceiver

CallSafeService.java

public class CallSafeService extends Service {

private BlackNumberDao dao;
private TelephonyManager tm;

@Override
public IBinder onBind(Intent intent) {
    // TODO Auto-generated method stub
    return null;
}

@Override
public void onCreate() {
    // TODO Auto-generated method stub
    super.onCreate();
    // 获取到黑名单的数据
    dao = new BlackNumberDao(this);

    // 获取到电话相关的服务
    tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
    // 初始化电话状态的监听
    MyPhoneStateListener listener = new MyPhoneStateListener();
    tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);

    // 初始化内部拦截短信的广播
    InnerSmsReceiver receiver = new InnerSmsReceiver();
    // 初始化一个短信拦截的action
    IntentFilter filter = new IntentFilter(
            "android.provider.Telephony.SMS_RECEIVED");
    filter.setPriority(Integer.MAX_VALUE);
    // 注册短信的广播
    registerReceiver(receiver, filter);
}

在内部类InnerSmsReceiver实现短信拦截的功能

CallSafeService.java

private class InnerSmsReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        // 获取到短信
        Object[] objs = (Object[]) intent.getExtras().get("pdus");

        for (Object obj : objs) {
            SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) obj);
            // 获取到短信的电话号码
            String number = smsMessage.getOriginatingAddress();
            // 获取到短信的内容
            String body = smsMessage.getMessageBody();
            // 获取到拦截模式
            String mode = dao.findNumberMode(number);
            // 如果是全部拦截或者是短信拦截。那么就全部拦截下来
            if (mode.equals("1") || mode.equals("2")) {
                System.out.println("拦截下来了");
                abortBroadcast();
            }

            // 智能拦截
            if (body.contains("fapiao")) {
                System.out.println("垃圾短信拦截下来了");
                abortBroadcast();
            }
        }
    }

}

在onCreate里操作dao通过电话号码获取拦截模式,然后给内部类

String mode = dao.findNumberMode(number);
// 如果是全部拦截或者是短信拦截。那么就全部拦截下来
if (mode.equals("1") || mode.equals("2")) {
    System.out.println("拦截下来了");
    abortBroadcast();
}

InnerSmsReceiver实现短信拦截的功能abortBroadcast(),我们可以把短信服务理解为一个有序广播,然后我们通过条件定位到手机号,当服务一促发就查询dao,当对比有该号码就拦截abortBroadcast()。 该服务打开时继续设置一个方法拦截一些垃圾短信,只要短信body包含了一些我们自己我想见到信息内容,就abortBroadcast(),上下文的方法

继续在CallSafeService实现电话号码拦截

使用getSystemService(TelephonyManager)获得电话服务,然后继续实现,这是之前学习服务时候学习过的电话窃听的一个例子 在电话不同的状态下进行拦截操作。(在手机铃响时就使用dao查询拦截的模式,从而进行分类拦截)

课4

继续实现拦截短信中结束电话的方法(在TelephonyManager查看源码):CallSafeService.class TelephonyManager里的getService是关于服务的知识,回忆起老师当时的调用远程服务的例子 挂掉电话的方法(观察源码后想到的方法) endcall()://使用反射的方法获得类 然后再获取方法,然后获取到ibunder远程服务(getService) 把远程服务的aidl拿过来 放在和该aidl一样的包名里(打开aidl里查看包名) 最后调用远程服务,把ibunder传进去,最后调用endcall()系统远程服务来挂断电话 课下回忆老师教我们远程服务的实例对比今天的课的内容 解决小bug,在设置中心关闭服务,但是后台服务却没有关,如何控制呢?把开关标记存在sp不合适,因为在应用设置处清除数据后sp没了,但是服务却依然在跑。 新建类ServiceIsRunning.java,该util类用来判断服务是否开启、 使用ActivityManager来判断正在运行的一些服务am.getRunningServices(50) 把传进来的服务名字和系统获得的服务名字做对比,如果名字相同,就进行关闭操作,然后设置中心SettingCenterActivity.class就使用该工具类来进行开关服务操作onstart();

CallSafeService.java

@Override public void onCreate() { 

    // TODO Auto-generated method stub super.onCreate(); // 获取到黑名单的数据 dao = new BlackNumberDao(this);

    // 获取到电话相关的服务
    tm = (TelephonyManager) getSystemService(TELEPHONY_SERVICE);
    // 初始化电话状态的监听
    MyPhoneStateListener listener = new MyPhoneStateListener();
    tm.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);

    // 初始化内部拦截短信的广播
    InnerSmsReceiver receiver = new InnerSmsReceiver();
    // 初始化一个短信拦截的action
    IntentFilter filter = new IntentFilter(
            "android.provider.Telephony.SMS_RECEIVED");
    filter.setPriority(Integer.MAX_VALUE);
    // 注册短信的广播
    registerReceiver(receiver, filter);
}

private class MyPhoneStateListener extends PhoneStateListener {

    @Override
    public void onCallStateChanged(int state, String incomingNumber) {
        switch (state) {
        // 闲置状态
        case TelephonyManager.CALL_STATE_IDLE:

            break;
        // 响铃状态
        case TelephonyManager.CALL_STATE_RINGING:

            String mode = dao.findNumberMode(incomingNumber);

            if(mode.equals("1")||mode.equals("3")){
                System.out.println("电话拦截");
                //挂断电话
                endcall();

            }
            break;
        // 通话状态
        case TelephonyManager.CALL_STATE_OFFHOOK:
            break;
        }
        super.onCallStateChanged(state, incomingNumber);
    }

}
/**
* 挂掉电话
*/
public void endcall() {
    try {
          //使用类加载器去加载一个类
        Class<?> clazz = getClassLoader().loadClass("android.os.ServiceManager");
        //获取到方法
        /**
         * 第一个参数是名字
         * 第二个参数是类型
         */
        Method method = clazz.getDeclaredMethod("getService", String.class);
        //实现调用这个方法
        //第一个参数:如果是静态的那么就是设置为null
        IBinder iBinder = (IBinder) method.invoke(null, TELEPHONY_SERVICE);
        //通过IBinder对象获取到ITelephony
        ITelephony iTelephony = ITelephony.Stub.asInterface(iBinder);
        //挂断电话
        iTelephony.endCall();
    } catch (Exception e) {
        e.printStackTrace();
    }


}

课5

实现号码归属地查询 在高级工具里设计case 7 新建ToolsActivity.java 布局activity_tools.xml

activity_tools.xml

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

    <TextView
        style="@style/textview_title_style"
        android:gravity="center"
        android:text="高级工具" />

    <TextView
        android:onClick="queryLocation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:drawableLeft="@android:drawable/star_big_off"
        android:textSize="24sp"
        android:clickable="true"
        android:focusable="true"
        android:enabled="true"
        android:background="@drawable/list_selector"
        android:text="归属地查询" />

</LinearLayout>

ToolsActivity.java

public class ToolsActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tools);
    }
    /**
     * 归属地查询
     * @param view
     */
    public void queryLocation(View view){
        Intent intent = new Intent(this,QueryLocationActivity.class);
        startActivity(intent);
    }
}


回到ToolsActivity.java实现号码归属地查询:queryLocation--->方法跳到下一个号码归属地查询的Activity:新建QueryLocationActivity+布局文件activityquerylocation.xml

activityquerylocation.xml

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

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/main_title_bg"
        android:gravity="center"
        android:text="号码归属地查询"
        android:textColor="#fff"
        android:textSize="24sp" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="输入你要查询的电话号码"
        android:textSize="20sp" />

    <EditText
        android:id="@+id/et_number"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/tv_address"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:text="归属地信息" />

    <Button 
        android:onClick="btQuery"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="点击查询"
        />

</LinearLayout>

QueryLocationActivity.java

public class QueryLocationActivity extends Activity {
    @ViewInject(R.id.et_number)
    private EditText et_number;
    @ViewInject(R.id.tv_address)
    private TextView tv_address;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_query_location);
        ViewUtils.inject(this);
    }

    /**
     * 点击查询归属地
     * 
     * @param view
     */
    public void btQuery(View view) {
        String number = et_number.getText().toString().trim();
        if(TextUtils.isEmpty(number)){
            Toast.makeText(QueryLocationActivity.this, "请输入查询号码", 0).show();

        }else{
            LocationDao dao = new LocationDao();
            String address = dao.getLocation(number);
            tv_address.setText("地理位置号码归属地 : " +address);
        }


    }
}


实现点击查询归属地的功能btQuery(查询数据库) 观察金山手机卫士发现,清空该app的数据后,重新打开时会自动导入之前的数据库 因此我们也模拟该app的导入数据库模式导入,即在打开首页时就导入进来 先把数据库放入到assets目录下 在SplashActivity里加上拷贝数据库方法copyDB,用来初始化数据库 把asset里的文件流拷贝到data/data下 考虑到如果拷贝的数据比较大,所以使用子线程来作拷贝操作 这里使用线程池来执行线程操作

SplashActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onCreate(savedInstanceState);
    // 设置没有标题 必须写到setcontview前面
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.activity_splash);
    ViewUtils.inject(this);
    // ImageView image_view = (ImageView) findViewById(R.id.image_view);
    // image_view.setBackgroundResource(R.drawable.ic_launcher);
    // 获取到包的管理者。
    PackageManager pm = getPackageManager();

    try {
        // 拷贝数据库到data/data目录下面
        copyDB("address.db");

SplashActivity.java

/**
 * 拷贝数据库
 * 
 * @param dbName
 *            数据库的名字
 */
private void copyDB(String dbName) {

    ExecutorService executorService = Executors.newFixedThreadPool(1);
    TaskRunnable taskRunnable = new TaskRunnable(dbName);
    executorService.execute(taskRunnable);


}

private class TaskRunnable implements Runnable {
    private String dbName = null;

    public TaskRunnable(String dbName) {
        this.dbName = dbName;
    }

    @Override
    public void run() {
        try {
            InputStream is = getAssets().open(dbName);
            // 获取到一个输出流.
            // 第一个参数是数据库的名字。第二个参数是模式。设置是私有的
            FileOutputStream fos = openFileOutput(dbName, 0);

            byte[] buffer = new byte[1024];

            int len = 0;

            while ((len = is.read(buffer)) != -1) {

                fos.write(buffer, 0, len);

            }
            is.close();
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

继续实现号码归属地查询,QueryLocationActivity中的btQuery 在edittext里进行输入号码,然后进行与数据库查询比较 新建一个为号码归属地查询的dao--->getLocation(返回地理位置)

LocationDao.java

public class LocationDao {
    /**
     * 返回地理位置
     * 
     * @param number
     *            查询的电话号码
     * @return
     */
    public String getLocation(String number) {
        String location = "";
        // 获取到Database
        // 第一个参数是数据库的路径,第二个参数是工厂默认不要。第三个参数是标记。设置只读
        SQLiteDatabase db = SQLiteDatabase.openDatabase(
                "/data/data/com.itheima.mobile47/files/address.db", null,
                SQLiteDatabase.OPEN_READONLY);

        Cursor cursor = db.rawQuery(
                "select location from data2 where id =( select outkey from data1 where id = ?)  ",
                new String[] { number.substring(0, 7) });

        if(cursor.moveToNext()){
            location = cursor.getString(0);
        }

        return location;
    }
}

(新技巧)在getLocation里获取数据库的代码是SQLiteDatabase db = SQLiteDatabase.openDatabase("/data/data/com.itheima.mobile47/files/address.db",null,SQLiteDatabase.READONLY); 拿到了db后,继续实现查询方法。需要做联表查询 1、select outkey from data1 where id = 1300020 得到id = 3 2、select location from data2 where id = 3 得到结果

课下对比一下以前使用数据库的方式和这节课的方式的方式的不同:

那是因为以前我们那样做是为了先创建一个数据库,而现在的是数据库已经存在,所以直接拿到数据库的对象

返回location 回到QueryLocationActivity.class,在btQuery里把location设置到控件TextView里

QueryLocationActivity.java

/**
 * 点击查询归属地
 * 
 * @param view
 */
public void btQuery(View view) {
    String number = et_number.getText().toString().trim();
    if(TextUtils.isEmpty(number)){
        Toast.makeText(QueryLocationActivity.this, "请输入查询号码", 0).show();

    }else{
        LocationDao dao = new LocationDao();
        String address = dao.getLocation(number);
        tv_address.setText("地理位置号码归属地 : " +address);
    }


}

技巧小结

如何让TextView实现可点击化:在该控件里加上focusable\clickable\enabled来实现

  • ViewHolder的详解(减低ListView里工作)

holder用来管理ListView里的GetView所绑定的xml文件里的控件 简单来说就是通过在下面 private class ViewHolder { TextView tvnumber; TextView tvmode; ImageView iv_delete; } ViewHolder里管理的控件变量,当在获取getView要绑定的xml资源到View view时,通过在绑定后把holder里管理的变量通过该view去findViewById获取对象并添加一个标记view.setTag(holder);,那么每次getView时就可以实现不用重复再重复的去findViewById获取对象,这就可以节省手机的cpu处理资源,大大减低ListView的效率

在设置的点击事件里进行操作集合移除的操作

但是如果不使用adapter的刷新方法,那么也看不出马上被删除的效果,解决这个小bug

CallSafeActivity.class

private class ViewHolder {
    TextView tv_number;
    TextView tv_mode;
    ImageView iv_delete;
}
private class CallSafeAdapter extends MyBaseAdapter<BlackNumberInfo, ListView> {

    private View view;
    private ViewHolder holder;
//  private BlackNumberInfo info;

    public CallSafeAdapter(CallSafeActivity callSafeActivity,
            List<BlackNumberInfo> lists) {
        super(lists, callSafeActivity);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if (convertView == null) {
            view = View.inflate(context, R.layout.item_call_safe, null);

            holder = new ViewHolder();

            holder.tv_number = (TextView) view.findViewById(R.id.tv_number);

            holder.tv_mode = (TextView) view.findViewById(R.id.tv_mode);

            holder.iv_delete = (ImageView) view.findViewById(R.id.iv_delete);


            // 添加一个标记
            view.setTag(holder);

        } else {
            view = convertView;
            holder = (ViewHolder) view.getTag();
        }

        final BlackNumberInfo info = lists.get(position);
        System.out.println("---------------------"+info.getNumber());
        //给imageview设置删除点击事件
        holder.iv_delete.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                String blackNumber = info.getNumber();
                //删除黑名单电话号码
                boolean result = dao.delete(blackNumber);
                //从集合里面移除黑名单电话号码
                lists.remove(info);

                if(result){
                    adapter.notifyDataSetChanged();
                }
            }
        });

listView里的适配器中数据刷新的技巧,为了节省cpu资源,不用每次显示listView时都要new出一个新的适配器

private Handler handler = new Handler() {

    public void handleMessage(android.os.Message msg) {
        // 设置loading的图片不可以见
        ll_loading.setVisibility(View.INVISIBLE);
        if (lists != null && lists.size() > 0) {
            //判断适配器里面是否有数据。第一次为null那么就需要创建一个适配器。
            //为了防止每一次就重新新建适配器。那么就只需要刷新界面
            if (adapter == null) {
                adapter = new CallSafeAdapter(CallSafeActivity.this, lists);

                list_view.setAdapter(adapter);
            } else {
                // 刷新界面
                adapter.notifyDataSetChanged();
            }
        }

    };
};
资料下载
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值