Android Question Crash on clicking selected CSBuilder text in EditText

RB Smissaert

Well-Known Member
Licensed User
Have a simple EditText that has a CSBuilder text in it (for SQL syntax highlighting). When I select a length of text (with the normal standard long click) and then do a click within that text I get a crash with this error message:

java.lang.IndexOutOfBoundsException: getChars (-1 ... 527) starts before 0
at android.text.SpannableStringBuilder.checkRange(SpannableStringBuilder.java:1314)
at android.text.SpannableStringBuilder.getChars(SpannableStringBuilder.java:1191)
at android.text.TextUtils.getChars(TextUtils.java:98)
at android.text.TextUtils.substring(TextUtils.java:289)
at android.view.inputmethod.BaseInputConnection.getSelectedText(BaseInputConnection.java:532)
at com.android.internal.view.IInputConnectionWrapper.executeMessage(IInputConnectionWrapper.java:286)
at com.android.internal.view.IInputConnectionWrapper$MyHandler.handleMessage(IInputConnectionWrapper.java:85)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6938)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

The problem is that I can't find where this error originates from. Running in debug mode does cause the same crash but it doesn't point to any B4A line. I tried stepping in debug mode by adding a Click event sub:

B4X:
Sub edtSQL_Click()
    Log("edtSQL_Click")
End Sub

But that doesn't lead me to the origin of the error either.

In case it matters the CSBuilder text does have clickable words, but the error also happens when clicking outside these clickable words. If formatting is turned off, so if the EditText holds plain text
then there is no error.

I am using the latest version of B4A and also the latest Android version.

RBS
 

RB Smissaert

Well-Known Member
Licensed User
This is the code that builds the CSBuilder:

B4X:
Sub InitSQLColours
 SQLColours(0) = Colors.RGB(0, 0, 0)
 SQLColours(1) = Colors.RGB(0, 0, 220)
 SQLColours(2) = Colors.RGB(200, 60, 0)
 SQLColours(3) = Colors.RGB(0, 0, 220)
 SQLColours(4) = Colors.RGB(220, 0, 0)
 SQLColours(5) = Colors.RGB(220, 0, 0)
 SQLColours(6) = Colors.RGB(0, 80, 255)
 SQLColours(7) = Colors.RGB(128, 0, 255)
 SQLColours(8) = Colors.RGB(0, 200, 0) 'literal
 SQLColours(9) = Colors.RGB(128, 128, 0) 'for brackets
 SQLColours(10) = Colors.RGB(140, 100, 0) 'for table names
 SQLColours(11) = Colors.RGB(0, 140, 100) 'for view names
 SQLColours(12) = Colors.RGB(100, 32, 200) 'for SQL replace strings
 SQLColours(13) = Colors.RGB(100, 32, 200) 'for variable SQL replace strings
 SQLColours(14) = Colors.RGB(100, 32, 200) 'for custom function, for now only SD2XLD and CDMinusXDays
End Sub

