Android获取通话记录的未接来电、自定义通知栏事件处理

此文非教程,作常用工具类记录,因此主要是代码---

--前言

   公司项目有这么一个需求,获取用户的通话记录,如果存在24小时内的未接来电则发送状态栏通知,通知上除了可以直接回拨还可以及引导用户打开app的指定模块/页面

因此涉及到知识点:

  1.获取通话记录,并判断是否未接

  2.发送自定义的通知栏信息,并处理响应事件

  3.实践发现点击了自定义通知的按钮,通知栏不会自动收起来因此需要手动收起通知栏

 

----1.  获取通话记录,需要动态权限授权,这里不做额外解释

   //获取通话记录肯定是通过ContentResolver查询了,先定义查询语句
    private static final Uri callUri = CallLog.Calls.CONTENT_URI;

    private static final String[] columns = {
            CallLog.Calls.CACHED_NAME// 通话记录的联系人
            , CallLog.Calls.NUMBER// 通话记录的电话号码
            , CallLog.Calls.DATE// 通话记录的日期
            , CallLog.Calls.DURATION// 通话时长
            , CallLog.Calls.TYPE// 通话类型
            , CallLog.Calls._ID// 通话ID
    };

 

/**
     * 读取数据
     *
     * @return 读取到的数据
     */
    private static List<CallLogEntity> getDataList(Context context) {
        // 1.获得ContentResolver
        ContentResolver resolver = context.getContentResolver();
        if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED) {
        }
        // 2.利用ContentResolver的query方法查询通话记录数据库
        /**
         * @param uri 需要查询的URI,(这个URI是ContentProvider提供的)
         * @param projection 需要查询的字段
         * @param selection sql语句where之后的语句
         * @param selectionArgs ?占位符代表的数据
         * @param sortOrder 排序方式
         */
        Cursor cursor = resolver.query(callUri, // 查询通话记录的URI
                columns
                , null, null, CallLog.Calls.DEFAULT_SORT_ORDER// 按照时间逆序排列,最近打的最先显示
        );
        List<CallLogEntity> list = new ArrayList<>();
        if (cursor == null) return list;
        // 3.通过Cursor获得数据
        while (cursor.moveToNext()) {
            String name = cursor.getString(cursor.getColumnIndex(CallLog.Calls.CACHED_NAME));
            String number = cursor.getString(cursor.getColumnIndex(CallLog.Calls.NUMBER));
            long dateLong = cursor.getLong(cursor.getColumnIndex(CallLog.Calls.DATE));
            int duration = cursor.getInt(cursor.getColumnIndex(CallLog.Calls.DURATION));
            int type = cursor.getInt(cursor.getColumnIndex(CallLog.Calls.TYPE));
            int id = cursor.getInt(cursor.getColumnIndex(CallLog.Calls._ID));
            if (isMobileNO(number)) {
                long day_lead = getTimeDistance(new Date(dateLong), new Date());
                if (day_lead < 2) {//只显示48小时以内通话记录,防止通话记录数据过多影响加载速度
                    CallLogEntity callLog = new CallLogEntity();
                    callLog.setCallId(id);
                    callLog.setName(name == null ? "未备注联系人" : name);
                    callLog.setNumber(number);
                    callLog.setDate(dateLong);
                    callLog.setTime(duration);
                    callLog.setType(type);
                    list.add(callLog);
                } else {
                    cursor.close();
                    return list;
                }
            }
        }
        cursor.close();
        return list;
    }

 

因为要根据时间过滤,所以写了一个计算时间距离的方法,略显复杂,其实简化可以直接用

(System.currentTimeMillis() - callLog.getDate())/DAY
     private static long DAY = 1000 * 60 * 60 * 24;

    /**
     * 获得两个日期间距多少天
     *
     * @param beginDate
     * @param endDate
     * @return
     */
    public static long getTimeDistance(Date beginDate, Date endDate) {
        Calendar fromCalendar = Calendar.getInstance();
        fromCalendar.setTime(beginDate);
        fromCalendar.set(Calendar.HOUR_OF_DAY, fromCalendar.getMinimum(Calendar.HOUR_OF_DAY));
        fromCalendar.set(Calendar.MINUTE, fromCalendar.getMinimum(Calendar.MINUTE));
        fromCalendar.set(Calendar.SECOND, fromCalendar.getMinimum(Calendar.SECOND));
        fromCalendar.set(Calendar.MILLISECOND, fromCalendar.getMinimum(Calendar.MILLISECOND));
        Calendar toCalendar = Calendar.getInstance();
        toCalendar.setTime(endDate);
        toCalendar.set(Calendar.HOUR_OF_DAY, fromCalendar.getMinimum(Calendar.HOUR_OF_DAY));
        toCalendar.set(Calendar.MINUTE, fromCalendar.getMinimum(Calendar.MINUTE));
        toCalendar.set(Calendar.SECOND, fromCalendar.getMinimum(Calendar.SECOND));
        toCalendar.set(Calendar.MILLISECOND, fromCalendar.getMinimum(Calendar.MILLISECOND));

        long dayDistance = (toCalendar.getTime().getTime() - fromCalendar.getTime().getTime()) / DAY;
        dayDistance = Math.abs(dayDistance);

        return dayDistance;
    }

