Skip to content

Commit 65498ba

Browse files
authored
feat: added logger in ClusterRendererMultipleItems (#1519)
* feat: added logger in ClusterRendererMultipleItems * docs: header * docs: header * fix: not needed
1 parent 8b57c3a commit 65498ba

File tree

3 files changed

+150
-14
lines changed

3 files changed

+150
-14
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.maps.android;
18+
19+
import android.util.Log;
20+
21+
/**
22+
* Utility class for logging renderer-related debug output.
23+
*
24+
* <p>Use {@link #setEnabled(boolean)} to toggle logging globally.
25+
* This class avoids the need for scattered conditionals in the codebase.</p>
26+
*/
27+
public final class RendererLogger {
28+
29+
private static boolean enabled = false;
30+
31+
private RendererLogger() {
32+
// Prevent instantiation
33+
}
34+
35+
/**
36+
* Enables or disables logging.
37+
*
38+
* @param value {@code true} to enable logging; {@code false} to disable it.
39+
*/
40+
public static void setEnabled(boolean value) {
41+
enabled = value;
42+
}
43+
44+
/**
45+
* Logs a debug message if logging is enabled.
46+
*
47+
* @param tag Tag for the log message.
48+
* @param message The debug message to log.
49+
*/
50+
public static void d(String tag, String message) {
51+
if (enabled) {
52+
Log.d(tag, message);
53+
}
54+
}
55+
56+
/**
57+
* Logs an info message if logging is enabled.
58+
*
59+
* @param tag Tag for the log message.
60+
* @param message The info message to log.
61+
*/
62+
public static void i(String tag, String message) {
63+
if (enabled) {
64+
Log.i(tag, message);
65+
}
66+
}
67+
68+
/**
69+
* Logs a warning message if logging is enabled.
70+
*
71+
* @param tag Tag for the log message.
72+
* @param message The warning message to log.
73+
*/
74+
public static void w(String tag, String message) {
75+
if (enabled) {
76+
Log.w(tag, message);
77+
}
78+
}
79+
80+
/**
81+
* Logs an error message if logging is enabled.
82+
*
83+
* @param tag Tag for the log message.
84+
* @param message The error message to log.
85+
*/
86+
public static void e(String tag, String message) {
87+
if (enabled) {
88+
Log.e(tag, message);
89+
}
90+
}
91+
}

library/src/main/java/com/google/maps/android/clustering/algo/GridBasedAlgorithm.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public class GridBasedAlgorithm<T extends ClusterItem> extends AbstractAlgorithm
4444

4545
private int mGridSize = DEFAULT_GRID_SIZE;
4646

47-
private final Set<T> mItems = Collections.synchronizedSet(new HashSet<T>());
47+
private final Set<T> mItems = Collections.synchronizedSet(new HashSet<>());
4848

4949
/**
5050
* Adds an item to the algorithm
@@ -126,8 +126,8 @@ public Set<? extends Cluster<T>> getClusters(float zoom) {
126126
long numCells = (long) Math.ceil(256 * Math.pow(2, zoom) / mGridSize);
127127
SphericalMercatorProjection proj = new SphericalMercatorProjection(numCells);
128128

129-
HashSet<Cluster<T>> clusters = new HashSet<Cluster<T>>();
130-
LongSparseArray<StaticCluster<T>> sparseArray = new LongSparseArray<StaticCluster<T>>();
129+
HashSet<Cluster<T>> clusters = new HashSet<>();
130+
LongSparseArray<StaticCluster<T>> sparseArray = new LongSparseArray<>();
131131

132132
synchronized (mItems) {
133133
for (T item : mItems) {
@@ -137,7 +137,7 @@ public Set<? extends Cluster<T>> getClusters(float zoom) {
137137

138138
StaticCluster<T> cluster = sparseArray.get(coord);
139139
if (cluster == null) {
140-
cluster = new StaticCluster<T>(proj.toLatLng(new Point(Math.floor(p.x) + .5, Math.floor(p.y) + .5)));
140+
cluster = new StaticCluster<>(proj.toLatLng(new Point(Math.floor(p.x) + .5, Math.floor(p.y) + .5)));
141141
sparseArray.put(coord, cluster);
142142
clusters.add(cluster);
143143
}

library/src/main/java/com/google/maps/android/clustering/view/ClusterRendererMultipleItems.java

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import com.google.android.gms.maps.model.Marker;
5353
import com.google.android.gms.maps.model.MarkerOptions;
5454
import com.google.maps.android.R;
55+
import com.google.maps.android.RendererLogger;
5556
import com.google.maps.android.clustering.Cluster;
5657
import com.google.maps.android.clustering.ClusterItem;
5758
import com.google.maps.android.clustering.ClusterManager;
@@ -107,10 +108,7 @@ public enum AnimationType {
107108

108109
public void setAnimationType(AnimationType type) {
109110
switch (type) {
110-
case LINEAR:
111-
animationInterp = new LinearInterpolator();
112-
break;
113-
case EASE_IN:
111+
case EASE_IN, ACCELERATE:
114112
animationInterp = new AccelerateInterpolator();
115113
break;
116114
case EASE_OUT:
@@ -125,9 +123,6 @@ public void setAnimationType(AnimationType type) {
125123
case BOUNCE:
126124
animationInterp = new BounceInterpolator();
127125
break;
128-
case ACCELERATE:
129-
animationInterp = new AccelerateInterpolator();
130-
break;
131126
case DECELERATE:
132127
animationInterp = new DecelerateInterpolator();
133128
break;
@@ -196,35 +191,47 @@ public ClusterRendererMultipleItems(Context context, GoogleMap map, ClusterManag
196191

197192
@Override
198193
public void onAdd() {
199-
mClusterManager.getMarkerCollection().setOnMarkerClickListener(marker -> mItemClickListener != null && mItemClickListener.onClusterItemClick(mMarkerCache.get(marker)));
194+
RendererLogger.d("ClusterRenderer", "Setting up MarkerCollection listeners");
195+
196+
mClusterManager.getMarkerCollection().setOnMarkerClickListener(marker -> {
197+
RendererLogger.d("ClusterRenderer", "Marker clicked: " + marker);
198+
return mItemClickListener != null && mItemClickListener.onClusterItemClick(mMarkerCache.get(marker));
199+
});
200200

201201
mClusterManager.getMarkerCollection().setOnInfoWindowClickListener(marker -> {
202+
RendererLogger.d("ClusterRenderer", "Info window clicked for marker: " + marker);
202203
if (mItemInfoWindowClickListener != null) {
203204
mItemInfoWindowClickListener.onClusterItemInfoWindowClick(mMarkerCache.get(marker));
204205
}
205206
});
206207

207208
mClusterManager.getMarkerCollection().setOnInfoWindowLongClickListener(marker -> {
209+
RendererLogger.d("ClusterRenderer", "Info window long-clicked for marker: " + marker);
208210
if (mItemInfoWindowLongClickListener != null) {
209211
mItemInfoWindowLongClickListener.onClusterItemInfoWindowLongClick(mMarkerCache.get(marker));
210212
}
211213
});
212214

215+
RendererLogger.d("ClusterRenderer", "Setting up ClusterMarkerCollection listeners");
216+
213217
mClusterManager.getClusterMarkerCollection().setOnMarkerClickListener(marker -> mClickListener != null && mClickListener.onClusterClick(mClusterMarkerCache.get(marker)));
214218

215219
mClusterManager.getClusterMarkerCollection().setOnInfoWindowClickListener(marker -> {
220+
RendererLogger.d("ClusterRenderer", "Info window clicked for cluster marker: " + marker);
216221
if (mInfoWindowClickListener != null) {
217222
mInfoWindowClickListener.onClusterInfoWindowClick(mClusterMarkerCache.get(marker));
218223
}
219224
});
220225

221226
mClusterManager.getClusterMarkerCollection().setOnInfoWindowLongClickListener(marker -> {
227+
RendererLogger.d("ClusterRenderer", "Info window long-clicked for cluster marker: " + marker);
222228
if (mInfoWindowLongClickListener != null) {
223229
mInfoWindowLongClickListener.onClusterInfoWindowLongClick(mClusterMarkerCache.get(marker));
224230
}
225231
});
226232
}
227233

234+
228235
@Override
229236
public void onRemove() {
230237
mClusterManager.getMarkerCollection().setOnMarkerClickListener(null);
@@ -270,6 +277,19 @@ public int getClusterTextAppearance(int clusterSize) {
270277
return R.style.amu_ClusterIcon_TextAppearance; // Default value
271278
}
272279

280+
/**
281+
* Enables or disables logging for the cluster renderer.
282+
*
283+
* <p>When enabled, the renderer will log internal operations such as cluster rendering,
284+
* marker updates, and other debug information. This is useful for development and debugging,
285+
* but should typically be disabled in production builds.</p>
286+
*
287+
* @param enabled {@code true} to enable logging; {@code false} to disable it.
288+
*/
289+
public void setLoggingEnabled(boolean enabled) {
290+
RendererLogger.setEnabled(enabled);
291+
}
292+
273293
@NonNull
274294
protected String getClusterText(int bucket) {
275295
if (bucket < BUCKETS[0]) {
@@ -447,8 +467,9 @@ public void run() {
447467

448468
try {
449469
visibleBounds = mProjection.getVisibleRegion().latLngBounds;
470+
RendererLogger.d("ClusterRenderer", "Visible bounds calculated: " + visibleBounds);
450471
} catch (Exception e) {
451-
e.printStackTrace();
472+
RendererLogger.e("ClusterRenderer", "Error getting visible bounds, defaulting to (0,0)");
452473
visibleBounds = LatLngBounds.builder().include(new LatLng(0, 0)).build();
453474
}
454475

@@ -462,6 +483,7 @@ public void run() {
462483
existingClustersOnScreen.add(point);
463484
}
464485
}
486+
RendererLogger.d("ClusterRenderer", "Existing clusters on screen found: " + existingClustersOnScreen.size());
465487
}
466488

467489
// Create the new markers and animate them to their new positions.
@@ -474,20 +496,25 @@ public void run() {
474496
if (closest != null) {
475497
LatLng animateFrom = mSphericalMercatorProjection.toLatLng(closest);
476498
markerModifier.add(true, new CreateMarkerTask(c, newMarkers, animateFrom));
499+
RendererLogger.d("ClusterRenderer", "Animating cluster from closest cluster: " + c.getPosition());
477500
} else {
478501
markerModifier.add(true, new CreateMarkerTask(c, newMarkers, null));
502+
RendererLogger.d("ClusterRenderer", "Animating cluster without closest point: " + c.getPosition());
479503
}
480504

481505
} else {
482506
markerModifier.add(onScreen, new CreateMarkerTask(c, newMarkers, null));
507+
RendererLogger.d("ClusterRenderer", "Adding cluster without animation: " + c.getPosition());
483508
}
484509
}
485510

486511
// Wait for all markers to be added.
487512
markerModifier.waitUntilFree();
513+
RendererLogger.d("ClusterRenderer", "All new markers added, count: " + newMarkers.size());
488514

489515
// Don't remove any markers that were just added. This is basically anything that had a hit in the MarkerCache.
490516
markersToRemove.removeAll(newMarkers);
517+
RendererLogger.d("ClusterRenderer", "Markers to remove after filtering new markers: " + markersToRemove.size());
491518

492519
// Find all of the new clusters that were added on-screen. These are candidates for markers to animate from.
493520
List<Point> newClustersOnScreen = null;
@@ -499,6 +526,7 @@ public void run() {
499526
newClustersOnScreen.add(p);
500527
}
501528
}
529+
RendererLogger.d("ClusterRenderer", "New clusters on screen found: " + newClustersOnScreen.size());
502530
}
503531

