Skip to content

Commit

Permalink
Ios: complete iOS vibration pattern supports (js)
Browse files Browse the repository at this point in the history
Summary:
This is a revised follow up version from #8574 ( originally implemented in `objc` )
This PR change the implementation in JS suggested by javache

**motivation**

To supports vibration pattern like android.

The [iOS vibration implementation link](http://stackoverflow.com/questions/12966467/are-there-apis-for-custom-vibrations-in-ios/13047464#13047464) mentioned by skv-headless at #6061 (comment), which will not be accepted by apple since that implementation uses a private API. Thus, I use pure public API `NSTimer` to implement it.

**Note**

Since vibration time on iOS is not configurable, there are slightly differences with android.
for example:

**Android Usage:**
`Vibration.vibrate([0, 500, 200, 500])`
==> V(0.5s) --wait(0.2s)--> V(0.5s)

`Vibration.vibrate([300, 500, 200, 500])`
==> --wait(0.3s)--> V(0.5s) --wait(0.2s)--> V(0.5s)

**iOS Usage:**
if first argument is 0, it will not be included in pattern array.
( vibration
Closes #9233

Differential Revision: D3775085

Pulled By: javache

fbshipit-source-id: 370495857d5581399de32d2bed1ea1bcce193e9d
  • Loading branch information
zxcpoiu authored and Facebook Github Bot 8 committed Aug 26, 2016
1 parent b01feb4 commit 2cfe774
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 10 deletions.
41 changes: 37 additions & 4 deletions Examples/UIExplorer/js/VibrationExample.js
Expand Up @@ -30,12 +30,45 @@ var {
Text,
TouchableHighlight,
Vibration,
Platform,
} = ReactNative;

exports.framework = 'React';
exports.title = 'Vibration';
exports.description = 'Vibration API';

var pattern, patternLiteral, patternDescription;
if (Platform.OS === 'android') {
pattern = [0, 500, 200, 500];
patternLiteral = '[0, 500, 200, 500]';
patternDescription = `${patternLiteral}
arg 0: duration to wait before turning the vibrator on.
arg with odd: vibration length.
arg with even: duration to wait before next vibration.
`;
} else {
pattern = [0, 1000, 2000, 3000];
patternLiteral = '[0, 1000, 2000, 3000]';
patternDescription = `${patternLiteral}
vibration length on iOS is fixed.
pattern controls durations BETWEEN each vibration only.
arg 0: duration to wait before turning the vibrator on.
subsequent args: duration to wait before next vibrattion.
`;
}

exports.examples = [
{
title: 'Pattern Descriptions',
render() {
return (
<View style={styles.wrapper}>
<Text>{patternDescription}</Text>
</View>
);
},
},
{
title: 'Vibration.vibrate()',
render() {
Expand All @@ -51,12 +84,12 @@ exports.examples = [
},
},
{
title: 'Vibration.vibrate([0, 500, 200, 500])',
title: `Vibration.vibrate(${patternLiteral})`,
render() {
return (
<TouchableHighlight
style={styles.wrapper}
onPress={() => Vibration.vibrate([0, 500, 200, 500])}>
onPress={() => Vibration.vibrate(pattern)}>
<View style={styles.button}>
<Text>Vibrate once</Text>
</View>
Expand All @@ -65,12 +98,12 @@ exports.examples = [
},
},
{
title: 'Vibration.vibrate([0, 500, 200, 500], true)',
title: `Vibration.vibrate(${patternLiteral}, true)`,
render() {
return (
<TouchableHighlight
style={styles.wrapper}
onPress={() => Vibration.vibrate([0, 500, 200, 500], true)}>
onPress={() => Vibration.vibrate(pattern, true)}>
<View style={styles.button}>
<Text>Vibrate until cancel</Text>
</View>
Expand Down
60 changes: 54 additions & 6 deletions Libraries/Vibration/Vibration.js
Expand Up @@ -20,12 +20,59 @@ var Platform = require('Platform');
*
* There will be no effect on devices that do not support Vibration, eg. the simulator.
*
* Note for android
* **Note for android**
* add `<uses-permission android:name="android.permission.VIBRATE"/>` to `AndroidManifest.xml`
*
* Vibration patterns are currently unsupported.
* **Android Usage:**
*
* [0, 500, 200, 500]
* V(0.5s) --wait(0.2s)--> V(0.5s)
*
* [300, 500, 200, 500]
* --wait(0.3s)--> V(0.5s) --wait(0.2s)--> V(0.5s)
*
* **iOS Usage:**
* if first argument is 0, it will not be included in pattern array.
*
* [0, 1000, 2000, 3000]
* V(fixed) --wait(1s)--> V(fixed) --wait(2s)--> V(fixed) --wait(3s)--> V(fixed)
*/

var _vibrating: boolean = false;
var _id: number = 0; // _id is necessary to prevent race condition.

function vibrateByPattern(pattern: Array<number>, repeat: boolean = false) {
if (_vibrating) {
return;
}
_vibrating = true;
if (pattern[0] === 0) {
RCTVibration.vibrate();
pattern = pattern.slice(1);
}
if (pattern.length === 0) {
_vibrating = false;
return;
}
setTimeout(() => vibrateScheduler(++_id, pattern, repeat, 1), pattern[0]);
}

function vibrateScheduler(id, pattern: Array<number>, repeat: boolean, nextIndex: number) {
if (!_vibrating || id !== _id) {
return;
}
RCTVibration.vibrate();
if (nextIndex >= pattern.length) {
if (repeat) {
nextIndex = 0;
} else {
_vibrating = false;
return;
}
}
setTimeout(() => vibrateScheduler(id, pattern, repeat, nextIndex+1), pattern[nextIndex]);
}

var Vibration = {
vibrate: function(pattern: number | Array<number> = 400, repeat: boolean = false) {
if (Platform.OS === 'android') {
Expand All @@ -37,23 +84,24 @@ var Vibration = {
throw new Error('Vibration pattern should be a number or array');
}
} else {
if (_vibrating) {
return;
}
if (typeof pattern === 'number') {
RCTVibration.vibrate();
} else if (Array.isArray(pattern)) {
console.warn('Vibration patterns are not supported on iOS');
vibrateByPattern(pattern, repeat);
} else {
throw new Error('Vibration pattern should be a number or array');
}
}
},
/**
* Stop vibration
*
* @platform android
*/
cancel: function() {
if (Platform.OS === 'ios') {
console.warn('Vibration.cancel is not supported on iOS');
_vibrating = false;
} else {
RCTVibration.cancel();
}
Expand Down

0 comments on commit 2cfe774

Please sign in to comment.