Sub FormatSQL(strSQL As String) As CSBuilder

 Dim i As Int
 Dim cs As CSBuilder
 Dim arrSQLWords() As SQLWord
 Dim UB As Int
 Dim strWordLower As String
 Dim tf As Typeface

 tf = Typeface.CreateNew(Typeface.MONOSPACE, Typeface.STYLE_ITALIC)

 cs.Initialize.Color (Colors.Black)

 If bNoSQLFormattingAtAll Or bNoSQLFormatting Then
  Return cs
 End If

 'note that we shouldn't remove comments here as otherwise the SQL in the editor will change!
 '-------------------------------------------------------------------------------------------
 'this is very quick, about 1 msecs
 arrSQLWords = GetSQLWords(strSQL, 0, "", False, False)
 ' For i = 0 To arrSQLWords.Length - 1
 '  General.RunLog("FormatSQL, Word: |" & arrSQLWords(i).strWord & "|")
 ' Next

 If arrSQLWords.Length = 0 Then
  Return cs
 End If

 General.StartSW (3)

 UB = arrSQLWords.Length - 1

 For i = 0 To UB
  'General.RunLog("FormatSQL, Word: " & arrSQLWords(i).strWord)

  If arrSQLWords(i).bWord Then
   If arrSQLWords(i).bLiteral Or arrSQLWords(i).bCommentStart Or arrSQLWords(i).bComment Or arrSQLWords(i).bCommentEnd Then
    'green for literal string or comment
    '-----------------------------------
    If arrSQLWords(i).bLiteral Or arrSQLWords(i).bComment Then
     cs.Color(SQLColours(8)).Append(arrSQLWords(i).strWord).Pop
    Else
     cs.Color(SQLColours(4)).Append(arrSQLWords(i).strWord).Pop
    End If
   Else    'If arrSQLWords(i).bLiteral
    strWordLower = arrSQLWords(i).strWord.ToLowerCase
    If mapSQLWordTypes.ContainsKey(strWordLower) Then
     Select Case mapSQLWordTypes.Get(strWordLower)
      Case 1
       'deal with SQL key word in square brackets or double quotes
       'no need to check previous byte as if not matching, SQL will fail Prepare
       '------------------------------------------------------------------------
       If arrSQLWords(i).lPos > 0 And _
                            (arrSQLWords(i).btNextByte = 34 Or arrSQLWords(i).btNextByte = 93) Then
        cs.Append (arrSQLWords(i).strWord)
       Else
        cs.Bold.Color(SQLColours(1)).Append(arrSQLWords(i).strWord).Pop.Pop
       End If
      Case 2
       cs.Bold.Color(SQLColours(2)).Append(arrSQLWords(i).strWord).Pop.Pop
      Case 3
       cs.Color(SQLColours(3)).Append(arrSQLWords(i).strWord).Pop
      Case 4
       cs.Color(SQLColours(4)).Append(arrSQLWords(i).strWord).Pop
      Case 5
       cs.Color(SQLColours(4)).Append(arrSQLWords(i).strWord).Pop
      Case 6
       cs.Color(SQLColours(6)).Append(arrSQLWords(i).strWord).Pop
      Case 7
       cs.Color(SQLColours(7)).Append(arrSQLWords(i).strWord).Pop
      Case 10
       'tables, take care for fields that have same name as a table or view
       '-------------------------------------------------------------------
       If arrSQLWords(i).strPreviousWord.ToLowerCase = "from" Or _
                           arrSQLWords(i).strPreviousWord.ToLowerCase = "join" Or _
                           arrSQLWords(i).strPreviousWord.ToLowerCase = "table" Or _
                           arrSQLWords(i).strPreviousWord.ToLowerCase = "into" Or _
                           arrSQLWords(i).strPreviousWord.ToLowerCase = "update" Or _
                           arrSQLWords(i).strPreviousWord.ToLowerCase = "exists" Then
        cs.Underline.Bold.Color(SQLColours(11)).Clickable("Table", _
                                       arrSQLWords(i).strWord).Append(arrSQLWords(i).strWord).Pop.Pop.Pop.Pop
       Else
        cs.Append (arrSQLWords(i).strWord)
       End If
      Case 11
       'views, take care for fields that have same name as a table or view
       '-----------------------------------------------------------------
       If arrSQLWords(i).strPreviousWord.ToLowerCase = "from" Or _
                           arrSQLWords(i).strPreviousWord.ToLowerCase = "join" Or _
                           arrSQLWords(i).strPreviousWord.ToLowerCase = "view" Then
        cs.Underline.Bold.Color(SQLColours(11)).Clickable("View", _
                                      arrSQLWords(i).strWord).Append(arrSQLWords(i).strWord).Pop.Pop.Pop.Pop
       Else
        cs.Append (arrSQLWords(i).strWord)
       End If
      Case 12
       'SQL replace strings
       cs.Underline.Color(SQLColours(12)).Clickable("Shortcut", _
                                     arrSQLWords(i).strWord).Append(arrSQLWords(i).strWord).Pop.Pop.Pop
      Case 13
       'SQL replace strings, based on variable
       cs.Typeface(tf).Underline.Color(SQLColours(13)).Clickable("VariableShortcut", _
                                     arrSQLWords(i).strWord).Append(arrSQLWords(i).strWord).Pop.Pop.Pop.Pop
      Case 14
       'custom SQL functions for now only SD2XLD
       cs.Typeface(tf).Color(SQLColours(6)).Append(arrSQLWords(i).strWord).Pop.Pop
     End Select
    Else    'If  mapSQLWordTypes.ContainsKey(strWordLower)
     'cs.Color(SQLColours(0)).Append(arrSQLWords(i).strWord).Pop
     cs.Append (arrSQLWords(i).strWord)
    End If    'If  mapSQLWordTypes.ContainsKey(strWordLower)
   End If    'If arrSQLWords(i).bLiteral
  Else    'If arrSQLWords(i).bWord
   If arrSQLWords(i).strWord.Trim.Length = 0 Then
    cs.Append (arrSQLWords(i).strWord)
   Else
    'only problem with this is that commas and % will be done like brackets
    cs.Color(SQLColours(9)).Append(arrSQLWords(i).strWord).Pop
   End If
  End If    'If arrSQLWords(i).bWord
 Next

 cs.PopAll

 cs.EnableClickEvents (edtSQL)
 'formatting takes a bit longer about 7 msecs for the Sudoku SQL
 '--------------------------------------------------------------
 'General.RunLog ("FormatSQL, time Formatting: " & General.StopSW(3) & " msecs")
 Return cs

End Sub

There is a lot of other code involved, but maybe somebody can spot a possible problem here.


RBS


It looks this is a know Android bug:

https://stackoverflow.com/questions...dsexception-getchars-7-0-has-end-before-start

There are some solutions mentioned there, but not sure how this should be coded in B4A.

