Skip to content

Commit 2cfe774

Browse files
zxcpoiuFacebook Github Bot 8
authored andcommitted
Ios: complete iOS vibration pattern supports (js)
Summary: This is a revised follow up version from facebook#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 facebook#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 facebook#9233 Differential Revision: D3775085 Pulled By: javache fbshipit-source-id: 370495857d5581399de32d2bed1ea1bcce193e9d
1 parent b01feb4 commit 2cfe774

File tree

2 files changed

+91
-10
lines changed

2 files changed

+91
-10
lines changed

Examples/UIExplorer/js/VibrationExample.js

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,45 @@ var {
3030
Text,
3131
TouchableHighlight,
3232
Vibration,
33+
Platform,
3334
} = ReactNative;
3435

3536
exports.framework = 'React';
3637
exports.title = 'Vibration';
3738
exports.description = 'Vibration API';
39+
40+
var pattern, patternLiteral, patternDescription;
41+
if (Platform.OS === 'android') {
42+
pattern = [0, 500, 200, 500];
43+
patternLiteral = '[0, 500, 200, 500]';
44+
patternDescription = `${patternLiteral}
45+
arg 0: duration to wait before turning the vibrator on.
46+
arg with odd: vibration length.
47+
arg with even: duration to wait before next vibration.
48+
`;
49+
} else {
50+
pattern = [0, 1000, 2000, 3000];
51+
patternLiteral = '[0, 1000, 2000, 3000]';
52+
patternDescription = `${patternLiteral}
53+
vibration length on iOS is fixed.
54+
pattern controls durations BETWEEN each vibration only.
55+
56+
arg 0: duration to wait before turning the vibrator on.
57+
subsequent args: duration to wait before next vibrattion.
58+
`;
59+
}
60+
3861
exports.examples = [
62+
{
63+
title: 'Pattern Descriptions',
64+
render() {
65+
return (
66+
<View style={styles.wrapper}>
67+
<Text>{patternDescription}</Text>
68+
</View>
69+
);
70+
},
71+
},
3972
{
4073
title: 'Vibration.vibrate()',
4174
render() {
@@ -51,12 +84,12 @@ exports.examples = [
5184
},
5285
},
5386
{
54-
title: 'Vibration.vibrate([0, 500, 200, 500])',
87+
title: `Vibration.vibrate(${patternLiteral})`,
5588
render() {
5689
return (
5790
<TouchableHighlight
5891
style={styles.wrapper}
59-
onPress={() => Vibration.vibrate([0, 500, 200, 500])}>
92+
onPress={() => Vibration.vibrate(pattern)}>
6093
<View style={styles.button}>
6194
<Text>Vibrate once</Text>
6295
</View>
@@ -65,12 +98,12 @@ exports.examples = [
6598
},
6699
},
67100
{
68-
title: 'Vibration.vibrate([0, 500, 200, 500], true)',
101+
title: `Vibration.vibrate(${patternLiteral}, true)`,
69102
render() {
70103
return (
71104
<TouchableHighlight
72105
style={styles.wrapper}
73-
onPress={() => Vibration.vibrate([0, 500, 200, 500], true)}>
106+
onPress={() => Vibration.vibrate(pattern, true)}>
74107
<View style={styles.button}>
75108
<Text>Vibrate until cancel</Text>
76109
</View>

Libraries/Vibration/Vibration.js

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,59 @@ var Platform = require('Platform');
2020
*
2121
* There will be no effect on devices that do not support Vibration, eg. the simulator.
2222
*
23-
* Note for android
23+
* **Note for android**
2424
* add `<uses-permission android:name="android.permission.VIBRATE"/>` to `AndroidManifest.xml`
2525
*
26-
* Vibration patterns are currently unsupported.
26+
* **Android Usage:**
27+
*
28+
* [0, 500, 200, 500]
29+
* V(0.5s) --wait(0.2s)--> V(0.5s)
30+
*
31+
* [300, 500, 200, 500]
32+
* --wait(0.3s)--> V(0.5s) --wait(0.2s)--> V(0.5s)
33+
*
34+
* **iOS Usage:**
35+
* if first argument is 0, it will not be included in pattern array.
36+
*
37+
* [0, 1000, 2000, 3000]
38+
* V(fixed) --wait(1s)--> V(fixed) --wait(2s)--> V(fixed) --wait(3s)--> V(fixed)
2739
*/
2840

41+
var _vibrating: boolean = false;
42+
var _id: number = 0; // _id is necessary to prevent race condition.
43+
44+
function vibrateByPattern(pattern: Array<number>, repeat: boolean = false) {
45+
if (_vibrating) {
46+
return;
47+
}
48+
_vibrating = true;
49+
if (pattern[0] === 0) {
50+
RCTVibration.vibrate();
51+
pattern = pattern.slice(1);
52+
}
53+
if (pattern.length === 0) {
54+
_vibrating = false;
55+
return;
56+
}
57+
setTimeout(() => vibrateScheduler(++_id, pattern, repeat, 1), pattern[0]);
58+
}
59+
60+
function vibrateScheduler(id, pattern: Array<number>, repeat: boolean, nextIndex: number) {
61+
if (!_vibrating || id !== _id) {
62+
return;
63+
}
64+
RCTVibration.vibrate();
65+
if (nextIndex >= pattern.length) {
66+
if (repeat) {
67+
nextIndex = 0;
68+
} else {
69+
_vibrating = false;
70+
return;
71+
}
72+
}
73+
setTimeout(() => vibrateScheduler(id, pattern, repeat, nextIndex+1), pattern[nextIndex]);
74+
}
75+
2976
var Vibration = {
3077
vibrate: function(pattern: number | Array<number> = 400, repeat: boolean = false) {
3178
if (Platform.OS === 'android') {
@@ -37,23 +84,24 @@ var Vibration = {
3784
throw new Error('Vibration pattern should be a number or array');
3885
}
3986
} else {
87+
if (_vibrating) {
88+
return;
89+
}
4090
if (typeof pattern === 'number') {
4191
RCTVibration.vibrate();
4292
} else if (Array.isArray(pattern)) {
43-
console.warn('Vibration patterns are not supported on iOS');
93+
vibrateByPattern(pattern, repeat);
4494
} else {
4595
throw new Error('Vibration pattern should be a number or array');
4696
}
4797
}
4898
},
4999
/**
50100
* Stop vibration
51-
*
52-
* @platform android
53101
*/
54102
cancel: function() {
55103
if (Platform.OS === 'ios') {
56-
console.warn('Vibration.cancel is not supported on iOS');
104+
_vibrating = false;
57105
} else {
58106
RCTVibration.cancel();
59107
}

0 commit comments

Comments
 (0)