Ios: complete iOS vibration pattern supports (js)
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 https://github.com/facebook/react-native/pull/6061#discussion_r54062592, 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 https://github.com/facebook/react-native/pull/9233 Differential Revision: D3775085 Pulled By: javache fbshipit-source-id: 370495857d5581399de32d2bed1ea1bcce193e9d
This commit is contained in:
parent
b01feb41d8
commit
2cfe774a56
|
@ -30,12 +30,45 @@ var {
|
||||||
Text,
|
Text,
|
||||||
TouchableHighlight,
|
TouchableHighlight,
|
||||||
Vibration,
|
Vibration,
|
||||||
|
Platform,
|
||||||
} = ReactNative;
|
} = ReactNative;
|
||||||
|
|
||||||
exports.framework = 'React';
|
exports.framework = 'React';
|
||||||
exports.title = 'Vibration';
|
exports.title = 'Vibration';
|
||||||
exports.description = 'Vibration API';
|
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 = [
|
exports.examples = [
|
||||||
|
{
|
||||||
|
title: 'Pattern Descriptions',
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<View style={styles.wrapper}>
|
||||||
|
<Text>{patternDescription}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Vibration.vibrate()',
|
title: 'Vibration.vibrate()',
|
||||||
render() {
|
render() {
|
||||||
|
@ -51,12 +84,12 @@ exports.examples = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Vibration.vibrate([0, 500, 200, 500])',
|
title: `Vibration.vibrate(${patternLiteral})`,
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<TouchableHighlight
|
<TouchableHighlight
|
||||||
style={styles.wrapper}
|
style={styles.wrapper}
|
||||||
onPress={() => Vibration.vibrate([0, 500, 200, 500])}>
|
onPress={() => Vibration.vibrate(pattern)}>
|
||||||
<View style={styles.button}>
|
<View style={styles.button}>
|
||||||
<Text>Vibrate once</Text>
|
<Text>Vibrate once</Text>
|
||||||
</View>
|
</View>
|
||||||
|
@ -65,12 +98,12 @@ exports.examples = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Vibration.vibrate([0, 500, 200, 500], true)',
|
title: `Vibration.vibrate(${patternLiteral}, true)`,
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<TouchableHighlight
|
<TouchableHighlight
|
||||||
style={styles.wrapper}
|
style={styles.wrapper}
|
||||||
onPress={() => Vibration.vibrate([0, 500, 200, 500], true)}>
|
onPress={() => Vibration.vibrate(pattern, true)}>
|
||||||
<View style={styles.button}>
|
<View style={styles.button}>
|
||||||
<Text>Vibrate until cancel</Text>
|
<Text>Vibrate until cancel</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -20,12 +20,59 @@ var Platform = require('Platform');
|
||||||
*
|
*
|
||||||
* There will be no effect on devices that do not support Vibration, eg. the simulator.
|
* 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`
|
* 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 = {
|
var Vibration = {
|
||||||
vibrate: function(pattern: number | Array<number> = 400, repeat: boolean = false) {
|
vibrate: function(pattern: number | Array<number> = 400, repeat: boolean = false) {
|
||||||
if (Platform.OS === 'android') {
|
if (Platform.OS === 'android') {
|
||||||
|
@ -37,10 +84,13 @@ var Vibration = {
|
||||||
throw new Error('Vibration pattern should be a number or array');
|
throw new Error('Vibration pattern should be a number or array');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
if (_vibrating) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (typeof pattern === 'number') {
|
if (typeof pattern === 'number') {
|
||||||
RCTVibration.vibrate();
|
RCTVibration.vibrate();
|
||||||
} else if (Array.isArray(pattern)) {
|
} else if (Array.isArray(pattern)) {
|
||||||
console.warn('Vibration patterns are not supported on iOS');
|
vibrateByPattern(pattern, repeat);
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Vibration pattern should be a number or array');
|
throw new Error('Vibration pattern should be a number or array');
|
||||||
}
|
}
|
||||||
|
@ -48,12 +98,10 @@ var Vibration = {
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Stop vibration
|
* Stop vibration
|
||||||
*
|
|
||||||
* @platform android
|
|
||||||
*/
|
*/
|
||||||
cancel: function() {
|
cancel: function() {
|
||||||
if (Platform.OS === 'ios') {
|
if (Platform.OS === 'ios') {
|
||||||
console.warn('Vibration.cancel is not supported on iOS');
|
_vibrating = false;
|
||||||
} else {
|
} else {
|
||||||
RCTVibration.cancel();
|
RCTVibration.cancel();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue