diff --git a/README.md b/README.md index 58e89ad7..5edd89c8 100644 --- a/README.md +++ b/README.md @@ -325,6 +325,16 @@ Starts or resumes playing an audio file. media.play(); ``` +### Parameters + +- __playAudioWhenScreenIsLocked__: Pass in this option to the `play` + method to specify whether you want to allow playback when the screen + is locked. If set to `true` (the default value), the state of the + hardware mute button is ignored, e.g.: + + var myMedia = new Media("http://audio.ibeat.org/content/p1rj1s/p1rj1s_-_rockGuitar.mp3") + myMedia.play({ playAudioWhenScreenIsLocked : false }) + ### Quick Example ```js @@ -355,14 +365,6 @@ function playAudio(url) { var myMedia = new Media("http://audio.ibeat.org/content/p1rj1s/p1rj1s_-_rockGuitar.mp3") myMedia.play({ numberOfLoops: 2 }) -- __playAudioWhenScreenIsLocked__: Pass in this option to the `play` - method to specify whether you want to allow playback when the screen - is locked. If set to `true` (the default value), the state of the - hardware mute button is ignored, e.g.: - - var myMedia = new Media("http://audio.ibeat.org/content/p1rj1s/p1rj1s_-_rockGuitar.mp3") - myMedia.play({ playAudioWhenScreenIsLocked : false }) - - __order of file search__: When only a file name or simple path is provided, iOS searches in the `www` directory for the file, then in the application's `documents/tmp` directory: @@ -370,6 +372,10 @@ function playAudio(url) { var myMedia = new Media("audio/beer.mp3") myMedia.play() // first looks for file in www/audio/beer.mp3 then in /documents/tmp/audio/beer.mp3 +### Windows Quirks + +- __playAudioWhenScreenIsLocked__: not supported. + ## media.release Releases the underlying operating system's audio resources. diff --git a/src/android/AudioHandler.java b/src/android/AudioHandler.java index 48211ad4..42725cae 100644 --- a/src/android/AudioHandler.java +++ b/src/android/AudioHandler.java @@ -18,10 +18,17 @@ Licensed to the Apache Software Foundation (ASF) under one */ package org.apache.cordova.media; +import java.util.ArrayList; +import java.util.HashMap; + import org.apache.cordova.CallbackContext; import org.apache.cordova.CordovaPlugin; import org.apache.cordova.CordovaResourceApi; -import org.apache.cordova.PermissionHelper; +import org.apache.cordova.LOG; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; import android.Manifest; import android.content.Context; @@ -29,18 +36,6 @@ Licensed to the Apache Software Foundation (ASF) under one import android.media.AudioManager; import android.media.AudioManager.OnAudioFocusChangeListener; import android.net.Uri; -import android.os.Build; - -import java.security.Permission; -import java.util.ArrayList; - -import org.apache.cordova.LOG; -import org.apache.cordova.PluginResult; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.HashMap; /** * This class called by CordovaActivity to play and record audio. @@ -57,8 +52,10 @@ public class AudioHandler extends CordovaPlugin { public static String TAG = "AudioHandler"; HashMap players; // Audio player object - ArrayList pausedForPhone; // Audio players that were paused when phone call came in - ArrayList pausedForFocus; // Audio players that were paused when focus was lost + ArrayList paused; // Audio players that were paused. Reasons: + boolean pausedForPhone = false; // paused when phone call came in + boolean pausedForFocus = false; // paused when focus was lost + boolean pausedForScreenLock = false; // paused when phone locked the screen private int origVolumeStream = -1; private CallbackContext messageChannel; @@ -77,8 +74,7 @@ public class AudioHandler extends CordovaPlugin { */ public AudioHandler() { this.players = new HashMap(); - this.pausedForPhone = new ArrayList(); - this.pausedForFocus = new ArrayList(); + this.paused = new ArrayList(); } @@ -129,13 +125,20 @@ else if (action.equals("resumeRecordingAudio")) { else if (action.equals("startPlayingAudio")) { String target = args.getString(1); String fileUriStr; + boolean playAudioWhenScreenIsLocked=true; try { Uri targetUri = resourceApi.remapUri(Uri.parse(target)); fileUriStr = targetUri.toString(); } catch (IllegalArgumentException e) { fileUriStr = target; } - this.startPlayingAudio(args.getString(0), FileHelper.stripFileProtocol(fileUriStr)); + try { + JSONObject playArgs = new JSONObject(args.getString(2)); + playAudioWhenScreenIsLocked = playArgs.getBoolean("playAudioWhenScreenIsLocked"); + } catch (JSONException e) { + playAudioWhenScreenIsLocked = true; + } + this.startPlayingAudio(args.getString(0), FileHelper.stripFileProtocol(fileUriStr),playAudioWhenScreenIsLocked); } else if (action.equals("seekToAudio")) { this.seekToAudio(args.getString(0), args.getInt(1)); @@ -225,21 +228,15 @@ public Object onMessage(String id, Object data) { if ("ringing".equals(data) || "offhook".equals(data)) { // Get all audio players and pause them - for (AudioPlayer audio : this.players.values()) { - if (audio.getState() == AudioPlayer.STATE.MEDIA_RUNNING.ordinal()) { - this.pausedForPhone.add(audio); - audio.pausePlaying(); - } - } + pausedForPhone = true; + pauseAll(); } // If phone idle, then resume playing those players we paused else if ("idle".equals(data)) { - for (AudioPlayer audio : this.pausedForPhone) { - audio.startPlaying(null); - } - this.pausedForPhone.clear(); + pausedForPhone = false; + resumePlaying(); } } return null; @@ -250,14 +247,19 @@ else if ("idle".equals(data)) { //-------------------------------------------------------------------------- private AudioPlayer getOrCreatePlayer(String id, String file) { + return getOrCreatePlayer(id,file,true); + } + + private AudioPlayer getOrCreatePlayer(String id, String file, boolean playAudioWhenScreenIsLocked) { AudioPlayer ret = players.get(id); if (ret == null) { if (players.isEmpty()) { onFirstPlayerCreated(); } - ret = new AudioPlayer(this, id, file); + ret = new AudioPlayer(this, id, file, playAudioWhenScreenIsLocked); players.put(id, ret); } + ret.setPlayAudioWhenScreenIsLocked(playAudioWhenScreenIsLocked); return ret; } @@ -315,8 +317,8 @@ public void resumeRecordingAudio(String id) { * @param id The id of the audio player * @param file The name of the audio file. */ - public void startPlayingAudio(String id, String file) { - AudioPlayer audio = getOrCreatePlayer(id, file); + public void startPlayingAudio(String id, String file, boolean playAudioWhenScreenIsLocked) { + AudioPlayer audio = getOrCreatePlayer(id, file, playAudioWhenScreenIsLocked); audio.startPlaying(file); getAudioFocus(); } @@ -400,20 +402,65 @@ else if (output == 1) { } } - public void pauseAllLostFocus() { + /** + * This method pauses all AudioPlayers. + * */ + public void pauseAll() { for (AudioPlayer audio : this.players.values()) { if (audio.getState() == AudioPlayer.STATE.MEDIA_RUNNING.ordinal()) { - this.pausedForFocus.add(audio); + this.paused.add(audio); audio.pausePlaying(); } } } - public void resumeAllGainedFocus() { - for (AudioPlayer audio : this.pausedForFocus) { - audio.startPlaying(null); + /** + * This method pauses all AudioPlayers nod marked with isPlayAudioWhenScreenIsLocked. + * */ + public void pauseOnScreenlock() { + for (AudioPlayer audio : this.players.values()) { + if (audio.getState() == AudioPlayer.STATE.MEDIA_RUNNING.ordinal() && !audio.isPlayAudioWhenScreenIsLocked()) { + this.paused.add(audio); + audio.pausePlaying(); + } } - this.pausedForFocus.clear(); + } + + /** + * This method resumes paused Audioplayers if the Activity has got AudioFocus and the phone is not ringing. + * * All Players if the CordovaActivity has been resumed. + * * Only Players marked with isPlayAudioWhenScreenIsLocked when the activity has not been resumed. + * */ + public void resumePlaying() { + if (!pausedForFocus && !pausedForPhone){ + if (!pausedForScreenLock){ // on Resumed (screen not locked) + for (AudioPlayer audio : this.paused) { + audio.startPlaying(null); + } + this.paused.clear(); + }else{ // on pause (Screen locked) + ArrayList remove = new ArrayList(); + for (AudioPlayer audio : this.paused) { + + if(audio.isPlayAudioWhenScreenIsLocked()){ + audio.startPlaying(null); + remove.add(audio); + } + } + paused.removeAll(remove); + } + } + } + + + public void pauseAllLostFocus() { + pausedForFocus = true; + pauseAll(); + } + + public void resumeAllGainedFocus() { + pausedForFocus = false; + resumePlaying(); } /** @@ -552,6 +599,20 @@ else if(PermissionHelper.hasPermission(this, permissions[RECORD_AUDIO])) } } + + @Override + public void onResume(boolean multitasking) { + super.onResume(multitasking); + pausedForScreenLock = false; + resumePlaying(); + } + + @Override + public void onPause(boolean multitasking) { + pausedForScreenLock = true; + pauseOnScreenlock(); + super.onPause(multitasking); + } /** * Get current amplitude of recording. diff --git a/src/android/AudioPlayer.java b/src/android/AudioPlayer.java index 171cde9f..324848f0 100644 --- a/src/android/AudioPlayer.java +++ b/src/android/AudioPlayer.java @@ -93,17 +93,29 @@ public enum STATE { MEDIA_NONE, private boolean prepareOnly = true; // playback after file prepare flag private int seekOnPrepared = 0; // seek to this location once media is prepared - /** + private boolean playAudioWhenScreenIsLocked = true; // If set to false the playback will be paused when app is paused + + public boolean isPlayAudioWhenScreenIsLocked() { + return playAudioWhenScreenIsLocked; + } + + public void setPlayAudioWhenScreenIsLocked(boolean playAudioWhenScreenIsLocked) { + this.playAudioWhenScreenIsLocked = playAudioWhenScreenIsLocked; + } + + /** * Constructor. * * @param handler The audio handler object * @param id The id of this audio player */ - public AudioPlayer(AudioHandler handler, String id, String file) { + public AudioPlayer(AudioHandler handler, String id, String file, boolean playAudioWhenScreenIsLocked) { this.handler = handler; this.id = id; this.audioFile = file; this.tempFiles = new LinkedList(); + + this.playAudioWhenScreenIsLocked = playAudioWhenScreenIsLocked; } private String generateTempFile() {