Search code examples
androiddelphidelphi-xe5

Delphi XE5 Android. How to use PowerManager.WakeLock?


I'd like to keep device on and avoid unloading my application from memory even while there is no user activity. Something like service. I made module like How to check if network is available on Android and iOS ( Delphi XE5 ) but system crushes when I run SetWakeLock:

unit Android.PowerManager;

interface

function SetWakeLock : boolean;
procedure ReleaseWakeLock;

implementation

uses
  System.SysUtils,
  Androidapi.JNI,
  Androidapi.JNIBridge,
  Androidapi.JNI.GraphicsContentViewText,
  Androidapi.JNI.JavaTypes,
  FMX.Helpers.Android;

type
  JPowerManager = interface;
  JWakeLock = interface;

  JWakeLockClass = interface(JObjectClass)
  ['{4CF7A13D-15A9-4DEE-8CA7-66600C188CB7}']
  end;

  [JavaSignature('android/os/PowerManager/WakeLock')]
  JWakeLock = interface(JObject)
  ['{55983EDC-782F-490A-BF0C-12207EB7829E}']
    {Methods}
    procedure acquire; cdecl;
    procedure release; cdecl;
    function isHeld: Boolean; cdecl;
  end;
  TJWakeLock = class(TJavaGenericImport<JWakeLockClass, JWakeLock>) end;

  JPowerManagerClass = interface(JObjectClass)
  ['{B127DD4E-1DA6-49E7-98BA-5966DC7E26FA}']
  end;

  [JavaSignature('android/os/PowerManager')]
  JPowerManager = interface(JObject)
  ['{241C3B3D-3DF0-489B-A33E-3CD7F5D26313}']
    {Methods}
    function newWakeLock(levelAndFlags: integer; tag: JString): JWakeLock; cdecl;
  end;
  TJPowerManager = class(TJavaGenericImport<JPowerManagerClass, JPowerManager>) end;

function GetPowerManager: JPowerManager;
var
  PowerServiceNative: JObject;
begin
  PowerServiceNative := SharedActivityContext.getSystemService(TJContext.JavaClass.POWER_SERVICE);
  if not Assigned(PowerServiceNative) then
    raise Exception.Create('Could not locate Power Service');
  Result := TJPowerManager.Wrap(
    (PowerServiceNative as ILocalObject).GetObjectID);
  if not Assigned(Result) then
    raise Exception.Create('Could not access Power Manager');
end;

var fWakeLock : JWakeLock = nil;

function SetWakeLock : boolean;
var
  PowerManager: JPowerManager;
begin
  result := fWakeLock<>nil;
  if result then begin
    PowerManager := GetPowerManager;
    fWakeLock := PowerManager.newWakeLock(1,StringToJString('VC'));  //PARTIAL_WAKE_LOCK =1
    Result := fWakeLock<>nil;
    if Result then begin
       fWakeLock.acquire;
       Result := fWakeLock.IsHeld;
    end;
  end else if not fWakeLock.IsHeld then
    fWakeLock.acquire;
end;

procedure ReleaseWakeLock;
begin
  if fWakeLock<>nil then begin
    fWakeLock.release;
  end;
end;

end.

Solution

  • Ignoring the wrong-looking logic that I cited in a comment, you're not referring to a nested class correctly in the interface declaration.

    This unit works for me. Note I'm using a screen wake lock (which is deprecated, but still works).

    unit Android.JNI.PowerManager;
    
    interface
    
    function AcquireWakeLock : Boolean;
    procedure ReleaseWakeLock;
    
    implementation
    
    uses
      System.SysUtils,
      Androidapi.JNI,
      Androidapi.JNIBridge,
      Androidapi.JNI.GraphicsContentViewText,
      Androidapi.JNI.JavaTypes,
      FMX.Helpers.Android;
    
    type
      JPowerManager = interface;
      JWakeLock = interface;
    
      JWakeLockClass = interface(JObjectClass)
      ['{918E171F-CDB8-4464-9507-F49272CE7636}']
      end;
    
      [JavaSignature('android/os/PowerManager$WakeLock')]
      JWakeLock = interface(JObject)
      ['{D17B1136-FA15-4AEB-85B1-2D490F0FD320}']
        {Methods}
        procedure acquire; cdecl;
        procedure release; cdecl;
        function isHeld: Boolean; cdecl;
      end;
      TJWakeLock = class(TJavaGenericImport<JWakeLockClass, JWakeLock>) end;
    
      JPowerManagerClass = interface(JObjectClass)
      ['{7D0696A2-ADEA-4158-AE1F-5E720DEDBCF9}']
        {Property methods}
        function _GetFULL_WAKE_LOCK: Integer; cdecl;
        function _GetSCREEN_BRIGHT_WAKE_LOCK: Integer; cdecl;
        function _GetSCREEN_DIM_WAKE_LOCK: Integer; cdecl;
        function _GetPARTIAL_WAKE_LOCK: Integer; cdecl;
        {Properties}
        //Keep screen on bright & keyboard on
        //Deprecated in API level 17 - Jelly Bean MR1
        property FULL_WAKE_LOCK: Integer read _GetFULL_WAKE_LOCK;
        //Keep screen on bright
        //Deprecated in API level 13 - Honeycomb MR2
        property SCREEN_BRIGHT_WAKE_LOCK: Integer read _GetSCREEN_BRIGHT_WAKE_LOCK;
        //Keep screen on dim
        //Deprecated in API level 17 - Jelly Bean MR1
        property SCREEN_DIM_WAKE_LOCK: Integer read _GetSCREEN_DIM_WAKE_LOCK;
        //Keep CPU running, screen & keyboard can go off
        property PARTIAL_WAKE_LOCK: Integer read _GetPARTIAL_WAKE_LOCK;
      end;
    
      [JavaSignature('android/os/PowerManager')]
      JPowerManager = interface(JObject)
      ['{DEAED658-4353-4D17-B0A3-8179E48BE87F}']
        {Methods}
        function newWakeLock(levelAndFlags: Integer; tag: JString): JWakeLock; cdecl;
      end;
      TJPowerManager = class(TJavaGenericImport<JPowerManagerClass, JPowerManager>) end;
    
    function GetPowerManager: JPowerManager;
    var
      PowerServiceNative: JObject;
    begin
      PowerServiceNative := SharedActivityContext.getSystemService(
        TJContext.JavaClass.POWER_SERVICE);
      if not Assigned(PowerServiceNative) then
        raise Exception.Create('Could not locate Power Service');
      Result := TJPowerManager.Wrap(
        (PowerServiceNative as ILocalObject).GetObjectID);
      if not Assigned(Result) then
        raise Exception.Create('Could not access Power Manager');
    end;
    
    var
      WakeLock: JWakeLock = nil;
    
    function AcquireWakeLock: Boolean;
    var
      PowerManager: JPowerManager;
    begin
      Result := Assigned(WakeLock);
      if not Result then
      begin
        PowerManager := GetPowerManager;
        WakeLock := PowerManager.newWakeLock(
          TJPowerManager.JavaClass.SCREEN_BRIGHT_WAKE_LOCK,
          StringToJString('Delphi'));
        Result := Assigned(WakeLock);
      end;
      if Result then
      begin
        if not WakeLock.IsHeld then
        begin
          WakeLock.acquire;
          Result := WakeLock.isHeld
        end;
      end;
    end;
    
    procedure ReleaseWakeLock;
    begin
      if Assigned(WakeLock) then
      begin
        WakeLock.release;
        WakeLock := nil
      end;
    end;
    
    end.