Search code examples
c++delphijava-native-interfacec++builderc++builder-xe7

Android JNI bridge Toast C++ not working - How to fix it?


I'm trying to solve a little problem with my hpp unit. I found some examples on the internet showing how to Toast an information on screen with an Android JNI bridge, but it was just in pascal (delphi), so I decided to use the same pas file but with it an hpp file for conversion. Till now I got it:

#ifndef Android_Jni_ToastHPP
#define Android_Jni_ToastHPP

#pragma delphiheader begin
#pragma option push
#pragma option -w-                                        // All warnings off
#pragma option -Vx                                        // Zero-length empty class member 
#pragma pack(push,8)
#include <FMX.Helpers.Android.hpp>                        // Pascal unit
#include <Androidapi.JNIBridge.hpp>                       // Pascal unit
#include <Androidapi.JNI.JavaTypes.hpp>                   // Pascal unit
#include <Androidapi.JNI.GraphicsContentViewText.hpp>     // Pascal unit
#include <Androidapi.JNI.Util.hpp>                        // Pascal unit
#include <Androidapi.JNI.App.hpp>                         // Pascal unit
#include <FMX.Surfaces.hpp>                               // Pascal unit

//-- user supplied -----------------------------------------------------------

namespace Android
{
namespace Jni
{
namespace Toast
{
//-- type declarations -------------------------------------------------------

extern DELPHI_PACKAGE Androidapi::Jni::Toast __fastcall Toast(const System::UnicodeString Msg, TToastLength duration);

#pragma pack(pop)

//-- type declarations -------------------------------------------------------
//-- var, const, procedure ---------------------------------------------------

}   /* namespace Android */
}   /* namespace JNI     */
}   /* namespace Toast   */
#if !defined(DELPHIHEADER_NO_IMPLICIT_NAMESPACE_USE) && !defined(NO_USING_NAMESPACE_ANDROID_JNI_TOAST)
using namespace Android::Jni::Toast;
#endif
#if !defined(DELPHIHEADER_NO_IMPLICIT_NAMESPACE_USE) && !defined(NO_USING_NAMESPACE_ANDROID_JNI)
using namespace Android::Jni;
#endif
#if !defined(DELPHIHEADER_NO_IMPLICIT_NAMESPACE_USE) && !defined(NO_USING_NAMESPACE_ANDROID)
using namespace Android;
#endif
#pragma pack(pop)
#pragma option pop

#pragma delphiheader end.
//-- end unit ----------------------------------------------------------------
#endif  // Androidapi_Jni_ToastHPP

If you would like to have the pascal unit, here is it:

unit Android.JNI.Toast;

interface

{$IFDEF ANDROID}

uses
  Androidapi.JNIBridge,
  Androidapi.JNI.JavaTypes,
  Androidapi.JNI.GraphicsContentViewText;
{$ENDIF}
{$IFDEF ANDROID}

type
  TToastLength = (LongToast, ShortToast);

  JToast = interface;

  JToastClass = interface(JObjectClass)
    ['{69E2D233-B9D3-4F3E-B882-474C8E1D50E9}']
    { Property methods }
    function _GetLENGTH_LONG: Integer; cdecl;
    function _GetLENGTH_SHORT: Integer; cdecl;
    { Methods }
    function init(context: JContext): JToast; cdecl; overload;
    function makeText(context: JContext; text: JCharSequence; duration: Integer)
      : JToast; cdecl;
    { Properties }
    property LENGTH_LONG: Integer read _GetLENGTH_LONG;
    property LENGTH_SHORT: Integer read _GetLENGTH_SHORT;
  end;

  [JavaSignature('android/widget/Toast')]
  JToast = interface(JObject)
    ['{FD81CC32-BFBC-4838-8893-9DD01DE47B00}']
    { Methods }
    procedure cancel; cdecl;
    function getDuration: Integer; cdecl;
    function getGravity: Integer; cdecl;
    function getHorizontalMargin: Single; cdecl;
    function getVerticalMargin: Single; cdecl;
    function getView: JView; cdecl;
    function getXOffset: Integer; cdecl;
    function getYOffset: Integer; cdecl;
    procedure setDuration(value: Integer); cdecl;
    procedure setGravity(gravity, xOffset, yOffset: Integer); cdecl;
    procedure setMargin(horizontalMargin, verticalMargin: Single); cdecl;
    procedure setText(s: JCharSequence); cdecl;
    procedure setView(view: JView); cdecl;
    procedure show; cdecl;
  end;

  TJToast = class(TJavaGenericImport<JToastClass, JToast>)
  end;

