react-native-dihola-shaking
Version:
DiHola Shaking API makes it easy to build fast and reliable ways to communicate between devices, just by shaking them. We provide such a secure and flexible protocol that this technology can be applied in any form of data exchange: Payment processing, fil
341 lines (274 loc) • 7.56 kB
JavaScript
import { PermissionsAndroid, Platform, AppState, Vibration } from "react-native";
import { map, filter } from "rxjs/operators";
import * as Sensors from "./sensors";
import ShakingCodes from "./codes";
import Geolocation from "./geolocation";
const URL = "https://api.diholapplication.com/shaking/connect";
const HEADERS = {
'accept': 'application/json',
'content-type': 'application/json'
};
const GRAVITY = 9.80665;
export default ShakingAPI = {
/*
* Client API key.
*/
API_KEY: "Get one at www.diholapp.com",
/*
* User unique identifier in the context of the app.
*/
user: "",
/*
* Latitude and longitude coordinates.
* Note: lat = lng = 0 is an invalid location.
*/
lat: 0,
lng: 0,
/*
* Sensibility for the shaking event.
*/
sensibility: Platform.select({
android: 40,
ios: 40 / GRAVITY
}),
/*
* Maximum time (in ms) between shaking events
* to be elegible for pairing.
*/
timingFilter: 2000,
/*
* Maximum distance (in meters)
* to be elegible for pairing.
*/
distanceFilter: 100,
/*
* Keep searching even if a user has been found.
* Allows to connect with multiple devices.
*/
keepSearching: false,
/*
* True if the location is provided programatically,
* otherwise the device location will be used.
*/
manualLocation: false,
/*
* Accelerometer subscription.
*/
subscription: null,
/*
* API status.
*/
stopped: true,
paused: false,
processing: false,
background: false,
/*
* Vibrate on shaking.
*/
vibrate: true,
start: function(){
if(this.stopped){
this.stopped = false;
this.paused = false;
this._requestLocation();
this._subscribe();
AppState.addEventListener('change', this._handleAppStateChange);
}
},
stop: function(){
if(!this.stopped){
this.stopped = true;
this.paused = false;
this.processing = false;
this.subscription && this.subscription.unsubscribe();
AppState.removeEventListener('change', this._handleAppStateChange);
}
},
_restart: function(){
if(!this.stopped && !this.processing && this.paused && !this.background){
this.paused = false;
this._requestLocation();
this._subscribe();
}
},
_pause: function(){
if(!this.paused){
this.paused = true;
this.subscription && this.subscription.unsubscribe();
}
},
simulate: function(){
this._connect();
},
configure: function(params){
const {
API_KEY,
user,
lat,
lng,
sensibility,
timingFilter,
distanceFilter,
keepSearching,
vibrate,
onShaking,
onSuccess,
onError
} = params;
this.API_KEY = API_KEY;
this.user = user;
if (sensibility !== undefined) this.setSensibility(sensibility);
if (timingFilter !== undefined) this.setTimingFilter(timingFilter);
if (distanceFilter !== undefined) this.setDistanceFilter(distanceFilter);
if (keepSearching !== undefined) this.setKeepSearching(keepSearching);
if (vibrate !== undefined) this.vibrate = vibrate;
if(lat !== undefined && lng !== undefined){
this.setLocation(lat, lng);
}
this.onShaking = onShaking;
this.onSuccess = onSuccess;
this.onError = onError;
this._handleAppStateChange = this._handleAppStateChange.bind(this);
return this;
},
setLocation: function(lat, lng){
this.lat = lat;
this.lng = lng;
this.manualLocation = true;
},
setUser: function(user){
this.user = user;
},
setSensibility: function(sensibility){
if(Platform.OS === 'ios') sensibility /= GRAVITY;
this.sensibility = sensibility;
},
setTimingFilter: function(timingFilter){
this.timingFilter = timingFilter;
},
setDistanceFilter: function(distanceFilter){
this.distanceFilter = distanceFilter;
},
setKeepSearching: function(keepSearching){
this.keepSearching = keepSearching;
},
_subscribe: function(){
this.subscription = Sensors.createAccelerometer()
.pipe(map(({ x, y, z }) => Math.abs(x) + Math.abs(y) + Math.abs(z)), filter(speed => speed > this.sensibility))
.subscribe(
speed => this._onShakingEvent(),
error => this.onError(ShakingCodes.SENSOR_ERROR)
)
},
_onShakingEvent: function(){
this._pause();
this.vibrate && Vibration.vibrate();
this.onShaking && this.onShaking();
this._requestLocation();
this._connect();
},
_connect: function(){
this.processing = true;
let requestConfig = {
method: 'POST',
headers: HEADERS,
body: JSON.stringify({
api_key: this.API_KEY,
id: this.user,
lat: this.lat,
lng: this.lng,
sensibility: this.sensibility,
distanceFilter: this.distanceFilter,
timingFilter: this.timingFilter,
keepSearching: this.keepSearching
})
}
fetch(URL, requestConfig)
.then(response => response.json())
.then(result => this._handleServerResponse(result))
.catch(err => {
this.processing = false;
this.onError(ShakingCodes.SERVER_ERROR);
setTimeout(() => {
this._restart()
}, 2000);
console.log(err)
})
},
_handleServerResponse: function(resp){
const { status, response } = resp;
if(status.code == 200){
this.onSuccess(response);
}
else if (status.code == 401){
this.onError(ShakingCodes.AUTHENTICATION_ERROR);
}
else if (status.code == 403){
this.onError(ShakingCodes.API_KEY_EXPIRED);
}
else {
this.onError(ShakingCodes.SERVER_ERROR);
}
this.processing = false;
this._restart();
},
_requestLocation: async function() {
if(this.manualLocation) return;
if(Platform.OS === 'ios') this._requestLocationIOS();
else this._requestLocationAndroid();
},
_requestLocationIOS: function(){
this._getCurrentPosition();
},
_requestLocationAndroid: async function(){
try {
const permission = await this._checkAndroidPermissions();
if (PermissionsAndroid.RESULTS.GRANTED === permission || permission === true) {
this._getCurrentPosition();
}
else if (permission === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN) {
this.onError(ShakingCodes.LOCATION_PERMISSION_ERROR);
this.stop();
}
else {
this.onError(ShakingCodes.LOCATION_PERMISSION_ERROR);
}
} catch (err) {
console.warn(err)
}
},
_checkAndroidPermissions: async function(){
const permission = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION
)
// In Android SDK versions lower than 23, granted is a bool
return permission;
},
_getCurrentPosition: function(){
Geolocation.getCurrentPosition(
(position) => {
this.lat = position.coords.latitude;
this.lng = position.coords.longitude;
},
(error) => {
if(error.code === 1){
this.onError(ShakingCodes.LOCATION_PERMISSION_ERROR);
}
else {
this.onError(ShakingCodes.LOCATION_DISABLED);
}
},
{ enableHighAccuracy: true, timeout: 5000, maximumAge: 10000 }
);
},
_handleAppStateChange: function(nextAppState) {
if (nextAppState === 'active') {
this.background = false;
this._restart();
}
else {
this.background = true;
this._pause();
}
}
}