Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CB-12146: (android) Adds support for "playAudioWhenScreenIsLocked" already used in iOS #121

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -355,21 +365,17 @@ 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:

var myMedia = new Media("audio/beer.mp3")
myMedia.play() // first looks for file in www/audio/beer.mp3 then in <application>/documents/tmp/audio/beer.mp3

### Windows Quirks

- __playAudioWhenScreenIsLocked__: not supported.

## media.release

Releases the underlying operating system's audio resources.
Expand Down
135 changes: 98 additions & 37 deletions src/android/AudioHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,29 +18,24 @@ 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;
import android.content.pm.PackageManager;
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.
Expand All @@ -57,8 +52,10 @@ public class AudioHandler extends CordovaPlugin {

public static String TAG = "AudioHandler";
HashMap<String, AudioPlayer> players; // Audio player object
ArrayList<AudioPlayer> pausedForPhone; // Audio players that were paused when phone call came in
ArrayList<AudioPlayer> pausedForFocus; // Audio players that were paused when focus was lost
ArrayList<AudioPlayer> 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;

Expand All @@ -77,8 +74,7 @@ public class AudioHandler extends CordovaPlugin {
*/
public AudioHandler() {
this.players = new HashMap<String, AudioPlayer>();
this.pausedForPhone = new ArrayList<AudioPlayer>();
this.pausedForFocus = new ArrayList<AudioPlayer>();
this.paused = new ArrayList<AudioPlayer>();
}


Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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<AudioPlayer> remove = new ArrayList<AudioPlayer>();
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();
}

/**
Expand Down Expand Up @@ -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.
Expand Down
16 changes: 14 additions & 2 deletions src/android/AudioPlayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>();

this.playAudioWhenScreenIsLocked = playAudioWhenScreenIsLocked;
}

private String generateTempFile() {
Expand Down