procedure Toast(const Msg: string; duration: TToastLength = ShortToast);

{$ENDIF}

implementation

{$IFDEF ANDROID}

uses
  FMX.Helpers.Android, Androidapi.Helpers;

procedure Toast(const Msg: string; duration: TToastLength);
var
  ToastLength: Integer;
begin
  if duration = ShortToast then
    ToastLength := TJToast.JavaClass.LENGTH_SHORT
  else
    ToastLength := TJToast.JavaClass.LENGTH_LONG;
  CallInUiThread(
    procedure
    begin
      TJToast.JavaClass.makeText(SharedActivityContext, StrToJCharSequence(Msg),
        ToastLength).show
    end);
end;
{$ENDIF}

end.

However I get error when compilling using this source... What con I do?

PS.: I arrived on that state just comparing AppMetthod sources with what I was making...

Log:

Checking project dependencies...
Compiling WebBrowser.cbproj (Debug, Android)
bccaarm command line for "uMain.cpp"
  c:\program files (x86)\embarcadero\studio\15.0\bin\bccaarm.exe -cc1 -D _DEBUG -isysroot 
  C:\Users\Public\Documents\Embarcadero\Studio\15.0\PlatformSDKs\android-ndk-r9c\platforms\android-14\arch-arm -idirafter =\usr\include -idirafter 
  C:\Users\Public\Documents\Embarcadero\Studio\15.0\PlatformSDKs\android-ndk-r9c\sources\cxx-stl\gnu-libstdc++\4.8\include -idirafter 
  C:\Users\Public\Documents\Embarcadero\Studio\15.0\PlatformSDKs\android-ndk-r9c\sources\cxx-stl\gnu-libstdc++\4.8\libs\armeabi-v7a\include -idirafter 
  C:\Users\Public\Documents\Embarcadero\Studio\15.0\PlatformSDKs\android-ndk-r9c\sources\android\native_app_glue -I 
  "C:\Users\Public\Documents\Embarcadero\Studio\15.0\Samples\CPP\Mobile Snippets\WebBrowser" -isystem "c:\program files 
  (x86)\embarcadero\studio\15.0\include" -isystem "c:\program files (x86)\embarcadero\studio\15.0\include\android\rtl" -isystem "c:\program files 
  (x86)\embarcadero\studio\15.0\include\android\fmx" -isystem "c:\program files (x86)\embarcadero\studio\15.0\include\android\crtl" -g 
  -fno-limit-debug-info -fborland-extensions -fborland-auto-refcount -nobuiltininc -nostdsysteminc -triple thumbv7-none-linux-androideabi -emit-obj 
  -mconstructor-aliases -pic-level 2 -target-abi aapcs-linux  -nostdinc++ -fdeprecated-macro -fexceptions -fcxx-exceptions -munwind-tables 
  -mstackrealign -fno-spell-checking -fno-use-cxa-atexit -main-file-name uMain.cpp -x c++ -std=c++11 -O0 -tU -o .\Android\Debug\uMain.o 
  -dependency-file .\Android\Debug\uMain.d -MT .\Android\Debug\uMain.o uMain.cpp 
[bccaarm Error] Android.JNI.Toast.hpp(36): expected a class or namespace
[bccaarm Error] Android.JNI.Toast.hpp(36): reference to 'Android' is ambiguous
  Android.JNI.Toast.hpp(28): candidate found by name lookup is 'Android'
  FMX.Helpers.Android.hpp(28): candidate found by name lookup is 'Fmx::Helpers::Android'
