You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by he...@apache.org on 2013/05/22 23:10:30 UTC

[1/5] git commit: added Cordova-JS api files

Updated Branches:
  refs/heads/master [created] 79bbaa4d5


added Cordova-JS api files


Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/commit/1603d34f
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/tree/1603d34f
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/diff/1603d34f

Branch: refs/heads/master
Commit: 1603d34fb02ad64d16dd65bcd82b40c0734766d7
Parents: 
Author: hermwong <he...@gmail.com>
Authored: Wed May 22 14:08:42 2013 -0700
Committer: hermwong <he...@gmail.com>
Committed: Wed May 22 14:08:42 2013 -0700

----------------------------------------------------------------------
 www/Media.js      |  195 ++++++++++++++++++++++++++++++++++++++++++++++++
 www/MediaError.js |   55 ++++++++++++++
 2 files changed, 250 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/blob/1603d34f/www/Media.js
----------------------------------------------------------------------
diff --git a/www/Media.js b/www/Media.js
new file mode 100644
index 0000000..083af36
--- /dev/null
+++ b/www/Media.js
@@ -0,0 +1,195 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ *
+*/
+
+var argscheck = require('cordova/argscheck'),
+    utils = require('cordova/utils'),
+    exec = require('cordova/exec');
+
+var mediaObjects = {};
+
+/**
+ * This class provides access to the device media, interfaces to both sound and video
+ *
+ * @constructor
+ * @param src                   The file name or url to play
+ * @param successCallback       The callback to be called when the file is done playing or recording.
+ *                                  successCallback()
+ * @param errorCallback         The callback to be called if there is an error.
+ *                                  errorCallback(int errorCode) - OPTIONAL
+ * @param statusCallback        The callback to be called when media status has changed.
+ *                                  statusCallback(int statusCode) - OPTIONAL
+ */
+var Media = function(src, successCallback, errorCallback, statusCallback) {
+    argscheck.checkArgs('SFFF', 'Media', arguments);
+    this.id = utils.createUUID();
+    mediaObjects[this.id] = this;
+    this.src = src;
+    this.successCallback = successCallback;
+    this.errorCallback = errorCallback;
+    this.statusCallback = statusCallback;
+    this._duration = -1;
+    this._position = -1;
+    exec(null, this.errorCallback, "Media", "create", [this.id, this.src]);
+};
+
+// Media messages
+Media.MEDIA_STATE = 1;
+Media.MEDIA_DURATION = 2;
+Media.MEDIA_POSITION = 3;
+Media.MEDIA_ERROR = 9;
+
+// Media states
+Media.MEDIA_NONE = 0;
+Media.MEDIA_STARTING = 1;
+Media.MEDIA_RUNNING = 2;
+Media.MEDIA_PAUSED = 3;
+Media.MEDIA_STOPPED = 4;
+Media.MEDIA_MSG = ["None", "Starting", "Running", "Paused", "Stopped"];
+
+// "static" function to return existing objs.
+Media.get = function(id) {
+    return mediaObjects[id];
+};
+
+/**
+ * Start or resume playing audio file.
+ */
+Media.prototype.play = function(options) {
+    exec(null, null, "Media", "startPlayingAudio", [this.id, this.src, options]);
+};
+
+/**
+ * Stop playing audio file.
+ */
+Media.prototype.stop = function() {
+    var me = this;
+    exec(function() {
+        me._position = 0;
+    }, this.errorCallback, "Media", "stopPlayingAudio", [this.id]);
+};
+
+/**
+ * Seek or jump to a new time in the track..
+ */
+Media.prototype.seekTo = function(milliseconds) {
+    var me = this;
+    exec(function(p) {
+        me._position = p;
+    }, this.errorCallback, "Media", "seekToAudio", [this.id, milliseconds]);
+};
+
+/**
+ * Pause playing audio file.
+ */
+Media.prototype.pause = function() {
+    exec(null, this.errorCallback, "Media", "pausePlayingAudio", [this.id]);
+};
+
+/**
+ * Get duration of an audio file.
+ * The duration is only set for audio that is playing, paused or stopped.
+ *
+ * @return      duration or -1 if not known.
+ */
+Media.prototype.getDuration = function() {
+    return this._duration;
+};
+
+/**
+ * Get position of audio.
+ */
+Media.prototype.getCurrentPosition = function(success, fail) {
+    var me = this;
+    exec(function(p) {
+        me._position = p;
+        success(p);
+    }, fail, "Media", "getCurrentPositionAudio", [this.id]);
+};
+
+/**
+ * Start recording audio file.
+ */
+Media.prototype.startRecord = function() {
+    exec(null, this.errorCallback, "Media", "startRecordingAudio", [this.id, this.src]);
+};
+
+/**
+ * Stop recording audio file.
+ */
+Media.prototype.stopRecord = function() {
+    exec(null, this.errorCallback, "Media", "stopRecordingAudio", [this.id]);
+};
+
+/**
+ * Release the resources.
+ */
+Media.prototype.release = function() {
+    exec(null, this.errorCallback, "Media", "release", [this.id]);
+};
+
+/**
+ * Adjust the volume.
+ */
+Media.prototype.setVolume = function(volume) {
+    exec(null, null, "Media", "setVolume", [this.id, volume]);
+};
+
+/**
+ * Audio has status update.
+ * PRIVATE
+ *
+ * @param id            The media object id (string)
+ * @param msgType       The 'type' of update this is
+ * @param value         Use of value is determined by the msgType
+ */
+Media.onStatus = function(id, msgType, value) {
+
+    var media = mediaObjects[id];
+
+    if(media) {
+        switch(msgType) {
+            case Media.MEDIA_STATE :
+                media.statusCallback && media.statusCallback(value);
+                if(value == Media.MEDIA_STOPPED) {
+                    media.successCallback && media.successCallback();
+                }
+                break;
+            case Media.MEDIA_DURATION :
+                media._duration = value;
+                break;
+            case Media.MEDIA_ERROR :
+                media.errorCallback && media.errorCallback(value);
+                break;
+            case Media.MEDIA_POSITION :
+                media._position = Number(value);
+                break;
+            default :
+                console.error && console.error("Unhandled Media.onStatus :: " + msgType);
+                break;
+        }
+    }
+    else {
+         console.error && console.error("Received Media.onStatus callback for unknown media :: " + id);
+    }
+
+};
+
+module.exports = Media;

