ankidroid / ankidroid/Anki-Android

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

Abierto
#20,557 6 comentarios 0 reacciones 1 asignado Reclamado por @david-allison Ver en GitHub
Card Browser Needs Triage
Lenguaje dominante
Kotlin
Estrellas
11.8k
Forks
2.9k
Merge medio
2 d 3 h
PR fusionados (30 d)
167

Descripción

### Checked for duplicates?

- [x] 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

```text
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

- [x] I have checked the [manual](https://ankidroid.org/docs/manual.html) and the [FAQ](https://github.com/ankidroid/Anki-Android/wiki/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](https://docs.ankidroid.org/manual.html#betaTesting))

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.