I've gotten the freeze three times total on my phone, and this time I was near a computer to do some debugging. This is what I found.
Clover uses the ViewTreeObserver in some places to wait for the layout phase to be done and then use the calculated views to do some second calculations. This is not always necessary and I've removed/changed code that used it needlessly. I used this mostly out of laziness to create a new view to implement onMeasure, and not having a good understanding of the measure system. This is bad for performance because a second measure pass is not needed most of the time.
I've misinterpreted the meaning of the return value of onPreDrawListener. I thought false meant continue with the rendering, true to request a new layout phase. This was the other way around and has been switched on all pieces of code that used it wrong.
A perfect example was the use of waitForLayout in the DrawerController. It would wait for a layout pass to get the total width of the containing view and change the width of the drawer. It also used the wrong return value so the layout was not triggered again when needed and triggeded when not needed. This is now fixed and an issue where the drawer width was wrong is now also fixed.
Secondly (and this has been an issue before), the listener would not get removed from the correct ViewTreeObserver in some cases, causing the listener to be triggered on every draw call. This is because Android creates a temporary ViewTreeObserver when a view is not attached to the window, and the callbacks are copied to a real ViewTreeObserver when the view gets attached. I'm still unsure why that does not work sometimes.
The final trigger of the bug was the measure callback that was placed on the width of the menu width on the toolbar (the things that switches the boards). This callback always returned false (thinking that was do not cancel) and canceled the draw every time. This is not harmful normally but when the listener is not removed because the ViewTreeObserver is temporary it would get stuck in an endless loop.
The weird thing is that touch handling still works correctly when Android gets stuck in such an endless loop. This was why you could still press the reply button and the keyboard would show up. It just doesn't get to the draw phase and then appears to look stuck.
I've placed some debug logs in the callbacks when the listener doesn't get removed, and it raises an IllegalArgumentException when the ViewTreeObserver is temporarily.
I don't want to tag beta releases because then f-droid would publish them, and I can only upload apk's on github outside of the repo in the releases section, which requires tagging.
I also want the website to point to these files, not the releases section.
Maintaining FastTextView because too much of a mess to maintain
because the post comment uses spannables extensively and the caching
became too difficult.
It is still used for the "n replies" and the date, these have simple requirements.
Previously when a controller because hidden, it was removed from the view hierachy, and reattached when shown again.
Now the views are only detached from the view hierachy when the whole controller gets destroyed, and the view visibility is changed on onHide and onShow.
This was done for performance reasons. Reattaching a controller takes too much time and is noticable when trying to swipe a controller away.