1. 程式人生 > >Android Notification動態顯示通話時間

Android Notification動態顯示通話時間

基於android N MTK釋放的原始碼
本文主要講解如何在 IncallUI 的notification 上面不停地更新顯示當前已通話多長時間,從而達到和incallUI通話介面上的通話時間一致。

主要思路

  1. 我們需要知道通話建立時的時間,即call 的狀態從 INCOMING或者DIALING 轉變成ACTIVE的時候
  2. 時間每秒鐘都會發生變化,所以我們就需要不停的更新notification的介面,我們這裡是不停的建立和notify同一個notification,已到達更新時間的效果
  3. 我們需要CallTimer執行緒不停的幫我們計算時間,並控制介面的更新

程式碼實現

這裡是在原始碼incallUI目錄下的StatusBarNotifier.java中修改

....省略部分程式碼
    //震動時長,這裡為不振動
    private static final long[] IN_CALL_VIBRATE_PATTERN_NULL = new long[] {0, 0, 0};
    //執行緒隔多久執行一次已ms為單位,這裡為1S
    private static final long CALL_TIME_UPDATE_INTERVAL_MS = 1000;

    //我們需要獲取一些全域性的變數,已到達不停的建立notification並更新同一個notification的UI
    private CallTimer mCallTimer;
    private
Notification.Builder mCopyBuilder; private int mCopyCallState; private ContactCacheEntry mCopyContactInfo; private int mCopyNotificationType; //當前notification的ID,通過這個ID我們可以一直更新同一個notification並且只會彈出一次 private Bitmap mCopyLargeIcon; public StatusBarNotifier(Context context, ContactInfoCache contactInfoCache) { .......省略部分程式碼 //StatusBarNotifier初始化的時候就建立CallTimer物件,用來計算時間和更新UI
mCallTimer = new CallTimer(new Runnable() { @Override public void run() { updateCallTime(); //更新UI的函式 } }); } @Override public void onStateChange(InCallState oldState, InCallState newState, CallList callList) { if (callList.getActiveCall() == null || callList.getActiveCall().getState() != Call.State.ACTIVE){ //當通話結束時需要取消計算時間的執行緒 mCallTimer.cancel(); } } //系統構建notification的方法 private void buildAndSendNotification(Call originalCall, ContactCacheEntry contactInfo) { ....省略部分程式碼 else if (callState == Call.State.ACTIVE && !mIsCallUiShown) { //儲存一個公共的變數到全部變數裡面,方便後面構造新的notification並重新整理 copyInfoFromHeadsUpView(builder, callState, contactInfo, notificationType, largeIcon); //下面是計算顯示的時間 final long callStart = call.getConnectTimeMillis(); final long duration = System.currentTimeMillis() - callStart; //建立一個HeadsUpView顯示在notification上面 RemoteViews inCall = createInCallHeadsUpView(duration / 1000, largeIcon); builder.setContent(inCall); builder.setCustomHeadsUpContentView(inCall); //系統原生的方法最後notify通知 fireNotification(builder, callState, contactInfo, notificationType); //開始我們的計時執行緒 mCallTimer.start(CALL_TIME_UPDATE_INTERVAL_MS); } .....省略部分程式碼 } private RemoteViews createInCallHeadsUpView(Long callDuration, Bitmap contactAvatar) { RemoteViews headsUpView = new RemoteViews(mContext.getPackageName(), R.layout.in_call_headsup); if (null != contactAvatar) { headsUpView.setImageViewBitmap(R.id.in_call_hu_avatar, contactAvatar); } //格式化時間 String callTimeElapsed = DateUtils.formatElapsedTime(callDuration); headsUpView.setTextViewText(R.id.in_call_hu_elapsedTime, callTimeElapsed); return headsUpView; } /*according the mCallTimer to update time data*/ public void updateCallTime() { Call call = CallList.getInstance().getActiveCall(); final long callStart = call.getConnectTimeMillis(); final long duration = System.currentTimeMillis() - callStart; RemoteViews inCall = createInCallHeadsUpView(duration / 1000, mCopyLargeIcon); mCopyBuilder.setContent(inCall); mCopyBuilder.setCustomHeadsUpContentView(inCall); Notification notification = mCopyBuilder.build(); notification.vibrate = IN_CALL_VIBRATE_PATTERN_NULL; mNotificationManager.notify(mCopyNotificationType, notification); } /*Change local variables to global variables*/ private void copyInfoFromHeadsUpView(Notification.Builder builder, int callState, ContactCacheEntry contactInfo, int notificationType, Bitmap largeIcon){ mCopyBuilder = builder; mCopyCallState = callState; mCopyContactInfo = contactInfo; mCopyNotificationType = notificationType; mCopyLargeIcon = largeIcon; } ........省略部分程式碼

CallTimer原始碼如下

package com.android.incallui;

import com.google.common.base.Preconditions;

import android.os.Handler;
import android.os.SystemClock;

/**
 * Helper class used to keep track of events requiring regular intervals.
 */
public class CallTimer extends Handler {
    private Runnable mInternalCallback;
    private Runnable mCallback;
    private long mLastReportedTime;
    private long mInterval;
    private boolean mRunning;

    public CallTimer(Runnable callback) {
        Preconditions.checkNotNull(callback);

        mInterval = 0;
        mLastReportedTime = 0;
        mRunning = false;
        mCallback = callback;
        mInternalCallback = new CallTimerCallback();
    }

    public boolean start(long interval) {
        if (interval <= 0) {
            return false;
        }

        // cancel any previous timer
        cancel();

        mInterval = interval;
        mLastReportedTime = SystemClock.uptimeMillis();

        mRunning = true;
        periodicUpdateTimer();

        return true;
    }

    public void cancel() {
        removeCallbacks(mInternalCallback);
        mRunning = false;
    }

    private void periodicUpdateTimer() {
        if (!mRunning) {
            return;
        }

        final long now = SystemClock.uptimeMillis();
        long nextReport = mLastReportedTime + mInterval;
        while (now >= nextReport) {
            nextReport += mInterval;
        }

        postAtTime(mInternalCallback, nextReport);
        mLastReportedTime = nextReport;

        // Run the callback
        mCallback.run();
    }

    private class CallTimerCallback implements Runnable {
        @Override
        public void run() {
            periodicUpdateTimer();
        }
    }
}