[core, darwin, android] Add onDidEnterIdle to MapObserver.

didEnterIdle fires whenever render completes and no repaint is scheduled.
This commit is contained in:
Chris Loer
2018-12-06 13:31:42 +02:00
committed by Chris Loer
parent 13e117b501
commit 70c2d5a4ca
14 changed files with 161 additions and 1 deletions

View File

@@ -39,6 +39,7 @@ public:
virtual void onDidFinishRenderingMap(RenderMode) {}
virtual void onDidFinishLoadingStyle() {}
virtual void onSourceChanged(style::Source&) {}
virtual void onDidEnterIdle() {}
};
} // namespace mbgl

View File

@@ -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<>();
@@ -170,6 +172,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 {
@@ -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();
}

View File

@@ -691,12 +691,32 @@ 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.
*
@@ -870,6 +890,19 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
void onDidFinishRenderingMap(boolean fully);
}
/**
* 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>

View File

@@ -1009,6 +1009,11 @@ final class NativeMapView {
}
}
@Keep
private void onDidEnterIdle() {
stateCallback.onDidEnterIdle();
}
@Keep
private void onDidFinishLoadingStyle() {
if (stateCallback != null) {
@@ -1472,6 +1477,8 @@ final class NativeMapView {
void onDidFinishRenderingMap(boolean fully);
void onDidEnterIdle();
void onSourceChanged(String sourceId);
}
}

View File

@@ -55,6 +55,9 @@ public class MapChangeReceiverTest {
@Mock
private MapView.OnDidFinishRenderingMapListener onDidFinishRenderingMapListener;
@Mock
private MapView.OnDidEnterIdleListener onDidEnterIdleListener;
@Mock
private MapView.OnDidFinishLoadingStyleListener onDidFinishLoadingStyleListener;
@@ -491,6 +494,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);

View File

@@ -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"));

View File

@@ -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);

View File

@@ -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;

View File

@@ -239,6 +239,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.

View File

@@ -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];

View File

@@ -151,6 +151,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.

View File

@@ -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) {}

View File

@@ -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
}

View File

@@ -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);