| /* |
| * Copyright (C) 2016 Google Inc. |
| * |
| * 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. <br> |
| * <br> |
| * <b>Usage Notes:</b><br> |
| * 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"<br> |
| * Basic operation is: mediaPlayOpen("file:///sdcard/MP3/sample.mp3","mytag",true)<br> |
| * 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. <br> |
| * If play=true, then the media file will play immediately, otherwise it will wait for a |
| * {@link #mediaPlayStart mediaPlayerStart} command. <br> |
| * When done with the resource, use {@link #mediaPlayClose mediaPlayClose} <br> |
| * You can get information about the loaded media with {@link #mediaPlayInfo mediaPlayInfo} This |
| * returns a map with the following elements: |
| * <ul> |
| * <li>"tag" - user supplied tag identifying this mediaPlayer. |
| * <li>"loaded" - true if loaded, false if not. If false, no other elements are returned. |
| * <li>"duration" - length of the media in milliseconds. |
| * <li>"position" - current position of playback in milliseconds. Controlled by |
| * {@link #mediaPlaySeek mediaPlaySeek} |
| * <li>"isplaying" - shows whether media is playing. Controlled by {@link #mediaPlayPause |
| * mediaPlayPause} and {@link #mediaPlayStart mediaPlayStart} |
| * <li>"url" - the url used to open this media. |
| * <li>"looping" - whether media will loop. Controlled by {@link #mediaPlaySetLooping |
| * mediaPlaySetLooping} |
| * </ul> |
| * <br> |
| * You can use {@link #mediaPlayList mediaPlayList} to get a list of the loaded tags. <br> |
| * {@link #mediaIsPlaying mediaIsPlaying} will return true if the media is playing.<br> |
| * <b>Events:</b><br> |
| * A playing media will throw a <b>"media"</b> 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. |
| * |
| * @author Robbie Matthews (rjmatthews62@gmail.com) |
| */ |
| |
| public class MediaPlayerFacade extends RpcReceiver implements MediaPlayer.OnCompletionListener { |
| |
| private final Service mService; |
| static private final Map<String, MediaPlayer> mPlayers = new Hashtable<String, MediaPlayer>(); |
| static private final Map<String, String> mUrls = new Hashtable<String, String>(); |
| |
| 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<String, Object> mediaPlayGetInfo( |
| @RpcParameter(name = "tag", description = "string identifying resource") |
| @RpcDefault(value = "default") |
| String tag) { |
| Map<String, Object> result = new HashMap<String, Object>(); |
| 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<String> 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<String, Object> data = new HashMap<String, Object>(); |
| data.put("action", "complete"); |
| data.put("tag", tag); |
| mEventFacade.postEvent("media", data); |
| } |
| } |
| |
| private String getTag(MediaPlayer player) { |
| for (Entry<String, MediaPlayer> m : mPlayers.entrySet()) { |
| if (m.getValue() == player) { |
| return m.getKey(); |
| } |
| } |
| return null; |
| } |
| } |