react-native-sound always showing current playback time as 0 - react-native

I am using react-native-sound to create an audio player, I want to get the current playback timings of the sound while it plays however when I use the getCurrentTime callback, it is only showing 0. Below is the code for the same:
const listenaudio = (audio, id) => {
console.log("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$")
if (playAudio != null) {
playAudio.release();
}
setsoundpause(id);
playAudio = new Sound(audio, Sound.MAIN_BUNDLE, (error) => {
console.log("rrr")
if (error) {
return;
}
// loaded successfully
// Play the sound with an onEnd callback
playAudio.play((success) => {
if (success) {
setsoundpause(0);
} else {
}
});
});
playAudio.setVolume(1);
playAudio.getCurrentTime((seconds) => console.log('at ' + seconds)); // here
};
It would be great if anyone could suggest how should I do it? Should I use seInterval() ?
Any leads would be appreciated.

I've made a player recently using react-native-sound, where I call getCurrentTime method after some milliseconds to get realtime player time.
My function to play audio
const timerRef = React.useRef();
const playSound = () => {
// Sound.MAIN_BUNDLE
try {
if (!playAudio || !playAudio.isLoaded()) return;
if (timerRef.current) clearInterval(timerRef.current);
timerRef.current = setInterval(() => {
playAudio.getCurrentTime((seconds, isPlaying) => {
setCurrentPlayTime(seconds); // HERE is time of current player
});
}, 300);
playAudio.play(success => {
if (!success) {
console.log('playback failed due to audio decoding errors');
}
stopSound();
});
} catch (err) {
console.log("can't play voice message", err);
}
};

Related

How can I keep redux saga responsive when app is in the background?

