google / ground-android

Ground mobile data collection app for Android
http://groundplatform.org
Apache License 2.0
243 stars 114 forks source link

Force quit on consecutive geometry tasks (com.google.maps.api.android.lib6.impl.as cannot be cast to android.view.ViewGroup) #2493

Closed sufyanAbbasi closed 1 month ago

sufyanAbbasi commented 2 months ago

Describe the bug

Received the following error when two subsequent location tasks are being rendered: the map doesn't render and we see this in the stack trace:

java.lang.ClassCastException: com.google.maps.api.android.lib6.impl.as cannot be cast to android.view.ViewGroup
  at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:537)
  at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:272)
  at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1943)
  at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1839)
  at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1782)
  at androidx.fragment.app.FragmentManager$5.run(FragmentManager.java:565)
  at android.os.Handler.handleCallback(Handler.java:958)
  at android.os.Handler.dispatchMessage(Handler.java:99)
  at android.os.Looper.loopOnce(Looper.java:205)
  at android.os.Looper.loop(Looper.java:294)
  at android.app.ActivityThread.main(ActivityThread.java:8177)
  at java.lang.reflect.Method.invoke(Native Method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)

I placed breakpoints at the exact line where the crash occurs and inspected the thing that was failing to cast as a ViewGroup. It turns out that the crash happens when it tries to render the compass of all things!

It happens specifically when I try to render the DrawAreaTaskFragment before or after a drop pin LOI task.

When I just have a Draw Area LOI task and no other, then it seems to work

To Reproduce Steps to reproduce the behavior:

  1. Publish a survey with the default capture location task and then an ad-hoc LOI task (e.g. capture location again).
  2. Zoom in and start a job, then see that the first task, the default capture task, is broken with a white screen.

Expected behavior

  1. The map is rendered.
sufyanAbbasi commented 1 month ago

This is unfortunately blocking the end to end tests as well as exasperated by the fact that we always have a capture location task as the first task by default, since ad-hoc surveys will always put the loiTask afterwards

sufyanAbbasi commented 1 month ago

Ok, I found the problem, it is so silly...

https://github.com/google/ground-android/blob/4b6a9d4e95d10899a42ecaea9319e0971e737421/ground/src/main/java/com/google/android/ground/ui/datacollection/tasks/point/DropPinTaskFragment.kt#L47

Here we create a RowLayout with a generated ID that is usually 0x5, that is, it returns the next ID that has not been assigned. However, when we go to render it here, the GoogleMapsFragment is also rendered on the page, so when we try to look for the container by ID, there is a Compass element on the GoogleMaps which also has ID 0x5, so at render time, it finds the Compass element, i.e., com.google.maps.api.android.lib6.impl.as, from the GoogleMapsFragment instead of the newly created RowLayout!

https://github.com/google/ground-android/blob/4b6a9d4e95d10899a42ecaea9319e0971e737421/ground/src/main/java/com/google/android/ground/ui/datacollection/tasks/point/DropPinTaskFragment.kt#L50

When I arbitrarily added 10,000 to the ID, the crash is prevented, hence I believe this is an ID conflict problem.

The fix is to fuzz the ID numbers to decrease the chance of collisions, or somehow get it to always find the RowLayout element every time, maybe there's a different add we could do? Not really sure.