Skip to content

IndexOutOfBoundsException on some Android devices #5111

@alfredvaa

Description

@alfredvaa

Summary

On some Android devices I get this error in Sentry when the user is viewing the Map.

Reproducible sample code

import MapView, {
  Polyline,
  Marker,
  LatLng,
  Region,
  PROVIDER_GOOGLE,
  PROVIDER_DEFAULT,
} from "react-native-maps";
import { Dimensions, View, Pressable, Platform } from "react-native";
import { iceGreen } from "../../constants/Colors";
import { Fontisto } from "@expo/vector-icons";
import { GeoJSONLineString, GeoJSONMultiPoint } from "./map-types";
import { toLatLng } from "./map-utils";
import { getBounds } from "geolib";
import { useEffect } from "react";
interface EdgeMarkerProps {
  coordinate: LatLng;
  color: string;
}

const EdgeMarker = ({ coordinate, color }: EdgeMarkerProps) => {
  if (!coordinate) {
    return null;
  }

  return (
    <Marker coordinate={coordinate}>
      <View
        style={{
          width: 8,
          height: 8,
          borderRadius: 6,
          backgroundColor: color,
        }}
      />
    </Marker>
  );
};

interface Props {
  path: GeoJSONLineString | LatLng[];
  secondPath?: GeoJSONLineString | LatLng[] | null;
  flags?: GeoJSONMultiPoint | LatLng[] | null;
  capturedFlags?: GeoJSONMultiPoint | LatLng[] | null;
  initialRegion?: Region;
  mapRef?: React.RefObject<MapView> | null;
  size?: "large" | "small" | "fullscreen";
  preview?: boolean;
  onPressFlag?: (point: LatLng, found: boolean) => void;
}

export const MiniMap = ({
  mapRef,
  path,
  secondPath,
  size = "large",
  preview,
  flags,
  capturedFlags,
  onPressFlag,
  initialRegion,
}: Props) => {
  const _path = !Array.isArray(path) ? toLatLng(path) : path;
  const _secondPath =
    secondPath && !Array.isArray(secondPath) ? toLatLng(secondPath) : secondPath;
  const _flags = flags && !Array.isArray(flags) ? toLatLng(flags) : flags;
  const _capturedFlags =
    capturedFlags && !Array.isArray(capturedFlags)
      ? toLatLng(capturedFlags)
      : capturedFlags;

  const flagFound = (flag: LatLng) =>
    !!_capturedFlags?.find(
      (f) => f.latitude === flag.latitude && flag.longitude === f.longitude
    );

  const bounds = _path?.length ? getBounds(_path) : null;

  return (
    <View
      style={
        size === "large"
          ? {
              width: Dimensions.get("screen").width,
            }
          : size === "fullscreen"
          ? {
              width: Dimensions.get("screen").width,
              height: Dimensions.get("screen").height,
            }
          : { width: 100, height: 80 }
      }
    >
      <MapView
        provider={
          Platform.OS === "android" ? PROVIDER_GOOGLE : PROVIDER_DEFAULT
        }
        ref={mapRef}
        mapType="satellite"
        userInterfaceStyle="light"
        style={
          size === "large"
            ? {
                width: Dimensions.get("screen").width,
                height: Dimensions.get("screen").width,
              }
            : size === "fullscreen"
            ? {
                width: Dimensions.get("screen").width,
                height: Dimensions.get("screen").height,
              }
            : { width: 100, height: 80 }
        }
        liteMode={preview}
        cacheEnabled={preview}
        showsUserLocation={!preview}
        showsMyLocationButton={false}
        initialRegion={
          _path.length
            ? {
                latitude: _path[Math.floor(_path.length / 2)].latitude,
                longitude: _path[Math.floor(_path.length / 2)].longitude,
                latitudeDelta: bounds ? bounds.maxLat - bounds.minLat : 0.0015,
                longitudeDelta: bounds
                  ? bounds.maxLng - bounds.minLng
                  : 0.00015,
              }
            : initialRegion
        }
      >
        {_path?.length ? (
          <Polyline coordinates={_path} strokeColor="#4484fc" strokeWidth={4} />
        ) : null}
        <EdgeMarker color="#4484fc" coordinate={_path[0]} />
        <EdgeMarker color="#4484fc" coordinate={_path[_path.length - 1]} />
        {_secondPath ? (
          <>
            {_secondPath?.length ? (
              <Polyline
                coordinates={_secondPath}
                strokeColor="#fc55b4"
                strokeWidth={4}
              />
            ) : null}
            <EdgeMarker color="#fc55b4" coordinate={_secondPath[0]} />
            <EdgeMarker
              color="#fc55b4"
              coordinate={_secondPath[_secondPath.length - 1]}
            />
          </>
        ) : null}

        {_flags?.map((flag, i) => (
          <Marker
            key={i}
            coordinate={flag}
            anchor={{ x: 0.5, y: 0.5 }}
            style={{ zIndex: 2 }}
            onPress={() => {
              onPressFlag?.(flag, flagFound(flag));
            }}
          >
            <View
              style={{
                height: 60,
                width: 60,
                justifyContent: "center",
                alignItems: "center",
              }}
            >
              <View
                style={{
                  width: 25,
                  height: 25,
                  backgroundColor: flagFound(flag) ? iceGreen : "white",
                  shadowColor: "green",
                  shadowOpacity: 0.8,
                  shadowRadius: 15,
                  shadowOffset: { width: 0, height: 3 },
                  borderRadius: 14,
                  justifyContent: "center",
                  alignItems: "center",
                }}
              >
                <Fontisto
                  name={flagFound(flag) ? "check" : "flag"}
                  size={12}
                  color={flagFound(flag) ? "white" : iceGreen}
                />
              </View>
            </View>
          </Marker>
        ))}
      </MapView>
    </View>
  );
};

Steps to reproduce

I have not been able to reproduce the issue myself as it seems to appear a bit random (perhaps only on some Android devices). The issue does not appear on ios. Stack trace is:

java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0
    at jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
    at jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
    at jdk.internal.util.Preconditions.checkIndex(Preconditions.java:266)
    at java.util.Objects.checkIndex(Objects.java:359)
    at java.util.ArrayList.get(ArrayList.java:434)
    at com.google.maps.api.android.lib6.lite.w.a(:com.google.android.gms.policy_maps_core_dynamite@241610205@241610202042.636179997.636179997:29)
    at com.google.maps.api.android.lib6.lite.y.e(:com.google.android.gms.policy_maps_core_dynamite@241610205@241610202042.636179997.636179997:41)
    at com.google.maps.api.android.lib6.lite.l.onDraw(:com.google.android.gms.policy_maps_core_dynamite@241610205@241610202042.636179997.636179997:197)
    at android.view.View.draw(View.java:25177)
    at com.google.maps.api.android.lib6.lite.ac.run(:com.google.android.gms.policy_maps_core_dynamite@241610205@241610202042.636179997.636179997:12)
    at android.os.Handler.handleCallback(Handler.java:958)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loopOnce(Looper.java:230)
    at android.os.Looper.loop(Looper.java:319)
    at android.app.ActivityThread.main(ActivityThread.java:8919)
    at java.lang.reflect.Method.invoke(Method.java)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:578)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1103)

Expected result

Not crashing

Actual result

Crashes

React Native Maps Version

1.14.0

What platforms are you seeing the problem on?

Android

React Native Version

0.74.2

What version of Expo are you using?

SDK 48

Device(s)

Only Android, SM-S901B and SM-G990B2

Additional information

Using Expo 51 but that was not available to select in the bug template.

The issue is similar to #4957 and it is the same app as well.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingstale

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions