chshkhr chshkhr - 6 months ago 38
Android Question

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 ( 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.

Answer

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.