504532
for (final MarkerWithPosition marker : markersToRemove) {
@@ -509,6 +537,7 @@ public void run() {
509537
if (closest != null) {
510538
LatLng animateTo = mSphericalMercatorProjection.toLatLng(closest);
511539
markerModifier.animateThenRemove(marker, marker.position, animateTo);
540+
RendererLogger.d("ClusterRenderer", "Animating then removing marker at position: " + marker.position);
512541
} else if (mClusterMarkerCache.mCache.keySet().iterator().hasNext() && mClusterMarkerCache.mCache.keySet().iterator().next().getItems().contains(marker.clusterItem)) {
513542
T foundItem = null;
514543
for (Cluster<T> cluster : mClusterMarkerCache.mCache.keySet()) {
@@ -518,27 +547,31 @@ public void run() {
518547
break;
519548
}
520549
}
521-
522550
}
523551
// Remove it because it will join a cluster
524552
markerModifier.animateThenRemove(marker, marker.position, foundItem.getPosition());
553+
RendererLogger.d("ClusterRenderer", "Animating then removing marker joining cluster at position: " + marker.position);
525554
} else {
526555
markerModifier.remove(true, marker.marker);
556+
RendererLogger.d("ClusterRenderer", "Removing marker without animation at position: " + marker.position);
527557
}
528558
} else {
529559
markerModifier.remove(onScreen, marker.marker);
560+
RendererLogger.d("ClusterRenderer", "Removing marker (onScreen=" + onScreen + ") at position: " + marker.position);
530561
}
531562
}
532563

