Einzelnen Beitrag anzeigen

knaeuel

Registriert seit: 2. Jul 2007
110 Beiträge
 
Delphi 10.3 Rio
 
#2

AW: Android AlarmManager periodisch einen Event bekommen

  Alt 26. Jul 2018, 10:40
Hi Kostas,

ich beschäftige mich zur Zeit auch mit dem Alarm Manager.
Bei meinem Projekt geht es darum, quasi eine Weckfunktion mit einzubauen.

Das Weckerklingeln an sich ist kein Problem, aber dabei auch den Bildschirm einzuschalten und mal eben am Lockscreen vorbei das Formular mit dem aus-Knopf anzuzeigen, ist problematisch.

Ein Ansatz wäre für mich, mit dem Alarmmanager einen System-Wecker einzutragen. Dann kümmert sich das System mit dem normalen Wecker um den Lockscreen und alles ist gut.

Ich werte aktuell den Code von 2 Beispiel-Apps aus dem Android Studio aus.

Ich habe schonmal folgende Frage(n):
  1. Die Action "const intentAction = 'de.firma.action.CHECK_ACTIVE';" - geht das so? Oder muss immer eine Action aus der Liste der Action-Konstanten vom Intent-Interface genutzt werden? (siehe Link zu Android Developers - Intent - Constants)
  2. Ich verstehe die Zeitangaben nicht so ganz. Zunächst dachte ich, es handelt sich einfach um UNIX-Zeitangaben. Aber wenn ich manuell einen Wecker eintrage (mit der Uhr-App vom System) und den dann mit dem folgenden Code einlese (das hat schon geklappt), dann rechnet Delphi die Zeitangabe mit UnixToDateTime in ein Datum im Jahr 2035 um, obwohl der Wecker auf "morgen früh um 7 Uhr" gestellt war.

Code zum Einlesen eines gestellten Weckers:
Delphi-Quellcode:
procedure Wecker_Einlesen;
var alarmman1:JAlarmManager;
    alarmclockinfo1:JAlarmManager_AlarmClockInfo;
    pendingintent1:JPendingIntent;
    triggertime1:int64;
begin
  alarmman1:=TAndroidhelper.AlarmManager;

  //Daten eines gestellten Weckers einlesen: (funzt, aber der Weckzeitpunkt liegt scheinbar im Jahr 2035, obwohl eigentlich morgen)
  alarmclockinfo1:=alarmman1.getNextAlarmClock;
  if alarmclockinfo1<>nil then
  begin
    pendingintent1:=alarmclockinfo1.getShowIntent;
    triggertime1:=alarmclockinfo1.getTriggerTime;
    ShowMessage('Der Wecker: '+DateTimeToStr(UnixToDateTime(triggertime1))); //2035?? dann komm ich zu spät
  end;
end;
Nebenbei: hier habe ich, genau wie du, einfach über "TAndroidhelper.AlarmManager" auf den AlarmManager zugegriffen. In der einen Java-App hieß es, man müsse den System-Service AlarmManager vom System abholen, also so (bzw. so wie in der folgenden Procedure):
Delphi-Quellcode:
java_obj:=TAndroidHelper.Context.getSystemService(TJContext.JavaClass.ALARM_SERVICE);
alarmman:=TJAlarmManager.Wrap((java_obj as ILocalObject).GetObjectID);
jetzt kommt der Delphi Code, den ich aus der Java-App portiert habe. Die Original-Java-Zeilen sind alle noch mit drin. Der englische Kommentar stammt komplett aus der Java-App. Netterweise habe ich die eingebundenen Units mitangegeben (blöde Sucherei ersparend):
(leider noch nicht kompilierbar)

Delphi-Quellcode:
uses System.DateUtils, Androidapi.JNI.GraphicsContentViewText, Androidapi.JNI.App,
     Androidapi.JNI.JavaTypes, Androidapi.JNIBridge, Androidapi.JNI.PlayServices, Androidapi.Helpers;


procedure TFormWecker.SetAlarm;
var intent : JIntent;
    fragment : JFragment;
    alarmType : integer;
    alarmman : JAlarmManager;
    java_obj : JObject;
    pendingIntent : JPendingIntent;
    ar_result : JActivityRecognitionResult;
    elapsedmillis : int64;
const ACTION_MAIN = 'android.intent.action.MAIN'; //Action ohne weitere Auswirkung
      FLAG_ACTIVITY_REORDER_TO_FRONT = 131072;
      REQUEST_CODE = 0; //jeder andere Wert wäre ebenso ok
      FIFTEEN_SEC_MILLIS = 15000;
      ELAPSED_REALTIME_WAKEUP = $00000002; //Gerät dabei aufwecken
      ELAPSED_REALTIME = $00000003; //Gerät nicht aufwecken
begin
  //Java Code und Kommentare aus Android-Studio Beispiel Projekt
  // BEGIN_INCLUDE (intent_fired_by_alarm)
  // First create an intent for the alarm to activate.
  // This code simply starts an Activity, or brings it [die App?] to the front if it has already
  // been created.
// Intent intent = new Intent(getActivity(), MainActivity.class);
// intent.setAction(Intent.ACTION_MAIN);
// intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
  // END_INCLUDE (intent_fired_by_alarm)

