Skip to content

Crash: IllegalStateException in BrowserColumnSelectionAdapter when toggling columns during drag-and-drop animation #20557

@JeffHuang04

Description

@JeffHuang04

Checked for duplicates?

  • This issue is not a duplicate

Does it also happen in the desktop version?

  • This bug does not occur in the latest version of Anki Desktop

What are the steps to reproduce this bug?

Description

A crash occurs in the Manage columns screen of the Card Browser. The crash is triggered when notifyItemRangeChanged is called within refreshDataset while the RecyclerView is still performing a layout pass or executing a recovery animation from ItemTouchHelper.
### version
Android Version: 9.0
Device: Google Medium Phone
App Version: 2.24.0alpha10-debug

Steps to Reproduce

Open Card Browser.
Tap the overflow menu (three dots) and select Change display order (or long-press a header to enter Manage columns).
Perform the following actions rapidly:
Use the six-dot drag handle on the right to reorder any column (e.g., drag "Note Type" up or down).
Immediately after releasing the drag (while the animation is still finishing), tap the "+" or "—" button in the middle to add/remove a column.
The app crashes with java.lang.IllegalStateException.
The crash is tested by automated testing tool, but I cannot reproduce by hand.

Expected behaviour

1.The app crashes with java.lang.IllegalStateException.

2.The app should handle the column toggle smoothly even if a drag-and-drop animation is active. Either the toggle action should wait for the animation to finish, or the UI should update without throwing an IllegalStateException. No crash should occur.

Debug info

Full Crash Log (Stacktrace)

> --------- beginning of crash
03-23 03:59:29.437 E/AndroidRuntime(22708): FATAL EXCEPTION: main
03-23 03:59:29.437 E/AndroidRuntime(22708): Process: com.ichi2.anki.debug, PID: 22708
03-23 03:59:29.437 E/AndroidRuntime(22708): java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling androidx.recyclerview.widget.RecyclerView{612e57c VFED..... ......ID 0,168-1080,2211 #7f0a0466 app:id/recycler_view}, adapter:com.ichi2.anki.browser.BrowserColumnSelectionAdapter@bfbde4, layout:androidx.recyclerview.widget.LinearLayoutManager@2c0e2, context:android.view.ContextThemeWrapper@41f1267
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.RecyclerView.assertNotInLayoutOrScroll(RecyclerView.java:3587)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.RecyclerView$RecyclerViewDataObserver.onItemRangeChanged(RecyclerView.java:6167)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.RecyclerView$AdapterDataObservable.notifyItemRangeChanged(RecyclerView.java:13401)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.RecyclerView$AdapterDataObservable.notifyItemRangeChanged(RecyclerView.java:13391)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.RecyclerView$Adapter.notifyItemRangeChanged(RecyclerView.java:8259)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at com.ichi2.anki.browser.BrowserColumnSelectionAdapter.refreshDataset(BrowserColumnSelectionAdapter.kt:111)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at com.ichi2.anki.browser.BrowserColumnSelectionFragment$setupRecyclerView$callback$1.clearView(BrowserColumnSelectionFragment.kt:187)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.ItemTouchHelper$3.onAnimationEnd(ItemTouchHelper.java:652)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at android.animation.Animator$AnimatorListener.onAnimationEnd(Animator.java:552)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at android.animation.ValueAnimator.endAnimation(ValueAnimator.java:1232)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at android.animation.ValueAnimator.cancel(ValueAnimator.java:1117)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.ItemTouchHelper$RecoverAnimation.cancel(ItemTouchHelper.java:2446)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.ItemTouchHelper.endRecoverAnimation(ItemTouchHelper.java:929)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.ItemTouchHelper.onChildViewDetachedFromWindow(ItemTouchHelper.java:911)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.RecyclerView.dispatchChildDetached(RecyclerView.java:8473)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.RecyclerView$6.removeViewAt(RecyclerView.java:1005)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.ChildHelper.removeViewAt(ChildHelper.java:212)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.RecyclerView$LayoutManager.removeViewAt(RecyclerView.java:9585)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.RecyclerView$LayoutManager.removeAndRecycleViewAt(RecyclerView.java:9856)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.LinearLayoutManager.recycleChildren(LinearLayoutManager.java:1524)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.LinearLayoutManager.recycleViewsFromStart(LinearLayoutManager.java:1574)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.LinearLayoutManager.recycleByLayoutState(LinearLayoutManager.java:1649)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1708)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.LinearLayoutManager.scrollBy(LinearLayoutManager.java:1485)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.LinearLayoutManager.scrollVerticallyBy(LinearLayoutManager.java:1218)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.RecyclerView.scrollStep(RecyclerView.java:2161)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.RecyclerView.scrollByInternal(RecyclerView.java:2260)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at androidx.recyclerview.widget.RecyclerView.onTouchEvent(RecyclerView.java:3958)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at android.view.View.dispatchTouchEvent(View.java:12513)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3024)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2705)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3030)
03-23 03:59:29.437 E/AndroidRuntime(22708): 	at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2719)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at com.android.internal.policy.DecorView.superDispatchTouchEvent(DecorView.java:440)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at com.android.internal.policy.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1830)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.app.Dialog.dispatchTouchEvent(Dialog.java:847)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at com.android.internal.policy.DecorView.dispatchTouchEvent(DecorView.java:398)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.View.dispatchPointerEvent(View.java:12752)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:5106)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:4909)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4426)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4479)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4445)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:4585)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4453)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:4642)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4426)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:4479)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:4445)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:4453)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:4426)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:7092)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:7061)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:7022)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:7195)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:186)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.InputEventReceiver.nativeConsumeBatchedInputEvents(Native Method)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.InputEventReceiver.consumeBatchedInputEvents(InputEventReceiver.java:177)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.ViewRootImpl.doConsumeBatchedInput(ViewRootImpl.java:7166)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.ViewRootImpl$ConsumeBatchedInputRunnable.run(ViewRootImpl.java:7218)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:949)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.Choreographer.doCallbacks(Choreographer.java:761)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.Choreographer.doFrame(Choreographer.java:690)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:935)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.os.Handler.handleCallback(Handler.java:873)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.os.Handler.dispatchMessage(Handler.java:99)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.os.Looper.loop(Looper.java:193)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at android.app.ActivityThread.main(ActivityThread.java:6669)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at java.lang.reflect.Method.invoke(Native Method)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
03-23 03:59:29.438 E/AndroidRuntime(22708): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
03-23 03:59:29.712 I/ActivityManager( 1882): START u0 {flg=0x10000000 cmp=com.ichi2.anki.debug/com.ichi2.anki.analytics.AnkiDroidCrashReportDialog (has extras)} from uid 10098
03-23 03:59:30.052 I/chatty  (23304): uid=10087(com.github.uiautomator) expire 3 lines