Im using React-Native and I have a saga that manages the state of a websocket. It implements re-tries and receives dispatches for things like location change to send it up to the server via websocket. I am using #mauron85/react-native-background-geolocation to grab location events in the root component of my app and I am dispatching them to redux. The route saga has a spawned generator that listens for this and will send it up. When the app is in foreground(currently in use) the socket can recover from network loss and continue receiving location events however when the app goes to the background as long as the socket is open, the saga responds to the events and can still send them up. As soon as I mark airplane mode and lose the socket, the saga stops responding to retries and other dispatch events such as the location update. When i remove airplane mode (while still in the background) it is not able to re-establish connection and receive dispatches. How can I approach this to be able to allow the app to recover without it ever transitioning back to the foreground.
Saga Code:
function* sender(socket: WebSocket, eventType, data?) {
const { token } = yield select(authState);
if (socket && socket.readyState === WebSocket.OPEN) {
let localData = {};
if (data) {
localData = data;
}
console.log(JSON.stringify({ type: eventType, data: { token, ...localData } }));
socket.send(JSON.stringify({ type: eventType, data: { token, ...localData } }));
if (eventType === ROUTE_SOCKET_MESSSAGE_TYPES.PING) {
const res = yield race({
success: take(ROUTE_SOCKET_MESSAGE_RECEIVED),
timeout: delay(SOCKET_PING_RESPONSE_EXPECTED)
});
if (res.timeout) {
console.log('ENCOUNTERED LAG IN MESSAGE RECEIPT');
yield put(markNetworkLost());
yield put({ type: ROUTE_SOCKET_RETRY_ACTION_TYPE });
} else {
yield put(markHasNetwork());
}
}
}
}
function* pinger(socket) {
while (true) {
yield call(sender, socket, ROUTE_SOCKET_MESSSAGE_TYPES.PING);
yield delay(FORTY_FIVE_SECONDS);
}
}
function* sendPosition(socket) {
while (true) {
const { latitude, longitude } = yield select(locationState);
if (latitude && longitude) {
yield call(positionDispatcher, socket, {
lat: latitude,
lng: longitude
});
}
yield take('locations/ON_LOCATION_CHANGE');
}
}
function* locationApiRequest() {
try {
const res = yield call(getLocation);
yield put(onPharmacyDetailsReceived(res.data));
} catch (error) {
console.log('Could not fetch pharmacy location details', error);
}
}
function* positionDispatcher(socket, position) {
yield call(sender, socket, ROUTE_SOCKET_MESSSAGE_TYPES.DRIVER_LOCATION, position);
}
function dispatchPhotoFetch(route) {
route.routeStops.forEach((stop) => {
if (stop.address.photos) {
console.log(stop.address.photos);
PhotoService.readLocalFileOrDownload(
stop.address.id,
stop.address.photos.map((photo) => new AddressPhoto(photo))
);
}
});
}
function* socketChannelListener(socketChannel, socket) {
let pingerProcess;
let positionProcess;
while (true) {
const payload = yield take(socketChannel); // take incoming message
const { type: baseSocketResponseType } = payload;
switch (baseSocketResponseType) {
case 'open':
yield fork(locationApiRequest);
pingerProcess = yield fork(pinger, socket);
positionProcess = yield fork(sendPosition, socket);
PendingTasks.activate(); // activate pending task upload when the socket opens
break;
case 'error':
console.log('ERROR', payload.reason, payload);
break;
case 'close':
console.log('CLOSE', payload.reason, payload);
if (payload.reason === 'Invalid creds') {
// console.log('ENCOUNTERED INVALID CREDENTIALS/STALE TOKEN');
yield put(autoLogout());
} else {
yield put(markNetworkLost());
yield put({ type: ROUTE_SOCKET_RETRY_ACTION_TYPE });
}
// console.log('CALLING CLOSE');
yield cancel(pingerProcess);
yield cancel(positionProcess);
return;
default:
break;
}
let type;
let data;
let operation;
let parsedData;
if (payload && payload.data) {
parsedData = JSON.parse(payload.data);
const { type: incomingType, data: incomingData, op } = parsedData;
type = incomingType;
data = incomingData;
operation = op;
}
// console.log('PARSED MESSAGE', parsedData);
switch (type) {
case ROUTE_SOCKET_TYPES.AUTH_STATUS:
switch (data) {
case PINGER_RESPONSE_TYPES.AUTH_SUCCESS:
yield put({ type: ROUTE_SOCKET_MESSAGE_RECEIVED });
break;
case PINGER_RESPONSE_TYPES.TOKEN_EXPIRED:
break;
default:
break;
}
break;
case ROUTE_SOCKET_TYPES.DRIVER_ROUTE:
console.log(
`received DRIVER_ROUTE with ${JSON.stringify(data.routeStops.length)} stop(s)`,
operation,
data
);
switch (operation) {
case DRIVER_ROUTE_OPERATIONS.I: {
const currentRoute = yield select(driverRouteState);
// if we do pick up a route..
if (!currentRoute || currentRoute.id !== data.id) {
yield fork(dispatchPhotoFetch, data);
yield put(onDriverRouteChange(data));
}
break;
}
case DRIVER_ROUTE_OPERATIONS.U:
// i believe we will ignore this if there are records in sqlite?
// create address photo objects first?
// yield put(onDriverRouteUpdate(data));
break;
case DRIVER_ROUTE_OPERATIONS.D:
// TODO: deletion of a route needs to be handled most likely.
break;
default:
break;
}
break;
case ROUTE_SOCKET_TYPES.DRIVER_ROUTE_LOG_REQUEST:
break;
default:
break;
}
}
}
function createSocketChannel(token) {
// TODO: we need to pull this from config....
const socket = new WebSocket(`${ROUTE_SOCKET_URL}${token}`);
// establishes a redux emitter that saga can wait for (like an action)
return {
socket,
socketChannel: eventChannel((emit) => {
socket.onmessage = (event) => {
// console.log('--MESSAGE received--', event);
emit(event);
};
socket.onopen = (evt) => {
emit(evt);
};
socket.onclose = (evt) => {
emit(evt);
};
socket.onerror = (evt) => {
emit(evt);
};
const unsubscribe = () => {
socket.onmessage = null;
};
return unsubscribe;
})
};
}
function* routeSocket(token): any {
const { socketChannel, socket } = yield call(createSocketChannel, token);
yield fork(socketChannelListener, socketChannel, socket);
}
// this method will be retried
export function* RouteSocketSaga(): any {
let task;
while (true) {
const routeSocketAction = yield take([
'DB_INITIALIZED',
PERSIST_REHYDRATE,
ON_AUTH_CHANGE,
ROUTE_SOCKET_RETRY_ACTION_TYPE // retry will attempt to start the saga again..
]);
console.log(routeSocketAction);
if (routeSocketAction.type === PERSIST_REHYDRATE) {
const currentRoute = yield select(driverRouteState);
if (currentRoute) {
yield fork(dispatchPhotoFetch, currentRoute);
}
}
// if the action is to retry, we will wait 5 seconds..
if (routeSocketAction.type === ROUTE_SOCKET_RETRY_ACTION_TYPE) {
yield delay(WAIT_BEFORE_RETRY_TIME);
}
const { token } = yield select(authState);
if (token) {
if (task) {
yield cancel(task);
}
task = yield fork(routeSocket, token);
}
}
}
Component code sending the location
BackgroundGeolocation.configure({
desiredAccuracy: BackgroundGeolocation.HIGH_ACCURACY,
stationaryRadius: 1,
distanceFilter: 1,
notificationTitle: 'Background tracking',
debug: false,
startOnBoot: false,
url: null,
stopOnTerminate: true,
locationProvider: BackgroundGeolocation.ACTIVITY_PROVIDER,
interval: 1000,
fastestInterval: 1000,
activitiesInterval: 1000,
startForeground: false,
stopOnStillActivity: false
});
BackgroundGeolocation.on('background', () => {
console.log('[INFO] App is in background');
setAppState(true);
});
BackgroundGeolocation.on('foreground', () => {
console.log('[INFO] App is in foreground');
setAppState(false);
});
BackgroundGeolocation.on('location', (location) => {
dispatch(onLocationChange({ latitude: location.latitude, longitude: location.longitude }));
console.log('LOCATION', location);
});
BackgroundGeolocation.on('stationary', (stationaryLocation) => {
console.log('STATIONARY LOCATION', stationaryLocation);
});
Ended up moving socket logic to the root component of my app and responding to location updates there which allowed me to keep the socket alive in the background and send location updates. E.g
locationSendingEventHandler = (location) => {
const {
coords: { latitude, longitude }
} = location;
console.log('LOCATION', location);
const { token } = this.props;
let socketReopenTriggered = false;
if ((!this.socket || (this.socket && this.socket.readyState >= 2)) && token) {
// 2: CLOSING 3: CLOSED ... two cases where location event should trigger socket to re-open and there is a token
socketReopenTriggered = true;
if (this.setupSocketTimeout) {
clearTimeout(this.setupSocketTimeout);
this.setupSocketTimeout = null;
}
this.setUpSocket('from location');
}
if (!socketReopenTriggered && this.socket && this.socket.readyState === 1) {
console.log('SENDING LOCATION', location, this.socket);
this.socket.send(
JSON.stringify({
type: 'DRIVER_LOCATION',
data: { token, ...{ lat: latitude, lng: longitude } }
})
);
}
};