http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/blob/1603d34f/www/MediaError.js
----------------------------------------------------------------------
diff --git a/www/MediaError.js b/www/MediaError.js
new file mode 100644
index 0000000..38002ac
--- /dev/null
+++ b/www/MediaError.js
@@ -0,0 +1,55 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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.
+ *
+*/
+
+/**
+ * This class contains information about any Media errors.
+*/
+/*
+ According to :: http://dev.w3.org/html5/spec-author-view/video.html#mediaerror
+ We should never be creating these objects, we should just implement the interface
+ which has 1 property for an instance, 'code'
+
+ instead of doing :
+    errorCallbackFunction( new MediaError(3,'msg') );
+we should simply use a literal :
+    errorCallbackFunction( {'code':3} );
+ */
+
+ var _MediaError = window.MediaError;
+
+
+if(!_MediaError) {
+    window.MediaError = _MediaError = function(code, msg) {
+        this.code = (typeof code != 'undefined') ? code : null;
+        this.message = msg || ""; // message is NON-standard! do not use!
+    };
+}
+
+_MediaError.MEDIA_ERR_NONE_ACTIVE    = _MediaError.MEDIA_ERR_NONE_ACTIVE    || 0;
+_MediaError.MEDIA_ERR_ABORTED        = _MediaError.MEDIA_ERR_ABORTED        || 1;
+_MediaError.MEDIA_ERR_NETWORK        = _MediaError.MEDIA_ERR_NETWORK        || 2;
+_MediaError.MEDIA_ERR_DECODE         = _MediaError.MEDIA_ERR_DECODE         || 3;
+_MediaError.MEDIA_ERR_NONE_SUPPORTED = _MediaError.MEDIA_ERR_NONE_SUPPORTED || 4;
+// TODO: MediaError.MEDIA_ERR_NONE_SUPPORTED is legacy, the W3 spec now defines it as below.
+// as defined by http://dev.w3.org/html5/spec-author-view/video.html#error-codes
+_MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED = _MediaError.MEDIA_ERR_SRC_NOT_SUPPORTED || 4;
+
+module.exports = _MediaError;


[3/5] git commit: added native iOS files

Posted by he...@apache.org.
added native iOS files


Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/commit/f7293aaf
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/tree/f7293aaf
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/diff/f7293aaf

Branch: refs/heads/master
Commit: f7293aaf912010e7fef9497395982f026f75287c
Parents: 0698245
Author: hermwong <he...@gmail.com>
Authored: Wed May 22 14:09:29 2013 -0700
Committer: hermwong <he...@gmail.com>
Committed: Wed May 22 14:09:29 2013 -0700

----------------------------------------------------------------------
 src/ios/CDVSound.h |  116 ++++++++
 src/ios/CDVSound.m |  702 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 818 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/blob/f7293aaf/src/ios/CDVSound.h
----------------------------------------------------------------------
diff --git a/src/ios/CDVSound.h b/src/ios/CDVSound.h
new file mode 100644
index 0000000..8dcf98e
--- /dev/null
+++ b/src/ios/CDVSound.h
@@ -0,0 +1,116 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+ */
+
+#import <Foundation/Foundation.h>
+#import <AudioToolbox/AudioServices.h>
+#import <AVFoundation/AVFoundation.h>
+
+#import "CDVPlugin.h"
+
+enum CDVMediaError {
+    MEDIA_ERR_ABORTED = 1,
+    MEDIA_ERR_NETWORK = 2,
+    MEDIA_ERR_DECODE = 3,
+    MEDIA_ERR_NONE_SUPPORTED = 4
+};
+typedef NSUInteger CDVMediaError;
+
+enum CDVMediaStates {
+    MEDIA_NONE = 0,
+    MEDIA_STARTING = 1,
+    MEDIA_RUNNING = 2,
+    MEDIA_PAUSED = 3,
+    MEDIA_STOPPED = 4
+};
+typedef NSUInteger CDVMediaStates;
+
+enum CDVMediaMsg {
+    MEDIA_STATE = 1,
+    MEDIA_DURATION = 2,
+    MEDIA_POSITION = 3,
+    MEDIA_ERROR = 9
+};
+typedef NSUInteger CDVMediaMsg;
+
+@interface CDVAudioPlayer : AVAudioPlayer
+{
+    NSString* mediaId;
+}
+@property (nonatomic, copy) NSString* mediaId;
+@end
+
+@interface CDVAudioRecorder : AVAudioRecorder
+{
+    NSString* mediaId;
+}
+@property (nonatomic, copy) NSString* mediaId;
+@end
+
+@interface CDVAudioFile : NSObject
+{
+    NSString* resourcePath;
+    NSURL* resourceURL;
+    CDVAudioPlayer* player;
+    CDVAudioRecorder* recorder;
+    NSNumber* volume;
+}
+
+@property (nonatomic, strong) NSString* resourcePath;
+@property (nonatomic, strong) NSURL* resourceURL;
+@property (nonatomic, strong) CDVAudioPlayer* player;
+@property (nonatomic, strong) NSNumber* volume;
+
+@property (nonatomic, strong) CDVAudioRecorder* recorder;
+
+@end
+
+@interface CDVSound : CDVPlugin <AVAudioPlayerDelegate, AVAudioRecorderDelegate>
+{
+    NSMutableDictionary* soundCache;
+    AVAudioSession* avSession;
+}
+@property (nonatomic, strong) NSMutableDictionary* soundCache;
+@property (nonatomic, strong) AVAudioSession* avSession;
+
+- (void)startPlayingAudio:(CDVInvokedUrlCommand*)command;
+- (void)pausePlayingAudio:(CDVInvokedUrlCommand*)command;
+- (void)stopPlayingAudio:(CDVInvokedUrlCommand*)command;
+- (void)seekToAudio:(CDVInvokedUrlCommand*)command;
+- (void)release:(CDVInvokedUrlCommand*)command;
+- (void)getCurrentPositionAudio:(CDVInvokedUrlCommand*)command;
+
+- (BOOL)hasAudioSession;
+
+// helper methods
+- (NSURL*)urlForRecording:(NSString*)resourcePath;
+- (NSURL*)urlForPlaying:(NSString*)resourcePath;
+- (NSURL*)urlForResource:(NSString*)resourcePath CDV_DEPRECATED(2.5, "Use specific api for playing or recording");
+
+- (CDVAudioFile*)audioFileForResource:(NSString*)resourcePath withId:(NSString*)mediaId CDV_DEPRECATED(2.5, "Use updated audioFileForResource api");
+
+- (CDVAudioFile*)audioFileForResource:(NSString*)resourcePath withId:(NSString*)mediaId doValidation:(BOOL)bValidate forRecording:(BOOL)bRecord;
+- (BOOL)prepareToPlay:(CDVAudioFile*)audioFile withId:(NSString*)mediaId;
+- (NSString*)createMediaErrorWithCode:(CDVMediaError)code message:(NSString*)message;
+
+- (void)startRecordingAudio:(CDVInvokedUrlCommand*)command;
+- (void)stopRecordingAudio:(CDVInvokedUrlCommand*)command;
+
+- (void)setVolume:(CDVInvokedUrlCommand*)command;
+
+@end