RBS
 
Upvote 0

Semen Matusovskiy

Well-Known Member
Licensed User
If this is a bug, take a look a date. 2010 year. Do you think that a bug was not corrected ?

Unlike I saw something similar in Google Console. For old OSes (Android 4), automatic test talked something similar.

If troubles happens in case of context menu, it's possible simply to prevent this event.
B4X:
Dim EditText1 As EditText
Dim reflector1 As Reflector
...
reflector1.Target = EditText1
reflector1.SetOnLongClickListener ("EditText1_LongClick")
...
Sub EditText1_LongClick (objectViewTag As Object) As Boolean

    Return True

End Sub
 
Last edited:
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
If this is a bug, take a look a date. 2010 year. Do you think that a bug was not corrected ?

Unlike I saw something similar in Google Console. For old OSes (Android 4), automatic test talked something similar.

If troubles happens in case of context menu, it's possible simply to prevent this event.
B4X:
Dim EditText1 As EditText
Dim reflector1 As Reflector
...
reflector1.Target = EditText1
reflector1.SetOnLongClickListener ("EditText1_LongClick")
...
Sub EditText1_LongClick (objectViewTag As Object) As Boolean

    Return True

End Sub


This simple code seems to solve my problem:

B4X:
Sub edtSQL_Click()
 If edtSQL.SelectionLength > 0 Then
  edtSQL.SetSelection(edtSQL.Text.Length - 1, edtSQL.Text.Length - 1)
 End If
End Sub


RBS
 
Upvote 0

Semen Matusovskiy

Well-Known Member
Licensed User
Clear.

Meanwhile I never was able to reproduce an error from Google Console's automatic test.
I attached a test code. It doesn't work like I want. But here is important another - I can't crash.

Strange bug.
 

Attachments

  • Emoji2.zip
    45.3 KB · Views: 137
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Have a simple EditText that has a CSBuilder text in it (for SQL syntax highlighting). When I select a length of text (with the normal standard long click) and then do a click within that text I get a crash with this error message:

java.lang.IndexOutOfBoundsException: getChars (-1 ... 527) starts before 0
at android.text.SpannableStringBuilder.checkRange(SpannableStringBuilder.java:1314)
at android.text.SpannableStringBuilder.getChars(SpannableStringBuilder.java:1191)
at android.text.TextUtils.getChars(TextUtils.java:98)
at android.text.TextUtils.substring(TextUtils.java:289)
at android.view.inputmethod.BaseInputConnection.getSelectedText(BaseInputConnection.java:532)
at com.android.internal.view.IInputConnectionWrapper.executeMessage(IInputConnectionWrapper.java:286)
at com.android.internal.view.IInputConnectionWrapper$MyHandler.handleMessage(IInputConnectionWrapper.java:85)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6938)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)

The problem is that I can't find where this error originates from. Running in debug mode does cause the same crash but it doesn't point to any B4A line. I tried stepping in debug mode by adding a Click event sub:

B4X:
Sub edtSQL_Click()
    Log("edtSQL_Click")
End Sub

But that doesn't lead me to the origin of the error either.

In case it matters the CSBuilder text does have clickable words, but the error also happens when clicking outside these clickable words. If formatting is turned off, so if the EditText holds plain text
then there is no error.

I am using the latest version of B4A and also the latest Android version.

RBS

Thanks to Ilan I have now serialized this troublesome CSBuilder and saved it as a file.
It doesn't contain EnableClickEvents so that needs to be added manually.
Attached my serialized CSBuilder.

RBS
 

Attachments

  • CSBuilder.txt
    285 bytes · Views: 129
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
Would be simpler to test if you create a small project that loads it and reproduces the error.

I have put together a demo project and attached the zipped project folder.
Noticed that the crash only happens if we do:

B4X:
cs.EnableClickEvents(edtSQL)

Without that all is fine.

RBS
 

Attachments

  • CrashDemo.zip
    60 KB · Views: 124
Upvote 0

Mahares

Expert
Licensed User
Noticed that the crash only happens if we do:
I tested your project without any change and got no errors. Here are the conditions:
1. OS 6 and OS 7
2. B4A 8.30, not 8.50
3. KVS2 version 2.21, RandomAccess 2.32, SQL 1.50


*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
** Activity (main) Resume **
285
Table : current_med clicked
285
Table : current_med clicked
** Activity (main) Pause, UserClosed = false **
 
Upvote 0

RB Smissaert

Well-Known Member
Licensed User
I tested your project without any change and got no errors. Here are the conditions:
1. OS 6 and OS 7
2. B4A 8.30, not 8.50
3. KVS2 version 2.21, RandomAccess 2.32, SQL 1.50


*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
** Activity (main) Resume **
285
Table : current_med clicked
285
Table : current_med clicked
** Activity (main) Pause, UserClosed = false **


Did you click a selected text?

RBS
 
Upvote 0
Top