bleManager.startDeviceScan not working on iOS react-native-ble-plx

I am trying to pair a gateway to React native app using the react-native-ble-plx.
The below source code works fine in Android wheras in iOS, the bleManager.startDeviceScan() is not triggered. Nothing happens after this step.
Any help is much appreciated!
Source code:
const connectBLE = () => {
const subscription = bleManager.onStateChange(async (state) => {
if (state === 'PoweredOn') {
subscription.remove();
scanAndConnect();
}
};
}
const scanAndConnect = () => {
bleManager.startDeviceScan(null, null, async (error, device) => {
if (error) {
showToast(error, 'error');
console.log('Handle error - scanning will be stopped automatically');
return;
}
console.log('Devices');
console.log(device.name);
// Check if it is a device you are looking for based on device name
if (device.name === "BLE_0006") {
// Stop scanning as we have found the device.
bleManager.stopDeviceScan();
// Establish Device connection.
device
.connect()
.then((deviceData) => {
/** Show Toast on Device disconnect */
bleManager.onDeviceDisconnected(
deviceData.id,
(connectionError, connectionData) => {
if (connectionError) {
console.log(connectionError);
}
console.log('Device is disconnected');
console.log(connectionData);
},
);
/** Discover All Services and Characteristics */
return device.discoverAllServicesAndCharacteristics();
})
.then(async (deviceObject) => {
console.log('deviceObject');
console.log(deviceObject);
/** Subscribe for the Readable service */
device.monitorCharacteristicForService(
Enum.bleConnectionInfo.customServiceUUID,
Enum.bleConnectionInfo.readCharacteristicUUID,
(error, characteristic) => {
if (error) {
console.log('Error in monitorCharacteristicForService');
console.log(error.message);
return;
}
console.log(characteristic.uuid, characteristic.value);
]);
},
);
})
.catch((error) => {
console.warn(error);
showToast(error, 'error');
});
}
});
}
This might be helpful for someone!
The Issue was resolved by moving the bleManager() initialization outside the functional component.

