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 3a27a9a11..5932f6fbf 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 @@ -33,6 +33,8 @@ class MapChangeReceiver implements NativeMapView.StateCallback { private final List onSourceChangedListenerList = new CopyOnWriteArrayList<>(); private final List onStyleImageMissingListenerList = new CopyOnWriteArrayList<>(); + private final List onCanRemoveUnusedStyleImageListenerList + = new CopyOnWriteArrayList<>(); @Override public void onCameraWillChange(boolean animated) { @@ -230,6 +232,29 @@ class MapChangeReceiver implements NativeMapView.StateCallback { } } + @Override + public boolean onCanRemoveUnusedStyleImage(String imageId) { + if (onCanRemoveUnusedStyleImageListenerList.isEmpty()) { + return true; + } + + try { + if (!onCanRemoveUnusedStyleImageListenerList.isEmpty()) { + boolean canRemove = true; + for (MapView.OnCanRemoveUnusedStyleImageListener listener : onCanRemoveUnusedStyleImageListenerList) { + canRemove &= listener.onCanRemoveUnusedStyleImage(imageId); + } + + return canRemove; + } + } catch (Throwable err) { + Logger.e(TAG, "Exception in onCanRemoveUnusedStyleImage", err); + throw err; + } + + return true; + } + void addOnCameraWillChangeListener(MapView.OnCameraWillChangeListener listener) { onCameraWillChangeListenerList.add(listener); } @@ -342,6 +367,14 @@ class MapChangeReceiver implements NativeMapView.StateCallback { onStyleImageMissingListenerList.remove(listener); } + void addOnCanRemoveUnusedStyleImageListener(MapView.OnCanRemoveUnusedStyleImageListener listener) { + onCanRemoveUnusedStyleImageListenerList.add(listener); + } + + void removeOnCanRemoveUnusedStyleImageListener(MapView.OnCanRemoveUnusedStyleImageListener listener) { + onCanRemoveUnusedStyleImageListenerList.remove(listener); + } + void clear() { onCameraWillChangeListenerList.clear(); onCameraIsChangingListenerList.clear(); @@ -357,5 +390,6 @@ class MapChangeReceiver implements NativeMapView.StateCallback { onDidFinishLoadingStyleListenerList.clear(); onSourceChangedListenerList.clear(); onStyleImageMissingListenerList.clear(); + onCanRemoveUnusedStyleImageListenerList.clear(); } } \ No newline at end of file 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 42c607360..cd31e2278 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 @@ -797,6 +797,33 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { mapChangeReceiver.removeOnStyleImageMissingListener(listener); } + /** + * Set a callback that's invoked when map needs to release unused image resources. + * + * A callback will be called only for unused images that were provided by the client via + * {@link OnStyleImageMissingListener#onStyleImageMissing(String)} listener interface. + * + * By default, platform will remove unused images from the style. By adding listener, default + * behavior can be overridden and client can control whether to release unused resources. + * + * @param listener The callback that's invoked when map needs to release unused image resources + */ + public void addOnCanRemoveUnusedStyleImageListener(@NonNull OnCanRemoveUnusedStyleImageListener listener) { + mapChangeReceiver.addOnCanRemoveUnusedStyleImageListener(listener); + } + + /** + * Removes a callback that's invoked when map needs to release unused image resources. + * + * When all listeners are removed, platform will fallback to default behavior, which is to remove + * unused images from the style. + * + * @param listener The callback that's invoked when map needs to release unused image resources + */ + public void removeOnCanRemoveUnusedStyleImageListener(@NonNull OnCanRemoveUnusedStyleImageListener listener) { + mapChangeReceiver.removeOnCanRemoveUnusedStyleImageListener(listener); + } + /** * Interface definition for a callback to be invoked when the camera will change. *

@@ -994,6 +1021,22 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback { void onStyleImageMissing(@NonNull String id); } + /** + * Interface definition for a callback to be invoked with an unused image identifier. + *

+ * {@link MapView#addOnCanRemoveUnusedStyleImageListener(OnCanRemoveUnusedStyleImageListener)} + *

+ */ + public interface OnCanRemoveUnusedStyleImageListener { + /** + * Called when the map needs to release unused image resources. + * + * @param id of an image that is not used by the map and can be removed from the style. + * @return true if image can be removed, false otherwise. + */ + boolean onCanRemoveUnusedStyleImage(@NonNull String id); + } + /** * Sets a callback object which will be triggered when the {@link MapboxMap} instance is ready to be used. * 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 1236d932e..3980790fd 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 @@ -1050,6 +1050,15 @@ final class NativeMapView implements NativeMap { } } + @Keep + private boolean onCanRemoveUnusedStyleImage(String imageId) { + if (stateCallback != null) { + return stateCallback.onCanRemoveUnusedStyleImage(imageId); + } + + return true; + } + @Keep protected void onSnapshotReady(@Nullable Bitmap mapContent) { if (checkState("OnSnapshotReady")) { @@ -1463,5 +1472,7 @@ final class NativeMapView implements NativeMap { void onSourceChanged(String sourceId); void onStyleImageMissing(String imageId); + + boolean onCanRemoveUnusedStyleImage(String imageId); } } diff --git a/platform/android/src/android_renderer_frontend.cpp b/platform/android/src/android_renderer_frontend.cpp index 6d235abe9..6862fabcb 100644 --- a/platform/android/src/android_renderer_frontend.cpp +++ b/platform/android/src/android_renderer_frontend.cpp @@ -54,6 +54,10 @@ public: delegate.invoke(&RendererObserver::onStyleImageMissing, id, done); } + void onRemoveUnusedStyleImages(const std::vector& ids) override { + delegate.invoke(&RendererObserver::onRemoveUnusedStyleImages, ids); + } + private: std::shared_ptr mailbox; ActorRef delegate; diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp index ae53bcc80..8493110d7 100755 --- a/platform/android/src/native_map_view.cpp +++ b/platform/android/src/native_map_view.cpp @@ -271,6 +271,20 @@ void NativeMapView::onStyleImageMissing(const std::string& imageId) { } } +bool NativeMapView::onCanRemoveUnusedStyleImage(const std::string& imageId) { + assert(vm != nullptr); + + android::UniqueEnv _env = android::AttachEnv(); + static auto& javaClass = jni::Class::Singleton(*_env); + static auto onCanRemoveUnusedStyleImage = javaClass.GetMethod(*_env, "onCanRemoveUnusedStyleImage"); + auto weakReference = javaPeer.get(*_env); + if (weakReference) { + return weakReference.Call(*_env, onCanRemoveUnusedStyleImage, jni::Make(*_env, imageId)); + } + + return true; +} + // JNI Methods // void NativeMapView::resizeView(jni::JNIEnv&, int w, int h) { diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp index 9e6ad73dd..4ea4781b3 100755 --- a/platform/android/src/native_map_view.hpp +++ b/platform/android/src/native_map_view.hpp @@ -69,6 +69,7 @@ public: void onDidFinishLoadingStyle() override; void onSourceChanged(mbgl::style::Source&) override; void onStyleImageMissing(const std::string&) override; + bool onCanRemoveUnusedStyleImage(const std::string&) override; // JNI //