Button bounce (excessive UP events)

SPLatMan

Member
Licensed User
Longtime User
Hi, I have been struggling with this one for several days, and finally managed to make a simple program (attached) that exhibits the problem.

I am developing a Human Machine Interface (HMI) program - SPLat Controls - SimpleHMI - that makes an Android device into a user interface for a separate electronic controller microcontroller board via Bluetooth. I want to allow a user to touch and hold a button and have, say, a relay on for the duration of that hold. For this purpose I am sending button_down and button_up messages to the host, when the user respectively touches and releases the button.

To recap: I need to reliably detect and act on downs and ups, in order to track if the button is currently being touched.

What is happening is that I am randomly (but not always) getting spurious up events. I touch a button, it raises a Down event, and then an up, while I am still touching. It then sometimes (during the same touch) raises another down and then finally an up when I release the button. It is not however raising multiple Click events. If I count Ups and Clicks (as the attached program does), there will be more ups than clicks.

Imagine the button controlling a robot's forward movement - the effect would be that for some touches it would move for exactly as long as I hold the button - the desired behaviour. Other times it would move momentarily and then stop. Other times it would move briefly, stop and then continue for as long as I hold the button.

I am using two devices: A Samsung Galaxy Ace 'phone and a Google Nexus 7 tablet. The problem happens more in the N7 (Android 4.1.1), with about 20% more ups being raised than clicks. It is less pronounced in the Samsung (Android 2.3.4). It also seems to happen more in the Samsung if the button is positioned a bit too low, so it overlaps the bottom of the screen.

In my real program I am using a finite state machine and a timer to allow buttons to be used in several modes (configured dynamically by commands sent from the host): Simple Click; Autorepeat; and "hold", which is the one I have been discussing above. These modes are all generated off the Down and Up events. Because I am getting spurious Up events, it also means the normal click mode produces spurious clicks.

I can't see that I am doing anything wrong. Is this a bug in Android? Has anyone encountered it before, and is there a workaround?

David Stonier-Gibson
Samsung Galaxy ACE, 2.3.4
Google Nexus 7, 4.1.1
SPLat Controls - OEM Embedded Machine Controllers
 
Last edited:

Erel