action before crash

> action: 8@click className=android.widget.TextView,instance=2 :android.widget.TextView@Sort Field
action: 9@click className=android.widget.TextView,instance=9 :android.widget.TextView@Card Modified
action: 15@clickLong className=android.widget.TextView,instance=5 :android.widget.TextView@Deck
action: 18@clickLong resource-id=com.ichi2.anki.debug:id/drag_handle :android.widget.ImageView@recycler items_com.ichi2.anki.CardBrowser_000001
action: 29@clickLong resource-id=com.ichi2.anki.debug:id/button_toggle_column :android.widget.ImageButton@recycler items_com.ichi2.anki.CardBrowser_000001
action: 27@clickLong className=android.widget.TextView,instance=9 :android.widget.TextView@recycler items_com.ichi2.anki.CardBrowser_000001
action: 3@back
action: 11@clickLong className=android.widget.TextView,instance=3 :android.widget.TextView@Note Type
action: 21@clickLong resource-id=com.ichi2.anki.debug:id/button_toggle_column :android.widget.ImageButton@recycler items_com.ichi2.anki.CardBrowser_000001
action: 24@click resource-id=com.ichi2.anki.debug:id/button_toggle_column :android.widget.ImageButton@recycler items_com.ichi2.anki.CardBrowser_000001
action: 35@clickLong className=android.widget.TextView,instance=11 :android.widget.TextView@recycler items_com.ichi2.anki.CardBrowser_000001
action: 9@clickLong resource-id=com.ichi2.anki.debug:id/button_toggle_column :android.widget.ImageButton@recycler items_com.ichi2.anki.CardBrowser_000001
action: 19@clickLong className=android.widget.TextView,instance=7 :android.widget.TextView@recycler items_com.ichi2.anki.CardBrowser_000001
--------- beginning of crash
03-23 03:59:29.437 22708 22708 E AndroidRuntime: FATAL EXCEPTION: main
03-23 03:59:29.437 22708 22708 E AndroidRuntime: Process: com.ichi2.anki.debug, PID: 22708

(Optional) Anything else you want to share?

No response

Research

  • I have checked the manual and the FAQ and could not find a solution to my issue
  • (Optional) I have confirmed the issue is not resolved in the latest alpha release (instructions)

Metadata

Metadata

Assignees

No one assigned

    Type

    Priority

    None yet

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions