Wednesday, 7 May 2014

Launching other activities from Delphi Android apps

Following on from my recent post about adding Android app splash screens in Delphi XE5 and Delphi XE6 I’ve now spent some time writing up details on how to launch various Android system activities. This includes the more interesting business of getting feedback from launched activities; a common use case here is using a barcode scanner and finding what barcode what scanned.

Again there are two articles, one for Delphi XE5 and one for Delphi XE6. The XE5 version is much longer as it involves documenting a whole bunch of command-line shenanigans (much like for the splash screen) to hook the activity results. This is made much easier in Delphi XE6, thankfully.

I’ve tried to do a thorough coverage of the subject matter, highlighting any issues I bumped into – I hope it is of use to anyone tinkering with Android apps in Delphi.

19 comments:

  1. Hello! Thank you for this interesting series and let us make our hands dirty with examples of Delphi apps on android.
    There is a problem in the build.bat: it declares EMBO_DEX and EMBO_DEX_JAR to the same classes.dex (classes.jar supposed) and later in javac command line classes.jar is mentioned. Unfortunately, I haven't been able to find in XE5 and Appmethod 13.0 classes.jar
    Because of that compile error happens
    src\com\blong\test\NativeActivitySubclass.java:12: error: package com.embarcadero.firemonkey does not exist
    public class NativeActivitySubclass extends com.embarcadero.firemonkey.FMXNativeActivity

    ReplyDelete
    Replies
    1. Hi, glad you find interest in the write-ups.
      Ignore EMBO_DEX_JAR - seems to be there in error. I'll fix that when I get chance.
      classes.jar is created as one of the steps in the article, just before running build.bat; it's created by running a tool over classes.dex.
      Search the web page of the article for classes.jar or for d2j-dex2jar and you'll see it.
      Maybe I should have highlighted that a bit more clearly, one way or another... I'll see about making a change to announce what is needed.

      Delete
    2. Thank you! After dex2jar build.bat has run all right. It was my fault to run build.bat before this prep step #3.

      Delete
    3. Cool, glad it's working!

      Delete
  2. Hi Brian

    Thanks for this very interesting article.
    I have one problem:
    As soon as I call

    SharedActivity.startActivityForResult(Intent,RequestCode);

    my application terminates and there is no chance that HandleActivityMessage is ever called.

    I am probably doing something wrong; but I have no idea what.

    Thanks if you have an answer

    Kaspar Neuenschwander, Switzerland

    ReplyDelete
    Replies
    1. Seems like you'll need to run up DDMS or monitor from the Android SDK to check the logcat output from the app when it dies. That may give you the hint as to what step you have done wrong.
      You could also double check with my sample and see if that works or also fails.

      Delete
  3. Hello Brian:
    Thankx a lot for your interesting articles about xe6 and android.

    Your source download link in "Launching activities and handling results in Delphi XE6 Android apps" article doesn't work.

    It would be possible that your fix it?

    I need to check your sample because i'm having a mistake when manage more than one activity in my project.

    Thanks in advance.
    José Manuel López

    ReplyDelete
    Replies
    1. Hi there - the link has been fixed now.

      Delete
  4. GPS receiver switches off in sleep mode?
    Exemple:

    ReplyDelete
    Replies
    1. I think that depends on how you write the app events. Generally many apps would benefit from disabling GPS updates when they go into the background, but I think in a Delphi app it's down to whether you choose to do so. I haven't tested the theory out yet.

      Delete
  5. I heard the AlarmManager you have any examples how to do in Delphi, Delphi exists in this JPendingIntent, JAlarmManager TJAlarmManager and more do not know how to implement. it exists in java:

    public class MyActivity extends Activity {

    private PendingIntent pendingIntent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_my);

    /* Retrieve a PendingIntent that will perform a broadcast */
    Intent alarmIntent = new Intent(MyActivity.this, AlarmReceiver.class);
    pendingIntent = PendingIntent.getBroadcast(MyActivity.this, 0, alarmIntent, 0);

    findViewById(R.id.startAlarm).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    start();
    }
    });

    findViewById(R.id.stopAlarm).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    cancel();
    }
    });

    findViewById(R.id.stopAlarmAt10).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
    startAt10();
    }
    });
    }

    public void start() {
    AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    int interval = 8000;

    manager.setInexactRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), interval, pendingIntent);
    Toast.makeText(this, "Alarm Set", Toast.LENGTH_SHORT).show();
    }

    public void cancel() {
    AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    manager.cancel(pendingIntent);
    Toast.makeText(this, "Alarm Canceled", Toast.LENGTH_SHORT).show();
    }

    public void startAt10() {
    AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    int interval = 1000 * 60 * 20;

    /* Set the alarm to start at 10:30 AM */
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(Calendar.HOUR_OF_DAY, 10);
    calendar.set(Calendar.MINUTE, 30);

    /* Repeating on every 20 minutes interval */
    manager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
    1000 * 60 * 20, pendingIntent);
    }

    }

    ReplyDelete
    Replies
    1. Hello - I haven't seen any code that uses the Alarmmanager yet, but I'm hoping to get some time to look at working up an example myself at some point soon.

      Delete
    2. Alas not. Sorry. WHat are you trying to do? I see on the Android docs page that if you want a timed event within your app's lifetime, a thread and Timer is recommended. However the alarm manager is to trigger events outside your app: http://developer.android.com/training/scheduling/alarms.html

      Delete
    3. However see the comment further down posted on 26 Feb '16 :)

      Delete
  6. good day
    My name is Luan
    if you can help me with the question I'm grateful below:


    Android gps background service Delphi xe6 or Xe7 or
    GPS is going in sleep mode in Android Service or
    GPS turning on after sleep mode

    ReplyDelete
    Replies
    1. I'm not entirely sure what you are asking, but it sounds to me like you need to have a browse through the FMX Android source and see where the GPS support code is, and check what polling frequency is being employed.I think you can force the GPS to be continually polled using the LocationManager requestLocationUpdates method overload that takes a minTime argument (you can look that up on http://d.android.com )
      It may well be that the FMX code responds to the app going into the background and cancels the location update request - all supposition currently as I haven't scoured that section of the source as yet.

      Delete
  7. Bom dia deu certo usando esse código:

    function DateTimeLocalToUnixMSecGMT(const ADateTime: TDateTime): Int64;
    begin
    Result := DateTimeToUnix(ADateTime) * MSecsPerSec -
    Round(TTimeZone.Local.UtcOffset.TotalMilliseconds);
    end;

    procedure Alarmmanager;
    var
    alarm:JIntent;
    pendingIntent:JPendingIntent;
    AlarmObj: JObject;
    Alarmmanager: JAlarmManager;
    Cada_Intervalo_Execute:Integer;
    begin
    {Para que Funcione Coloque isso no AndroidManifest.xml



    Coloque esse procedimento em um time com inteval 1700
    assim sua aplicação será excutado em BackBround/ Sleep Mode modo de Sono}
    //Cada itervalo de Um segundo vai ser executado
    //Como o time que tem a propriedade interval
    //Cada_Intervalo_Execute:=1000; um segundo

    //Verificar se o erro está qui
    alarm := TJIntent.Create;
    //No create já que o time sempre vai ficar executando

    alarm.setClass(SharedActivityContext, Alarm.getClass);

    pendingIntent:=TJPendingIntent.JavaClass.getService(
    SharedActivityContext.getApplicationContext,1,alarm,0 );

    AlarmObj :=SharedActivity.getSystemService(TJContext.JavaClass.ALARM_SERVICE);
    Alarmmanager :=
    TJAlarmManager.Wrap((AlarmObj as ILocalObject).GetObjectID);

    Alarmmanager.setRepeating(
    TJAlarmManager.JavaClass.ELAPSED_REALTIME_WAKEUP,
    DateTimeLocalToUnixMSecGMT(Time),1000,pendingIntent);

    end;

    ReplyDelete
    Replies
    1. Ooh, very nice. Thanks for the post! When I get chance I will try this out :)

      Delete