summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/mbgl/map/map_observer.hpp1
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapChangeReceiver.java25
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java35
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java7
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapChangeReceiverTest.java33
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapChangeActivity.java1
-rwxr-xr-xplatform/android/src/native_map_view.cpp9
-rwxr-xr-xplatform/android/src/native_map_view.hpp1
-rw-r--r--platform/ios/src/MGLMapViewDelegate.h13
-rw-r--r--platform/macos/src/MGLMapView.mm14
-rw-r--r--platform/macos/src/MGLMapViewDelegate.h13
-rw-r--r--platform/macos/test/MGLMapViewDelegateIntegrationTests.swift2
-rw-r--r--scripts/changelog_staging/idle-event.json6
-rw-r--r--src/mbgl/map/map.cpp2
14 files changed, 161 insertions, 1 deletions
diff --git a/include/mbgl/map/map_observer.hpp b/include/mbgl/map/map_observer.hpp
index f63e5f2af3..9c3345d614 100644
--- a/include/mbgl/map/map_observer.hpp
+++ b/include/mbgl/map/map_observer.hpp
@@ -39,6 +39,7 @@ public:
virtual void onDidFinishRenderingMap(RenderMode) {}
virtual void onDidFinishLoadingStyle() {}
virtual void onSourceChanged(style::Source&) {}
+ virtual void onDidEnterIdle() {}
};
} // namespace mbgl
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapChangeReceiver.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapChangeReceiver.java
index a12c6c7fb3..f569ae3af9 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapChangeReceiver.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapChangeReceiver.java
@@ -26,6 +26,8 @@ class MapChangeReceiver implements NativeMapView.StateCallback {
= new CopyOnWriteArrayList<>();
private final List<MapView.OnDidFinishRenderingMapListener> onDidFinishRenderingMapListenerList
= new CopyOnWriteArrayList<>();
+ private final List<MapView.OnDidEnterIdleListener> onDidEnterIdleListenerList
+ = new CopyOnWriteArrayList<>();
private final List<MapView.OnDidFinishLoadingStyleListener> onDidFinishLoadingStyleListenerList
= new CopyOnWriteArrayList<>();
private final List<MapView.OnSourceChangedListener> onSourceChangedListenerList = new CopyOnWriteArrayList<>();
@@ -171,6 +173,20 @@ class MapChangeReceiver implements NativeMapView.StateCallback {
}
@Override
+ public void onDidEnterIdle() {
+ try {
+ if (!onDidEnterIdleListenerList.isEmpty()) {
+ for (MapView.OnDidEnterIdleListener listener : onDidEnterIdleListenerList) {
+ listener.onDidEnterIdle();
+ }
+ }
+ } catch (Throwable err) {
+ Logger.e(TAG, "Exception in onDidEnterIdle", err);
+ throw err;
+ }
+ }
+
+ @Override
public void onDidFinishLoadingStyle() {
try {
if (!onDidFinishLoadingStyleListenerList.isEmpty()) {
@@ -278,6 +294,14 @@ class MapChangeReceiver implements NativeMapView.StateCallback {
onDidFinishRenderingMapListenerList.remove(listener);
}
+ void addOnDidEnterIdleListener(MapView.OnDidEnterIdleListener listener) {
+ onDidEnterIdleListenerList.add(listener);
+ }
+
+ void removeOnDidEnterIdleListener(MapView.OnDidEnterIdleListener listener) {
+ onDidEnterIdleListenerList.remove(listener);
+ }
+
void addOnDidFinishLoadingStyleListener(MapView.OnDidFinishLoadingStyleListener listener) {
onDidFinishLoadingStyleListenerList.add(listener);
}
@@ -305,6 +329,7 @@ class MapChangeReceiver implements NativeMapView.StateCallback {
onDidFinishRenderingFrameList.clear();
onWillStartRenderingMapListenerList.clear();
onDidFinishRenderingMapListenerList.clear();
+ onDidEnterIdleListenerList.clear();
onDidFinishLoadingStyleListenerList.clear();
onSourceChangedListenerList.clear();
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
index b4c977cf15..8a29ae8070 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapView.java
@@ -691,13 +691,33 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
/**
* Remove a callback that's invoked when the map has finished rendering.
*
- * @param listener The callback that's invoked when the map has finished rendering
+ * @param listener The callback that's invoked when the map has has finished rendering.
*/
public void removeOnDidFinishRenderingMapListener(OnDidFinishRenderingMapListener listener) {
mapChangeReceiver.removeOnDidFinishRenderingMapListener(listener);
}
/**
+ * Set a callback that's invoked when the map has entered the idle state.
+ *
+ * @param listener The callback that's invoked when the map has entered the idle state.
+ */
+ public void addOnDidEnterIdleListener(OnDidEnterIdleListener listener) {
+ mapChangeReceiver.addOnDidEnterIdleListener(listener);
+ }
+
+ /**
+ * Remove a callback that's invoked when the map has entered the idle state.
+ *
+ * @param listener The callback that's invoked when the map has entered the idle state.
+ */
+ public void removeOnDidEnterIdleListener(OnDidEnterIdleListener listener) {
+ mapChangeReceiver.removeOnDidEnterIdleListener(listener);
+ }
+
+ /**
+
+ /**
* Set a callback that's invoked when the style has finished loading.
*
* @param listener The callback that's invoked when the style has finished loading
@@ -871,6 +891,19 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
}
/**
+ * Interface definition for a callback to be invoked when the map has entered the idle state.
+ * <p>
+ * {@link MapView#addOnDidEnterIdleListener(OnDidEnterIdleListener)}
+ * </p>
+ */
+ public interface OnDidEnterIdleListener {
+ /**
+ * Called when the map has entered the idle state.
+ */
+ void onDidEnterIdle();
+ }
+
+ /**
* Interface definition for a callback to be invoked when the map has loaded the style.
* <p>
* {@link MapView#addOnDidFailLoadingMapListener(OnDidFailLoadingMapListener)}
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
index bdbca4fbca..59840f5bf3 100755
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
@@ -1010,6 +1010,11 @@ final class NativeMapView {
}
@Keep
+ private void onDidEnterIdle() {
+ stateCallback.onDidEnterIdle();
+ }
+
+ @Keep
private void onDidFinishLoadingStyle() {
if (stateCallback != null) {
stateCallback.onDidFinishLoadingStyle();
@@ -1472,6 +1477,8 @@ final class NativeMapView {
void onDidFinishRenderingMap(boolean fully);
+ void onDidEnterIdle();
+
void onSourceChanged(String sourceId);
}
}
diff --git a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapChangeReceiverTest.java b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapChangeReceiverTest.java
index 061fa7819a..848cf4a804 100644
--- a/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapChangeReceiverTest.java
+++ b/platform/android/MapboxGLAndroidSDK/src/test/java/com/mapbox/mapboxsdk/maps/MapChangeReceiverTest.java
@@ -56,6 +56,9 @@ public class MapChangeReceiverTest {
private MapView.OnDidFinishRenderingMapListener onDidFinishRenderingMapListener;
@Mock
+ private MapView.OnDidEnterIdleListener onDidEnterIdleListener;
+
+ @Mock
private MapView.OnDidFinishLoadingStyleListener onDidFinishLoadingStyleListener;
@Mock
@@ -492,6 +495,36 @@ public class MapChangeReceiverTest {
}
@Test
+ public void testOnDidEnterIdleListener() {
+ mapChangeEventManager.addOnDidEnterIdleListener(onDidEnterIdleListener);
+ mapChangeEventManager.onDidEnterIdle();
+ verify(onDidEnterIdleListener).onDidEnterIdle();
+ mapChangeEventManager.removeOnDidEnterIdleListener(onDidEnterIdleListener);
+ mapChangeEventManager.onDidEnterIdle();
+ verify(onDidEnterIdleListener).onDidEnterIdle();
+
+ mapChangeEventManager.addOnDidEnterIdleListener(onDidEnterIdleListener);
+ Logger.setLoggerDefinition(loggerDefinition);
+ Exception exc = new RuntimeException();
+ doThrow(exc).when(onDidEnterIdleListener).onDidEnterIdle();
+ try {
+ mapChangeEventManager.onDidEnterIdle();
+ Assert.fail("The exception should've been re-thrown.");
+ } catch (RuntimeException throwable) {
+ verify(loggerDefinition).e(anyString(), anyString(), eq(exc));
+ }
+
+ Error err = new ExecutionError("", new Error());
+ doThrow(err).when(onDidEnterIdleListener).onDidEnterIdle();
+ try {
+ mapChangeEventManager.onDidEnterIdle();
+ Assert.fail("The exception should've been re-thrown.");
+ } catch (ExecutionError throwable) {
+ verify(loggerDefinition).e(anyString(), anyString(), eq(err));
+ }
+ }
+
+ @Test
public void testOnDidFinishLoadingStyleListener() {
mapChangeEventManager.addOnDidFinishLoadingStyleListener(onDidFinishLoadingStyleListener);
mapChangeEventManager.onDidFinishLoadingStyle();
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapChangeActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapChangeActivity.java
index 75a21157e9..7fe4a47217 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapChangeActivity.java
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/maplayout/MapChangeActivity.java
@@ -32,6 +32,7 @@ public class MapChangeActivity extends AppCompatActivity {
mapView.addOnDidFinishLoadingStyleListener(() -> Timber.v("OnDidFinishLoadingStyle"));
mapView.addOnDidFinishRenderingFrameListener(fully -> Timber.v("OnDidFinishRenderingFrame: fully: %s", fully));
mapView.addOnDidFinishRenderingMapListener(fully -> Timber.v("OnDidFinishRenderingMap: fully: %s", fully));
+ mapView.addOnDidEnterIdleListener(() -> Timber.v("OnDidEnterIdle"));
mapView.addOnSourceChangedListener(sourceId -> Timber.v("OnSourceChangedListener: source with id: %s", sourceId));
mapView.addOnWillStartLoadingMapListener(() -> Timber.v("OnWillStartLoadingMap"));
mapView.addOnWillStartRenderingFrameListener(() -> Timber.v("OnWillStartRenderingFrame"));
diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp
index a750baa62c..d724afa923 100755
--- a/platform/android/src/native_map_view.cpp
+++ b/platform/android/src/native_map_view.cpp
@@ -186,6 +186,15 @@ void NativeMapView::onDidFinishRenderingMap(MapObserver::RenderMode mode) {
javaPeer.get(*_env).Call(*_env, onDidFinishRenderingMap, (jboolean) (mode != MapObserver::RenderMode::Partial));
}
+void NativeMapView::onDidEnterIdle() {
+ assert(vm != nullptr);
+
+ android::UniqueEnv _env = android::AttachEnv();
+ static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
+ static auto onDidEnterIdle = javaClass.GetMethod<void ()>(*_env, "onDidEnterIdle");
+ javaPeer.get(*_env).Call(*_env, onDidEnterIdle);
+}
+
void NativeMapView::onDidFinishLoadingStyle() {
assert(vm != nullptr);
diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp
index b1f8354d46..44e9259b2a 100755
--- a/platform/android/src/native_map_view.hpp
+++ b/platform/android/src/native_map_view.hpp
@@ -67,6 +67,7 @@ public:
void onDidFinishRenderingFrame(MapObserver::RenderMode) override;
void onWillStartRenderingMap() override;
void onDidFinishRenderingMap(MapObserver::RenderMode) override;
+ void onDidEnterIdle() override;
void onDidFinishLoadingStyle() override;
void onSourceChanged(mbgl::style::Source&) override;
diff --git a/platform/ios/src/MGLMapViewDelegate.h b/platform/ios/src/MGLMapViewDelegate.h
index 9175669dc9..8c1d3d98c0 100644
--- a/platform/ios/src/MGLMapViewDelegate.h
+++ b/platform/ios/src/MGLMapViewDelegate.h
@@ -240,6 +240,19 @@ NS_ASSUME_NONNULL_BEGIN
- (void)mapViewDidFinishRenderingFrame:(MGLMapView *)mapView fullyRendered:(BOOL)fullyRendered;
/**
+ Tells the delegate that the map view is entering an idle state, and no more
+ drawing will be necessary until new data is loaded or there is some interaction
+ with the map.
+
+ - No camera transitions are in progress
+ - All currently requested tiles have loaded
+ - All fade/transition animations have completed
+
+ @param mapView The map view that has just entered the idle state.
+ */
+- (void)mapViewDidEnterIdle:(MGLMapView *)mapView;
+
+/**
Tells the delegate that the map has just finished loading a style.
This method is called during the initialization of the map view and after any
diff --git a/platform/macos/src/MGLMapView.mm b/platform/macos/src/MGLMapView.mm
index 2f379ab406..eb877216d2 100644
--- a/platform/macos/src/MGLMapView.mm
+++ b/platform/macos/src/MGLMapView.mm
@@ -934,6 +934,16 @@ public:
}
}
+- (void)mapViewDidEnterIdle {
+ if (!_mbglMap) {
+ return;
+ }
+
+ if ([self.delegate respondsToSelector:@selector(mapViewDidEnterIdle)]) {
+ [self.delegate mapViewDidEnterIdle:self];
+ }
+}
+
- (void)mapViewDidFinishLoadingStyle {
if (!_mbglMap) {
return;
@@ -3050,6 +3060,10 @@ public:
bool fullyRendered = mode == mbgl::MapObserver::RenderMode::Full;
[nativeView mapViewDidFinishRenderingMapFullyRendered:fullyRendered];
}
+
+ void onDidEnterIdle() override {
+ [nativeView mapViewDidEnterIdle];
+ }
void onDidFinishLoadingStyle() override {
[nativeView mapViewDidFinishLoadingStyle];
diff --git a/platform/macos/src/MGLMapViewDelegate.h b/platform/macos/src/MGLMapViewDelegate.h
index 2a8b28c1b4..ad59f5bd39 100644
--- a/platform/macos/src/MGLMapViewDelegate.h
+++ b/platform/macos/src/MGLMapViewDelegate.h
@@ -152,6 +152,19 @@ NS_ASSUME_NONNULL_BEGIN
- (void)mapViewDidFinishRenderingFrame:(MGLMapView *)mapView fullyRendered:(BOOL)fullyRendered;
/**
+ Tells the delegate that the map view is entering an idle state, and no more
+ drawing will be necessary until new data is loaded or there is some interaction
+ with the map.
+
+ - No camera transitions are in progress
+ - All currently requested tiles have loaded
+ - All fade/transition animations have completed
+
+ @param mapView The map view that has just entered the idle state.
+ */
+- (void)mapViewDidEnterIdle:(MGLMapView *)mapView;
+
+/**
Tells the delegate that the map has just finished loading a style.
This method is called during the initialization of the map view and after any
diff --git a/platform/macos/test/MGLMapViewDelegateIntegrationTests.swift b/platform/macos/test/MGLMapViewDelegateIntegrationTests.swift
index 00635d97eb..109c279a09 100644
--- a/platform/macos/test/MGLMapViewDelegateIntegrationTests.swift
+++ b/platform/macos/test/MGLMapViewDelegateIntegrationTests.swift
@@ -24,6 +24,8 @@ extension MGLMapViewDelegateIntegrationTests: MGLMapViewDelegate {
func mapViewDidFinishRenderingFrame(_ mapView: MGLMapView, fullyRendered: Bool) {}
func mapViewDidFinishRenderingMap(_ mapView: MGLMapView, fullyRendered: Bool) {}
+
+ func mapViewDidEnterIdle(_ mapView: MGLMapView) {}
func mapViewDidFailLoadingMap(_ mapView: MGLMapView, withError error: Error) {}
diff --git a/scripts/changelog_staging/idle-event.json b/scripts/changelog_staging/idle-event.json
new file mode 100644
index 0000000000..96db06eb99
--- /dev/null
+++ b/scripts/changelog_staging/idle-event.json
@@ -0,0 +1,6 @@
+{
+ "core": "Add onDidEnterIdle to MapObserver, which fires whenever render completes and no repaint is scheduled.",
+ "darwin": "Add mapViewDidEnterIdle to MGLMapViewDelegate, which fires whenever render completes and no repaint is scheduled.",
+ "android": "Add onDidEnterIdle listener to MapView, which fires whenever render completes and no repaint is scheduled.",
+ "issue": 13469
+}
diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp
index c3bebae837..5e80f1512b 100644
--- a/src/mbgl/map/map.cpp
+++ b/src/mbgl/map/map.cpp
@@ -209,6 +209,8 @@ void Map::Impl::onDidFinishRenderingFrame(RenderMode renderMode, bool needsRepai
if (needsRepaint || transform.inTransition()) {
onUpdate();
+ } else if (rendererFullyLoaded) {
+ observer.onDidEnterIdle();
}
} else if (stillImageRequest && rendererFullyLoaded) {
auto request = std::move(stillImageRequest);