feat(step097): preserve B fixes for unified V15 source

This commit is contained in:
tei
2026-09-27 08:41:27 +09:00
parent b1239ac3b1
commit f3a44c7d5f
18 changed files with 711 additions and 36 deletions

View File

@@ -729,12 +729,8 @@ jni::jboolean NativeMapView::setContinuousLayerRange(JNIEnv& env,
}
jni::jboolean NativeMapView::setRenderSegmentPlan(JNIEnv& env, const jni::String& planJson) {
const auto plan = parseRenderSegmentPlanJSON(jni::Make<std::string>(env, planJson));
if (plan) return map->setRenderSegmentPlan(*plan);
RenderSegmentPlan invalid;
invalid.schemaVersion = 0;
map->setRenderSegmentPlan(std::move(invalid));
return false;
auto plan = renderSegmentPlanFromJSONOrInvalid(jni::Make<std::string>(env, planJson));
return map->setRenderSegmentPlan(std::move(plan));
}
jni::Local<jni::String> NativeMapView::getRenderSegmentDiagnostics(JNIEnv& env) {

View File

@@ -12,6 +12,7 @@ import androidx.annotation.FloatRange;
import androidx.annotation.IntRange;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.Size;
import androidx.annotation.UiThread;
@@ -43,6 +44,7 @@ import org.maplibre.android.log.Logger;
import org.maplibre.android.offline.OfflineRegionDefinition;
import org.maplibre.android.style.expressions.Expression;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -68,6 +70,7 @@ public final class MapLibreMap {
private final OnGesturesManagerInteractionListener onGesturesManagerInteractionListener;
private final List<Style.OnStyleLoaded> awaitingStyleGetters = new ArrayList<>();
private final List<OnDeveloperAnimationListener> developerAnimationStartedListeners;
private final List<OnDestroyListener> onDestroyListeners = new ArrayList<>();
@Nullable
private Style.OnStyleLoaded styleLoadedCallback;
@@ -91,6 +94,19 @@ public final class MapLibreMap {
private boolean debugActive;
private boolean started;
private boolean destroyed;
/** Internal map-lifetime observer used by map-owned integrations. */
@RestrictTo(RestrictTo.Scope.LIBRARY)
public interface OnDestroyListener {
void onDestroy();
}
/** Removable registration for an internal observer of this concrete map's destruction. */
@RestrictTo(RestrictTo.Scope.LIBRARY)
public interface OnDestroyListenerRegistration {
void remove();
}
MapLibreMap(NativeMap map, Transform transform, UiSettings ui, Projection projection,
OnGesturesManagerInteractionListener listener, CameraChangeDispatcher cameraChangeDispatcher,
@@ -254,6 +270,24 @@ public final class MapLibreMap {
}
}
/** Registers a removable observer for destruction of this concrete map instance. */
@NonNull
@RestrictTo(RestrictTo.Scope.LIBRARY)
public OnDestroyListenerRegistration addOnDestroyListener(@NonNull OnDestroyListener listener) {
if (destroyed) {
listener.onDestroy();
return () -> { };
}
onDestroyListeners.add(listener);
WeakReference<MapLibreMap> mapReference = new WeakReference<>(this);
return () -> {
MapLibreMap map = mapReference.get();
if (map != null) {
map.onDestroyListeners.remove(listener);
}
};
}
/**
* Called when the hosting Activity/Fragment onStart() method is called.
*/
@@ -304,6 +338,16 @@ public final class MapLibreMap {
* Called when the hosting Activity/Fragment onDestroy()/onDestroyView() method is called.
*/
void onDestroy() {
if (destroyed) {
return;
}
destroyed = true;
List<OnDestroyListener> listeners = new ArrayList<>(onDestroyListeners);
onDestroyListeners.clear();
for (OnDestroyListener listener : listeners) {
listener.onDestroy();
}
disposeWeatherRuntime();
locationComponent.onDestroy();
if (style != null) {
style.clear();

View File

@@ -0,0 +1,52 @@
package org.maplibre.android.maps;
import org.junit.Test;
import org.mockito.Mockito;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.Assert.assertEquals;
/** Exercises the concrete MapLibreMap.onDestroy() dispatch path used by Android integrations. */
public class MapLibreMapDestroyListenerTest {
private MapLibreMap newMap() {
MapLibreMap map = new MapLibreMap(
null,
null,
null,
null,
null,
Mockito.mock(CameraChangeDispatcher.class),
Collections.emptyList());
map.injectLocationComponent(Mockito.mock(org.maplibre.android.location.LocationComponent.class));
return map;
}
@Test
public void onDestroyNotifiesRegisteredListenerOnceAndLateRegistrationImmediately() {
MapLibreMap map = newMap();
AtomicInteger notifications = new AtomicInteger();
map.addOnDestroyListener(notifications::incrementAndGet);
map.onDestroy();
map.onDestroy();
assertEquals(1, notifications.get());
map.addOnDestroyListener(notifications::incrementAndGet);
assertEquals(2, notifications.get());
}
@Test
public void removedListenerIsNotCalledByOnDestroy() {
MapLibreMap map = newMap();
AtomicInteger notifications = new AtomicInteger();
MapLibreMap.OnDestroyListenerRegistration registration =
map.addOnDestroyListener(notifications::incrementAndGet);
registration.remove();
map.onDestroy();
assertEquals(0, notifications.get());
}
}

View File

@@ -3163,12 +3163,10 @@ static void *windowScreenContext = &windowScreenContext;
- (BOOL)setRenderSegmentPlanJSON:(NSString *)planJSON {
if (!_mbglMap) return NO;
const auto plan = mbgl::parseRenderSegmentPlanJSON(planJSON.UTF8String);
if (plan) return self.mbglMap.setRenderSegmentPlan(*plan);
mbgl::RenderSegmentPlan invalid;
invalid.schemaVersion = 0;
self.mbglMap.setRenderSegmentPlan(std::move(invalid));
return NO;
const char *utf8 = planJSON.UTF8String;
const NSUInteger length = [planJSON lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
auto plan = mbgl::renderSegmentPlanFromJSONOrInvalid(std::string(utf8, length));
return self.mbglMap.setRenderSegmentPlan(std::move(plan));
}
- (NSString *)renderSegmentDiagnosticsJSON {