Browse files

added media plugin

  • Loading branch information...
1 parent 98231b1 commit b0b47d8a6110f75d571bcc3df810a907404be44a @playerx playerx committed Feb 26, 2014
Showing with 6,621 additions and 8 deletions.
  1. +16 −1 Mobile/platforms/ios/.staging/www/cordova_plugins.js
  2. +197 −0 Mobile/platforms/ios/.staging/www/plugins/org.apache.cordova.media/www/Media.js
  3. +57 −0 Mobile/platforms/ios/.staging/www/plugins/org.apache.cordova.media/www/MediaError.js
  4. +6 −0 Mobile/platforms/ios/Pitching.xcodeproj/project.pbxproj
  5. BIN ...s/Pitching.xcodeproj/project.xcworkspace/xcuserdata/ez.xcuserdatad/UserInterfaceState.xcuserstate
  6. +0 −2 Mobile/platforms/ios/Pitching/Pitching-Info.plist
  7. +116 −0 Mobile/platforms/ios/Pitching/Plugins/org.apache.cordova.media/CDVSound.h
  8. +725 −0 Mobile/platforms/ios/Pitching/Plugins/org.apache.cordova.media/CDVSound.m
  9. +6 −3 Mobile/platforms/ios/Pitching/config.xml
  10. +17 −1 Mobile/platforms/ios/www/cordova_plugins.js
  11. +197 −0 Mobile/platforms/ios/www/plugins/org.apache.cordova.media/www/Media.js
  12. +57 −0 Mobile/platforms/ios/www/plugins/org.apache.cordova.media/www/MediaError.js
  13. +1 −1 Mobile/plugins/ios.json
  14. +1 −0 Mobile/plugins/org.apache.cordova.media/.fetch.json
  15. +202 −0 Mobile/plugins/org.apache.cordova.media/LICENSE
  16. +22 −0 Mobile/plugins/org.apache.cordova.media/README.md
  17. +59 −0 Mobile/plugins/org.apache.cordova.media/RELEASENOTES.md
  18. +492 −0 Mobile/plugins/org.apache.cordova.media/doc/index.md
  19. +14 −0 Mobile/plugins/org.apache.cordova.media/package.json
  20. +141 −0 Mobile/plugins/org.apache.cordova.media/plugin.xml
  21. +357 −0 Mobile/plugins/org.apache.cordova.media/src/android/AudioHandler.java
  22. +555 −0 Mobile/plugins/org.apache.cordova.media/src/android/AudioPlayer.java
  23. +38 −0 Mobile/plugins/org.apache.cordova.media/src/android/FileHelper.java
  24. +237 −0 Mobile/plugins/org.apache.cordova.media/src/blackberry10/index.js
  25. +116 −0 Mobile/plugins/org.apache.cordova.media/src/ios/CDVSound.h
  26. +725 −0 Mobile/plugins/org.apache.cordova.media/src/ios/CDVSound.m
  27. +202 −0 Mobile/plugins/org.apache.cordova.media/src/tizen/MediaProxy.js
  28. +128 −0 Mobile/plugins/org.apache.cordova.media/src/ubuntu/media.cpp
  29. +267 −0 Mobile/plugins/org.apache.cordova.media/src/ubuntu/media.h
  30. +183 −0 Mobile/plugins/org.apache.cordova.media/src/windows8/MediaProxy.js
  31. +647 −0 Mobile/plugins/org.apache.cordova.media/src/wp/AudioPlayer.cs
  32. +590 −0 Mobile/plugins/org.apache.cordova.media/src/wp/Media.cs
  33. +195 −0 Mobile/plugins/org.apache.cordova.media/www/Media.js
  34. +55 −0 Mobile/plugins/org.apache.cordova.media/www/MediaError.js
