Thursday, March 2, 2017

Intercept KeyEvent on Android with Delphi

Some days ago, I had the needed to intercept the KeyDown Event on Android. In particullary the App have to respond to FormKeyDown Event. After some work I found out that there is an opened issue about this topic (https://quality.embarcadero.com/browse/RSP-10111 - vote it if you need its resolution). So I set to work to find a solution/workaround to solve my need. I studied Android SDK and Delphi internal mechanism and I found out a way to resolve it:
  • By adding a JFMXTextListener to JFMXTextEditorProxy
JFMXTextEditorProxy is an object provided by the JFMXNativeActivity (The activity for all your Android application in Delphi) and provides the method addTextListener to register a JFMXTextListener.
JFMXTextListener is an interface that supply 3 methods to manage text event:

  1.    procedure onComposingText(beginPosition: Integer; endPosition: Integer); cdecl;
  2.    procedure onSkipKeyEvent(event: JKeyEvent); cdecl;
  3.    procedure onTextUpdated(text: JCharSequence; position: Integer); cdecl;
I created a new class TCustomAndroidTextListener that implements JFMXTextListener so I can intercept the KeyEvent in these methods:

TCustomAndroidTextListener = class(TJavaLocal, JFMXTextListener)
  private
    FForm: TCommonCustomForm;
    FLastStr: string;
  protected
    function TryAsKey(const aChar: Char; var Key: Word): Boolean;
  public
    constructor Create(aForm: TCommonCustomForm);
    procedure onComposingText(beginPosition: Integer;
      endPosition: Integer); cdecl;
    procedure onSkipKeyEvent(event: JKeyEvent); cdecl;
    procedure onTextUpdated(text: JCharSequence; position: Integer); cdecl;
  end;

Now I had to add my TextListener, TCustomAndroidTextListener, to JFMXTextEditorProxy so that implemented method can be invoked when the characters are typed:

procedure RegisterTextListener(const aForm: TCommonCustomForm);
var
  FFMXTxp: JFMXTextEditorProxy;
begin
  if Assigned(FTextListener) then
    exit;
  FTextListener := TCustomAndroidTextListener.Create(aForm);
  FFMXTxp := FMX.Platform.Android.MainActivity.getTextEditorProxy;
  // the code below should be equivalent to INPUT_ALPHABET
  // FFMXTxp.setInputType(TJInputType.JavaClass.TYPE_CLASS_TEXT or
  // TJInputType.JavaClass.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD or
  // TJInputType.JavaClass.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
  FFMXTxp.setInputType(TJFMXTextEditorProxy.JavaClass.INPUT_ALPHABET);
  FFMXTxp.addTextListener(FTextListener);
end;

I created a new sample, AndroidKeyEvent, and added it to my demos repository.
In the sample, if you look the code in onTextUpdated method you can notice a solution to understand what the user is typing and propagate keydown event on the form object passed in constructor. This solution is not complete to 100% because is very hard to figure out a way to understand what user is typing if autocomplete mode is on keyboard. It seems to work fine if autocomplete is off, this is why is set the input type to TJFMXTextEditorProxy.JavaClass.INPUT_ALPHABET.
In the coming days I will continue to work on the issue to achieve a better and complete solution. In the meantime I wanted to share the code with you so if you want to contribute you can do it.

1 comment :

  1. Thanks for the good sample. Maybe can You help us: Our goal is, to turn off predictive input for TEdit. We tried to implement Your listener for an Edit control, but we are unable to turn it off.

    ReplyDelete