----2.发送自定义通知--兼容所有版本的通知发送

NotificationManager notificationManager = (NotificationManager) mContext.get().getSystemService(Context.NOTIFICATION_SERVICE);
                try {
                    Notification.Builder builder;
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                        String channelId = BuildConfig.APPLICATION_ID.concat("_call");
                        NotificationChannel notificationChannel = new NotificationChannel(channelId, "未接来电", NotificationManager.IMPORTANCE_DEFAULT);
                        notificationChannel.enableVibration(false);
                        notificationChannel.enableLights(false);
                        notificationChannel.setVibrationPattern(new long[]{0});
                        notificationChannel.setSound(null, null);
                        notificationManager.createNotificationChannel(notificationChannel);
                        builder = new Notification.Builder(context1, channelId);
                    } else {
                        builder = new Notification.Builder(context1);
                    }

                    RemoteViews contentView = new RemoteViews(context1.getPackageName(),R.layout.view_miss_call_remote_view);
                    //重拨
                    Intent intent_tel = new Intent(context1, CallReceiver.class);
                    intent_tel.setAction("com.evan.intent.action.MISS_CALL_RECALL");
                    intent_tel.putExtra("phone",callLogEntity.getNumber());
                    PendingIntent pending_intent_tel = PendingIntent.getBroadcast(context1, 2, intent_tel, PendingIntent.FLAG_UPDATE_CURRENT);
                    contentView.setOnClickPendingIntent(R.id.tel,pending_intent_tel);
                    //打开app的某个页面
                    Intent intent_open = new Intent(context1, StartActivity.class);
                    intent_open.putExtra("phone",callLogEntity.getNumber());
                    PendingIntent pending_open = PendingIntent.getActivity(context1, 2, intent_open, PendingIntent.FLAG_UPDATE_CURRENT);
                    contentView.setOnClickPendingIntent(R.id.open,pending_open);
                    //标题
                    contentView.setTextViewText(R.id.title,callLogEntity.getNumber());
                    //副标题
                    contentView.setTextViewText(R.id.sub_title,"未接来电 - "+new SimpleDateFormat("MM-dd HH:mm").format(callLogEntity.getDate()));
                    //构建通知
                    builder.setTicker("未接来电")
                            .setSmallIcon(R.mipmap.ic_launcher)
                            .setContentIntent(pending_open)
                            .setAutoCancel(true)
                            .setOnlyAlertOnce(true);

                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                        builder.setCustomContentView(contentView);
                    }else {
                        builder.setContent(contentView);
                    }
                    Notification notification = builder.build();
                    notificationManager.notify(MISS_CALL_NOTIFY_ID, notification);
                } catch (Exception e) {
                    e.printStackTrace();
                }

 

注意remoteViews的使用,如果是发送广播必须指定接受类,否则可能收不到广播

 

 

--- 3.点击remoteViews里面的控件通知栏是不会收起来的,通知也不会取消,都需要手动操作

收起通知需要声明此权限:

<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
 public static void cancelNotify(){
        NotificationManager notificationManager = (NotificationManager) CallShowApplication.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.cancel(MISS_CALL_NOTIFY_ID);
        try {
            Object statusBarManager = CallShowApplication.getContext().getSystemService("statusbar");
            Method collapse;
            if (Build.VERSION.SDK_INT <= 16) {
                collapse = statusBarManager.getClass().getMethod("collapse");
            } else {
                collapse = statusBarManager.getClass().getMethod("collapsePanels");
            }
            collapse.invoke(statusBarManager);
        } catch (Exception localException) {
            localException.printStackTrace();
        }
    }

 

 

 

 

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

此类完整代码:

public class CallLogUtil {
    private static final int MISS_CALL_NOTIFY_ID = 10294;
    private static long DAY = 1000 * 60 * 60 * 24;
    private static Box<CallLogEntity> sCallLogBox;
    private static final Uri callUri = CallLog.Calls.CONTENT_URI;
    private static final String[] columns = {
            CallLog.Calls.CACHED_NAME// 通话记录的联系人
            , CallLog.Calls.NUMBER// 通话记录的电话号码
            , CallLog.Calls.DATE// 通话记录的日期
            , CallLog.Calls.DURATION// 通话时长
            , CallLog.Calls.TYPE// 通话类型
            , CallLog.Calls._ID// 通话ID
    };

