Move headset hook from Telephony to Telecomm. (1/2)

Also sets up the mute and speakerphone statusbar icons triggered by
their respective states (including triggering from the headset hook).

Bug: 15190069
Change-Id: I990110816ec37c168e3646290a538be3cd23be35
diff --git a/src/com/android/telecomm/HeadsetMediaButton.java b/src/com/android/telecomm/HeadsetMediaButton.java
new file mode 100644
index 0000000..db34f59
--- /dev/null
+++ b/src/com/android/telecomm/HeadsetMediaButton.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2014, 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.telecomm;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.media.session.MediaSession;
+import android.media.session.MediaSessionManager;
+import android.view.KeyEvent;
+
+/**
+ * Static class to handle listening to the headset media buttons.
+ */
+final class HeadsetMediaButton {
+
+    /**
+     * Broadcast receiver for the ACTION_MEDIA_BUTTON broadcast intent.
+     *
+     * This functionality isn't lumped in with the other intents in TelecommBroadcastReceiver
+     * because we instantiate this as a totally separate BroadcastReceiver instance, since we need
+     * to manually adjust its IntentFilter's priority (to make sure we get these intents *before*
+     * the media player.)
+     */
+    private final class MediaButtonBroadcastReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
+            Log.v(this, "MediaButtonBroadcastReceiver.onReceive()...  event = %s.", event);
+            if ((event != null) && (event.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK)) {
+                boolean consumed = handleHeadsetHook(event);
+                Log.v(this, "==> handleHeadsetHook(): consumed = %b.", consumed);
+                if (consumed) {
+                    abortBroadcast();
+                }
+            } else {
+                if (CallsManager.getInstance().hasAnyCalls()) {
+                    // If the phone is anything other than completely idle, then we consume and
+                    // ignore any media key events, otherwise it is too easy to accidentally start
+                    // playing music while a phone call is in progress.
+                    Log.v(this, "MediaButtonBroadcastReceiver: consumed");
+                    abortBroadcast();
+                }
+            }
+        }
+    }
+
+    // Types of media button presses
+    static final int SHORT_PRESS = 1;
+    static final int LONG_PRESS = 2;
+
+    private final MediaSession.Callback mSessionCallback = new MediaSession.Callback() {
+        @Override
+        public void onMediaButtonEvent(Intent intent) {
+            KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
+            Log.v(this, "SessionCallback.onMediaButton()...  event = %s.", event);
+            if ((event != null) && (event.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK)) {
+                Log.v(this, "SessionCallback: HEADSETHOOK");
+                boolean consumed = handleHeadsetHook(event);
+                Log.v(this, "==> handleHeadsetHook(): consumed = %b.", consumed);
+            }
+        }
+    };
+
+    private final MediaButtonBroadcastReceiver mMediaButtonReceiver =
+            new MediaButtonBroadcastReceiver();
+
+    private final CallsManager mCallsManager;
+
+    private final MediaSession mSession;
+
+    HeadsetMediaButton(Context context, CallsManager callsManager) {
+        mCallsManager = callsManager;
+
+        // Use a separate receiver (from TelecommBroadcastReceiver) for ACTION_MEDIA_BUTTON
+        // broadcasts, since we need to manually adjust its priority (to make sure we get these
+        // intents *before* the media player.)
+        IntentFilter mediaButtonIntentFilter =
+                new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
+
+        // Make sure we're higher priority than the media player's MediaButtonIntentReceiver (which
+        // currently has the default priority of zero; see apps/Music/AndroidManifest.xml.)
+        mediaButtonIntentFilter.setPriority(1);
+
+        context.registerReceiver(mMediaButtonReceiver, mediaButtonIntentFilter);
+
+        // register the component so it gets priority for calls
+        AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        am.registerMediaButtonEventReceiverForCalls(new ComponentName(context.getPackageName(),
+                MediaButtonBroadcastReceiver.class.getName()));
+
+        // Register a MediaSession but don't enable it yet. This is a
+        // replacement for MediaButtonReceiver
+        MediaSessionManager msm =
+                (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
+        mSession = msm.createSession(HeadsetMediaButton.class.getSimpleName());
+        mSession.addCallback(mSessionCallback);
+        mSession.setFlags(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY
+                | MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
+    }
+
+    /**
+     * Handles the wired headset button while in-call.
+     *
+     * @return true if we consumed the event.
+     */
+    private boolean handleHeadsetHook(KeyEvent event) {
+        Log.d(this, "handleHeadsetHook()...%s %s", event.getAction(), event.getRepeatCount());
+
+        if (event.isLongPress()) {
+            return mCallsManager.onMediaButton(LONG_PRESS);
+        } else if (event.getAction() == KeyEvent.ACTION_UP && event.getRepeatCount() == 0) {
+            return mCallsManager.onMediaButton(SHORT_PRESS);
+        }
+
+        return true;
+    }
+}