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);
    }
}

 

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