Checked for duplicates?
Does it also happen in the desktop version?
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
Checked for duplicates?
Does it also happen in the desktop version?
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
(Optional) Anything else you want to share?
No response
Research