Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.googlecode.android_scripting.facade.media;
     18 
     19 import java.util.ArrayList;
     20 import java.util.List;
     21 import java.util.concurrent.Callable;
     22 
     23 import android.app.Service;
     24 import android.content.ComponentName;
     25 import android.content.Context;
     26 import android.media.session.MediaController;
     27 import android.media.session.MediaSession;
     28 import android.media.session.MediaSessionManager;
     29 import android.media.session.MediaSessionManager.OnActiveSessionsChangedListener;
     30 import android.media.session.PlaybackState;
     31 import android.media.session.MediaSession.Callback;
     32 import android.view.KeyEvent;
     33 
     34 import com.googlecode.android_scripting.Log;
     35 import com.googlecode.android_scripting.MainThread;
     36 import com.googlecode.android_scripting.facade.EventFacade;
     37 import com.googlecode.android_scripting.facade.FacadeManager;
     38 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
     39 import com.googlecode.android_scripting.rpc.Rpc;
     40 import com.googlecode.android_scripting.rpc.RpcDefault;
     41 import com.googlecode.android_scripting.rpc.RpcParameter;
     42 
     43 /**
     44  * Expose functionalities of MediaSession related classes
     45  * like MediaSession, MediaSessionManager, MediaController.
     46  *
     47  */
     48 public class MediaSessionFacade extends RpcReceiver {
     49 
     50     private final Service mService;
     51     private final EventFacade mEventFacade;
     52     private final MediaSession mSession;
     53     private final MediaSessionManager mManager;
     54     private final OnActiveSessionsChangedListener mSessionListener;
     55     private final Callback mCallback;
     56 
     57     private List<MediaController> mActiveControllers = null;
     58 
     59     public MediaSessionFacade(FacadeManager manager) {
     60         super(manager);
     61         mService = manager.getService();
     62         mEventFacade = manager.getReceiver(EventFacade.class);
     63         Log.d("Creating MediaSession.");
     64         mSession = new MediaSession(mService, "SL4A");
     65         mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
     66         mManager = (MediaSessionManager) mService.getSystemService(Context.MEDIA_SESSION_SERVICE);
     67         mCallback = new MediaButtonCallback(mEventFacade);
     68         mSessionListener = new MediaSessionListener();
     69         mManager.addOnActiveSessionsChangedListener(mSessionListener,
     70                 new ComponentName(mService.getPackageName(), this.getClass().getName()));
     71         mSession.setActive(true);
     72     }
     73 
     74     private class MediaSessionListener implements OnActiveSessionsChangedListener {
     75 
     76         @Override
     77         public void onActiveSessionsChanged(List<MediaController> controllers) {
     78             Log.d("Active MediaSessions have changed. Update current controller.");
     79             int size = controllers.size();
     80             for (int i = 0; i < size; i++) {
     81                 MediaController controller = controllers.get(i);
     82                 long flags = controller.getFlags();
     83                 // We only care about sessions that handle transport controls,
     84                 // which will be true for apps using RCC
     85                 if ((flags & MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS) != 0) {
     86                     Log.d("The current active MediaSessions is " + controller.getTag());
     87                     return;
     88                 }
     89             }
     90         }
     91     }
     92 
     93     @Rpc(description = "Retrieve a list of active sessions.")
     94     public List<String> mediaGetActiveSessions() {
     95         mActiveControllers = mManager.getActiveSessions(null);
     96         List<String> results = new ArrayList<String>();
     97         for (MediaController mc : mActiveControllers) {
     98             results.add(mc.getTag());
     99         }
    100         return results;
    101     }
    102 
    103     @Rpc(description = "Add callback to media session.")
    104     public void mediaSessionAddCallback() {
    105         MainThread.run(mService, new Callable<Object>() {
    106             @Override
    107             public Object call() throws Exception {
    108                 Log.d("Adding callback.");
    109                 mSession.setCallback(mCallback);
    110                 PlaybackState.Builder bob = new PlaybackState.Builder();
    111                 bob.setActions(PlaybackState.ACTION_PLAY |
    112                                PlaybackState.ACTION_PAUSE |
    113                                PlaybackState.ACTION_STOP);
    114                 bob.setState(PlaybackState.STATE_PLAYING, 0, 1);
    115                 mSession.setPlaybackState(bob.build());
    116                 return null;
    117             }
    118         });
    119     }
    120 
    121     @Rpc(description = "Whether current media session is active.")
    122     public Boolean mediaSessionIsActive() {
    123         return mSession.isActive();
    124     }
    125 
    126     @Rpc(description = "Simulate a media key press.")
    127     public void mediaDispatchMediaKeyEvent(String key) {
    128         int keyCode;
    129         if (key.equals("Play")) {
    130             keyCode = KeyEvent.KEYCODE_MEDIA_PLAY;
    131         } else if (key.equals("Pause")) {
    132             keyCode = KeyEvent.KEYCODE_MEDIA_PAUSE;
    133         } else if (key.equals("Stop")) {
    134             keyCode = KeyEvent.KEYCODE_MEDIA_STOP;
    135         } else if (key.equals("Next")) {
    136             keyCode = KeyEvent.KEYCODE_MEDIA_NEXT;
    137         } else if (key.equals("Previous")) {
    138             keyCode = KeyEvent.KEYCODE_MEDIA_PREVIOUS;
    139         } else if (key.equals("Forward")) {
    140             keyCode = KeyEvent.KEYCODE_MEDIA_FAST_FORWARD;
    141         } else if (key.equals("Rewind")) {
    142             keyCode = KeyEvent.KEYCODE_MEDIA_REWIND;
    143         } else {
    144             Log.d("Unrecognized media key.");
    145             return;
    146         }
    147         KeyEvent keyDown = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
    148         KeyEvent keyUp = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
    149         mManager.dispatchMediaKeyEvent(keyDown);
    150         mManager.dispatchMediaKeyEvent(keyUp);
    151     }
    152 
    153     private MediaController getMediaController(int idx) {
    154         return mActiveControllers.get(idx);
    155     }
    156 
    157     @Rpc(description = "Call Play on the currently active media session.")
    158     public void mediaSessionPlay(@RpcParameter(name = "index") @RpcDefault(value = "0")
    159                                  Integer idx) {
    160         getMediaController(idx).getTransportControls().play();
    161     }
    162 
    163     @Rpc(description = "Call Pause on the currently active media session.")
    164     public void mediaSessionPause(@RpcParameter(name = "index") @RpcDefault(value = "0")
    165                                   Integer idx) {
    166         getMediaController(idx).getTransportControls().pause();
    167     }
    168 
    169     @Rpc(description = "Call Stop on the currently active media session.")
    170     public void mediaSessionStop(@RpcParameter(name = "index") @RpcDefault(value = "0")
    171                                  Integer idx) {
    172         getMediaController(idx).getTransportControls().stop();
    173     }
    174 
    175     @Rpc(description = "Call Next on the currently active media session.")
    176     public void mediaSessionNext(@RpcParameter(name = "index") @RpcDefault(value = "0")
    177                                  Integer idx) {
    178         getMediaController(idx).getTransportControls().skipToNext();
    179     }
    180 
    181     @Override
    182     public void shutdown() {
    183         mSession.setCallback(null);
    184         mSession.release();
    185     }
    186 }
    187