http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/blob/f7293aaf/src/ios/CDVSound.m
----------------------------------------------------------------------
diff --git a/src/ios/CDVSound.m b/src/ios/CDVSound.m
new file mode 100644
index 0000000..71eab59
--- /dev/null
+++ b/src/ios/CDVSound.m
@@ -0,0 +1,702 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements.  See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership.  The ASF licenses this file
+ to you 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.
+ */
+
+#import "CDVSound.h"
+#import "NSArray+Comparisons.h"
+#import "CDVJSON.h"
+
+#define DOCUMENTS_SCHEME_PREFIX @"documents://"
+#define HTTP_SCHEME_PREFIX @"http://"
+#define HTTPS_SCHEME_PREFIX @"https://"
+#define RECORDING_WAV @"wav"
+
+@implementation CDVSound
+
+@synthesize soundCache, avSession;
+
+- (NSURL*)urlForResource:(NSString*)resourcePath
+{
+    NSURL* resourceURL = nil;
+    NSString* filePath = nil;
+
+    // first try to find HTTP:// or Documents:// resources
+
+    if ([resourcePath hasPrefix:HTTP_SCHEME_PREFIX] || [resourcePath hasPrefix:HTTPS_SCHEME_PREFIX]) {
+        // if it is a http url, use it
+        NSLog(@"Will use resource '%@' from the Internet.", resourcePath);
+        resourceURL = [NSURL URLWithString:resourcePath];
+    } else if ([resourcePath hasPrefix:DOCUMENTS_SCHEME_PREFIX]) {
+        NSString* docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
+        filePath = [resourcePath stringByReplacingOccurrencesOfString:DOCUMENTS_SCHEME_PREFIX withString:[NSString stringWithFormat:@"%@/", docsPath]];
+        NSLog(@"Will use resource '%@' from the documents folder with path = %@", resourcePath, filePath);
+    } else {
+        // attempt to find file path in www directory
+        filePath = [self.commandDelegate pathForResource:resourcePath];
+        if (filePath != nil) {
+            NSLog(@"Found resource '%@' in the web folder.", filePath);
+        } else {
+            filePath = resourcePath;
+            NSLog(@"Will attempt to use file resource '%@'", filePath);
+        }
+    }
+    // check that file exists for all but HTTP_SHEME_PREFIX
+    if (filePath != nil) {
+        // try to access file
+        NSFileManager* fMgr = [[NSFileManager alloc] init];
+        if (![fMgr fileExistsAtPath:filePath]) {
+            resourceURL = nil;
+            NSLog(@"Unknown resource '%@'", resourcePath);
+        } else {
+            // it's a valid file url, use it
+            resourceURL = [NSURL fileURLWithPath:filePath];
+        }
+    }
+    return resourceURL;
+}
+
+// Maps a url for a resource path for recording
+- (NSURL*)urlForRecording:(NSString*)resourcePath
+{
+    NSURL* resourceURL = nil;
+    NSString* filePath = nil;
+    NSString* docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
+
+    // first check for correct extension
+    if ([[resourcePath pathExtension] caseInsensitiveCompare:RECORDING_WAV] != NSOrderedSame) {
+        resourceURL = nil;
+        NSLog(@"Resource for recording must have %@ extension", RECORDING_WAV);
+    } else if ([resourcePath hasPrefix:DOCUMENTS_SCHEME_PREFIX]) {
+        // try to find Documents:// resources
+        filePath = [resourcePath stringByReplacingOccurrencesOfString:DOCUMENTS_SCHEME_PREFIX withString:[NSString stringWithFormat:@"%@/", docsPath]];
+        NSLog(@"Will use resource '%@' from the documents folder with path = %@", resourcePath, filePath);
+    } else {
+        // if resourcePath is not from FileSystem put in tmp dir, else attempt to use provided resource path
+        NSString* tmpPath = [NSTemporaryDirectory()stringByStandardizingPath];
+        BOOL isTmp = [resourcePath rangeOfString:tmpPath].location != NSNotFound;
+        BOOL isDoc = [resourcePath rangeOfString:docsPath].location != NSNotFound;
+        if (!isTmp && !isDoc) {
+            // put in temp dir
+            filePath = [NSString stringWithFormat:@"%@/%@", tmpPath, resourcePath];
+        } else {
+            filePath = resourcePath;
+        }
+    }
+
+    if (filePath != nil) {
+        // create resourceURL
+        resourceURL = [NSURL fileURLWithPath:filePath];
+    }
+    return resourceURL;
+}
+
+// Maps a url for a resource path for playing
+// "Naked" resource paths are assumed to be from the www folder as its base
+- (NSURL*)urlForPlaying:(NSString*)resourcePath
+{
+    NSURL* resourceURL = nil;
+    NSString* filePath = nil;
+
+    // first try to find HTTP:// or Documents:// resources
+
+    if ([resourcePath hasPrefix:HTTP_SCHEME_PREFIX] || [resourcePath hasPrefix:HTTPS_SCHEME_PREFIX]) {
+        // if it is a http url, use it
+        NSLog(@"Will use resource '%@' from the Internet.", resourcePath);
+        resourceURL = [NSURL URLWithString:resourcePath];
+    } else if ([resourcePath hasPrefix:DOCUMENTS_SCHEME_PREFIX]) {
+        NSString* docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
+        filePath = [resourcePath stringByReplacingOccurrencesOfString:DOCUMENTS_SCHEME_PREFIX withString:[NSString stringWithFormat:@"%@/", docsPath]];
+        NSLog(@"Will use resource '%@' from the documents folder with path = %@", resourcePath, filePath);
+    } else {
+        // attempt to find file path in www directory or LocalFileSystem.TEMPORARY directory
+        filePath = [self.commandDelegate pathForResource:resourcePath];
+        if (filePath == nil) {
+            // see if this exists in the documents/temp directory from a previous recording
+            NSString* testPath = [NSString stringWithFormat:@"%@/%@", [NSTemporaryDirectory()stringByStandardizingPath], resourcePath];
+            if ([[NSFileManager defaultManager] fileExistsAtPath:testPath]) {
+                // inefficient as existence will be checked again below but only way to determine if file exists from previous recording
+                filePath = testPath;
+                NSLog(@"Will attempt to use file resource from LocalFileSystem.TEMPORARY directory");
+            } else {
+                // attempt to use path provided
+                filePath = resourcePath;
+                NSLog(@"Will attempt to use file resource '%@'", filePath);
+            }
+        } else {
+            NSLog(@"Found resource '%@' in the web folder.", filePath);
+        }
+    }
+    // check that file exists for all but HTTP_SHEME_PREFIX
+    if (filePath != nil) {
+        // create resourceURL
+        resourceURL = [NSURL fileURLWithPath:filePath];
+        // try to access file
+        NSFileManager* fMgr = [NSFileManager defaultManager];
+        if (![fMgr fileExistsAtPath:filePath]) {
+            resourceURL = nil;
+            NSLog(@"Unknown resource '%@'", resourcePath);
+        }
+    }
+
+    return resourceURL;
+}
+
+- (CDVAudioFile*)audioFileForResource:(NSString*)resourcePath withId:(NSString*)mediaId
+{
+    // will maintain backwards compatibility with original implementation
+    return [self audioFileForResource:resourcePath withId:mediaId doValidation:YES forRecording:NO];
+}
+
+// Creates or gets the cached audio file resource object
+- (CDVAudioFile*)audioFileForResource:(NSString*)resourcePath withId:(NSString*)mediaId doValidation:(BOOL)bValidate forRecording:(BOOL)bRecord
+{
+    BOOL bError = NO;
+    CDVMediaError errcode = MEDIA_ERR_NONE_SUPPORTED;
+    NSString* errMsg = @"";
+    NSString* jsString = nil;
+    CDVAudioFile* audioFile = nil;
+    NSURL* resourceURL = nil;
+
+    if ([self soundCache] == nil) {
+        [self setSoundCache:[NSMutableDictionary dictionaryWithCapacity:1]];
+    } else {
+        audioFile = [[self soundCache] objectForKey:mediaId];
+    }
+    if (audioFile == nil) {
+        // validate resourcePath and create
+        if ((resourcePath == nil) || ![resourcePath isKindOfClass:[NSString class]] || [resourcePath isEqualToString:@""]) {
+            bError = YES;
+            errcode = MEDIA_ERR_ABORTED;
+            errMsg = @"invalid media src argument";
+        } else {
+            audioFile = [[CDVAudioFile alloc] init];
+            audioFile.resourcePath = resourcePath;
+            audioFile.resourceURL = nil;  // validate resourceURL when actually play or record
+            [[self soundCache] setObject:audioFile forKey:mediaId];
+        }
+    }
+    if (bValidate && (audioFile.resourceURL == nil)) {
+        if (bRecord) {
+            resourceURL = [self urlForRecording:resourcePath];
+        } else {
+            resourceURL = [self urlForPlaying:resourcePath];
+        }
+        if (resourceURL == nil) {
+            bError = YES;
+            errcode = MEDIA_ERR_ABORTED;
+            errMsg = [NSString stringWithFormat:@"Cannot use audio file from resource '%@'", resourcePath];
+        } else {
+            audioFile.resourceURL = resourceURL;
+        }
+    }
+
+    if (bError) {
+        jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:errcode message:errMsg]];
+        [self.commandDelegate evalJs:jsString];
+    }
+
+    return audioFile;
+}
+
+// returns whether or not audioSession is available - creates it if necessary
+- (BOOL)hasAudioSession
+{
+    BOOL bSession = YES;
+
+    if (!self.avSession) {
+        NSError* error = nil;
+
+        self.avSession = [AVAudioSession sharedInstance];
+        if (error) {
+            // is not fatal if can't get AVAudioSession , just log the error
+            NSLog(@"error creating audio session: %@", [[error userInfo] description]);
+            self.avSession = nil;
+            bSession = NO;
+        }
+    }
+    return bSession;
+}
+
+// helper function to create a error object string
+- (NSString*)createMediaErrorWithCode:(CDVMediaError)code message:(NSString*)message
+{
+    NSMutableDictionary* errorDict = [NSMutableDictionary dictionaryWithCapacity:2];
+
+    [errorDict setObject:[NSNumber numberWithUnsignedInt:code] forKey:@"code"];
+    [errorDict setObject:message ? message:@"" forKey:@"message"];
+    return [errorDict JSONString];
+}
+
+- (void)create:(CDVInvokedUrlCommand*)command
+{
+    NSString* mediaId = [command.arguments objectAtIndex:0];
+    NSString* resourcePath = [command.arguments objectAtIndex:1];
+
+    CDVAudioFile* audioFile = [self audioFileForResource:resourcePath withId:mediaId doValidation:NO forRecording:NO];
+
+    if (audioFile == nil) {
+        NSString* errorMessage = [NSString stringWithFormat:@"Failed to initialize Media file with path %@", resourcePath];
+        NSString* jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:MEDIA_ERR_ABORTED message:errorMessage]];
+        [self.commandDelegate evalJs:jsString];
+    } else {
+        CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK];
+        [self.commandDelegate sendPluginResult:result callbackId:command.callbackId];
+    }
+}
+
+- (void)setVolume:(CDVInvokedUrlCommand*)command
+{
+    NSString* callbackId = command.callbackId;
+
+#pragma unused(callbackId)
+    NSString* mediaId = [command.arguments objectAtIndex:0];
+    NSNumber* volume = [command.arguments objectAtIndex:1 withDefault:[NSNumber numberWithFloat:1.0]];
+
+    CDVAudioFile* audioFile;
+    if ([self soundCache] == nil) {
+        [self setSoundCache:[NSMutableDictionary dictionaryWithCapacity:1]];
+    } else {
+        audioFile = [[self soundCache] objectForKey:mediaId];
+        audioFile.volume = volume;
+        if (audioFile.player) {
+            audioFile.player.volume = [volume floatValue];
+        }
+        [[self soundCache] setObject:audioFile forKey:mediaId];
+    }
+
+    // don't care for any callbacks
+}
+
+- (void)startPlayingAudio:(CDVInvokedUrlCommand*)command
+{
+    NSString* callbackId = command.callbackId;
+
+#pragma unused(callbackId)
+    NSString* mediaId = [command.arguments objectAtIndex:0];
+    NSString* resourcePath = [command.arguments objectAtIndex:1];
+    NSDictionary* options = [command.arguments objectAtIndex:2 withDefault:nil];
+
+    BOOL bError = NO;
+    NSString* jsString = nil;
+
+    CDVAudioFile* audioFile = [self audioFileForResource:resourcePath withId:mediaId doValidation:YES forRecording:NO];
+    if ((audioFile != nil) && (audioFile.resourceURL != nil)) {
+        if (audioFile.player == nil) {
+            bError = [self prepareToPlay:audioFile withId:mediaId];
+        }
+        if (!bError) {
+            // audioFile.player != nil  or player was successfully created
+            // get the audioSession and set the category to allow Playing when device is locked or ring/silent switch engaged
+            if ([self hasAudioSession]) {
+                NSError* __autoreleasing err = nil;
+                NSNumber* playAudioWhenScreenIsLocked = [options objectForKey:@"playAudioWhenScreenIsLocked"];
+                BOOL bPlayAudioWhenScreenIsLocked = YES;
+                if (playAudioWhenScreenIsLocked != nil) {
+                    bPlayAudioWhenScreenIsLocked = [playAudioWhenScreenIsLocked boolValue];
+                }
+
+                NSString* sessionCategory = bPlayAudioWhenScreenIsLocked ? AVAudioSessionCategoryPlayback : AVAudioSessionCategorySoloAmbient;
+                [self.avSession setCategory:sessionCategory error:&err];
+                if (![self.avSession setActive:YES error:&err]) {
+                    // other audio with higher priority that does not allow mixing could cause this to fail
+                    NSLog(@"Unable to play audio: %@", [err localizedFailureReason]);
+                    bError = YES;
+                }
+            }
+            if (!bError) {
+                NSLog(@"Playing audio sample '%@'", audioFile.resourcePath);
+                NSNumber* loopOption = [options objectForKey:@"numberOfLoops"];
+                NSInteger numberOfLoops = 0;
+                if (loopOption != nil) {
+                    numberOfLoops = [loopOption intValue] - 1;
+                }
+                audioFile.player.numberOfLoops = numberOfLoops;
+                if (audioFile.player.isPlaying) {
+                    [audioFile.player stop];
+                    audioFile.player.currentTime = 0;
+                }
+                if (audioFile.volume != nil) {
+                    audioFile.player.volume = [audioFile.volume floatValue];
+                }
+
+                [audioFile.player play];
+                double position = round(audioFile.player.duration * 1000) / 1000;
+                jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%.3f);\n%@(\"%@\",%d,%d);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_DURATION, position, @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_STATE, MEDIA_RUNNING];
+                [self.commandDelegate evalJs:jsString];
+            }
+        }
+        if (bError) {
+            /*  I don't see a problem playing previously recorded audio so removing this section - BG
+            NSError* error;
+            // try loading it one more time, in case the file was recorded previously
+            audioFile.player = [[ AVAudioPlayer alloc ] initWithContentsOfURL:audioFile.resourceURL error:&error];
+            if (error != nil) {
+                NSLog(@"Failed to initialize AVAudioPlayer: %@\n", error);
+                audioFile.player = nil;
+            } else {
+                NSLog(@"Playing audio sample '%@'", audioFile.resourcePath);
+                audioFile.player.numberOfLoops = numberOfLoops;
+                [audioFile.player play];
+            } */
+            // error creating the session or player
+            // jsString = [NSString stringWithFormat: @"%@(\"%@\",%d,%d);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR,  MEDIA_ERR_NONE_SUPPORTED];
+            jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:MEDIA_ERR_NONE_SUPPORTED message:nil]];
+            [self.commandDelegate evalJs:jsString];
+        }
+    }
+    // else audioFile was nil - error already returned from audioFile for resource
+    return;
+}
+
+- (BOOL)prepareToPlay:(CDVAudioFile*)audioFile withId:(NSString*)mediaId
+{
+    BOOL bError = NO;
+    NSError* __autoreleasing playerError = nil;
+
+    // create the player
+    NSURL* resourceURL = audioFile.resourceURL;
+
+    if ([resourceURL isFileURL]) {
+        audioFile.player = [[CDVAudioPlayer alloc] initWithContentsOfURL:resourceURL error:&playerError];
+    } else {
+        NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:resourceURL];
+        NSString* userAgent = [self.commandDelegate userAgent];
+        if (userAgent) {
+            [request setValue:userAgent forHTTPHeaderField:@"User-Agent"];
+        }
+
+        NSURLResponse* __autoreleasing response = nil;
+        NSData* data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&playerError];
+        if (playerError) {
+            NSLog(@"Unable to download audio from: %@", [resourceURL absoluteString]);
+        } else {
+            // bug in AVAudioPlayer when playing downloaded data in NSData - we have to download the file and play from disk
+            CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault);
+            CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuidRef);
+            NSString* filePath = [NSString stringWithFormat:@"%@/%@", [NSTemporaryDirectory()stringByStandardizingPath], uuidString];
+            CFRelease(uuidString);
+            CFRelease(uuidRef);
+
+            [data writeToFile:filePath atomically:YES];
+            NSURL* fileURL = [NSURL fileURLWithPath:filePath];
+            audioFile.player = [[CDVAudioPlayer alloc] initWithContentsOfURL:fileURL error:&playerError];
+        }
+    }
+
+    if (playerError != nil) {
+        NSLog(@"Failed to initialize AVAudioPlayer: %@\n", [playerError localizedDescription]);
+        audioFile.player = nil;
+        if (self.avSession) {
+            [self.avSession setActive:NO error:nil];
+        }
+        bError = YES;
+    } else {
+        audioFile.player.mediaId = mediaId;
+        audioFile.player.delegate = self;
+        bError = ![audioFile.player prepareToPlay];
+    }
+    return bError;
+}
+
+- (void)stopPlayingAudio:(CDVInvokedUrlCommand*)command
+{
+    NSString* mediaId = [command.arguments objectAtIndex:0];
+    CDVAudioFile* audioFile = [[self soundCache] objectForKey:mediaId];
+    NSString* jsString = nil;
+
+    if ((audioFile != nil) && (audioFile.player != nil)) {
+        NSLog(@"Stopped playing audio sample '%@'", audioFile.resourcePath);
+        [audioFile.player stop];
+        audioFile.player.currentTime = 0;
+        jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%d);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_STATE, MEDIA_STOPPED];
+    }  // ignore if no media playing
+    if (jsString) {
+        [self.commandDelegate evalJs:jsString];
+    }
+}
+
+- (void)pausePlayingAudio:(CDVInvokedUrlCommand*)command
+{
+    NSString* mediaId = [command.arguments objectAtIndex:0];
+    NSString* jsString = nil;
+    CDVAudioFile* audioFile = [[self soundCache] objectForKey:mediaId];
+
+    if ((audioFile != nil) && (audioFile.player != nil)) {
+        NSLog(@"Paused playing audio sample '%@'", audioFile.resourcePath);
+        [audioFile.player pause];
+        jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%d);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_STATE, MEDIA_PAUSED];
+    }
+    // ignore if no media playing
+
+    if (jsString) {
+        [self.commandDelegate evalJs:jsString];
+    }
+}
+
+- (void)seekToAudio:(CDVInvokedUrlCommand*)command
+{
+    // args:
+    // 0 = Media id
+    // 1 = path to resource
+    // 2 = seek to location in milliseconds
+
+    NSString* mediaId = [command.arguments objectAtIndex:0];
+
+    CDVAudioFile* audioFile = [[self soundCache] objectForKey:mediaId];
+    double position = [[command.arguments objectAtIndex:1] doubleValue];
+
+    if ((audioFile != nil) && (audioFile.player != nil)) {
+        NSString* jsString;
+        double posInSeconds = position / 1000;
+        if (posInSeconds >= audioFile.player.duration) {
+            // The seek is past the end of file.  Stop media and reset to beginning instead of seeking past the end.
+            [audioFile.player stop];
+            audioFile.player.currentTime = 0;
+            jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%.3f);\n%@(\"%@\",%d,%d);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_POSITION, 0.0, @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_STATE, MEDIA_STOPPED];
+            // NSLog(@"seekToEndJsString=%@",jsString);
+        } else {
+            audioFile.player.currentTime = posInSeconds;
+            jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%f);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_POSITION, posInSeconds];
+            // NSLog(@"seekJsString=%@",jsString);
+        }
+
+        [self.commandDelegate evalJs:jsString];
+    }
+}
+
+- (void)release:(CDVInvokedUrlCommand*)command
+{
+    NSString* mediaId = [command.arguments objectAtIndex:0];
+
+    if (mediaId != nil) {
+        CDVAudioFile* audioFile = [[self soundCache] objectForKey:mediaId];
+        if (audioFile != nil) {
+            if (audioFile.player && [audioFile.player isPlaying]) {
+                [audioFile.player stop];
+            }
+            if (audioFile.recorder && [audioFile.recorder isRecording]) {
+                [audioFile.recorder stop];
+            }
+            if (self.avSession) {
+                [self.avSession setActive:NO error:nil];
+                self.avSession = nil;
+            }
+            [[self soundCache] removeObjectForKey:mediaId];
+            NSLog(@"Media with id %@ released", mediaId);
+        }
+    }
+}
+
+- (void)getCurrentPositionAudio:(CDVInvokedUrlCommand*)command
+{
+    NSString* callbackId = command.callbackId;
+    NSString* mediaId = [command.arguments objectAtIndex:0];
+
+#pragma unused(mediaId)
+    CDVAudioFile* audioFile = [[self soundCache] objectForKey:mediaId];
+    double position = -1;
+
+    if ((audioFile != nil) && (audioFile.player != nil) && [audioFile.player isPlaying]) {
+        position = round(audioFile.player.currentTime * 1000) / 1000;
+    }
+    CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDouble:position];
+    NSString* jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%.3f);\n%@", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_POSITION, position, [result toSuccessCallbackString:callbackId]];
+    [self.commandDelegate evalJs:jsString];
+}
+
+- (void)startRecordingAudio:(CDVInvokedUrlCommand*)command
+{
+    NSString* callbackId = command.callbackId;
+
+#pragma unused(callbackId)
+
+    NSString* mediaId = [command.arguments objectAtIndex:0];
+    CDVAudioFile* audioFile = [self audioFileForResource:[command.arguments objectAtIndex:1] withId:mediaId doValidation:YES forRecording:YES];
+    NSString* jsString = nil;
+    NSString* errorMsg = @"";
+
+    if ((audioFile != nil) && (audioFile.resourceURL != nil)) {
+        NSError* __autoreleasing error = nil;
+
+        if (audioFile.recorder != nil) {
+            [audioFile.recorder stop];
+            audioFile.recorder = nil;
+        }
+        // get the audioSession and set the category to allow recording when device is locked or ring/silent switch engaged
+        if ([self hasAudioSession]) {
+            [self.avSession setCategory:AVAudioSessionCategoryRecord error:nil];
+            if (![self.avSession setActive:YES error:&error]) {
+                // other audio with higher priority that does not allow mixing could cause this to fail
+                errorMsg = [NSString stringWithFormat:@"Unable to record audio: %@", [error localizedFailureReason]];
+                // jsString = [NSString stringWithFormat: @"%@(\"%@\",%d,%d);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, MEDIA_ERR_ABORTED];
+                jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:MEDIA_ERR_ABORTED message:errorMsg]];
+                [self.commandDelegate evalJs:jsString];
+                return;
+            }
+        }
+
+        // create a new recorder for each start record
+        audioFile.recorder = [[CDVAudioRecorder alloc] initWithURL:audioFile.resourceURL settings:nil error:&error];
+
+        bool recordingSuccess = NO;
+        if (error == nil) {
+            audioFile.recorder.delegate = self;
+            audioFile.recorder.mediaId = mediaId;
+            recordingSuccess = [audioFile.recorder record];
+            if (recordingSuccess) {
+                NSLog(@"Started recording audio sample '%@'", audioFile.resourcePath);
+                jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%d);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_STATE, MEDIA_RUNNING];
+            }
+        }
+
+        if ((error != nil) || (recordingSuccess == NO)) {
+            if (error != nil) {
+                errorMsg = [NSString stringWithFormat:@"Failed to initialize AVAudioRecorder: %@\n", [error localizedFailureReason]];
+            } else {
+                errorMsg = @"Failed to start recording using AVAudioRecorder";
+            }
+            audioFile.recorder = nil;
+            if (self.avSession) {
+                [self.avSession setActive:NO error:nil];
+            }
+            jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:MEDIA_ERR_ABORTED message:errorMsg]];
+        }
+    } else {
+        // file did not validate
+        NSString* errorMsg = [NSString stringWithFormat:@"Could not record audio at '%@'", audioFile.resourcePath];
+        jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:MEDIA_ERR_ABORTED message:errorMsg]];
+    }
+    if (jsString) {
+        [self.commandDelegate evalJs:jsString];
+    }
+    return;
+}
+
+- (void)stopRecordingAudio:(CDVInvokedUrlCommand*)command
+{
+    NSString* mediaId = [command.arguments objectAtIndex:0];
+
+    CDVAudioFile* audioFile = [[self soundCache] objectForKey:mediaId];
+    NSString* jsString = nil;
+
+    if ((audioFile != nil) && (audioFile.recorder != nil)) {
+        NSLog(@"Stopped recording audio sample '%@'", audioFile.resourcePath);
+        [audioFile.recorder stop];
+        // no callback - that will happen in audioRecorderDidFinishRecording
+    }
+    // ignore if no media recording
+    if (jsString) {
+        [self.commandDelegate evalJs:jsString];
+    }
+}
+
+- (void)audioRecorderDidFinishRecording:(AVAudioRecorder*)recorder successfully:(BOOL)flag
+{
+    CDVAudioRecorder* aRecorder = (CDVAudioRecorder*)recorder;
+    NSString* mediaId = aRecorder.mediaId;
+    CDVAudioFile* audioFile = [[self soundCache] objectForKey:mediaId];
+    NSString* jsString = nil;
+
+    if (audioFile != nil) {
+        NSLog(@"Finished recording audio sample '%@'", audioFile.resourcePath);
+    }
+    if (flag) {
+        jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%d);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_STATE, MEDIA_STOPPED];
+    } else {
+        // jsString = [NSString stringWithFormat: @"%@(\"%@\",%d,%d);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, MEDIA_ERR_DECODE];
+        jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:MEDIA_ERR_DECODE message:nil]];
+    }
+    if (self.avSession) {
+        [self.avSession setActive:NO error:nil];
+    }
+    [self.commandDelegate evalJs:jsString];
+}
+
+- (void)audioPlayerDidFinishPlaying:(AVAudioPlayer*)player successfully:(BOOL)flag
+{
+    CDVAudioPlayer* aPlayer = (CDVAudioPlayer*)player;
+    NSString* mediaId = aPlayer.mediaId;
+    CDVAudioFile* audioFile = [[self soundCache] objectForKey:mediaId];
+    NSString* jsString = nil;
+
+    if (audioFile != nil) {
+        NSLog(@"Finished playing audio sample '%@'", audioFile.resourcePath);
+    }
+    if (flag) {
+        audioFile.player.currentTime = 0;
+        jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%d);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_STATE, MEDIA_STOPPED];
+    } else {
+        // jsString = [NSString stringWithFormat: @"%@(\"%@\",%d,%d);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, MEDIA_ERR_DECODE];
+        jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('cordova/plugin/Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:MEDIA_ERR_DECODE message:nil]];
+    }
+    if (self.avSession) {
+        [self.avSession setActive:NO error:nil];
+    }
+    [self.commandDelegate evalJs:jsString];
+}
+
+- (void)onMemoryWarning
+{
+    [[self soundCache] removeAllObjects];
+    [self setSoundCache:nil];
+    [self setAvSession:nil];
+
+    [super onMemoryWarning];
+}
+
+- (void)dealloc
+{
+    [[self soundCache] removeAllObjects];
+}
+
+- (void)onReset
+{
+    for (CDVAudioFile* audioFile in [[self soundCache] allValues]) {
+        if (audioFile != nil) {
+            if (audioFile.player != nil) {
+                [audioFile.player stop];
+                audioFile.player.currentTime = 0;
+            }
+            if (audioFile.recorder != nil) {
+                [audioFile.recorder stop];
+            }
+        }
+    }
+
+    [[self soundCache] removeAllObjects];
+}
+
+@end
+
+@implementation CDVAudioFile
+
+@synthesize resourcePath;
+@synthesize resourceURL;
+@synthesize player, volume;
+@synthesize recorder;
+
+@end
+@implementation CDVAudioPlayer
+@synthesize mediaId;
+
+@end
+
+@implementation CDVAudioRecorder
+@synthesize mediaId;
+
+@end


