Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import android.os.Build;
import android.util.Log;

import com.annimon.stream.Optional;
import com.annimon.stream.Stream;
import com.annimon.stream.function.Function;
Expand All @@ -26,38 +27,68 @@ public class GeofenceController {
}

public void start(Function<? super Object, ? super Object> successCallback, Function<? super Object, ? super Object> failureCallback) {
if (mGeofenceRepository.getGeofences().isEmpty()) {
start(successCallback, failureCallback, mGeofenceRepository.getGeofences());
}

public void start(Function<? super Object, ? super Object> successCallback, Function<? super Object, ? super Object> failureCallback, List<Geofence> geofences) {
if (geofences.isEmpty()) {
Log.w(getClass().getSimpleName(), "Starting geofences with none set, exiting");
return;
}
mGeofenceActivator.setGeofencesActivated(true);
mGeofenceEngine.addGeofences(mGeofenceRepository.getGeofences(), successCallback, failureCallback);

mGeofenceEngine.addGeofences(
geofences,
successResult -> {
mGeofenceActivator.setGeofencesActivated(true);
return successCallback.apply(successResult);
},
failureCallback
);
}

public void stop(Function<? super Object, ? super Object> successCallback, Function<? super Object, ? super Object> failureCallback) {
if (mGeofenceRepository.getGeofences().isEmpty()) {
Log.w(getClass().getSimpleName(), "Stopping geofences with none set, exiting");
return;
}
mGeofenceActivator.setGeofencesActivated(false);
List<String> geofenceIds = getGeofenceIds();

List<String> geofenceIds = getGeofenceIdsToRemove();
if (!geofenceIds.isEmpty()) {
mGeofenceEngine.removeGeofences(geofenceIds, successCallback, failureCallback);
mGeofenceEngine.removeGeofences(
geofenceIds,
successResult -> {
mGeofenceActivator.setGeofencesActivated(false);
return successCallback.apply(successResult);
},
failureCallback
);
}
}

public void restart(Function<? super Object, ? super Object> successCallback, Function<? super Object, ? super Object> failureCallback) {
if (mGeofenceActivator.areGeofencesActivated()) {
start(successCallback, failureCallback);
final List<Geofence> geofences = Stream.of(mGeofenceRepository.getGeofences())
.filterNot(GeofenceController::isInvalidGeofence)
.toList();

start(successCallback, failureCallback, geofences);
}
}

private List<String> getGeofenceIds() {
private List<String> getGeofenceIdsToRemove() {
return Stream.of(mGeofenceRepository.getGeofences())
.filterNot(GeofenceController::isInvalidGeofence)
.map(Geofence::getId)
.toList();
}

private static boolean isInvalidGeofence(Geofence geofence) {
// Removes geofences where `setGeofencesActivated` was set to `true` before crashing
// They cannot be restarted (it'll crash) and they cannot be removed (they were never added)
// Geofences added from 1.2.1 should never meet this condition
return geofence.getRadius() == 0;
}

public void addGeofences(List<Geofence> geofences) {
mGeofenceRepository.addGeofences(geofences);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
package co.uk.hive.reactnativegeolocation;

import co.uk.hive.reactnativegeolocation.geofence.*;
import com.annimon.stream.Stream;
import com.annimon.stream.function.Function;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;

import java.util.Arrays;
import java.util.List;

import co.uk.hive.reactnativegeolocation.geofence.Geofence;
import co.uk.hive.reactnativegeolocation.geofence.GeofenceActivator;
import co.uk.hive.reactnativegeolocation.geofence.GeofenceController;
import co.uk.hive.reactnativegeolocation.geofence.GeofenceEngine;
import co.uk.hive.reactnativegeolocation.geofence.GeofenceRepository;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;

@SuppressWarnings("Convert2Lambda")
Expand All @@ -32,11 +43,18 @@ public class GeofenceControllerTest {
@InjectMocks
private GeofenceController mSut;

private List<Geofence> mGeofences = Arrays.asList(
private List<Geofence> mMixedGeofences = Arrays.asList(
TestData.createGeofence("1"),
TestData.createGeofence("2"));
TestData.createGeofence("2"),
TestData.createInvalidGeofence("3")
);

private List<Geofence> mGeofences = mMixedGeofences.subList(0, mMixedGeofences.size() - 1);

@Mock
private Function<? super Object, ? super Object> mSuccessCallback;

private Function<? super Object, ? super Object> mCallback = new Function<Object, Object>() {
private Function<? super Object, ? super Object> mFailureCallback = new Function<Object, Object>() {
@Override
public Object apply(Object ignored) {
return null;
Expand All @@ -46,20 +64,34 @@ public Object apply(Object ignored) {
@Test
public void startsGeofences() {
given(mGeofenceRepository.getGeofences()).willReturn(mGeofences);
doAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return ((Function) invocation.getArgument(1)).apply(null);
}
}).when(mGeofenceEngine).addGeofences(anyList(), any(Function.class), any(Function.class));

mSut.start(mCallback, mCallback);
mSut.start(mSuccessCallback, mFailureCallback);

verify(mGeofenceEngine).addGeofences(mGeofences, mCallback, mCallback);
verify(mGeofenceEngine).addGeofences(eq(mGeofences), any(Function.class), eq(mFailureCallback));
verify(mSuccessCallback).apply(null);
verify(mGeofenceActivator).setGeofencesActivated(true);
}

@Test
public void stopsGeofences() {
given(mGeofenceRepository.getGeofences()).willReturn(mGeofences);
doAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return ((Function) invocation.getArgument(1)).apply(null);
}
}).when(mGeofenceEngine).removeGeofences(anyList(), any(Function.class), any(Function.class));

mSut.stop(mCallback, mCallback);
mSut.stop(mSuccessCallback, mFailureCallback);

verify(mGeofenceEngine).removeGeofences(Arrays.asList("1", "2"), mCallback, mCallback);
verify(mGeofenceEngine).removeGeofences(eq(Arrays.asList("1", "2")), any(Function.class), eq(mFailureCallback));
verify(mSuccessCallback).apply(null);
verify(mGeofenceActivator).setGeofencesActivated(false);
}

Expand All @@ -68,12 +100,33 @@ public void restartsGeofences() {
given(mGeofenceRepository.getGeofences()).willReturn(mGeofences);
given(mGeofenceActivator.areGeofencesActivated()).willReturn(true);

mSut.restart(mCallback, mCallback);
mSut.restart(mSuccessCallback, mFailureCallback);

verify(mGeofenceActivator).areGeofencesActivated();
verify(mGeofenceEngine).addGeofences(eq(mGeofences), any(), any());
}

@Test
public void filtersInvalidGeofencesOnRestart() {
given(mGeofenceRepository.getGeofences()).willReturn(mMixedGeofences);
given(mGeofenceActivator.areGeofencesActivated()).willReturn(true);

mSut.restart(mSuccessCallback, mFailureCallback);

verify(mGeofenceActivator).areGeofencesActivated();
verify(mGeofenceEngine).addGeofences(eq(mGeofences), any(), any());
}

@Test
public void filtersInvalidGeofencesOnStop() {
given(mGeofenceRepository.getGeofences()).willReturn(mMixedGeofences);

mSut.stop(mSuccessCallback, mFailureCallback);

final List<String> expectedIds = Stream.of(mGeofences).map(Geofence::getId).toList();
verify(mGeofenceEngine).removeGeofences(eq(expectedIds), any(Function.class), eq(mFailureCallback));
}

@Test
public void interactsWithRepository() {
mSut.addGeofences(mGeofences);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

class TestData {
static Geofence createGeofence(String id) {
return new Geofence(id, 1, 0, 0, false, false, false, 0);
}

static Geofence createInvalidGeofence(String id) {
return new Geofence(id, 0, 0, 0, false, false, false, 0);
}
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@connected-home/react-native-geolocation",
"version": "1.2.0",
"version": "1.2.1",
"description": "Basic geolocation + geofencing. Delegates to react-native-background-geolocation for iOS.",
"main": "index.js",
"scripts": {
Expand Down