diff --git a/demo/src/main/java/com/google/maps/android/utils/demo/ClusteringDiffDemoActivity.java b/demo/src/main/java/com/google/maps/android/utils/demo/ClusteringDiffDemoActivity.java index 626556009..01cb5d6b8 100644 --- a/demo/src/main/java/com/google/maps/android/utils/demo/ClusteringDiffDemoActivity.java +++ b/demo/src/main/java/com/google/maps/android/utils/demo/ClusteringDiffDemoActivity.java @@ -131,7 +131,7 @@ protected void onBeforeClusterRendered(@NonNull Cluster cluster, @NonNul } @Override - protected void onClusterUpdated(@NonNull Cluster cluster, Marker marker) { + protected void onClusterUpdated(@NonNull Cluster cluster, @NonNull Marker marker) { // Same implementation as onBeforeClusterRendered() (to update cached markers) marker.setIcon(getClusterIcon(cluster)); } diff --git a/library/src/main/java/com/google/maps/android/clustering/view/ClusterRendererMultipleItems.java b/library/src/main/java/com/google/maps/android/clustering/view/ClusterRendererMultipleItems.java index f39d7bc1e..72053b3cf 100644 --- a/library/src/main/java/com/google/maps/android/clustering/view/ClusterRendererMultipleItems.java +++ b/library/src/main/java/com/google/maps/android/clustering/view/ClusterRendererMultipleItems.java @@ -31,7 +31,6 @@ import android.os.Looper; import android.os.Message; import android.os.MessageQueue; -import android.util.Log; import android.util.SparseArray; import android.view.ViewGroup; import android.view.animation.DecelerateInterpolator; @@ -92,7 +91,7 @@ public class ClusterRendererMultipleItems implements Clus * Markers that are currently on the map. */ private Set mMarkers = Collections.newSetFromMap( - new ConcurrentHashMap()); + new ConcurrentHashMap<>()); /** * Icons for each bucket. @@ -147,19 +146,11 @@ public ClusterRendererMultipleItems(Context context, GoogleMap map, ClusterManag @Override public void onAdd() { - mClusterManager.getMarkerCollection().setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() { - @Override - public boolean onMarkerClick(@NonNull Marker marker) { - return mItemClickListener != null && mItemClickListener.onClusterItemClick(mMarkerCache.get(marker)); - } - }); + mClusterManager.getMarkerCollection().setOnMarkerClickListener(marker -> mItemClickListener != null && mItemClickListener.onClusterItemClick(mMarkerCache.get(marker))); - mClusterManager.getMarkerCollection().setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() { - @Override - public void onInfoWindowClick(@NonNull Marker marker) { - if (mItemInfoWindowClickListener != null) { - mItemInfoWindowClickListener.onClusterItemInfoWindowClick(mMarkerCache.get(marker)); - } + mClusterManager.getMarkerCollection().setOnInfoWindowClickListener(marker -> { + if (mItemInfoWindowClickListener != null) { + mItemInfoWindowClickListener.onClusterItemInfoWindowClick(mMarkerCache.get(marker)); } }); @@ -350,31 +341,6 @@ protected boolean shouldRenderAsCluster(@NonNull Cluster cluster) { return cluster.getSize() >= mMinClusterSize; } - /** - * Determines if the new clusters should be rendered on the map, given the old clusters. This - * method is primarily for optimization of performance, and the default implementation simply - * checks if the new clusters are equal to the old clusters, and if so, it returns false. - *

- * However, there are cases where you may want to re-render the clusters even if they didn't - * change. For example, if you want a cluster with one item to render as a cluster above - * a certain zoom level and as a marker below a certain zoom level (even if the contents of the - * clusters themselves did not change). In this case, you could check the zoom level in an - * implementation of this method and if that zoom level threshold is crossed return true, else - * {@code return super.shouldRender(oldClusters, newClusters)}. - *

- * Note that always returning true from this method could potentially have negative performance - * implications as clusters will be re-rendered on each pass even if they don't change. - * - * @param oldClusters The clusters from the previous iteration of the clustering algorithm - * @param newClusters The clusters from the current iteration of the clustering algorithm - * @return true if the new clusters should be rendered on the map, and false if they should not. This - * method is primarily for optimization of performance, and the default implementation simply - * checks if the new clusters are equal to the old clusters, and if so, it returns false. - */ - protected boolean shouldRender(@NonNull Set> oldClusters, @NonNull Set> newClusters) { - return true; - } - /** * Transforms the current view (represented by DefaultClusterRenderer.mClusters and DefaultClusterRenderer.mZoom) to a * new zoom level and set of clusters. @@ -424,10 +390,6 @@ public void setMapZoom(float zoom) { @SuppressLint("NewApi") public void run() { - if (!shouldRender(immutableOf(ClusterRendererMultipleItems.this.mClusters), immutableOf(clusters))) { - mCallback.run(); - return; - } final MarkerModifier markerModifier = new MarkerModifier(); @@ -578,10 +540,6 @@ public void setAnimationDuration(long animationDurationMs) { mAnimationDurationMs = animationDurationMs; } - private Set> immutableOf(Set> clusters) { - return clusters != null ? Collections.unmodifiableSet(clusters) : Collections.emptySet(); - } - private static double distanceSquared(Point a, Point b) { return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y); } diff --git a/library/src/test/java/com/google/maps/android/clustering/ClusterManagerTest.java b/library/src/test/java/com/google/maps/android/clustering/ClusterManagerTest.java new file mode 100644 index 000000000..d7e77a283 --- /dev/null +++ b/library/src/test/java/com/google/maps/android/clustering/ClusterManagerTest.java @@ -0,0 +1,122 @@ +/* + * Copyright 2015 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.maps.android.clustering; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import android.content.Context; + +import com.google.android.gms.maps.GoogleMap; +import com.google.maps.android.clustering.algo.Algorithm; +import com.google.maps.android.collections.MarkerManager; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.Arrays; +import java.util.Collection; + +public class ClusterManagerTest { + + private ClusterManager classUnderTest; + private Algorithm algorithm; + + private Context mockContext; + private GoogleMap mockMap; + private MarkerManager mockMarkerManager; + + @Before + public void setUp() { + // Create mocks for dependencies + mockContext = mock(Context.class); + mockMap = mock(GoogleMap.class); + mockMarkerManager = mock(MarkerManager.class); + algorithm = mock(Algorithm.class); + + // Initialize the class under test with the mocked dependencies + classUnderTest = new ClusterManager(mockContext, mockMap, mockMarkerManager) { + @Override + public Algorithm getAlgorithm() { + return algorithm; + } + }; + } + + @Test + public void testDiff() { + // Use a specific class type for T + Class type = (Class) String.class; // Replace with actual type + algorithm = mock(Algorithm.class); + + classUnderTest = spy(new ClusterManager(mock(Context.class), mock(GoogleMap.class), mock(MarkerManager.class)) { + @Override + public Algorithm getAlgorithm() { + return algorithm; + } + }); + + // Mock input collections using the explicit type + T addItem1 = mock(type); + T addItem2 = mock(type); + Collection add = Arrays.asList(addItem1, addItem2); + + T removeItem = mock(type); + Collection remove = Arrays.asList(removeItem); + + T modifyItem1 = mock(type); + T modifyItem2 = mock(type); + T modifyItem3 = mock(type); + Collection modify = Arrays.asList(modifyItem1, modifyItem2, modifyItem3); + + // Call the method under test + classUnderTest.diff(add, remove, modify); + + // Verify algorithm lock and unlock + verify(algorithm).lock(); + verify(algorithm).unlock(); + + // Verify add items + for (T item : add) { + verify(algorithm).addItem(item); + } + + // Verify remove items + verify(algorithm).removeItems(remove); + + // Verify modify items + for (T item : modify) { + verify(classUnderTest).updateItem(item); + } + } + + @Test + public void testDiffWithNullCollections() { + // Call the method with null collections + classUnderTest.diff(null, null, null); + + // Verify locking and unlocking + verify(algorithm).lock(); + verify(algorithm).unlock(); + + // Verify no operations are called + verifyNoMoreInteractions(algorithm); + } +} \ No newline at end of file