diff --git a/src/ios/CDVMobileAccessibility.h b/src/ios/CDVMobileAccessibility.h index 2d5bc1c..77d08d0 100644 --- a/src/ios/CDVMobileAccessibility.h +++ b/src/ios/CDVMobileAccessibility.h @@ -20,8 +20,14 @@ #import @interface CDVMobileAccessibility : CDVPlugin { + NSString* callbackId; } +@property (strong) NSString* callbackId; + - (void) isVoiceOverRunning:(CDVInvokedUrlCommand*)command; +- (void) isClosedCaptioningEnabled:(CDVInvokedUrlCommand*)command; +- (void) start:(CDVInvokedUrlCommand*)command; +- (void) stop:(CDVInvokedUrlCommand*)command; @end diff --git a/src/ios/CDVMobileAccessibility.m b/src/ios/CDVMobileAccessibility.m index 38dbd68..28f3e81 100644 --- a/src/ios/CDVMobileAccessibility.m +++ b/src/ios/CDVMobileAccessibility.m @@ -26,6 +26,8 @@ @implementation CDVMobileAccessibility +@synthesize callbackId; + // ////////////////////////////////////////////////// - (id)settingForKey:(NSString*)key @@ -49,16 +51,75 @@ - (void)dealloc { - // since this is ARC, remove observers only + [self stop:nil]; +} + +- (void)onReset +{ + [self stop:nil]; } // ////////////////////////////////////////////////// #pragma Plugin interface -- (void) isVoiceOverRunning:(CDVInvokedUrlCommand*)command +- (void)isVoiceOverRunning:(CDVInvokedUrlCommand*)command { - // TODO: + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:UIAccessibilityIsVoiceOverRunning()]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; +} + +- (void)isClosedCaptioningEnabled:(CDVInvokedUrlCommand*)command +{ + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:UIAccessibilityIsClosedCaptioningEnabled()]; + [self.commandDelegate sendPluginResult:result callbackId:command.callbackId]; +} + + +- (void)mobileAccessibilityStatusChanged:(NSNotification *)notification +{ + if (self.callbackId) { + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self getMobileAccessibilityStatus]]; + [result setKeepCallbackAsBool:YES]; + [self.commandDelegate sendPluginResult:result callbackId:self.callbackId]; + } +} + +/* Get the current mobile accessibility status. */ +- (NSDictionary*)getMobileAccessibilityStatus +{ + NSMutableDictionary* mobileAccessibilityData = [NSMutableDictionary dictionaryWithCapacity:2]; + [mobileAccessibilityData setObject:[NSNumber numberWithBool:UIAccessibilityIsVoiceOverRunning()] forKey:@"isVoiceOverRunning"]; + [mobileAccessibilityData setObject:[NSNumber numberWithBool:UIAccessibilityIsClosedCaptioningEnabled()] forKey:@"isClosedCaptioningEnabled"]; + return mobileAccessibilityData; +} + + +/* turn on MobileAccessibility monitoring*/ +- (void)start:(CDVInvokedUrlCommand*)command +{ + self.callbackId = command.callbackId; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mobileAccessibilityStatusChanged:) + name:UIAccessibilityVoiceOverStatusChanged object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(mobileAccessibilityStatusChanged:) + name:UIAccessibilityClosedCaptioningStatusDidChangeNotification object:nil]; +} + +- (void)stop:(CDVInvokedUrlCommand*)command +{ + // callback one last time to clear the callback function on JS side + if (self.callbackId) + { + CDVPluginResult* result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:[self getMobileAccessibilityStatus]]; + [result setKeepCallbackAsBool:NO]; + [self.commandDelegate sendPluginResult:result callbackId:self.callbackId]; + } + self.callbackId = nil; + [[NSNotificationCenter defaultCenter] removeObserver:self + name:UIAccessibilityVoiceOverStatusChanged object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self + name:UIAccessibilityClosedCaptioningStatusDidChangeNotification object:nil]; } @end diff --git a/www/mobile-accessibility.js b/www/mobile-accessibility.js index 5944e47..d43061b 100644 --- a/www/mobile-accessibility.js +++ b/www/mobile-accessibility.js @@ -22,12 +22,74 @@ var argscheck = require('cordova/argscheck'), utils = require('cordova/utils'), exec = require('cordova/exec'); - + var MobileAccessibility = function() { + this._isVoiceOverRunning = false; + this._isClosedCaptioningEnabled = false; + // Create new event handlers on the window (returns a channel instance) + this.channels = { + voiceoverstatuschanged:cordova.addWindowEventHandler("voiceoverstatuschanged"), + closedcaptioningstatusdidchange:cordova.addWindowEventHandler("closedcaptioningstatusdidchange") + }; + for (var key in this.channels) { + this.channels[key].onHasSubscribersChange = MobileAccessibility.onHasSubscribersChange; + } }; -MobileAccessibility.isVoiceOverRunning = function() { - exec(null, null, "MobileAccessibility", "isVoiceOverRunning", []); +function handlers() { + return mobileAccessibility.channels.voiceoverstatuschanged.numHandlers + + mobileAccessibility.channels.closedcaptioningstatusdidchange.numHandlers; }; -module.exports = MobileAccessibility; +/** + * Event handlers for when callbacks get registered for mobileAccessibility. + * Keep track of how many handlers we have so we can start and stop the native MobileAccessibility listener + * appropriately. + */ +MobileAccessibility.onHasSubscribersChange = function() { + // If we just registered the first handler, make sure native listener is started. + if (this.numHandlers === 1 && handlers() === 1) { + exec(mobileAccessibility._status, mobileAccessibility._error, "MobileAccessibility", "start", []); + } else if (handlers() === 0) { + exec(null, null, "MobileAccessibility", "stop", []); + } +}; + +MobileAccessibility.prototype.isVoiceOverRunning = function(callback) { + exec(callback, null, "MobileAccessibility", "isVoiceOverRunning", []); +}; + +MobileAccessibility.prototype.isClosedCaptioningEnabled = function(callback) { + exec(callback, null, "MobileAccessibility", "isClosedCaptioningEnabled", []); +}; + +/** + * Callback for mobileAccessibility status + * + * @param {Object} info keys: isVoiceOverRunning, isClosedCaptioningEnabled + */ +MobileAccessibility.prototype._status = function(info) { + if (info) { + var me = mobileAccessibility; + if (me._isVoiceOverRunning !== info.isVoiceOverRunning) { + cordova.fireWindowEvent("voiceoverstatuschanged", info); + me._isVoiceOverRunning = info.isVoiceOverRunning; + } + if (me._isClosedCaptioningEnabled !== info.isClosedCaptioningEnabled) { + cordova.fireWindowEvent("closedcaptioningstatusdidchange", info); + me._isClosedCaptioningEnabled = info.isClosedCaptioningEnabled; + } + } +}; + +/** + * Error callback for MobileAccessibility start + */ +MobileAccessibility.prototype._error = function(e) { + console.log("Error initializing MobileAccessibility: " + e); +}; + +var mobileAccessibility = new MobileAccessibility(); + +module.exports = mobileAccessibility; +start \ No newline at end of file