[bccaarm Error] Android.JNI.Toast.hpp(36): expected unqualified-id
[bccaarm Error] Android.JNI.Toast.hpp(47): expected a class or namespace
[bccaarm Error] Android.JNI.Toast.hpp(47): reference to 'Android' is ambiguous
  Android.JNI.Toast.hpp(28): candidate found by name lookup is 'Android'
  FMX.Helpers.Android.hpp(28): candidate found by name lookup is 'Fmx::Helpers::Android'
[bccaarm Error] Android.JNI.Toast.hpp(47): expected namespace name
[bccaarm Error] Android.JNI.Toast.hpp(50): expected a class or namespace
[bccaarm Error] Android.JNI.Toast.hpp(50): reference to 'Android' is ambiguous
  Android.JNI.Toast.hpp(28): candidate found by name lookup is 'Android'
  FMX.Helpers.Android.hpp(28): candidate found by name lookup is 'Fmx::Helpers::Android'
[bccaarm Error] Android.JNI.Toast.hpp(50): expected namespace name
[bccaarm Error] Android.JNI.Toast.hpp(53): reference to 'Android' is ambiguous
  Android.JNI.Toast.hpp(28): candidate found by name lookup is 'Android'
  FMX.Helpers.Android.hpp(28): candidate found by name lookup is 'Fmx::Helpers::Android'
[bccaarm Error] uMain.cpp(32): use of undeclared identifier 'Toast'
Failed
Elapsed time: 00:00:05.8

EDIT:

New log file:

[bccaarm Error] Androidapi.JNI.Toast.hpp(36): no type named 'Toast' in namespace 'Androidapi::Jni'
[bccaarm Error] Androidapi.JNI.Toast.hpp(36): expected unqualified-id
[bccaarm Error] uMain.cpp(32): unexpected namespace name 'Toast': expected expression
Failed

Since now, thanks a lot.