[2/5] git commit: added native Android source file

Posted by he...@apache.org.
added native Android source file


Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/commit/0698245f
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/tree/0698245f
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/diff/0698245f

Branch: refs/heads/master
Commit: 0698245ff0b537d9a501abd5120a24a41490fd1c
Parents: 1603d34
Author: hermwong <he...@gmail.com>
Authored: Wed May 22 14:09:08 2013 -0700
Committer: hermwong <he...@gmail.com>
Committed: Wed May 22 14:09:08 2013 -0700

----------------------------------------------------------------------
 src/android/AudioHandler.java |  362 ++++++++++++++++++++++++++++++++++++
 1 files changed, 362 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/blob/0698245f/src/android/AudioHandler.java
----------------------------------------------------------------------
diff --git a/src/android/AudioHandler.java b/src/android/AudioHandler.java
new file mode 100644
index 0000000..20c3c4e
--- /dev/null
+++ b/src/android/AudioHandler.java
@@ -0,0 +1,362 @@
+/*
+       Licensed to the Apache Software Foundation (ASF) under one
+       or more contributor license agreements.  See the NOTICE file
+       distributed with this work for additional information
+       regarding copyright ownership.  The ASF licenses this file
+       to you 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 org.apache.cordova;
+
+import org.apache.cordova.api.CallbackContext;
+import org.apache.cordova.api.CordovaPlugin;
+import org.apache.cordova.api.DataResource;
+import android.content.Context;
+import android.media.AudioManager;
+
+import java.util.ArrayList;
+
+import org.apache.cordova.api.PluginResult;
+import org.json.JSONArray;
+import org.json.JSONException;
+import java.util.HashMap;
+
+/**
+ * This class called by CordovaActivity to play and record audio.
+ * The file can be local or over a network using http.
+ *
+ * Audio formats supported (tested):
+ * 	.mp3, .wav
+ *
+ * Local audio files must reside in one of two places:
+ * 		android_asset: 		file name must start with /android_asset/sound.mp3
+ * 		sdcard:				file name is just sound.mp3
+ */
+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
+
+    /**
+     * Constructor.
+     */
+    public AudioHandler() {
+        this.players = new HashMap<String, AudioPlayer>();
+        this.pausedForPhone = new ArrayList<AudioPlayer>();
+    }
+
+    public String getFilePath(String url, String source){
+        DataResource dataResource = DataResource.initiateNewDataRequestForUri(url, this.webView.pluginManager, cordova, source);
+        return dataResource.getRealFile().getPath();
+    }
+
+    /**
+     * Executes the request and returns PluginResult.
+     * @param action 		The action to execute.
+     * @param args 			JSONArry of arguments for the plugin.
+     * @param callbackContext		The callback context used when calling back into JavaScript.
+     * @return 				A PluginResult object with a status and message.
+     */
+    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
+        PluginResult.Status status = PluginResult.Status.OK;
+        String result = "";
+
+        if (action.equals("startRecordingAudio")) {
+            this.startRecordingAudio(args.getString(0), getFilePath(args.getString(1), "AudioHandler.startRecordingAudio"));
+        }
+        else if (action.equals("stopRecordingAudio")) {
+            this.stopRecordingAudio(args.getString(0));
+        }
+        else if (action.equals("startPlayingAudio")) {
+            this.startPlayingAudio(args.getString(0), getFilePath(args.getString(1), "AudioHandler.startPlayingAudio"));
+        }
+        else if (action.equals("seekToAudio")) {
+            this.seekToAudio(args.getString(0), args.getInt(1));
+        }
+        else if (action.equals("pausePlayingAudio")) {
+            this.pausePlayingAudio(args.getString(0));
+        }
+        else if (action.equals("stopPlayingAudio")) {
+            this.stopPlayingAudio(args.getString(0));
+        } else if (action.equals("setVolume")) {
+           try {
+               this.setVolume(args.getString(0), Float.parseFloat(args.getString(1)));
+           } catch (NumberFormatException nfe) {
+               //no-op
+           }
+        } else if (action.equals("getCurrentPositionAudio")) {
+            float f = this.getCurrentPositionAudio(args.getString(0));
+            callbackContext.sendPluginResult(new PluginResult(status, f));
+            return true;
+        }
+        else if (action.equals("getDurationAudio")) {
+            float f = this.getDurationAudio(args.getString(0), args.getString(1));
+            callbackContext.sendPluginResult(new PluginResult(status, f));
+            return true;
+        }
+        else if (action.equals("create")) {
+            String id = args.getString(0);
+            String src = getFilePath(args.getString(1), "AudioHandler.create");
+            AudioPlayer audio = new AudioPlayer(this, id, src);
+            this.players.put(id, audio);
+        }
+        else if (action.equals("release")) {
+            boolean b = this.release(args.getString(0));
+            callbackContext.sendPluginResult(new PluginResult(status, b));
+            return true;
+        }
+        else { // Unrecognized action.
+            return false;
+        }
+
+        callbackContext.sendPluginResult(new PluginResult(status, result));
+
+        return true;
+    }
+
+    /**
+     * Stop all audio players and recorders.
+     */
+    public void onDestroy() {
+        for (AudioPlayer audio : this.players.values()) {
+            audio.destroy();
+        }
+        this.players.clear();
+    }
+
+    /**
+     * Stop all audio players and recorders on navigate.
+     */
+    @Override
+    public void onReset() {
+        onDestroy();
+    }
+
+    /**
+     * Called when a message is sent to plugin.
+     *
+     * @param id            The message id
+     * @param data          The message data
+     * @return              Object to stop propagation or null
+     */
+    public Object onMessage(String id, Object data) {
+
+        // If phone message
+        if (id.equals("telephone")) {
+
+            // If phone ringing, then pause playing
+            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();
+                    }
+                }
+
+            }
+
+            // 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();
+            }
+        }
+        return null;
+    }
+
+    //--------------------------------------------------------------------------
+    // LOCAL METHODS
+    //--------------------------------------------------------------------------
+
+    /**
+     * Release the audio player instance to save memory.
+     * @param id				The id of the audio player
+     */
+    private boolean release(String id) {
+        if (!this.players.containsKey(id)) {
+            return false;
+        }
+        AudioPlayer audio = this.players.get(id);
+        this.players.remove(id);
+        audio.destroy();
+        return true;
+    }
+
+    /**
+     * Start recording and save the specified file.
+     * @param id				The id of the audio player
+     * @param file				The name of the file
+     */
+    public void startRecordingAudio(String id, String file) {
+        AudioPlayer audio = this.players.get(id);
+        if ( audio == null) {
+            audio = new AudioPlayer(this, id, file);
+            this.players.put(id, audio);
+        }
+        audio.startRecording(file);
+    }
+
+    /**
+     * Stop recording and save to the file specified when recording started.
+     * @param id				The id of the audio player
+     */
+    public void stopRecordingAudio(String id) {
+        AudioPlayer audio = this.players.get(id);
+        if (audio != null) {
+            audio.stopRecording();
+        }
+    }
+
+    /**
+     * Start or resume playing audio file.
+     * @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 = this.players.get(id);
+        if (audio == null) {
+            audio = new AudioPlayer(this, id, file);
+            this.players.put(id, audio);
+        }
+        audio.startPlaying(file);
+    }
+
+    /**
+     * Seek to a location.
+     * @param id				The id of the audio player
+     * @param milliseconds		int: number of milliseconds to skip 1000 = 1 second
+     */
+    public void seekToAudio(String id, int milliseconds) {
+        AudioPlayer audio = this.players.get(id);
+        if (audio != null) {
+            audio.seekToPlaying(milliseconds);
+        }
+    }
+
+    /**
+     * Pause playing.
+     * @param id				The id of the audio player
+     */
+    public void pausePlayingAudio(String id) {
+        AudioPlayer audio = this.players.get(id);
+        if (audio != null) {
+            audio.pausePlaying();
+        }
+    }
+
+    /**
+     * Stop playing the audio file.
+     * @param id				The id of the audio player
+     */
+    public void stopPlayingAudio(String id) {
+        AudioPlayer audio = this.players.get(id);
+        if (audio != null) {
+            audio.stopPlaying();
+            //audio.destroy();
+            //this.players.remove(id);
+        }
+    }
+
+    /**
+     * Get current position of playback.
+     * @param id				The id of the audio player
+     * @return 					position in msec
+     */
+    public float getCurrentPositionAudio(String id) {
+        AudioPlayer audio = this.players.get(id);
+        if (audio != null) {
+            return (audio.getCurrentPosition() / 1000.0f);
+        }
+        return -1;
+    }
+
+    /**
+     * Get the duration of the audio file.
+     * @param id				The id of the audio player
+     * @param file				The name of the audio file.
+     * @return					The duration in msec.
+     */
+    public float getDurationAudio(String id, String file) {
+
+        // Get audio file
+        AudioPlayer audio = this.players.get(id);
+        if (audio != null) {
+            return (audio.getDuration(file));
+        }
+
+        // If not already open, then open the file
+        else {
+            audio = new AudioPlayer(this, id, file);
+            this.players.put(id, audio);
+            return (audio.getDuration(file));
+        }
+    }
+
+    /**
+     * Set the audio device to be used for playback.
+     *
+     * @param output			1=earpiece, 2=speaker
+     */
+    @SuppressWarnings("deprecation")
+    public void setAudioOutputDevice(int output) {
+        AudioManager audiMgr = (AudioManager) this.cordova.getActivity().getSystemService(Context.AUDIO_SERVICE);
+        if (output == 2) {
+            audiMgr.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_SPEAKER, AudioManager.ROUTE_ALL);
+        }
+        else if (output == 1) {
+            audiMgr.setRouting(AudioManager.MODE_NORMAL, AudioManager.ROUTE_EARPIECE, AudioManager.ROUTE_ALL);
+        }
+        else {
+            System.out.println("AudioHandler.setAudioOutputDevice() Error: Unknown output device.");
+        }
+    }
+
+    /**
+     * Get the audio device to be used for playback.
+     *
+     * @return					1=earpiece, 2=speaker
+     */
+    @SuppressWarnings("deprecation")
+    public int getAudioOutputDevice() {
+        AudioManager audiMgr = (AudioManager) this.cordova.getActivity().getSystemService(Context.AUDIO_SERVICE);
+        if (audiMgr.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_EARPIECE) {
+            return 1;
+        }
+        else if (audiMgr.getRouting(AudioManager.MODE_NORMAL) == AudioManager.ROUTE_SPEAKER) {
+            return 2;
+        }
+        else {
+            return -1;
+        }
+    }
+
+    /**
+     * Set the volume for an audio device
+     *
+     * @param id				The id of the audio player
+     * @param volume            Volume to adjust to 0.0f - 1.0f
+     */
+    public void setVolume(String id, float volume) {
+        AudioPlayer audio = this.players.get(id);
+        if (audio != null) {
+            audio.setVolume(volume);
+        } else {
+            System.out.println("AudioHandler.setVolume() Error: Unknown Audio Player " + id);
+        }
+    }
+}