B4X founder
Staff member
Licensed User
Longtime User
If the Up event happens when the finger is out of the button area then a click event will not be raised (because the user didn't click on the button).

I wasn't able to reproduce the spurious up events when I tested your program.

Try to disable the click event and see if you get spurious up events.

Note that you should use File - Export as zip when uploading files to the forum (otherwise you are uploading many unnecessary files).
 

SPLatMan

Member
Licensed User
Longtime User
Thank you Erel. I will try disabling the click event - when I learn how. Comment about uploads noted.

I also thougt of an alternative solution in my situation, which is to ignore Up events and use Click events as a proxy. With a state machine processing events that is very simple.

David Stonier-Gibson
Samsung Galaxy ACE, 2.3.4
Google Nexus 7, 4.1.1
http://splatco.com
 

hangloose99

Member
Licensed User
Longtime User
Hi, i have the same issue as described above.

The solution to use the click event instead of the up event did not work.
Because if you press a button and slide away without releasing the touchscreen, the click event will not execute.

Is there an other possibility?
 

stevel05

Expert
Licensed User
Longtime User
If you use Reflection to set the on touch listener, you can get the MotionEvent which makes a lot of information available to you through reflection and may help.
 

SPLatMan

Member
Licensed User
Longtime User
Update

I just had a direct email from "Mike", who has the same problem, so I thought I'd post my reply here.

This was part of my SimpleHMI project. It's ages since I worked on it, so I am very rusty on the details. Here's what I can extract fairly easily:

SimpleHMI sends out messages, via Bluetooth, to a host controller, when the user presses buttons. So, they are not "consumed" within the app itself. The app allows a finite number of buttons to exist. They are supported by a data array that includes a state variable, ButtonParam(i). When the actual buttons are declared their array index is stored in the .Tag property of the button.

Buttons on their own generate events for Down, Up, and Click events. The event handlers call a Finite State Machine (FSM) that processes the events. Here's that FSM:

B4X:
Sub fsmHMIButton(Ev As String, Index As Int)

'****************************************************************
' FIX (To do): Use Click rather than Up
'****************************************************************


'fsm for user butttons. This was instituted in Beta 0.3 to allow for auto repeats.
'The button click event has been replaced by down and up events. This fsm then
'synthesises clicks back to the host according to the mode.
'Input events are
'   "D" Down
'   "U" Up
'   "T" Timer tick from tmrHMIButtons
'   "I" Initialise
'
'States are
'   0   'Idle
'   1   'Down in simple mode
'   2   'Initial down in autorepeat mode, timing until first auto-generated click
'   3   'Timing 2nd, 3rd etc click in autorepeat mode
'   4    'Down in hold mode

'
'Note that only one button can be processed at a time, and is saved in iCurrentButton
'   The timer doesn't know button numbers, hence the Index argument is optional

    Dim Parm As String
'Log (cmdHMIUserButton(Index).Text & " " & Ev & "  " & tmrHMIButtons.Enabled)
    Select Case Ev
    Case "D" 'Down
        Select Case ButtonParam(Index).iState
        Case 0   'Idle
            Select Case ButtonParam(Index).iMode    'Test the button mode
            Case kiHMIButtonMode_Default
                 iCurrentButton = Index    'Signal which button we are workng with, for future calls
                ButtonParam(iCurrentButton).iState = 1
            Case kiHMIButtonMode_Repeats
                 iCurrentButton = Index    'Signal which button we are workng with, for future calls
                Parm = ButtonParam(iCurrentButton).Click & HMIstrEOM    'Initial click on first touch
                SendString(Parm)
                ButtonParam(iCurrentButton).iState = 2
                tmrHMIButtons.Interval = 250
                tmrHMIButtons.Enabled = True
            Case kiHMIButtonMode_Hold   
                 iCurrentButton = Index    'Signal which button we are workng with, for future calls
                ButtonParam(iCurrentButton).iState = 4
                Parm = ButtonParam(iCurrentButton).Click & HMIstrInputTerminator & "1" & HMIstrEOM
                SendString(Parm)
            End Select
        Case 1   'Down in simple mode
        Case 2   'Initial down in autorepeat mode, timing until first auto-generated click
        Case 3   'Timing 2nd, 3rd etc click in autorepeat mode
        Case 4   'Down in Hold mode
        End Select
    Case "U" 'Up
        Select Case ButtonParam(Index).iState
        Case 0   'Idle
        Case 1   'Down in simple mode
            Parm = ButtonParam(iCurrentButton).Click & HMIstrEOM
            SendString(Parm)
            ButtonParam(Index).iState = 0
            iCurrentButton = -1        'No longer have an active button
        Case 2   'Initial down in autorepeat mode, timing until first auto-generated click
            'Up here means buttoon released before the first autorepeat tick. Generate a click and reset
            Parm = ButtonParam(iCurrentButton).Click & HMIstrEOM
            SendString(Parm)
            ButtonParam(Index).iState = 0
            iCurrentButton = -1        'No longer have an active button
            tmrHMIButtons.Enabled = False    ' Stop timer events if there is no current button.
        Case 3   'Timing 2nd, 3rd etc click in autorepeat mode
            ButtonParam(iCurrentButton).iState = 0
            iCurrentButton = -1        'No longer have an active button
            tmrHMIButtons.Enabled = False
        Case 4   'Down in Hold mode
            Parm = ButtonParam(iCurrentButton).Click & HMIstrInputTerminator & "0" & HMIstrEOM
            SendString(Parm)
            ButtonParam(Index).iState = 0
            iCurrentButton = -1        'No longer have an active button
        End Select
    Case "T" 'Timer tick from tmrHMIButtons
        If iCurrentButton < 0 Then  'defensive. Stop timer events if there is no current button.
            tmrHMIButtons.Enabled = False
        Else
            Select Case ButtonParam(iCurrentButton).iState
            Case 0   'Idle
                tmrHMIButtons.Enabled = False  'defensive
            Case 1   'Down in simple mode
            Case 2   'Initial down in autorepeat mode, timing until first auto-generated click
                'Generate the first timed click
                Parm = ButtonParam(iCurrentButton).Click & HMIstrEOM
                SendString(Parm)
                tmrHMIButtons.Interval = 1000 / ButtonParam(iCurrentButton).iRepeatRate
                ButtonParam(iCurrentButton).iState = 3
            Case 3   'Timing 2nd, 3rd etc click in autorepeat mode
                Parm = ButtonParam(iCurrentButton).Click & HMIstrEOM
                SendString(Parm)
            Case 4   'Down in Hold mode
            End Select
        End If
    Case "I" 'Initialise
        iCurrentButton = -1 'imobilize
    End Select
End Sub

Here's one of the event handlers:

B4X:
Sub cmdHMIUserButton_Up()
    Dim Btn As Button
    Btn = Sender
    Log("U " & Btn.tag)
    fsmHMIButton ("U", Btn.tag) 'Call the button fsm    Build 195
End Sub

The final product of this, for me, is calls to SendString(). You will want something else.

You could simplify this. The main point for you is that without a registered Down event there can be no Up event and hence no synthesised Click. I have no idea why I have a note-to-self about re-instating the use of Click; it's too long ago. If you are keeping it simple you could store the state variable in th ebutton .Tag directly. I used a data structure and index because I track several other data items per button.
 
Top