Add AudioManager and MediaSession support in sl4a.

Add limited amount of functionalities from both classes
in order to support PTS automation.
Put facade imports in alphabetic order.

Change-Id: Ie385a88e2ef8a9ff0ef088cedc3898a774299ee8
diff --git a/Common/src/com/googlecode/android_scripting/facade/AudioManagerFacade.java b/Common/src/com/googlecode/android_scripting/facade/AudioManagerFacade.java
new file mode 100644
index 0000000..a18d620
--- /dev/null
+++ b/Common/src/com/googlecode/android_scripting/facade/AudioManagerFacade.java
@@ -0,0 +1,68 @@
+

+package com.googlecode.android_scripting.facade;

+

+import android.app.Service;

+import android.content.Context;

+import android.media.AudioManager;

+import android.media.AudioManager.OnAudioFocusChangeListener;

+

+import com.googlecode.android_scripting.jsonrpc.RpcReceiver;

+import com.googlecode.android_scripting.rpc.Rpc;

+

+public class AudioManagerFacade extends RpcReceiver {

+

+    private final Service mService;

+    private final EventFacade mEventFacade;

+    private final AudioManager mAudio;

+    private final OnAudioFocusChangeListener mFocusChangeListener;

+    private boolean mIsFocused;

+

+    public AudioManagerFacade(FacadeManager manager) {

+        super(manager);

+        mService = manager.getService();

+        mEventFacade = manager.getReceiver(EventFacade.class);

+        mAudio = (AudioManager) mService.getSystemService(Context.AUDIO_SERVICE);

+        mFocusChangeListener = new OnAudioFocusChangeListener() {

+            public void onAudioFocusChange(int focusChange) {

+                if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {

+                    mIsFocused = false;

+                } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {

+                    mIsFocused = true;

+                }

+            }

+        };

+    }

+

+    @Rpc(description = "Checks whether any music is active.")

+    public Boolean audioIsMusicActive() {

+        return mAudio.isMusicActive();

+    }

+

+    @Rpc(description = "Checks whether A2DP audio routing to the Bluetooth headset is on or off.")

+    public Boolean audioIsBluetoothA2dpOn() {

+        return mAudio.isBluetoothA2dpOn();

+    }

+

+    @Rpc(description = "Request audio focus for sl4a.")

+    public Boolean audioRequestAudioFocus() {

+        int status = mAudio.requestAudioFocus(mFocusChangeListener,

+                                              AudioManager.STREAM_MUSIC,

+                                              AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);

+        if (status == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {

+            mIsFocused = true;

+            return true;

+        }

+        mIsFocused = false;

+        return false;

+    }

+

+    @Rpc(description = "Whether sl4a has the audio focus or not.")

+    public Boolean audioIsFocused() {

+        return mIsFocused;

+    }

+

+    @Override

+    public void shutdown() {

+        mAudio.abandonAudioFocus(mFocusChangeListener);

+    }

+}

diff --git a/Common/src/com/googlecode/android_scripting/facade/MediaSessionFacade.java b/Common/src/com/googlecode/android_scripting/facade/MediaSessionFacade.java
new file mode 100644
index 0000000..5af0f35
--- /dev/null
+++ b/Common/src/com/googlecode/android_scripting/facade/MediaSessionFacade.java
@@ -0,0 +1,100 @@
+

+package com.googlecode.android_scripting.facade;

+

+import java.util.concurrent.Callable;

+

+import android.app.Service;

+import android.content.Context;

+import android.content.Intent;

+import android.media.session.MediaSession;

+import android.media.session.MediaSessionManager;

+import android.media.session.PlaybackState;

+import android.media.session.MediaSession.Callback;

+import android.view.KeyEvent;

+

+import com.googlecode.android_scripting.Log;

+import com.googlecode.android_scripting.MainThread;

+import com.googlecode.android_scripting.jsonrpc.RpcReceiver;

+import com.googlecode.android_scripting.rpc.Rpc;

+

+public class MediaSessionFacade extends RpcReceiver {

+

+    private final Service mService;

+    private final EventFacade mEventFacade;

+    private MediaSessionManager mMediaManager;

+    private MediaSession mMedia;

+    private Callback mCallback;

+

+    public MediaSessionFacade(FacadeManager manager) {

+        super(manager);

+        mService = manager.getService();

+        mEventFacade = manager.getReceiver(EventFacade.class);

+        Log.d("Creating MediaSession.");

+        mMediaManager = (MediaSessionManager) mService.getSystemService(Context.MEDIA_SESSION_SERVICE);

+        mMedia = mMediaManager.createSession("SL4A");

+        mMedia.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);

+        mMedia.setActive(true);

+        mCallback = new ButtonCallback(mEventFacade);

+    }

+

+    public static class ButtonCallback extends MediaSession.Callback {

+        private final EventFacade mEventFacade;

+        public ButtonCallback(EventFacade eventFacade) {

+            this.mEventFacade = eventFacade;

+        }

+        @Override

+        public void onMediaButtonEvent(Intent mediaButtonIntent) {

+            String action = mediaButtonIntent.getAction();

+            Log.d("Received intent with action " + action);

+            if (action.equals(Intent.ACTION_MEDIA_BUTTON)) {

+                KeyEvent event = (KeyEvent) mediaButtonIntent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);

+                int keyAction = event.getAction();

+                Log.d("Received KeyEvent with action " + keyAction);

+                if (keyAction == KeyEvent.ACTION_DOWN) {

+                    int keyCode = event.getKeyCode();

+                    Log.d("Received ACTION_DOWN with keycode " + keyCode);

+                    if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {

+                        mEventFacade.postEvent("mediaKeyOnPlay", null);

+                    } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {

+                        mEventFacade.postEvent("mediaOnPause", null);

+                    } else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP) {

+                        mEventFacade.postEvent("mediaOnStop", null);

+                    } else if (keyCode == KeyEvent.KEYCODE_MEDIA_NEXT) {

+                        mEventFacade.postEvent("mediaOnNext", null);

+                    } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PREVIOUS) {

+                        mEventFacade.postEvent("mediaOnPrevious", null);

+                    } else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) {

+                        mEventFacade.postEvent("mediaOnPlayPause", null);

+                    }

+                }

+            }

+        }

+    }

+

+    @Rpc(description = "Checks whether any music is active.")

+    public void mediaSessionAddCallback() {

+        MainThread.run(mService, new Callable<Object>() {

+            @Override

+            public Object call() throws Exception {

+                Log.d("Adding callback.");

+                mMedia.addCallback(mCallback);

+                PlaybackState state = new PlaybackState();

+                state.setActions(PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_STOP);

+                state.setState(PlaybackState.STATE_PLAYING, 0, 1);

+                mMedia.setPlaybackState(state);

+                return null;

+            }

+        });

+    }

+

+    @Rpc(description = "Whether current media session is active.")

+    public Boolean mediaSessionIsActive() {

+        return mMedia.isActive();

+    }

+

+    @Override

+    public void shutdown() {

+        mMedia.removeCallback(mCallback);

+        mMedia.release();

+    }

+}

diff --git a/ScriptingLayer/src/com/googlecode/android_scripting/facade/FacadeConfiguration.java b/ScriptingLayer/src/com/googlecode/android_scripting/facade/FacadeConfiguration.java
index 39451bb..cde8f19 100644
--- a/ScriptingLayer/src/com/googlecode/android_scripting/facade/FacadeConfiguration.java
+++ b/ScriptingLayer/src/com/googlecode/android_scripting/facade/FacadeConfiguration.java
@@ -17,6 +17,7 @@
 package com.googlecode.android_scripting.facade;
 
 import com.google.common.collect.Maps;
