Adding duration time to calls

Adding chromometer support in the notification.
More manual update process for in-call UI (Call card).

Change-Id: I9a1598969d92296dc38b7485bd9fb872ad501e7b
diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java
index 552a42a..3598aaf 100644
--- a/InCallUI/src/com/android/incallui/CallCardFragment.java
+++ b/InCallUI/src/com/android/incallui/CallCardFragment.java
@@ -39,11 +39,15 @@
 public class CallCardFragment extends BaseFragment<CallCardPresenter>
         implements CallCardPresenter.CallCardUi {
 
+    // Primary caller info
     private TextView mPhoneNumber;
     private TextView mNumberLabel;
     private TextView mName;
-    private ImageView mPhoto;
     private TextView mCallStateLabel;
+    private ImageView mPhoto;
+    private TextView mElapsedTime;
+
+    // Secondary caller info
     private ViewStub mSecondaryCallInfo;
     private TextView mSecondaryCallName;
     private ImageView mSecondaryPhoto;
@@ -73,6 +77,7 @@
         mSecondaryCallInfo = (ViewStub) view.findViewById(R.id.secondary_call_info);
         mPhoto = (ImageView) view.findViewById(R.id.photo);
         mCallStateLabel = (TextView) view.findViewById(R.id.callStateLabel);
+        mElapsedTime = (TextView) view.findViewById(R.id.elapsedTime);
 
         // This method call will begin the callbacks on CallCardUi. We need to ensure
         // everything needed for the callbacks is set up before this is called.
@@ -189,6 +194,19 @@
         }
     }
 
+    @Override
+    public void setPrimaryCallElapsedTime(boolean show, String callTimeElapsed) {
+        if (show) {
+            if (mElapsedTime.getVisibility() != View.VISIBLE) {
+                AnimationUtils.Fade.show(mElapsedTime);
+            }
+            mElapsedTime.setText(callTimeElapsed);
+        } else {
+            // hide() animation has no effect if it is already hidden.
+            AnimationUtils.Fade.hide(mElapsedTime, View.INVISIBLE);
+        }
+    }
+
     private void setDrawableToImageView(ImageView view, Drawable photo) {
         if (photo == null) {
             photo = view.getResources().getDrawable(R.drawable.picture_unknown);
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
index 8a855c1..330957e 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.text.format.DateUtils;
 
 import com.android.incallui.AudioModeProvider.AudioModeListener;
 import com.android.incallui.ContactInfoCache.ContactCacheEntry;
@@ -35,14 +36,25 @@
 public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
         implements InCallStateListener, AudioModeListener, ContactInfoCacheCallback {
 
+    private static final long CALL_TIME_UPDATE_INTERVAL = 1000; // in milliseconds
+
     private AudioModeProvider mAudioModeProvider;
     private ContactInfoCache mContactInfoCache;
     private Call mPrimary;
     private Call mSecondary;
     private ContactCacheEntry mPrimaryContactInfo;
     private ContactCacheEntry mSecondaryContactInfo;
+    private CallTimer mCallTimer;
 
     public CallCardPresenter() {
+
+        // create the call timer
+        mCallTimer = new CallTimer(new Runnable() {
+            @Override
+            public void run() {
+                updateCallTime();
+            }
+        });
     }
 
     @Override
@@ -66,11 +78,6 @@
         mSecondaryContactInfo = null;
     }
 
-    public void setContactInfoCache(ContactInfoCache cache) {
-        mContactInfoCache = cache;
-        startContactInfoSearch();
-    }
-
     @Override
     public void onStateChange(InCallState state, CallList callList) {
         final CallCardUi ui = getUi();
@@ -106,6 +113,16 @@
         // It is in that callback that we set the values into the Ui.
         startContactInfoSearch();
 
+        // Start/Stop the call time update timer
+        if (mPrimary != null && mPrimary.getState() == Call.State.ACTIVE) {
+            Logger.d(this, "Starting the calltime timer");
+            mCallTimer.start(CALL_TIME_UPDATE_INTERVAL);
+        } else {
+            Logger.d(this, "Canceling the calltime timer");
+            mCallTimer.cancel();
+            ui.setPrimaryCallElapsedTime(false, null);
+        }
+
         // Set the call state
         if (mPrimary != null) {
             final boolean bluetoothOn = mAudioModeProvider != null &&
@@ -129,6 +146,25 @@
     public void onSupportedAudioMode(int mask) {
     }
 
+    public void updateCallTime() {
+        final CallCardUi ui = getUi();
+
+        if (ui == null || mPrimary == null || mPrimary.getState() != Call.State.ACTIVE) {
+            ui.setPrimaryCallElapsedTime(false, null);
+            mCallTimer.cancel();
+        }
+
+        final long callStart = mPrimary.getConnectTime();
+        final long duration = System.currentTimeMillis() - callStart;
+        ui.setPrimaryCallElapsedTime(true, DateUtils.formatElapsedTime(duration / 1000));
+    }
+
+
+    public void setContactInfoCache(ContactInfoCache cache) {
+        mContactInfoCache = cache;
+        startContactInfoSearch();
+    }
+
     /**
      * Starts a query for more contact data for the save primary and secondary calls.
      */
@@ -245,5 +281,6 @@
         void setPrimary(String number, String name, String label, Drawable photo);
         void setSecondary(boolean show, String number, String name, String label, Drawable photo);
         void setCallState(int state, Call.DisconnectCause cause, boolean bluetoothOn);
+        void setPrimaryCallElapsedTime(boolean show, String duration);
     }
 }
diff --git a/InCallUI/src/com/android/incallui/CallTimer.java b/InCallUI/src/com/android/incallui/CallTimer.java
new file mode 100644
index 0000000..dcaa786
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/CallTimer.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+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();
+        final 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();
+        }
+    }
+}
diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
index e2dc42c..0c39dc8 100644
--- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java
+++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
@@ -222,6 +222,13 @@
         builder.setContentTitle(contentTitle);
         builder.setLargeIcon(largeIcon);
 
+        if (call.getState() == Call.State.ACTIVE) {
+            builder.setUsesChronometer(true);
+            builder.setWhen(call.getConnectTime());
+        } else {
+            builder.setUsesChronometer(false);
+        }
+
         // Add special Content for calls that are ongoing
         if (InCallState.INCALL == state || InCallState.OUTGOING == state) {
             addHangupAction(builder);