Sunday, November 3, 2013

How to create Android services using Delphi XE5 using TTimer

Delphi XE5 does not provide the ability to create Android services, yet. However, there is a simple workaround: If you put a TTimer on a form, this form and this TTimer will also continue to run after switching to another app, or after closing the screen. This way, you can make a simple app that does something even when not in use.

Saturday, November 2, 2013

Delphi for Android uses 0-based strings

Delphi handles strings differently when compiling for Windows and Android.

Windows has 1-based strings, i.e. the first character in a string is s[1].

Android has 0-based strings, i.e. the first character in a string is s[0].

Also, you should expect, that future versions of Delphi will not allow you to write this code:

s[1]:='a';

Android apps start slowly? Here is the trick

When you run an Android app, it will be a slow startup because of the Android technology choices. The local emulator is an emulator, which means slow. If you attach a phone for development, the app still needs to be packaged, the old app needs to be uninstalled, the new version needs to be installed etc. All this takes time.

Fortunately, there is a solution: You can make your app compile for both Android and Windows, and debug most functionality as a Windows app. These guidelines can help you:

  1. Put all Android-specific code into units that have a name that starts with "Android". For instance, if you want to send an SMS, put the code for that into an AndroidSms.pas unit.
  2. Use {$IFDEF ANDROID} for code that you want to execute only when deployed on Android.
  3. Exclude Android* units from the dpr file
Here is an example of a uses-clause that compiles with both Android and Windows:

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
{$IFDEF ANDROID}
  AndroidSms,
{$ENDIF}
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls;

Here is an example of some code that does one thing on Android, and another thing when compiling to Windows:

procedure TForm2.SendSMS (target,messagestr:string);
begin
  {$IFDEF ANDROID}
  AndroidSms.SendSms(target,messagestr);
  {$ELSE}
  Memo1.Lines.Add (target+': '+messagestr);
  {$ENDIF}
end;

As you can see in this last example, the Windows version will visualize to the programmer, that the app was meant to send an SMS at this point, instead of actually doing it. This speeds up testing a lot.

Friday, November 1, 2013

Fetching files using https (SSL) using Delphi for Android

Copy & paste solution, which logs in, supports https and returns the result as one large string:

uses
  Classes, SysUtils,
  IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP,
  IdSSLOpenSSL;

function HttpsAuthGet(Url, Username, Password: string):string;
var
  ssl: TIdSSLIOHandlerSocketOpenSSL;
  http: TIdHTTP;
begin
  http := TIdHttp.Create(nil);
  try
    ssl := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
    try
      http.IOHandler := ssl;
      http.Request.BasicAuthentication:=True;
      http.Request.Username := Username;
      http.Request.Password := Password;
      Result:=http.Get(Url);
      if (http.ResponseCode<200) or (http.ResponseCode>=300) then
        raise Exception.Create(http.ResponseText);
    finally
      FreeAndNil(ssl);
    end;
  finally
    FreeAndNil(http);
  end;
end;

Download file to Android app using Delphi

This is a very simple one, because it works the same way as it did with Delphi for Windows:

function HttpGet(const url: string): string;
var
  HTTP: TIdHTTP;
begin
  HTTP := TIdHTTP.Create(nil);
  try
    Result:=http.Get(url);
  finally
    FreeAndNil (http);
  end;
end;

Where did PAnsiChar go with Delphi XE5 and Android?

Olaf Monien has written about this here.

Sunday, October 27, 2013

How to fetch SMS messages from the Android inbox using Delphi

The code speaks for itself:

uses
  SysUtils,
  FMX.Helpers.Android,
  Androidapi.JNI.GraphicsContentViewText,
  Androidapi.JNI.Net,
  Androidapi.JNI.JavaTypes,
  Androidapi.JNI.Telephony;

function FetchSms:string;
var
  cursor: JCursor;
  uri: Jnet_Uri;
  address,person,msgdatesent,protocol,msgread,msgstatus,msgtype,
  msgreplypathpresent,subject,body,
  servicecenter,locked:string;
  msgunixtimestampms:int64;
  addressidx,personidx,msgdateidx,msgdatesentidx,protocolidx,msgreadidx,
  msgstatusidx,msgtypeidx,msgreplypathpresentidx,subjectidx,bodyidx,
  servicecenteridx,lockedidx:integer;
begin
  uri:=StrToJURI('content://sms/inbox');
  cursor := SharedActivity.getContentResolver.query(uri, nil, nil,nil,nil);
  addressidx:=cursor.getColumnIndex(StringToJstring('address'));
  personidx:=cursor.getColumnIndex(StringToJstring('person'));
  msgdateidx:=cursor.getColumnIndex(StringToJstring('date'));
  msgdatesentidx:=cursor.getColumnIndex(StringToJstring('date_sent'));
  protocolidx:=cursor.getColumnIndex(StringToJstring('protocol'));
  msgreadidx:=cursor.getColumnIndex(StringToJstring('read'));
  msgstatusidx:=cursor.getColumnIndex(StringToJstring('status'));
  msgtypeidx:=cursor.getColumnIndex(StringToJstring('type'));
  msgreplypathpresentidx:=cursor.getColumnIndex(StringToJstring('reply_path_present'));
  subjectidx:=cursor.getColumnIndex(StringToJstring('subject'));
  bodyidx:=cursor.getColumnIndex(StringToJstring('body'));
  servicecenteridx:=cursor.getColumnIndex(StringToJstring('service_center'));
  lockedidx:=cursor.getColumnIndex(StringToJstring('locked'));
  while (cursor.moveToNext) do begin
    address:=JStringToString(cursor.getString(addressidx));
    person:=JStringToString(cursor.getString(personidx));
    msgunixtimestampms:=cursor.getLong(msgdateidx);
    msgdatesent:=JStringToString(cursor.getString(msgdatesentidx));
    protocol:=JStringToString(cursor.getString(protocolidx));
    msgread:=JStringToString(cursor.getString(msgreadidx));
    msgstatus:=JStringToString(cursor.getString(msgstatusidx));
    msgtype:=JStringToString(cursor.getString(msgtypeidx));
    msgreplypathpresent:=JStringToString(cursor.getString(msgreplypathpresentidx));
    subject:=JStringToString(cursor.getString(subjectidx));
    body:=JStringToString(cursor.getString(bodyidx));
    servicecenter:=JStringToString(cursor.getString(servicecenteridx));
    locked:=JStringToString(cursor.getString(lockedidx));
    Result:=IntToStr(trunc(msgunixtimestampms/1000))+' '+address+' '+body;
  end;
end;