intent := TJIntent.Create;
intent.setAction(StringToJString(ACTION_MAIN));
intent.setFlags(FLAG_ACTIVITY_REORDER_TO_FRONT);

  // BEGIN_INCLUDE (pending_intent_for_alarm)
  // Because the intent must be fired by a system service from outside the application,
  // it's necessary to wrap it in a PendingIntent. Providing a different process with
  // a PendingIntent gives that other process permission to fire the intent that this
  // application has created.
  // Also, this code creates a PendingIntent to start an Activity. To create a
  // BroadcastIntent instead, simply call getBroadcast instead of getIntent.
// PendingIntent pendingIntent=PendingIntent.getActivity(getActivity(),REQUEST_CODE,intent,0);
pendingIntent:=TJPendingIntent.JavaClass.getActivity(fragment.getActivity,REQUEST_CODE,intent,0);

  // END_INCLUDE (pending_intent_for_alarm)

  // BEGIN_INCLUDE (configure_alarm_manager)
  // There are two clock types for alarms, ELAPSED_REALTIME and RTC.
  // ELAPSED_REALTIME uses time since system boot as a reference, and RTC uses UTC (wall
  // clock) time. This means ELAPSED_REALTIME is suited to setting an alarm according to
  // passage of time (every 15 seconds, 15 minutes, etc), since it isn't affected by
  // timezone/locale. RTC is better suited for alarms that should be dependant on current
  // locale.

  // Both types have a WAKEUP version, which says to wake up the device if the screen is
  // off. This is useful for situations such as alarm clocks. Abuse of this flag is an
  // efficient way to skyrocket the uninstall rate of an application, so use with care.
  // For most situations, ELAPSED_REALTIME will suffice.
// int alarmType = AlarmManager.ELAPSED_REALTIME;
// final int FIFTEEN_SEC_MILLIS = 15000;
alarmType:=ELAPSED_REALTIME_WAKEUP;

  // The AlarmManager, like most system services, isn't created by application code, but
  // requested from the system.
// AlarmManager alarmManager = (AlarmManager)
// getActivity().getSystemService(getActivity().ALARM_SERVICE);
java_obj:=TAndroidHelper.Context.getSystemService(TJContext.JavaClass.ALARM_SERVICE);
alarmman:=TJAlarmManager.Wrap((java_obj as ILocalObject).GetObjectID);

  // setRepeating takes a start delay and period between alarms as arguments.
  // The below code fires after 15 seconds, and repeats every 15 seconds. This is very
  // useful for demonstration purposes, but horrendous for production. Don't be that dev.
// alarmManager.setRepeating(alarmType, SystemClock.elapsedRealtime() + FIFTEEN_SEC_MILLIS,
// FIFTEEN_SEC_MILLIS, pendingIntent);
//alarmman.setRepeating(alarmType, JActivityRecognitionResult.getElapsedRealtimeMillis + FIFTEEN_SEC_MILLIS,
// FIFTEEN_SEC_MILLIS, pendingIntent);
ar_result:=TJActivityRecognitionResult.Create;
elapsedmillis:=ar_result.getElapsedRealtimeMillis;
alarmman.setRepeating(alarmType, elapsedmillis + FIFTEEN_SEC_MILLIS,
                      FIFTEEN_SEC_MILLIS, pendingIntent);

  // END_INCLUDE (configure_alarm_manager);
end;
(leider noch nicht kompilierbar)
Die Procedure soll einen Alarm erzeugen, der sich alle 15 Sekunden wiederholt.

Man beachte hier die Action "ACTION_MAIN" - diese Action hat keine zusätzliche Auswirkung.
Das Flag "FLAG_ACTIVITY_REORDER_TO_FRONT" soll wohl dafür sorgen, dass die App bei jedem Alarm in den Vordergrund gebracht wird.

Ansonsten passiert gar nichts

Dieser Alarm soll sich an der "ELAPSED_REALTIME" orientieren. Das ist die vergangene Zeit seit Systemstart. Die Konstantenwerte habe ich ebenfalls von Android Developers.

Das Programm stürzt bisher bei allen meinen Versuchen, die Elapsed_Systemtime einzulesen, ab:
Delphi-Quellcode:
//absturz:
//ar_result:=TJActivityRecognitionResult.Create;
//elapsedmillis:=ar_result.getElapsedRealtimeMillis;

//absturz
//java_obj:=TAndroidHelper.Context.getSystemService(TJContext.JavaClass.ACTIVITY_SERVICE);
//ar_result:=TJActivityRecognitionResult.Wrap((java_obj as ILocalObject).GetObjectID);
//elapsedmillis:=ar_result.getElapsedRealtimeMillis;

//absturz:
//java_obj:=TAndroidHelper.Context.getSystemService(TJContext.JavaClass.LOCATION_SERVICE);
//location:=TJLocation.Wrap((java_obj as ILocalObject).GetObjectID);
//elapsedmillis:=location.getElapsedRealtimeNanos * 1000000;
Hast du inzwischen mehr herausgefunden?

Gruß,
knaeuel/Wolfgang
Wolfgang

Geändert von knaeuel (26. Jul 2018 um 11:50 Uhr) Grund: fehlende Portierung ergänzt
  Mit Zitat antworten Zitat