    /**
     *  这里为了避免剩下文引用泄漏 使用了弱引用
     *  此方法用于查找48小时内未接听的电话,并且没有通知过
     *  查询过程可能耗时,因此使用了Rxjava处理
     */
    public static void refreshCallLog(Context context) {
        final WeakReference<Context> mContext = new WeakReference<>(context);
        Observable.create(new ObservableOnSubscribe<CallLogEntity>() {
            @Override
            public void subscribe(ObservableEmitter<CallLogEntity> emitter) throws Exception {
                if (hasPermission(mContext.get())) {
                    List<CallLogEntity> logs = getDataList(mContext.get());
                    for (CallLogEntity log : logs) {
                        if (log.getType() == CallLog.Calls.MISSED_TYPE && !callIsHandle(log.getCallId())) {
                            emitter.onNext(log);
                            break;
                        }
                    }
                }
                emitter.onComplete();

            }
        }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<CallLogEntity>() {
            @Override
            public void accept(CallLogEntity callLogEntity) throws Exception {
                if (callLogEntity == null || mContext.get() == null) return;
                createNotify(callLogEntity, mContext.get());
            }
        });
    }

    private static void createNotify(CallLogEntity callLogEntity, Context context) {
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        try {
            Notification.Builder builder;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                String channelId = BuildConfig.APPLICATION_ID.concat("_call");
                NotificationChannel notificationChannel = new NotificationChannel(channelId, "未接来电", NotificationManager.IMPORTANCE_DEFAULT);
                notificationChannel.enableVibration(false);
                notificationChannel.enableLights(false);
                notificationChannel.setVibrationPattern(new long[]{0});
                notificationChannel.setSound(null, null);
                notificationManager.createNotificationChannel(notificationChannel);
                builder = new Notification.Builder(context, channelId);
            } else {
                builder = new Notification.Builder(context);
            }

            RemoteViews contentView = new RemoteViews(context.getPackageName(),R.layout.view_miss_call_remote_view);
            //重拨  此处必须指定接收器类,不能隐式发送
            Intent intent_tel = new Intent(context, CallReceiver.class);
            intent_tel.setAction("com.evan.intent.action.MISS_CALL_RECALL");
            intent_tel.putExtra("phone",callLogEntity.getNumber());
            PendingIntent pending_intent_tel = PendingIntent.getBroadcast(context, 2, intent_tel, PendingIntent.FLAG_UPDATE_CURRENT);
            contentView.setOnClickPendingIntent(R.id.tel,pending_intent_tel);
            //跳转app任意界面
            Intent intent_open = new Intent(context, StartActivity.class);
            intent_open.putExtra("phone",callLogEntity.getNumber());
            PendingIntent pending_open = PendingIntent.getActivity(context, 2, intent_open, PendingIntent.FLAG_UPDATE_CURRENT);
            contentView.setOnClickPendingIntent(R.id.open,pending_open);
            //标题
            contentView.setTextViewText(R.id.title,callLogEntity.getNumber());
            //副标题
            contentView.setTextViewText(R.id.sub_title,"未接来电 - "+new SimpleDateFormat("MM-dd HH:mm").format(callLogEntity.getDate()));
            //构建通知
            builder.setTicker("未接来电")
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setContentIntent(pending_open)
                    .setAutoCancel(true)
                    .setOnlyAlertOnce(true);

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                builder.setCustomContentView(contentView);
            }else {
                builder.setContent(contentView);
            }
            Notification notification = builder.build();
            notificationManager.notify(MISS_CALL_NOTIFY_ID, notification);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    //判断是否用读取通话记录权限
    private static boolean hasPermission(Context context) {
        if (context == null) return false;
        if (Build.VERSION.SDK_INT >= 23) {
            //1. 检测是否添加权限   PERMISSION_GRANTED  表示已经授权并可以使用
            if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED) {
                return false;
            } else {//手机为Android6.0的版本,权限已授权可以使用
                return true;
            }
        } else {//手机为Android6.0以前的版本,可以使用
            return true;
        }
    }

    /**
     * 读取数据
     *
     * @return 读取到的数据
     */
    private static List<CallLogEntity> getDataList(Context context) {
        // 1.获得ContentResolver
        ContentResolver resolver = context.getContentResolver();
        // 2.利用ContentResolver的query方法查询通话记录数据库
        /**
         * @param uri 需要查询的URI,(这个URI是ContentProvider提供的)
         * @param projection 需要查询的字段
         * @param selection sql语句where之后的语句
         * @param selectionArgs ?占位符代表的数据
         * @param sortOrder 排序方式
         */
        Cursor cursor = resolver.query(callUri, // 查询通话记录的URI
                columns
                , null, null, CallLog.Calls.DEFAULT_SORT_ORDER// 按照时间逆序排列,最近打的最先显示
        );
        List<CallLogEntity> list = new ArrayList<>();
        if (cursor == null) return list;
        // 3.通过Cursor获得数据
        while (cursor.moveToNext()) {
            String name = cursor.getString(cursor.getColumnIndex(CallLog.Calls.CACHED_NAME));
            String number = cursor.getString(cursor.getColumnIndex(CallLog.Calls.NUMBER));
            long dateLong = cursor.getLong(cursor.getColumnIndex(CallLog.Calls.DATE));
            int duration = cursor.getInt(cursor.getColumnIndex(CallLog.Calls.DURATION));
            int type = cursor.getInt(cursor.getColumnIndex(CallLog.Calls.TYPE));
            //type : CallLog.Calls.MISSED_TYPE 未接 CallLog.Calls.INCOMING_TYPE 打入 CallLog.Calls.OUTGOING_TYPE 拨出
            int id = cursor.getInt(cursor.getColumnIndex(CallLog.Calls._ID));
            if (isMobileNO(number)) {
                long day_lead = getTimeDistance(new Date(dateLong), new Date());
                if (day_lead < 2) {//只显示48小时以内通话记录,防止通话记录数据过多影响加载速度
                    CallLogEntity callLog = new CallLogEntity();
                    callLog.setCallId(id);
                    callLog.setName(name == null ? "未备注联系人" : name);
                    callLog.setNumber(number);
                    callLog.setDate(dateLong);
                    callLog.setTime(duration);
                    callLog.setType(type);
                    list.add(callLog);
                } else {
                    cursor.close();
                    return list;
                }
            }
        }
        cursor.close();
        return list;
    }

    //验证手机号是否正确ֻ
    public static boolean isMobileNO(String s) {
        Pattern p = Pattern.compile("^(13[0-9]|14[57]|15[0-35-9]|17[6-8]|18[0-9])[0-9]{8}$");
        Matcher m = p.matcher(s);
        return m.matches();
    }

    /**
     * 获得两个日期间距多少天
     *
     * @param beginDate
     * @param endDate
     * @return
     */
    public static long getTimeDistance(Date beginDate, Date endDate) {
        Calendar fromCalendar = Calendar.getInstance();
        fromCalendar.setTime(beginDate);
        fromCalendar.set(Calendar.HOUR_OF_DAY, fromCalendar.getMinimum(Calendar.HOUR_OF_DAY));
        fromCalendar.set(Calendar.MINUTE, fromCalendar.getMinimum(Calendar.MINUTE));
        fromCalendar.set(Calendar.SECOND, fromCalendar.getMinimum(Calendar.SECOND));
        fromCalendar.set(Calendar.MILLISECOND, fromCalendar.getMinimum(Calendar.MILLISECOND));
        Calendar toCalendar = Calendar.getInstance();
        toCalendar.setTime(endDate);
        toCalendar.set(Calendar.HOUR_OF_DAY, fromCalendar.getMinimum(Calendar.HOUR_OF_DAY));
        toCalendar.set(Calendar.MINUTE, fromCalendar.getMinimum(Calendar.MINUTE));
        toCalendar.set(Calendar.SECOND, fromCalendar.getMinimum(Calendar.SECOND));
        toCalendar.set(Calendar.MILLISECOND, fromCalendar.getMinimum(Calendar.MILLISECOND));

        long dayDistance = (toCalendar.getTime().getTime() - fromCalendar.getTime().getTime()) / DAY;
        dayDistance = Math.abs(dayDistance);

        return dayDistance;
    }

    /*取消通知并收起通知栏*/
    public static void cancelNotify(){
        NotificationManager notificationManager = (NotificationManager) CallShowApplication.getContext().getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.cancel(MISS_CALL_NOTIFY_ID);
        try {
            Object statusBarManager = CallShowApplication.getContext().getSystemService("statusbar");
            Method collapse;
            if (Build.VERSION.SDK_INT <= 16) {
                collapse = statusBarManager.getClass().getMethod("collapse");
            } else {
                collapse = statusBarManager.getClass().getMethod("collapsePanels");
            }
            collapse.invoke(statusBarManager);
        } catch (Exception localException) {
            localException.printStackTrace();
        }
    }

    /*此方法不必关注,用于初始化数据存储*/
    public static void initBox() {
        sCallLogBox = ObjectBox.get().boxFor(CallLogEntity.class);
    }

    /*判断本次通话是否已经发送过通知*/
    public static boolean callIsHandle(long callId) {
        return sCallLogBox != null &&
                sCallLogBox.query()
                        .equal(CallLogEntity_.callId, callId)
                        .build().count() > 0;
    }

    /*记录本次未接通话已经通知过用户,避免重复提示*/
    public static void putCallHandle(CallLogEntity callLogEntity) {
        if (sCallLogBox == null) initBox();
        sCallLogBox.put(callLogEntity);
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章