View
17 Mobile/platforms/ios/.staging/www/cordova_plugins.js
@@ -27,6 +27,20 @@ module.exports = [
"merges": [
"navigator.notification"
]
+ },
+ {
+ "file": "plugins/org.apache.cordova.media/www/MediaError.js",
+ "id": "org.apache.cordova.media.MediaError",
+ "clobbers": [
+ "window.MediaError"
+ ]
+ },
+ {
+ "file": "plugins/org.apache.cordova.media/www/Media.js",
+ "id": "org.apache.cordova.media.Media",
+ "clobbers": [
+ "window.Media"
+ ]
}
];
module.exports.metadata =
@@ -36,7 +50,8 @@ module.exports.metadata =
"org.apache.cordova.dialogs": "0.2.6",
"org.apache.cordova.inappbrowser": "0.3.1",
"org.apache.cordova.vibration": "0.3.7",
- "com.phonegap.plugins.jokutils": "0.1.0"
+ "com.phonegap.plugins.jokutils": "0.1.0",
+ "org.apache.cordova.media": "0.2.8"
}
// BOTTOM OF METADATA
});
View
197 Mobile/platforms/ios/.staging/www/plugins/org.apache.cordova.media/www/Media.js
@@ -0,0 +1,197 @@
+cordova.define("org.apache.cordova.media.Media", function(require, exports, module) { /*
+ *
+ * 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;
+
+});
View
57 Mobile/platforms/ios/.staging/www/plugins/org.apache.cordova.media/www/MediaError.js
@@ -0,0 +1,57 @@
+cordova.define("org.apache.cordova.media.MediaError", function(require, exports, module) { /*
+ *
+ * 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;
+
+});
View
6 Mobile/platforms/ios/Pitching.xcodeproj/project.pbxproj
@@ -49,6 +49,7 @@
D4A0D8761607E02300AEF8BB /* Default-568h@2x~iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = D4A0D8751607E02300AEF8BB /* Default-568h@2x~iphone.png */; };
D57D1EC11CAC45D3B7714FEA /* CDVNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 1F63DA9BC7424E01938AC80C /* CDVNotification.m */; };
D581DC6A958341A69F98448E /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 507F2F98A3DC452986EC1572 /* AudioToolbox.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
+ F9F5B2D0226B4365843BC6C5 /* CDVSound.m in Sources */ = {isa = PBXBuildFile; fileRef = B4EFD945C99247F3B433F791 /* CDVSound.m */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -121,7 +122,9 @@
8FC38F018F58463993289B02 /* CDVVibration.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVVibration.h; path = org.apache.cordova.vibration/CDVVibration.h; sourceTree = "<group>"; };
900710A7FDA04B88A7EF69E7 /* CDVDevice.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVDevice.h; path = org.apache.cordova.device/CDVDevice.h; sourceTree = "<group>"; };
9D83BD41A2244D4FB5AC300C /* CDVJokUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVJokUtils.m; path = com.phonegap.plugins.jokutils/CDVJokUtils.m; sourceTree = "<group>"; };
+ B4EFD945C99247F3B433F791 /* CDVSound.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVSound.m; path = org.apache.cordova.media/CDVSound.m; sourceTree = "<group>"; };
D4A0D8751607E02300AEF8BB /* Default-568h@2x~iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-568h@2x~iphone.png"; sourceTree = "<group>"; };
+ E22BF64466FE4BBBB978770E /* CDVSound.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVSound.h; path = org.apache.cordova.media/CDVSound.h; sourceTree = "<group>"; };
EB87FDF21871DA7A0020F90C /* merges */ = {isa = PBXFileReference; lastKnownFileType = folder; name = merges; path = ../../merges; sourceTree = "<group>"; };
EB87FDF31871DA8E0020F90C /* www */ = {isa = PBXFileReference; lastKnownFileType = folder; name = www; path = ../../www; sourceTree = "<group>"; };
EB87FDF41871DAF40020F90C /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = config.xml; path = ../../config.xml; sourceTree = "<group>"; };
@@ -266,6 +269,8 @@
8FC38F018F58463993289B02 /* CDVVibration.h */,
9D83BD41A2244D4FB5AC300C /* CDVJokUtils.m */,
EFB57FDA910F479398941482 /* CDVJokUtils.h */,
+ B4EFD945C99247F3B433F791 /* CDVSound.m */,
+ E22BF64466FE4BBBB978770E /* CDVSound.h */,
);
name = Plugins;
path = Pitching/Plugins;
@@ -467,6 +472,7 @@
01AF4C42EC354FE496ED9AD8 /* CDVInAppBrowser.m in Sources */,
755383D60B9547579784DE90 /* CDVVibration.m in Sources */,
8B4D213D204A4827944A6025 /* CDVJokUtils.m in Sources */,
+ F9F5B2D0226B4365843BC6C5 /* CDVSound.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
View
BIN ...ng.xcodeproj/project.xcworkspace/xcuserdata/ez.xcuserdatad/UserInterfaceState.xcuserstate
Binary file not shown.
View
2 Mobile/platforms/ios/Pitching/Pitching-Info.plist
@@ -32,8 +32,6 @@
<true/>
<key>NSMainNibFile</key>
<string></string>
- <key>NSMainNibFile~ipad</key>
- <string></string>
<key>UIStatusBarHidden</key>
<true/>
<key>UISupportedInterfaceOrientations</key>
View
116 Mobile/platforms/ios/Pitching/Plugins/org.apache.cordova.media/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 <Cordova/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
View
725 Mobile/platforms/ios/Pitching/Plugins/org.apache.cordova.media/CDVSound.m
@@ -0,0 +1,725 @@
+/*
+ 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 <Cordova/NSArray+Comparisons.h>
+#import <Cordova/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('org.apache.cordova.media.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('org.apache.cordova.media.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]];
+
+ if ([self soundCache] != nil) {
+ CDVAudioFile* audioFile = [[self soundCache] objectForKey:mediaId];
+ if (audioFile != nil) {
+ 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('org.apache.cordova.media.Media').onStatus", mediaId, MEDIA_DURATION, position, @"cordova.require('org.apache.cordova.media.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('org.apache.cordova.media.Media').onStatus", mediaId, MEDIA_ERROR, MEDIA_ERR_NONE_SUPPORTED];
+ jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('org.apache.cordova.media.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('org.apache.cordova.media.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('org.apache.cordova.media.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('org.apache.cordova.media.Media').onStatus", mediaId, MEDIA_POSITION, 0.0, @"cordova.require('org.apache.cordova.media.Media').onStatus", mediaId, MEDIA_STATE, MEDIA_STOPPED];
+ // NSLog(@"seekToEndJsString=%@",jsString);
+ } else {
+ audioFile.player.currentTime = posInSeconds;
+ jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%f);", @"cordova.require('org.apache.cordova.media.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('org.apache.cordova.media.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];
+ __block NSString* jsString = nil;
+ __block NSString* errorMsg = @"";
+
+ if ((audioFile != nil) && (audioFile.resourceURL != nil)) {
+ void (^startRecording)(void) = ^{
+ 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('org.apache.cordova.media.Media').onStatus", mediaId, MEDIA_ERROR, MEDIA_ERR_ABORTED];
+ jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('org.apache.cordova.media.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('org.apache.cordova.media.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('org.apache.cordova.media.Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:MEDIA_ERR_ABORTED message:errorMsg]];
+ }
+ };
+
+ SEL rrpSel = NSSelectorFromString(@"requestRecordPermission:");
+ if ([self hasAudioSession] && [self.avSession respondsToSelector:rrpSel])
+ {
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+ [self.avSession performSelector:rrpSel withObject:^(BOOL granted){
+ if (granted) {
+ startRecording();
+ } else {
+ NSString* msg = @"Error creating audio session, microphone permission denied.";
+ NSLog(@"%@", msg);
+ audioFile.recorder = nil;
+ if (self.avSession) {
+ [self.avSession setActive:NO error:nil];
+ }
+ jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('org.apache.cordova.media.Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:MEDIA_ERR_ABORTED message:msg]];
+ [self.commandDelegate evalJs:jsString];
+ }
+ }];
+#pragma clang diagnostic pop
+ } else {
+ startRecording();
+ }
+
+ } else {
+ // file did not validate
+ NSString* errorMsg = [NSString stringWithFormat:@"Could not record audio at '%@'", audioFile.resourcePath];
+ jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('org.apache.cordova.media.Media').onStatus", mediaId, MEDIA_ERROR, [self createMediaErrorWithCode:MEDIA_ERR_ABORTED message:errorMsg]];
+ [self.commandDelegate evalJs:jsString];
+ }
+}
+
+- (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('org.apache.cordova.media.Media').onStatus", mediaId, MEDIA_STATE, MEDIA_STOPPED];
+ } else {
+ // jsString = [NSString stringWithFormat: @"%@(\"%@\",%d,%d);", @"cordova.require('org.apache.cordova.media.Media').onStatus", mediaId, MEDIA_ERROR, MEDIA_ERR_DECODE];
+ jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('org.apache.cordova.media.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('org.apache.cordova.media.Media').onStatus", mediaId, MEDIA_STATE, MEDIA_STOPPED];
+ } else {
+ // jsString = [NSString stringWithFormat: @"%@(\"%@\",%d,%d);", @"cordova.require('org.apache.cordova.media.Media').onStatus", mediaId, MEDIA_ERROR, MEDIA_ERR_DECODE];
+ jsString = [NSString stringWithFormat:@"%@(\"%@\",%d,%@);", @"cordova.require('org.apache.cordova.media.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
View
9 Mobile/platforms/ios/Pitching/config.xml
@@ -19,6 +19,9 @@
<feature name="LocalStorage">
<param name="ios-package" value="CDVLocalStorage" />
</feature>
+ <feature name="JokUtils">
+ <param name="ios-package" value="CDVJokUtils" />
+ </feature>
<feature name="Device">
<param name="ios-package" value="CDVDevice" />
</feature>
@@ -28,6 +31,9 @@
<feature name="InAppBrowser">
<param name="ios-package" value="CDVInAppBrowser" />
</feature>
+ <feature name="Media">
+ <param name="ios-package" value="CDVSound" />
+ </feature>
<feature name="Vibration">
<param name="ios-package" value="CDVVibration" />
</feature>
@@ -45,7 +51,4 @@
<preference name="Orientation" value="landscape" />
<preference name="DisallowOverscroll" value="true" />
<preference name="HideKeyboardFormAccessoryBar" value="true" />
- <feature name="JokUtils">
- <param name="ios-package" value="CDVJokUtils" />
- </feature>
</widget>
View
18 Mobile/platforms/ios/www/cordova_plugins.js
@@ -27,6 +27,20 @@ module.exports = [
"merges": [
"navigator.notification"
]
+ },
+ {
+ "file": "plugins/org.apache.cordova.media/www/MediaError.js",
+ "id": "org.apache.cordova.media.MediaError",
+ "clobbers": [
+ "window.MediaError"
+ ]
+ },
+ {
+ "file": "plugins/org.apache.cordova.media/www/Media.js",
+ "id": "org.apache.cordova.media.Media",
+ "clobbers": [
+ "window.Media"
+ ]
}
];
module.exports.metadata =
@@ -35,7 +49,9 @@ module.exports.metadata =
"org.apache.cordova.device": "0.2.8",
"org.apache.cordova.dialogs": "0.2.6",
"org.apache.cordova.inappbrowser": "0.3.1",
- "org.apache.cordova.vibration": "0.3.7"
+ "org.apache.cordova.vibration": "0.3.7",
+ "com.phonegap.plugins.jokutils": "0.1.0",
+ "org.apache.cordova.media": "0.2.8"
}
// BOTTOM OF METADATA
});
View
197 Mobile/platforms/ios/www/plugins/org.apache.cordova.media/www/Media.js
@@ -0,0 +1,197 @@
+cordova.define("org.apache.cordova.media.Media", function(require, exports, module) { /*
+ *
+ * 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;
+
+});
View
57 Mobile/platforms/ios/www/plugins/org.apache.cordova.media/www/MediaError.js
@@ -0,0 +1,57 @@
+cordova.define("org.apache.cordova.media.MediaError", function(require, exports, module) { /*
+ *
+ * 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;
+
+});
View
2 Mobile/plugins/ios.json
@@ -1 +1 @@
-{"prepare_queue":{"installed":[],"uninstalled":[]},"config_munge":{"config.xml":{"/*":{"<feature name=\"Device\"><param name=\"ios-package\" value=\"CDVDevice\" /></feature>":1,"<feature name=\"Notification\"><param name=\"ios-package\" value=\"CDVNotification\" /></feature>":1,"<feature name=\"InAppBrowser\"><param name=\"ios-package\" value=\"CDVInAppBrowser\" /></feature>":1,"<feature name=\"Vibration\"><param name=\"ios-package\" value=\"CDVVibration\" /></feature>":1,"<feature name=\"JokUtils\"><param name=\"ios-package\" value=\"CDVJokUtils\" /></feature>":1}},"framework":{"AudioToolbox.framework":{"true":2},"CoreGraphics.framework":{"false":1}}},"installed_plugins":{"org.apache.cordova.device":{"PACKAGE_NAME":"io.jok.pitching"},"org.apache.cordova.dialogs":{"PACKAGE_NAME":"io.jok.pitching"},"org.apache.cordova.inappbrowser":{"PACKAGE_NAME":"io.jok.pitching"},"org.apache.cordova.vibration":{"PACKAGE_NAME":"io.jok.pitching"},"com.phonegap.plugins.jokutils":{"PACKAGE_NAME":"io.jok.pitching"}},"dependent_plugins":{}}
+{"prepare_queue":{"installed":[],"uninstalled":[]},"config_munge":{"config.xml":{"/*":{"<feature name=\"Device\"><param name=\"ios-package\" value=\"CDVDevice\" /></feature>":1,"<feature name=\"Notification\"><param name=\"ios-package\" value=\"CDVNotification\" /></feature>":1,"<feature name=\"InAppBrowser\"><param name=\"ios-package\" value=\"CDVInAppBrowser\" /></feature>":1,"<feature name=\"Vibration\"><param name=\"ios-package\" value=\"CDVVibration\" /></feature>":1,"<feature name=\"JokUtils\"><param name=\"ios-package\" value=\"CDVJokUtils\" /></feature>":1,"<feature name=\"Media\"><param name=\"ios-package\" value=\"CDVSound\" /></feature>":1}},"framework":{"AudioToolbox.framework":{"true":2},"CoreGraphics.framework":{"false":1}}},"installed_plugins":{"org.apache.cordova.device":{"PACKAGE_NAME":"io.jok.pitching"},"org.apache.cordova.dialogs":{"PACKAGE_NAME":"io.jok.pitching"},"org.apache.cordova.inappbrowser":{"PACKAGE_NAME":"io.jok.pitching"},"org.apache.cordova.vibration":{"PACKAGE_NAME":"io.jok.pitching"},"com.phonegap.plugins.jokutils":{"PACKAGE_NAME":"io.jok.pitching"},"org.apache.cordova.media":{"PACKAGE_NAME":"io.jok.pitching"}},"dependent_plugins":{}}
View
1 Mobile/plugins/org.apache.cordova.media/.fetch.json
@@ -0,0 +1 @@
+{"source":{"type":"local","path":"/var/folders/2z/x2m50m5d3wvg8_w4kk1nwslh0000gn/T/org.apache.cordova.media/package"}}
View
202 Mobile/plugins/org.apache.cordova.media/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
View
22 Mobile/plugins/org.apache.cordova.media/README.md
@@ -0,0 +1,22 @@
+<!---
+ license: 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.
+-->
+
+# org.apache.cordova.media
+
+Plugin documentation: [doc/index.md](doc/index.md)
View
59 Mobile/plugins/org.apache.cordova.media/RELEASENOTES.md
@@ -0,0 +1,59 @@
+<!--
+#
+# 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.
+#
+-->
+# Release Notes
+
+### 0.2.1 (Sept 5, 2013)
+* CB-4432 copyright notice change
+
+### 0.2.3 (Sept 25, 2013)
+* CB-4889 bumping&resetting version
+* [windows8] commandProxy was moved
+* CB-4889 renaming references
+* CB-4889 renaming org.apache.cordova.core.media to org.apache.cordova.media
+* [CB-4847] iOS 7 microphone access requires user permission - if denied, CDVCapture, CDVSound does not handle it properly
+* Rename CHANGELOG.md -> RELEASENOTES.md
+* [CB-4799] Fix incorrect JS references within native code on Android & iOS
+* Fix compiler/lint warnings
+* Rename plugin id from AudioHandler -> media
+* [CB-4763] Remove reference to cordova-android's FileHelper.
+* [CB-4752] Incremented plugin version on dev branch.
+
+### 0.2.4 (Oct 9, 2013)
+* [CB-4928] plugin-media doesn't load on windows8
+* [CB-4915] Incremented plugin version on dev branch.
+
+### 0.2.5 (Oct 28, 2013)
+* CB-5128: add repo + issue tag to plugin.xml for media plugin
+* [CB-5010] Incremented plugin version on dev branch.
+
+
+### 0.2.6 (Dec 4, 2013)
+* [ubuntu] specify policy_group
+* add ubuntu platform
+* Added amazon-fireos platform. Change to use amazon-fireos as a platform if the user agent string contains 'cordova-amazon-fireos'
+
+### 0.2.7 (Jan 02, 2014)
+* CB-5658 Add doc/index.md for Media plugin
+* Adding READ_PHONE_STATE to the plugin permissions
+
+### 0.2.8 (Feb 05, 2014)
+* Add preliminary support for Tizen.
+* [CB-4755] Fix crash in Media.setVolume on iOS
View
492 Mobile/plugins/org.apache.cordova.media/doc/index.md
@@ -0,0 +1,492 @@
+<!---
+ 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.
+-->
+
+# org.apache.cordova.media
+
+This plugin provides the ability to record and play back audio files on a device.
+
+__NOTE__: The current implementation does not adhere to a W3C
+specification for media capture, and is provided for convenience only.
+A future implementation will adhere to the latest W3C specification
+and may deprecate the current APIs.
+
+## Installation
+
+ cordova plugin add org.apache.cordova.media
+
+## Supported Platforms
+
+- Android
+- BlackBerry 10
+- iOS
+- Windows Phone 7 and 8
+- Tizen
+- Windows 8
+
+## Windows Phone Quirks
+
+- Only one media file can be played back at a time.
+
+- There are strict restrictions on how your application interacts with other media. See the [Microsoft documentation for details][url].
+
+[url]: http://msdn.microsoft.com/en-us/library/windowsphone/develop/hh184838(v=vs.92).aspx
+
+## Media
+
+ var media = new Media(src, mediaSuccess, [mediaError], [mediaStatus]);
+
+### Parameters
+
+- __src__: A URI containing the audio content. _(DOMString)_
+
+- __mediaSuccess__: (Optional) The callback that executes after a `Media` object has completed the current play, record, or stop action. _(Function)_
+
+- __mediaError__: (Optional) The callback that executes if an error occurs. _(Function)_
+
+- __mediaStatus__: (Optional) The callback that executes to indicate status changes. _(Function)_
+
+### Constants
+
+The following constants are reported as the only parameter to the
+`mediaStatus` callback:
+
+- `Media.MEDIA_NONE` = 0;
+- `Media.MEDIA_STARTING` = 1;
+- `Media.MEDIA_RUNNING` = 2;
+- `Media.MEDIA_PAUSED` = 3;
+- `Media.MEDIA_STOPPED` = 4;
+
+### Methods
+
+- `media.getCurrentPosition`: Returns the current position within an audio file.
+
+- `media.getDuration`: Returns the duration of an audio file.
+
+- `media.play`: Start or resume playing an audio file.
+
+- `media.pause`: Pause playback of an audio file.
+
+- `media.release`: Releases the underlying operating system's audio resources.
+
+- `media.seekTo`: Moves the position within the audio file.
+
+- `media.setVolume`: Set the volume for audio playback.
+
+- `media.startRecord`: Start recording an audio file.
+
+- `media.stopRecord`: Stop recording an audio file.
+
+- `media.stop`: Stop playing an audio file.
+
+### Additional ReadOnly Parameters
+
+- __position__: The position within the audio playback, in seconds.
+ - Not automatically updated during play; call `getCurrentPosition` to update.
+
+- __duration__: The duration of the media, in seconds.
+
+
+## media.getCurrentPosition
+
+Returns the current position within an audio file. Also updates the `Media` object's `position` parameter.
+
+ media.getCurrentPosition(mediaSuccess, [mediaError]);
+
+### Parameters
+
+- __mediaSuccess__: The callback that is passed the current position in seconds.
+
+- __mediaError__: (Optional) The callback to execute if an error occurs.
+
+### Quick Example
+
+ // Audio player
+ //
+ var my_media = new Media(src, onSuccess, onError);
+
+ // Update media position every second
+ var mediaTimer = setInterval(function () {
+ // get media position
+ my_media.getCurrentPosition(
+ // success callback
+ function (position) {
+ if (position > -1) {
+ console.log((position) + " sec");
+ }
+ },
+ // error callback
+ function (e) {
+ console.log("Error getting pos=" + e);
+ }
+ );
+ }, 1000);
+
+
+## media.getDuration
+
+Returns the duration of an audio file in seconds. If the duration is unknown, it returns a value of -1.
+
+
+ media.getDuration();
+
+### Quick Example
+
+ // Audio player
+ //
+ var my_media = new Media(src, onSuccess, onError);
+
+ // Get duration
+ var counter = 0;
+ var timerDur = setInterval(function() {
+ counter = counter + 100;
+ if (counter > 2000) {
+ clearInterval(timerDur);
+ }
+ var dur = my_media.getDuration();
+ if (dur > 0) {
+ clearInterval(timerDur);
+ document.getElementById('audio_duration').innerHTML = (dur) + " sec";
+ }
+ }, 100);
+
+
+## media.pause
+
+Pauses playing an audio file.
+
+ media.pause();
+
+
+### Quick Example
+
+ // Play audio
+ //
+ function playAudio(url) {
+ // Play the audio file at url
+ var my_media = new Media(url,
+ // success callback
+ function () { console.log("playAudio():Audio Success"); },
+ // error callback
+ function (err) { console.log("playAudio():Audio Error: " + err); }
+ );
+
+ // Play audio
+ my_media.play();
+
+ // Pause after 10 seconds
+ setTimeout(function () {
+ media.pause();
+ }, 10000);
+ }
+
+
+## media.play
+
+Starts or resumes playing an audio file.
+
+ media.play();
+
+
+### Quick Example
+
+ // Play audio
+ //
+ function playAudio(url) {
+ // Play the audio file at url
+ var my_media = new Media(url,
+ // success callback
+ function () {
+ console.log("playAudio():Audio Success");
+ },
+ // error callback
+ function (err) {
+ console.log("playAudio():Audio Error: " + err);
+ }
+ );
+ // Play audio
+ my_media.play();
+ }
+
+
+### iOS Quirks
+
+- __numberOfLoops__: Pass this option to the `play` method to specify
+ the number of times you want the media file to play, e.g.:
+
+ 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
+
+## media.release
+
+Releases the underlying operating system's audio resources.
+This is particularly important for Android, since there are a finite amount of
+OpenCore instances for media playback. Applications should call the `release`
+function for any `Media` resource that is no longer needed.
+
+ media.release();
+
+
+### Quick Example
+
+ // Audio player
+ //
+ var my_media = new Media(src, onSuccess, onError);
+
+ my_media.play();
+ my_media.stop();
+ my_media.release();
+
+
+## media.seekTo
+
+Sets the current position within an audio file.
+
+ media.seekTo(milliseconds);
+
+### Parameters
+
+- __milliseconds__: The position to set the playback position within the audio, in milliseconds.
+
+
+### Quick Example
+
+ // Audio player
+ //
+ var my_media = new Media(src, onSuccess, onError);
+ my_media.play();
+ // SeekTo to 10 seconds after 5 seconds
+ setTimeout(function() {
+ my_media.seekTo(10000);
+ }, 5000);
+
+
+### BlackBerry 10 Quirks
+
+- Not supported on BlackBerry OS 5 devices.
+
+## media.setVolume
+
+Set the volume for an audio file.
+
+ media.setVolume(volume);
+
+### Parameters
+
+- __volume__: The volume to set for playback. The value must be within the range of 0.0 to 1.0.
+
+### Supported Platforms
+
+- Android
+- iOS
+
+### Quick Example
+
+ // Play audio
+ //
+ function playAudio(url) {
+ // Play the audio file at url
+ var my_media = new Media(url,
+ // success callback
+ function() {
+ console.log("playAudio():Audio Success");
+ },
+ // error callback
+ function(err) {
+ console.log("playAudio():Audio Error: "+err);
+ });
+
+ // Play audio
+ my_media.play();
+
+ // Mute volume after 2 seconds
+ setTimeout(function() {
+ my_media.setVolume('0.0');
+ }, 2000);
+
+ // Set volume to 1.0 after 5 seconds
+ setTimeout(function() {
+ my_media.setVolume('1.0');
+ }, 5000);
+ }
+
+
+## media.startRecord
+
+Starts recording an audio file.
+
+ media.startRecord();
+
+### Supported Platforms
+
+- Android
+- iOS
+- Windows Phone 7 and 8
+- Windows 8
+
+### Quick Example
+
+ // Record audio
+ //
+ function recordAudio() {
+ var src = "myrecording.mp3";
+ var mediaRec = new Media(src,
+ // success callback
+ function() {
+ console.log("recordAudio():Audio Success");
+ },
+
+ // error callback
+ function(err) {
+ console.log("recordAudio():Audio Error: "+ err.code);
+ });
+
+ // Record audio
+ mediaRec.startRecord();
+ }
+
+
+### Android Quirks
+
+- Android devices record audio in Adaptive Multi-Rate format. The specified file should end with a _.amr_ extension.
+
+### iOS Quirks
+
+- iOS only records to files of type _.wav_ and returns an error if the file name extension is not correct.
+
+- If a full path is not provided, the recording is placed in the application's `documents/tmp` directory. This can be accessed via the `File` API using `LocalFileSystem.TEMPORARY`. Any subdirectory specified at record time must already exist.
+
+- Files can be recorded and played back using the documents URI:
+
+ var myMedia = new Media("documents://beer.mp3")
+
+### Tizen Quirks
+
+- Not supported on Tizen devices.
+
+## media.stop
+
+Stops playing an audio file.
+
+ media.stop();
+
+### Quick Example
+
+ // Play audio
+ //
+ function playAudio(url) {
+ // Play the audio file at url
+ var my_media = new Media(url,
+ // success callback
+ function() {
+ console.log("playAudio():Audio Success");
+ },
+ // error callback
+ function(err) {
+ console.log("playAudio():Audio Error: "+err);
+ }
+ );
+
+ // Play audio
+ my_media.play();
+
+ // Pause after 10 seconds
+ setTimeout(function() {
+ my_media.stop();
+ }, 10000);
+ }
+
+
+## media.stopRecord
+
+Stops recording an audio file.
+
+ media.stopRecord();
+
+### Supported Platforms
+
+- Android
+- iOS
+- Windows Phone 7 and 8
+- Windows 8
+
+### Quick Example
+
+ // Record audio
+ //
+ function recordAudio() {
+ var src = "myrecording.mp3";
+ var mediaRec = new Media(src,
+ // success callback
+ function() {
+ console.log("recordAudio():Audio Success");
+ },
+
+ // error callback
+ function(err) {
+ console.log("recordAudio():Audio Error: "+ err.code);
+ }
+ );
+
+ // Record audio
+ mediaRec.startRecord();
+
+ // Stop recording after 10 seconds
+ setTimeout(function() {
+ mediaRec.stopRecord();
+ }, 10000);
+ }
+
+
+### Tizen Quirks
+
+- Not supported on Tizen devices.
+
+## MediaError
+
+A `MediaError` object is returned to the `mediaError` callback
+function when an error occurs.
+
+### Properties
+
+- __code__: One of the predefined error codes listed below.
+
+- __message__: An error message describing the details of the error.
+
+### Constants
+
+- `MediaError.MEDIA_ERR_ABORTED`
+- `MediaError.MEDIA_ERR_NETWORK`
+- `MediaError.MEDIA_ERR_DECODE`
+- `MediaError.MEDIA_ERR_NONE_SUPPORTED`
+
View
14 Mobile/plugins/org.apache.cordova.media/package.json
@@ -0,0 +1,14 @@
+{
+ "version": "0.2.8",
+ "name": "org.apache.cordova.media",
+ "cordova_name": "Media",