React Native UseEffect function is not working according to order

I want to get user's current location and set it into AsyncStorage a array. I will do it in the useEffect hook. But the problem is my functions are not working that according to given order. Here are my code
useEffect(() => {
getUserLocation();
setUserLocation();
check();
}, []);
/*Get User's Currunt Location*/
const getUserLocation = () => {
GetLocation.getCurrentPosition({
enableHighAccuracy: true,
timeout: 15000,
})
.then((location) => {
var lt = location.latitude;
var lg = location.longitude;
setlatitude(lt);
setlongitude(lg);
console.log("getUserLocation", lt, lg);
})
.catch((error) => {
const { code, message } = error;
console.warn(code, message);
});
};
/*Set User's Currunt Location to AsyncStorage*/
const setUserLocation = async () => {
try {
await AsyncStorage.setItem("user_location", JSON.stringify(userLocation));
console.log("setUserLocation", userLocation);
} catch (error) {
console.log("error setting user location");
}
};
const check = () => {
AsyncStorage.getItem("user_location", (err, result) => {
if (result !== null) {
console.log("check", result);
setlatitude(result.latitude);
setlongitude(result.longitude);
} else {
console.log("Data Not Found");
}
});
};
Whenever you use .then you are scheduling your code to run at some point in the future, when the promise has completed. So setUserLocation runs before the then of getUserLocation.
Also, it looks like your getUserLocation set react state, which won't be available until the next render. We use effects to manage this.
// Get the location on mount
useEffect(getUserLocation, []);
// Whenever the location updates, set it into storage
useEffect(() => setUserLocation().then(check), [latitude, longitude]);

Why expo background location stop sending location after 5 min?

I defined the task in App.js, everthing works fine! but after 5 min it stops sending the location, I dont know why.
I'm using this package: https://docs.expo.io/versions/latest/sdk/location/#background-location-methods
TaskManager.defineTask(LOCATION_TASK_NAME, async ({ data, error }) => {
console.log("Esta entrando al task")
if (error) {
console.log(error)
// Error occurred - check `error.message` for more details.
return;
}
if (data) {
const { locations } = data;
let getLastTime = await AsyncStorage.getItem('lastTime');
if(getLastTime){
let timeInterval = moment(new Date()).diff(getLastTime,'seconds')
if(timeInterval<=10) {
return
}
}
await LocationAPI.sendCurrentLocation(locations[0].coords).then(async res=>{
await AsyncStorage.setItem('lastTime', new Date().toString())
}).catch(err=>{
console.log(err)
console.log("en el task")
})
// do something with the locations captured in the background
}
});

Playing sound on cards

I have application with cards on stack (a lot of cards - about 500)
I card component I added:
function getVoice() {
try {
const mySound = new Sound(getSoundName(id), Sound.MAIN_BUNDLE,
(error) => {
if (error) {
Alert.alert('Error:', error.message)
return;
}
})
if (mySound != null) {
mySound.play((success, error) => {
if (success) {
Alert.alert('success')
} else {
Alert.alert('err')
}
mySound.release()
})
}
}
catch{
}
};
An alert is triggered here err
What is wrong?
I tried another option.
I called new Sound() at the beginning of component loading
Here the sound was playing but works on up to 8 cards. Later there is the same mistake as now
It works fine:
const getVoice = () => {
try {
const sound = new Sound(getSoundName(id), null, (error) => {
if (error) {
return;
}
sound.play();
});
}
catch{
}
};