533564
// Wait until all marker removal operations are completed.
534565
markerModifier.waitUntilFree();
566+
RendererLogger.d("ClusterRenderer", "All marker removal operations completed.");
535567

536568
mMarkers = newMarkers;
537569
ClusterRendererMultipleItems.this.mClusters = clusters;
538570
mZoom = zoom;
539571

540572
// Run the callback once everything is done.
541573
mCallback.run();
574+
RendererLogger.d("ClusterRenderer", "Cluster update callback executed.");
542575
}
543576
}
544577

@@ -1076,13 +1109,16 @@ public CreateMarkerTask(Cluster<T> c, Set<MarkerWithPosition> markersAdded, LatL
10761109
private void perform(MarkerModifier markerModifier) {
10771110
// Don't show small clusters. Render the markers inside, instead.
10781111
if (!shouldRenderAsCluster(cluster)) {
1112+
RendererLogger.d("ClusterRenderer", "Rendering individual cluster items, count: " + cluster.getItems().size());
10791113
for (T item : cluster.getItems()) {
10801114
Marker marker = mMarkerCache.get(item);
10811115
MarkerWithPosition<T> markerWithPosition;
10821116
LatLng currentLocation = item.getPosition();
10831117
if (marker == null) {
1118+
RendererLogger.d("ClusterRenderer", "Creating new marker for cluster item at position: " + currentLocation);
10841119
MarkerOptions markerOptions = new MarkerOptions();
10851120
if (animateFrom != null) {
1121+
RendererLogger.d("ClusterRenderer", "Animating from position: " + animateFrom);
10861122
markerOptions.position(animateFrom);
10871123
} else if (mClusterMarkerCache.mCache.keySet().iterator().hasNext() && mClusterMarkerCache.mCache.keySet().iterator().next().getItems().contains(item)) {
10881124
T foundItem = null;
@@ -1095,6 +1131,7 @@ private void perform(MarkerModifier markerModifier) {
10951131
}
10961132
}
10971133
currentLocation = foundItem.getPosition();
1134+
RendererLogger.d("ClusterRenderer", "Found item in cache for animation at position: " + currentLocation);
10981135
markerOptions.position(currentLocation);
10991136
} else {
11001137
markerOptions.position(item.getPosition());
@@ -1108,13 +1145,17 @@ private void perform(MarkerModifier markerModifier) {
11081145
mMarkerCache.put(item, marker);
11091146
if (animateFrom != null) {
11101147
markerModifier.animate(markerWithPosition, animateFrom, item.getPosition());
1148+
RendererLogger.d("ClusterRenderer", "Animating marker from " + animateFrom + " to " + item.getPosition());
11111149
} else if (currentLocation != null) {
11121150
markerModifier.animate(markerWithPosition, currentLocation, item.getPosition());
1151+
RendererLogger.d("ClusterRenderer", "Animating marker from " + currentLocation + " to " + item.getPosition());
11131152
}
11141153
} else {
11151154
markerWithPosition = new MarkerWithPosition<>(marker, item);
11161155
markerModifier.animate(markerWithPosition, marker.getPosition(), item.getPosition());
1156+
RendererLogger.d("ClusterRenderer", "Animating existing marker from " + marker.getPosition() + " to " + item.getPosition());
11171157
if (!markerWithPosition.position.equals(item.getPosition())) {
1158+
RendererLogger.d("ClusterRenderer", "Updating cluster item marker position");
11181159
onClusterItemUpdated(item, marker);
11191160
}
11201161
}
@@ -1125,19 +1166,23 @@ private void perform(MarkerModifier markerModifier) {
11251166
}
11261167

11271168
// Handle cluster markers
1169+
RendererLogger.d("ClusterRenderer", "Rendering cluster marker at position: " + cluster.getPosition());
11281170
Marker marker = mClusterMarkerCache.get(cluster);
11291171
MarkerWithPosition markerWithPosition;
11301172
if (marker == null) {
1173+
RendererLogger.d("ClusterRenderer", "Creating new cluster marker");
11311174
MarkerOptions markerOptions = new MarkerOptions().position(animateFrom == null ? cluster.getPosition() : animateFrom);
11321175
onBeforeClusterRendered(cluster, markerOptions);
11331176
marker = mClusterManager.getClusterMarkerCollection().addMarker(markerOptions);
11341177
mClusterMarkerCache.put(cluster, marker);
11351178
markerWithPosition = new MarkerWithPosition(marker, null);
11361179
if (animateFrom != null) {
11371180
markerModifier.animate(markerWithPosition, animateFrom, cluster.getPosition());
1181+
RendererLogger.d("ClusterRenderer", "Animating cluster marker from " + animateFrom + " to " + cluster.getPosition());
11381182
}
11391183
} else {
11401184
markerWithPosition = new MarkerWithPosition(marker, null);
1185+
RendererLogger.d("ClusterRenderer", "Updating existing cluster marker");
11411186
onClusterUpdated(cluster, marker);
11421187
}
11431188
onClusterRendered(cluster, marker);

0 commit comments

Comments
 (0)