Skip to content
Open
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
8 changes: 7 additions & 1 deletion src/android/AudioHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -544,7 +544,13 @@ private void promptForRecord()
}
else if(PermissionHelper.hasPermission(this, permissions[RECORD_AUDIO]))
{
getWritePermission(WRITE_EXTERNAL_STORAGE);
//do not need permission, because it will save to scoped storage directories
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
this.startRecordingAudio(recordId, FileHelper.stripFileProtocol(fileUriStr));
}
else {
getWritePermission(WRITE_EXTERNAL_STORAGE);
}
}
else
{
Expand Down
60 changes: 57 additions & 3 deletions src/android/AudioPlayer.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ Licensed to the Apache Software Foundation (ASF) under one
import java.io.OutputStream;
import java.io.IOException;
import java.util.LinkedList;
import org.apache.cordova.PermissionHelper;
import android.Manifest;
import android.os.Build;

/**
* This class implements the audio playback and recording capabilities used by Cordova.
Expand Down Expand Up @@ -109,7 +112,23 @@ public AudioPlayer(AudioHandler handler, String id, String file) {
private String generateTempFile() {
String tempFileName = null;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
tempFileName = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmprecording-" + System.currentTimeMillis() + ".3gp";

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { //deprecated after sdk 29,

//if we have storage write permission we keep doing it this way
if(hasWritePermission()) {
tempFileName = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmprecording-" + System.currentTimeMillis() + ".3gp";
} else {
//otherwise we get a directory private to the app (scoped storage)
//new way
tempFileName = this.handler.cordova.getActivity().getApplicationContext().getExternalFilesDir(null).getAbsolutePath() + "/tmprecording-" + System.currentTimeMillis() + ".3gp";
}

} else {
//prior to sdk 29, we keep asking permissions as before so we write to the same place as before
tempFileName = Environment.getExternalStorageDirectory().getAbsolutePath() + "/tmprecording-" + System.currentTimeMillis() + ".3gp";
}

} else {
tempFileName = "/data/data/" + handler.cordova.getActivity().getPackageName() + "/cache/tmprecording-" + System.currentTimeMillis() + ".3gp";
}
Expand Down Expand Up @@ -186,10 +205,24 @@ public void moveFile(String file) {

if (!file.startsWith("/")) {
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
file = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + file;

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { //deprecated after sdk 29,
//if we have storage write permission we keep doing it this way
if(hasWritePermission()) {
file = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + file;
} else {
//new path
file = this.handler.cordova.getActivity().getApplicationContext().getExternalFilesDir(null).getAbsolutePath() + File.separator + file;
}
} else {
//same as before
file = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + file;
}

} else {
file = "/data/data/" + handler.cordova.getActivity().getPackageName() + "/cache/" + file;
}

}

int size = this.tempFiles.size();
Expand Down Expand Up @@ -649,6 +682,10 @@ private boolean readyPlayer(String file) {
return true;
}
} else {
if (this.player == null) {
this.player = new MediaPlayer();
this.player.setOnErrorListener(this);
}
//reset the player
this.player.reset();
try {
Expand Down Expand Up @@ -698,7 +735,20 @@ private void loadAudioFile(String file) throws IllegalArgumentException, Securit
fileInputStream.close();
}
else {
this.player.setDataSource(Environment.getExternalStorageDirectory().getPath() + "/" + file);

if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { //deprecated after sdk 29,
//if we have storage write permission we keep doing it this way
if(hasWritePermission()) {
this.player.setDataSource(Environment.getExternalStorageDirectory().getPath() + "/" + file);
} else {
//new way
this.player.setDataSource(this.handler.cordova.getActivity().getApplicationContext().getExternalFilesDir(null).getPath() + "/" + file);
}

} else {
this.player.setDataSource(Environment.getExternalStorageDirectory().getPath() + "/" + file);
}

}
}
this.setState(STATE.MEDIA_STARTING);
Expand Down Expand Up @@ -757,4 +807,8 @@ public float getCurrentAmplitude() {
}
return 0;
}

private boolean hasWritePermission() {
return PermissionHelper.hasPermission(this.handler, Manifest.permission.WRITE_EXTERNAL_STORAGE);
}
}
21 changes: 18 additions & 3 deletions src/ios/CDVSound.m
Original file line number Diff line number Diff line change
Expand Up @@ -380,8 +380,14 @@ - (void)startPlayingAudio:(CDVInvokedUrlCommand*)command
}
if (!bError) {
NSLog(@"Playing audio sample '%@'", audioFile.resourcePath);

BOOL isLocalFile = [audioFile.resourceURL isFileURL] || [resourcePath hasPrefix:CDVFILE_PREFIX] || [resourcePath hasPrefix:DOCUMENTS_SCHEME_PREFIX];

double duration = 0;
if (avPlayer.currentItem && avPlayer.currentItem.asset) {
//avoid play a file:// or a cdvfile:// with the incorrect player (if before we played a remote sound)
//it can happen when we alternate between local & remote URLS.
//as a consequence we don´t hear the sounds.
if (avPlayer.currentItem && avPlayer.currentItem.asset && !isLocalFile) {
CMTime time = avPlayer.currentItem.asset.duration;
duration = CMTimeGetSeconds(time);
if (isnan(duration)) {
Expand Down Expand Up @@ -459,6 +465,9 @@ - (BOOL)prepareToPlay:(CDVAudioFile*)audioFile withId:(NSString*)mediaId
// create the player
NSURL* resourceURL = audioFile.resourceURL;

//check if the file is local or over network
BOOL isLocalFile = [resourceURL isFileURL] || [resourceURL.absoluteString hasPrefix:CDVFILE_PREFIX] || [resourceURL.absoluteString hasPrefix:DOCUMENTS_SCHEME_PREFIX];

if ([resourceURL isFileURL]) {
audioFile.player = [[CDVAudioPlayer alloc] initWithContentsOfURL:resourceURL error:&playerError];
} else {
Expand Down Expand Up @@ -496,8 +505,14 @@ - (BOOL)prepareToPlay:(CDVAudioFile*)audioFile withId:(NSString*)mediaId
} else {
audioFile.player.mediaId = mediaId;
audioFile.player.delegate = self;
if (avPlayer == nil)
bError = ![audioFile.player prepareToPlay];
//if (avPlayer == nil) ??
//one should not be related with the other, what matters is the player that we are going to use & the resource location
//without this check if we previously played a remote sound we have a avPlayer instance with an currentItem, therefore the
//prepareToPlay above would not be called, and the current sound could end up on the wrong player
if(isLocalFile) {
bError = ![audioFile.player prepareToPlay];
}

}
return bError;
}
Expand Down