[5/5] git commit: added initial config file

Posted by he...@apache.org.
added initial config file


Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/commit/79bbaa4d
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/tree/79bbaa4d
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/diff/79bbaa4d

Branch: refs/heads/master
Commit: 79bbaa4d5e3139b46010b8342f349e7bb989ab69
Parents: f1b4963
Author: hermwong <he...@gmail.com>
Authored: Wed May 22 14:10:11 2013 -0700
Committer: hermwong <he...@gmail.com>
Committed: Wed May 22 14:10:11 2013 -0700

----------------------------------------------------------------------
 plugin.xml |   38 ++++++++++++++++++++++++++++++++++++++
 1 files changed, 38 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/blob/79bbaa4d/plugin.xml
----------------------------------------------------------------------
diff --git a/plugin.xml b/plugin.xml
new file mode 100644
index 0000000..adee300
--- /dev/null
+++ b/plugin.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0"
+xmlns:android="http://schemas.android.com/apk/res/android"
+id="org.apache.cordova.core.AudioHandler"
+    version="0.1.0">
+    <name>Media</name>
+
+    <js-module src="www/MediaError.js" name="MediaError">
+        <clobbers target="window.MediaError" />
+    </js-module>
+    
+    <js-module src="www/Media.js" name="Media">
+        <clobbers target="window.Media" />
+    </js-module>
+    
+    <!-- android -->
+    <platform name="android">
+        <config-file target="res/xml/config.xml" parent="/cordova/plugins">
+            <plugin name="Media" value="org.apache.cordova.core.AudioHandler"/>
+        </config-file>
+
+        <source-file src="AudioHandler.java" target-dir="org/apache/cordova/core" />
+     </platform>
+     
+     <!-- ios -->
+     <platform name="ios">    
+         <config-file target="config.xml" parent="/*">
+             <feature name="Media">
+                 <param name="ios-package" value="CDVSound" /> 
+             </feature>
+         </config-file>
+         <header-file src="src/ios/CDVSound.h" />
+         <source-file src="src/ios/CDVSound.m" />
+     </platform>
+    
+            
+</plugin>


[4/5] git commit: added README file

Posted by he...@apache.org.
added README file


Project: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/commit/f1b49633
Tree: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/tree/f1b49633
Diff: http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/diff/f1b49633

Branch: refs/heads/master
Commit: f1b49633b18e7366fb65bea2b4b167331a130eff
Parents: f7293aa
Author: hermwong <he...@gmail.com>
Authored: Wed May 22 14:09:50 2013 -0700
Committer: hermwong <he...@gmail.com>
Committed: Wed May 22 14:09:50 2013 -0700

----------------------------------------------------------------------
 README.md |    2 ++
 1 files changed, 2 insertions(+), 0 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-plugin-media/blob/f1b49633/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..b155fc7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,2 @@
+cordova-plugin-media
+--------------------