Solution

  • The .hpp file is malformed.

    None of the public types defined in the interface section of the .pas file are defined in the .hpp file. In particular, the TToastLength enum is missing. And Toast() is declared as a procedure in the .pas file and thus has no return value, but it has an (erroneous) return value in the .hpp file.

    Did you (or someone else) create the .hpp file manually? I suspect this is the case, for two noticable reasons:

    1. the Android_JNI_ToastHPP define in the initial #ifndef/#define does not match the Fmx_Helpers_AndroidHPP in the corresponding #endif.

    2. the Delphi compiler always names C++ namespaces for Pascal unit names by uppercasing the first letter and lowercasing the remaining letters, but this .hpp file has an all-uppercase JNI namespace.

    The Delphi compiler would not makes those mistakes.

    With that said, Android.JNI.Toast.pas should be renamed to Androidapi.JNI.Toast.pas (and the .hpp file renamed and its namespaces updated accordingly) for consistency with Embarcadero's other JNI units (and to match the example in this article). That should also help alleviate the reference to 'Android' is ambiguous errors as well.

    I strongly suggest you correct the .pas file, run it through the Delphi compiler once to produce the correct .hpp file, and then use it as-is in your C++ code.

    Androidapi.JNI.Toast.pas:

    unit Androidapi.JNI.Toast;
    
    interface
    
    {$IFDEF ANDROID}
    
    uses
      Androidapi.JNIBridge,
      Androidapi.JNI.JavaTypes,
      Androidapi.JNI.GraphicsContentViewText;
    
    type
      TToastLength = (LongToast, ShortToast);
    
      JToast = interface;
    
      JToastClass = interface(JObjectClass)
        ['{69E2D233-B9D3-4F3E-B882-474C8E1D50E9}']
        { Property methods }
        function _GetLENGTH_LONG: Integer; cdecl;
        function _GetLENGTH_SHORT: Integer; cdecl;
        { Methods }
        function init(context: JContext): JToast; cdecl; overload;
        function makeText(context: JContext; text: JCharSequence; duration: Integer)
          : JToast; cdecl;
        { Properties }
        property LENGTH_LONG: Integer read _GetLENGTH_LONG;
        property LENGTH_SHORT: Integer read _GetLENGTH_SHORT;
      end;
    
      [JavaSignature('android/widget/Toast')]
      JToast = interface(JObject)
        ['{FD81CC32-BFBC-4838-8893-9DD01DE47B00}']
        { Methods }
        procedure cancel; cdecl;
        function getDuration: Integer; cdecl;
        function getGravity: Integer; cdecl;
        function getHorizontalMargin: Single; cdecl;
        function getVerticalMargin: Single; cdecl;
        function getView: JView; cdecl;
        function getXOffset: Integer; cdecl;
        function getYOffset: Integer; cdecl;
        procedure setDuration(value: Integer); cdecl;
        procedure setGravity(gravity, xOffset, yOffset: Integer); cdecl;
        procedure setMargin(horizontalMargin, verticalMargin: Single); cdecl;
        procedure setText(s: JCharSequence); cdecl;
        procedure setView(view: JView); cdecl;
        procedure show; cdecl;
      end;
    
      TJToast = class(TJavaGenericImport<JToastClass, JToast>)
      end;
    
    procedure Toast(const Msg: string; duration: TToastLength = ShortToast);
    
    {$ENDIF}
    
    implementation
    
    {$IFDEF ANDROID}
    
    uses
      FMX.Helpers.Android, Androidapi.Helpers;
    
    procedure Toast(const Msg: string; duration: TToastLength);
    var
      ToastLength: Integer;
    begin
      if duration = ShortToast then
        ToastLength := TJToast.JavaClass.LENGTH_SHORT
      else
        ToastLength := TJToast.JavaClass.LENGTH_LONG;
      CallInUiThread(
        procedure
        begin
          TJToast.JavaClass.makeText(SharedActivityContext, StrToJCharSequence(Msg),
            ToastLength).show
        end);
    end;
    {$ENDIF}
    
    end.
    

    Androidapi.JNI.Toast.hpp:

    // CodeGear C++Builder
    // Copyright (c) 1995, 2014 by Embarcadero Technologies, Inc.
    // All rights reserved
    
    // (DO NOT EDIT: machine generated header) 'Androidapi.JNI.Toast.pas' rev: 28.00 (Android)
    
    #ifndef Androidapi_Jni_ToastHPP
    #define Androidapi_Jni_ToastHPP
    
    #pragma delphiheader begin
    #pragma option push
    #pragma option -w-      // All warnings off
    #pragma option -Vx      // Zero-length empty class member 
    #pragma pack(push,8)
    #include <System.hpp>   // Pascal unit
    #include <SysInit.hpp>  // Pascal unit
    #include <Androidapi.JNIBridge.hpp> // Pascal unit
    #include <Androidapi.JNI.JavaTypes.hpp> // Pascal unit
    #include <Androidapi.JNI.GraphicsContentViewText.hpp>   // Pascal unit
    #include <System.Rtti.hpp>  // Pascal unit
    
    //-- user supplied -----------------------------------------------------------
    
    namespace Androidapi
    {
    namespace Jni
    {
    namespace Toast
    {
    //-- type declarations -------------------------------------------------------
    enum DECLSPEC_DENUM TToastLength : unsigned char { LongToast, ShortToast };
    
    __interface JToastClass;
    typedef System::DelphiInterface<JToastClass> _di_JToastClass;
    __interface JToast;
    typedef System::DelphiInterface<JToast> _di_JToast;
    __interface  INTERFACE_UUID("{69E2D233-B9D3-4F3E-B882-474C8E1D50E9}") JToastClass  : public Androidapi::Jni::Javatypes::JObjectClass 
    {
    
    public:
        virtual int __cdecl _GetLENGTH_LONG(void) = 0 ;
        virtual int __cdecl _GetLENGTH_SHORT(void) = 0 ;
        HIDESBASE virtual _di_JToast __cdecl init(Androidapi::Jni::Graphicscontentviewtext::_di_JContext context) = 0 /* overload */;
        virtual _di_JToast __cdecl makeText(Androidapi::Jni::Graphicscontentviewtext::_di_JContext context, Androidapi::Jni::Javatypes::_di_JCharSequence text, int duration) = 0 ;
        __property int LENGTH_LONG = {read=_GetLENGTH_LONG};
        __property int LENGTH_SHORT = {read=_GetLENGTH_SHORT};
    };
    
    __interface  INTERFACE_UUID("{FD81CC32-BFBC-4838-8893-9DD01DE47B00}") JToast  : public Androidapi::Jni::Javatypes::JObject 
    {
    
    public:
        virtual void __cdecl cancel(void) = 0 ;
        virtual int __cdecl getDuration(void) = 0 ;
        virtual int __cdecl getGravity(void) = 0 ;
        virtual float __cdecl getHorizontalMargin(void) = 0 ;
        virtual float __cdecl getVerticalMargin(void) = 0 ;
        virtual Androidapi::Jni::Graphicscontentviewtext::_di_JView __cdecl getView(void) = 0 ;
        virtual int __cdecl getXOffset(void) = 0 ;
        virtual int __cdecl getYOffset(void) = 0 ;
        virtual void __cdecl setDuration(int value) = 0 ;
        virtual void __cdecl setGravity(int gravity, int xOffset, int yOffset) = 0 ;
        virtual void __cdecl setMargin(float horizontalMargin, float verticalMargin) = 0 ;
        virtual void __cdecl setText(Androidapi::Jni::Javatypes::_di_JCharSequence s) = 0 ;
        virtual void __cdecl setView(Androidapi::Jni::Graphicscontentviewtext::_di_JView view) = 0 ;
        virtual void __cdecl show(void) = 0 ;
    };
    
    class DELPHICLASS TJToast;
    #pragma pack(push,4)
    class PASCALIMPLEMENTATION TJToast : public Androidapi::Jnibridge::TJavaGenericImport__2<_di_JToastClass,_di_JToast> 
    {
        typedef Androidapi::Jnibridge::TJavaGenericImport__2<_di_JToastClass,_di_JToast>  inherited;
    
    public:
        /* TJavaImport.Create */ inline __fastcall TJToast(void * ID, void * ClsID, Androidapi::Jnibridge::TJavaVTable* VTable) : Androidapi::Jnibridge::TJavaGenericImport__2<_di_JToastClass,_di_JToast> (ID, ClsID, VTable) { }
        /* TJavaImport.Destroy */ inline __fastcall virtual ~TJToast(void) { }
    
    };
    
    #pragma pack(pop)
    
    //-- var, const, procedure ---------------------------------------------------
    extern DELPHI_PACKAGE void __fastcall Toast(const System::UnicodeString Msg, TToastLength duration = (TToastLength)(0x1));
    }   /* namespace Toast */
    }   /* namespace Jni */
    }   /* namespace Androidapi */
    #if !defined(DELPHIHEADER_NO_IMPLICIT_NAMESPACE_USE) && !defined(NO_USING_NAMESPACE_ANDROIDAPI_JNI_TOAST)
    using namespace Androidapi::Jni::Toast;
    #endif
    #if !defined(DELPHIHEADER_NO_IMPLICIT_NAMESPACE_USE) && !defined(NO_USING_NAMESPACE_ANDROIDAPI_JNI)
    using namespace Androidapi::Jni;
    #endif
    #if !defined(DELPHIHEADER_NO_IMPLICIT_NAMESPACE_USE) && !defined(NO_USING_NAMESPACE_ANDROIDAPI)
    using namespace Androidapi;
    #endif
    #pragma pack(pop)
    #pragma option pop
    
    #pragma delphiheader end.
    //-- end unit ----------------------------------------------------------------
    #endif  // Androidapi_Jni_ToastHPP