Adding hangup capability to in-call notification.

Change-Id: I7ba4dd0824f41d9989e36bff10fbfca68596134e
diff --git a/InCallUI/AndroidManifest.xml b/InCallUI/AndroidManifest.xml
index cc0ffec..0b603e0 100644
--- a/InCallUI/AndroidManifest.xml
+++ b/InCallUI/AndroidManifest.xml
@@ -24,6 +24,7 @@
     <uses-permission android:name="android.permission.VIBRATE"/>
 
     <application
+            android:name="InCallApp"
             android:label="@string/inCallLabel"
             android:supportsRtl="true">
 
@@ -47,5 +48,12 @@
             </intent-filter>
         </service>
 
+        <!-- BroadcastReceiver for receiving Intents from Notification mechanism. -->
+        <receiver android:name="InCallApp$NotificationBroadcastReceiver" exported="false">
+            <intent-filter>
+                <action android:name="com.android.incallui.ACTION_HANG_UP_ONGOING_CALL" />
+            </intent-filter>
+        </receiver>
+
     </application>
 </manifest>
diff --git a/InCallUI/res/drawable-hdpi/stat_sys_phone_call_end.png b/InCallUI/res/drawable-hdpi/stat_sys_phone_call_end.png
new file mode 100644
index 0000000..bc74937
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/stat_sys_phone_call_end.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/stat_sys_phone_call_end.png b/InCallUI/res/drawable-mdpi/stat_sys_phone_call_end.png
new file mode 100644
index 0000000..15e7e67
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/stat_sys_phone_call_end.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/stat_sys_phone_call_end.png b/InCallUI/res/drawable-xhdpi/stat_sys_phone_call_end.png
new file mode 100644
index 0000000..b0b1bc1
--- /dev/null
+++ b/InCallUI/res/drawable-xhdpi/stat_sys_phone_call_end.png
Binary files differ
diff --git a/InCallUI/src/com/android/incallui/InCallApp.java b/InCallUI/src/com/android/incallui/InCallApp.java
new file mode 100644
index 0000000..299baf3
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/InCallApp.java
@@ -0,0 +1,71 @@
+/*
+ * 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 android.app.Application;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Configuration;
+
+/**
+ * Top-level Application class for the InCall app.
+ */
+public class InCallApp extends Application {
+
+    /**
+     * Intent Action used for hanging up the current call from Notification bar. This will
+     * choose first ringing call, first active call, or first background call (typically in
+     * HOLDING state).
+     */
+    public static final String ACTION_HANG_UP_ONGOING_CALL =
+            "com.android.incallui.ACTION_HANG_UP_ONGOING_CALL";
+
+    public InCallApp() {
+    }
+
+    @Override
+    public void onCreate() {
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newConfig) {
+        // TODO(klp): See what configuration changes we need for klp
+        super.onConfigurationChanged(newConfig);
+    }
+
+    /**
+     * Accepts broadcast Intents which will be prepared by {@link StatusBarNotifier} and thus
+     * sent from framework's notification mechanism (which is outside Phone context).
+     * This should be visible from outside, but shouldn't be in "exported" state.
+     *
+     */
+    public static class NotificationBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            Logger.v(this, "Broadcast from Notification: ", action);
+
+            if (action.equals(ACTION_HANG_UP_ONGOING_CALL)) {
+                // TODO: Commands of this nature should exist in the CallList or a
+                //       CallController class that has access to CallCommandClient and
+                //       CallList.
+                InCallPresenter.getInstance().hangUpOngoingCall();
+            }
+        }
+    }
+}
diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java
index 17889d0..c56d980 100644
--- a/InCallUI/src/com/android/incallui/InCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/InCallPresenter.java
@@ -149,6 +149,20 @@
     }
 
     /**
+     * Hangs up any active or outgoing calls.
+     */
+    public void hangUpOngoingCall() {
+        Call call = mCallList.getOutgoingCall();
+        if (call == null) {
+            call = mCallList.getActiveOrBackgroundCall();
+        }
+
+        if (call != null) {
+            CallCommandClient.getInstance().disconnectCall(call.getCallId());
+        }
+    }
+
+    /**
      * When the state of in-call changes, this is the first method to get called. It determines if
      * the UI needs to be started or finished depending on the new state and does it.
      */
diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
index 49c8de3..857b6ce 100644
--- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java
+++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.content.Intent;
 
+import com.android.incallui.InCallApp.NotificationBroadcastReceiver;
 import com.android.incallui.InCallPresenter.InCallState;
 import com.android.services.telephony.common.Call;
 
@@ -36,6 +37,7 @@
 
     private final Context mContext;
     private final NotificationManager mNotificationManager;
+    private InCallState mInCallState = InCallState.HIDDEN;
 
     public StatusBarNotifier(Context context) {
         Preconditions.checkNotNull(context);
@@ -128,37 +130,36 @@
      *   handling a new incoming call for the first time.
      */
     private void updateInCallNotification(boolean allowFullScreenIntent, InCallState state) {
-        int resId;
         Logger.d(this, "updateInCallNotification(allowFullScreenIntent = "
                      + allowFullScreenIntent + ")...");
 
+        // First, we dont need to continue issuing new notifications if the state hasn't
+        // changed from the last time we did this.
+        if (mInCallState == state) {
+            return;
+        }
+        mInCallState = state;
+
         if (!state.isConnectingOrConnected()) {
             cancelInCall();
             return;
         }
 
-        final PendingIntent inCallPendingIntent = getLaunchPendingIntent();
+        final PendingIntent inCallPendingIntent = createLaunchPendingIntent();
         final Notification.Builder builder = getNotificationBuilder();
+        builder.setContentIntent(inCallPendingIntent);
 
         /*
-         * Set up the Intents that will get fires when the user interacts with the notificaiton.
+         * Set up the Intents that will get fired when the user interacts with the notificaiton.
          */
-        builder.setContentIntent(inCallPendingIntent);
         if (allowFullScreenIntent) {
             configureFullScreenIntent(builder, inCallPendingIntent);
         }
 
-        // TODO(klp): Does this need to include OUTGOING state as well?
-        if (InCallState.INCALL == state) {
-            addActiveCallIntents(builder);
-        }
-
-
         /*
          * Set up notification Ui.
          */
-        setUpNotificationUi(builder);
-
+        setUpNotification(builder, state);
 
         /*
          * Fire off the notification
@@ -171,7 +172,13 @@
     /**
      * Sets up the main Ui for the notification
      */
-    private void setUpNotificationUi(Notification.Builder builder) {
+    private void setUpNotification(Notification.Builder builder, InCallState state) {
+
+        // Add special Content for calls that are ongoing
+        if (InCallState.INCALL == state || InCallState.OUTGOING == state) {
+            addActiveCallIntents(builder);
+        }
+
         // set the content
         builder.setContentText(mContext.getString(R.string.notification_ongoing_call));
         builder.setSmallIcon(R.drawable.stat_sys_phone_call);
@@ -179,11 +186,11 @@
 
     private void addActiveCallIntents(Notification.Builder builder) {
         Logger.i(this, "Will show \"hang-up\" action in the ongoing active call Notification");
+
         // TODO: use better asset.
-        // TODO(klp): uncomment this for "hang-up" capability
-        //builder.addAction(R.drawable.stat_sys_phone_call_end,
-        //        mContext.getText(R.string.notification_action_end_call),
-        //        PhoneGlobals.createHangUpOngoingCallPendingIntent(mContext));
+        builder.addAction(R.drawable.stat_sys_phone_call_end,
+                mContext.getText(R.string.notification_action_end_call),
+                createHangUpOngoingCallPendingIntent(mContext));
     }
 
     /**
@@ -239,7 +246,7 @@
         return builder;
     }
 
-    private PendingIntent getLaunchPendingIntent() {
+    private PendingIntent createLaunchPendingIntent() {
 
         final Intent intent = new Intent(Intent.ACTION_MAIN, null);
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
@@ -256,4 +263,14 @@
 
         return inCallPendingIntent;
     }
+
+    /**
+     * Returns PendingIntent for hanging up ongoing phone call. This will typically be used from
+     * Notification context.
+     */
+    private static PendingIntent createHangUpOngoingCallPendingIntent(Context context) {
+        final Intent intent = new Intent(InCallApp.ACTION_HANG_UP_ONGOING_CALL, null,
+                context, NotificationBroadcastReceiver.class);
+        return PendingIntent.getBroadcast(context, 0, intent, 0);
+    }
 }