/* * Copyright (C) 2017 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.googlecode.android_scripting.facade.media; import android.app.Service; import android.media.MediaPlayer; import android.net.Uri; import com.googlecode.android_scripting.facade.EventFacade; import com.googlecode.android_scripting.facade.FacadeManager; import com.googlecode.android_scripting.jsonrpc.RpcReceiver; import com.googlecode.android_scripting.rpc.Rpc; import com.googlecode.android_scripting.rpc.RpcDefault; import com.googlecode.android_scripting.rpc.RpcParameter; import java.util.HashMap; import java.util.Hashtable; import java.util.Map; import java.util.Set; import java.util.Map.Entry; /** * This facade exposes basic mediaPlayer functionality.
*
* Usage Notes:
* mediaPlayerFacade maintains a list of media streams, identified by a user supplied tag. If the * tag is null or blank, this tag defaults to "default"
* Basic operation is: mediaPlayOpen("file:///sdcard/MP3/sample.mp3","mytag",true)
* This will look for a media file at /sdcard/MP3/sample.mp3. Other urls should work. If the file * exists and is playable, this will return a true otherwise it will return a false.
* If play=true, then the media file will play immediately, otherwise it will wait for a * {@link #mediaPlayStart mediaPlayerStart} command.
* When done with the resource, use {@link #mediaPlayClose mediaPlayClose}
* You can get information about the loaded media with {@link #mediaPlayInfo mediaPlayInfo} This * returns a map with the following elements: * *
* You can use {@link #mediaPlayList mediaPlayList} to get a list of the loaded tags.
* {@link #mediaIsPlaying mediaIsPlaying} will return true if the media is playing.
* Events:
* A playing media will throw a "media" event on completion. NB: In remote mode, a media file * will continue playing after the script has finished unless an explicit {@link #mediaPlayClose * mediaPlayClose} event is called. * */ public class MediaPlayerFacade extends RpcReceiver implements MediaPlayer.OnCompletionListener { private final Service mService; static private final Map mPlayers = new Hashtable(); static private final Map mUrls = new Hashtable(); private final EventFacade mEventFacade; public MediaPlayerFacade(FacadeManager manager) { super(manager); mService = manager.getService(); mEventFacade = manager.getReceiver(EventFacade.class); } private String getDefault(String tag) { return (tag == null || tag.equals("")) ? "default" : tag; } private MediaPlayer getPlayer(String tag) { tag = getDefault(tag); return mPlayers.get(tag); } private String getUrl(String tag) { tag = getDefault(tag); return mUrls.get(tag); } private void putMp(String tag, MediaPlayer player, String url) { tag = getDefault(tag); mPlayers.put(tag, player); mUrls.put(tag, url); } private void removeMp(String tag) { tag = getDefault(tag); MediaPlayer player = mPlayers.get(tag); if (player != null) { player.stop(); player.release(); } mPlayers.remove(tag); mUrls.remove(tag); } @Rpc(description = "Open a media file", returns = "true if play successful") public synchronized boolean mediaPlayOpen(@RpcParameter(name = "url", description = "url of media resource") String url, @RpcParameter(name = "tag", description = "string identifying resource") @RpcDefault(value = "default") String tag, @RpcParameter(name = "play", description = "start playing immediately") @RpcDefault(value = "true") Boolean play) { removeMp(tag); MediaPlayer player = getPlayer(tag); player = MediaPlayer.create(mService, Uri.parse(url)); if (player != null) { putMp(tag, player, url); player.setOnCompletionListener(this); if (play) { player.start(); } } return player != null; } @Rpc(description = "pause playing media file", returns = "true if successful") public synchronized boolean mediaPlayPause( @RpcParameter(name = "tag", description = "string identifying resource") @RpcDefault(value = "default") String tag) { MediaPlayer player = getPlayer(tag); if (player == null) { return false; } player.pause(); return true; } @Rpc(description = "Start playing media file.", returns = "true if successful") public synchronized boolean mediaPlayStart( @RpcParameter(name = "tag", description = "string identifying resource") @RpcDefault(value = "default") String tag) { MediaPlayer player = getPlayer(tag); if (player == null) { return false; } player.start(); return mediaIsPlaying(tag); } @Rpc(description = "Stop playing media file.", returns = "true if successful") public synchronized boolean mediaPlayStop( @RpcParameter(name = "tag", description = "string identifying resource") @RpcDefault(value = "default") String tag) { MediaPlayer player = getPlayer(tag); if (player == null) { return false; } player.stop(); return !mediaIsPlaying(tag) && player.getCurrentPosition() == 0; } @Rpc(description = "Stop all players.") public synchronized void mediaPlayStopAll() { for (MediaPlayer p : mPlayers.values()) { p.stop(); } } @Rpc(description = "Seek To Position", returns = "New Position (in ms)") public synchronized int mediaPlaySeek(@RpcParameter(name = "msec", description = "Position in millseconds") Integer msec, @RpcParameter(name = "tag", description = "string identifying resource") @RpcDefault(value = "default") String tag) { MediaPlayer player = getPlayer(tag); if (player == null) { return 0; } player.seekTo(msec); return player.getCurrentPosition(); } @Rpc(description = "Close media file", returns = "true if successful") public synchronized boolean mediaPlayClose( @RpcParameter(name = "tag", description = "string identifying resource") @RpcDefault(value = "default") String tag) throws Exception { if (!mPlayers.containsKey(tag)) { return false; } removeMp(tag); return true; } @Rpc(description = "Checks if media file is playing.", returns = "true if playing") public synchronized boolean mediaIsPlaying( @RpcParameter(name = "tag", description = "string identifying resource") @RpcDefault(value = "default") String tag) { MediaPlayer player = getPlayer(tag); return (player == null) ? false : player.isPlaying(); } @Rpc(description = "Information on current media", returns = "Media Information") public synchronized Map mediaPlayGetInfo( @RpcParameter(name = "tag", description = "string identifying resource") @RpcDefault(value = "default") String tag) { Map result = new HashMap(); MediaPlayer player = getPlayer(tag); result.put("tag", getDefault(tag)); if (player == null) { result.put("loaded", false); } else { result.put("loaded", true); result.put("duration", player.getDuration()); result.put("position", player.getCurrentPosition()); result.put("isplaying", player.isPlaying()); result.put("url", getUrl(tag)); result.put("looping", player.isLooping()); } return result; } @Rpc(description = "Lists currently loaded media", returns = "List of Media Tags") public Set mediaPlayList() { return mPlayers.keySet(); } @Rpc(description = "Set Looping", returns = "True if successful") public synchronized boolean mediaPlaySetLooping(@RpcParameter(name = "enabled") @RpcDefault(value = "true") Boolean enabled, @RpcParameter(name = "tag", description = "string identifying resource") @RpcDefault(value = "default") String tag) { MediaPlayer player = getPlayer(tag); if (player == null) { return false; } player.setLooping(enabled); return true; } @Rpc(description = "Checks if media file is playing.", returns = "true if playing") public synchronized void mediaSetNext( @RpcParameter(name = "tag", description = "string identifying resource") @RpcDefault(value = "default") String tag, @RpcParameter(name = "next", description = "tag of the next track to play.") String next) { MediaPlayer player = getPlayer(tag); MediaPlayer nPlayer = getPlayer(next); if (player == null) { throw new NullPointerException("Non-existent player tag " + tag); } if (nPlayer == null) { throw new NullPointerException("Non-existent player tag " + next); } player.setNextMediaPlayer(nPlayer); } @Override public synchronized void shutdown() { for (String key : mPlayers.keySet()) { MediaPlayer player = mPlayers.get(key); if (player != null) { player.stop(); player.release(); player = null; } } mPlayers.clear(); mUrls.clear(); } @Override public void onCompletion(MediaPlayer player) { String tag = getTag(player); if (tag != null) { Map data = new HashMap(); data.put("action", "complete"); data.put("tag", tag); mEventFacade.postEvent("media", data); } } private String getTag(MediaPlayer player) { for (Entry m : mPlayers.entrySet()) { if (m.getValue() == player) { return m.getKey(); } } return null; } }