+import com.googlecode.android_scripting.Log;
 import com.googlecode.android_scripting.facade.bluetooth.BluetoothA2dpFacade;
 import com.googlecode.android_scripting.facade.bluetooth.BluetoothAvrcpFacade;
 import com.googlecode.android_scripting.facade.bluetooth.BluetoothConnectionFacade;
@@ -64,15 +65,21 @@
     sSdkLevel = android.os.Build.VERSION.SDK_INT;
 
     sFacadeClassList = new HashSet<Class<? extends RpcReceiver>>();
+    sFacadeClassList.add(ActivityResultFacade.class);
     sFacadeClassList.add(AndroidFacade.class);
     sFacadeClassList.add(ApplicationManagerFacade.class);
+    sFacadeClassList.add(AudioManagerFacade.class);
+    sFacadeClassList.add(BatteryManagerFacade.class);
     sFacadeClassList.add(CameraFacade.class);
     sFacadeClassList.add(CommonIntentsFacade.class);
     sFacadeClassList.add(ContactsFacade.class);
     sFacadeClassList.add(EventFacade.class);
     sFacadeClassList.add(LocationFacade.class);
     sFacadeClassList.add(PhoneFacade.class);
+    sFacadeClassList.add(PreferencesFacade.class);
+    sFacadeClassList.add(MediaPlayerFacade.class);
     sFacadeClassList.add(MediaRecorderFacade.class);
+    sFacadeClassList.add(MediaSessionFacade.class);
     sFacadeClassList.add(SensorManagerFacade.class);
     sFacadeClassList.add(SettingsFacade.class);
     // Could not get SmsFacade to compile. APIs are deprecated
@@ -82,10 +89,6 @@
     sFacadeClassList.add(WakeLockFacade.class);
     sFacadeClassList.add(WifiManagerFacade.class);
     sFacadeClassList.add(UiFacade.class);
-    sFacadeClassList.add(BatteryManagerFacade.class);
-    sFacadeClassList.add(ActivityResultFacade.class);
-    sFacadeClassList.add(MediaPlayerFacade.class);
-    sFacadeClassList.add(PreferencesFacade.class);
 
     if (sSdkLevel >= 4) {
       sFacadeClassList.add(TextToSpeechFacade.class);
@@ -95,13 +98,13 @@
 
     if (sSdkLevel >= 5) {
       sFacadeClassList.add(BluetoothFacade.class);
-      sFacadeClassList.add(BluetoothRfcommFacade.class);
-      sFacadeClassList.add(BluetoothConnectionFacade.class);
       sFacadeClassList.add(BluetoothA2dpFacade.class);
       sFacadeClassList.add(BluetoothAvrcpFacade.class);
+      sFacadeClassList.add(BluetoothConnectionFacade.class);
       sFacadeClassList.add(BluetoothHspFacade.class);
       sFacadeClassList.add(BluetoothHidFacade.class);
       sFacadeClassList.add(BluetoothMapFacade.class);
+      sFacadeClassList.add(BluetoothRfcommFacade.class);
     }
 
     if (sSdkLevel >= 7) {
@@ -116,8 +119,8 @@
       sFacadeClassList.add(BluetoothLeScanFacade.class);
       sFacadeClassList.add(BluetoothGattFacade.class);
       sFacadeClassList.add(BluetoothLeAdvertiseFacade.class);
-      sFacadeClassList.add(WifiScannerFacade.class);
       sFacadeClassList.add(WifiPasspointManagerFacade.class);
+      sFacadeClassList.add(WifiScannerFacade.class);
 
     }
 
@@ -169,6 +172,7 @@
       if (method.isAnnotationPresent(RpcStartEvent.class)) {
         String eventName = method.getAnnotation(RpcStartEvent.class).value();
         if (map.containsKey(eventName)) {
+          Log.d("Duplicate eventName " + eventName);
           throw new RuntimeException("Duplicate start event method descriptor found.");
         }
         map.put(eventName, descriptor);