[Android]BASS

This is the forum for miscellaneous technical/programming questions.

Moderator: 2ffat

Lena
BCBJ Master
BCBJ Master
Posts: 696
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]BASS

Post by Lena »

Thank You!
Is it my steps correctly?
1. I create a service in it code with the launch of audio.
2. And in the application, send broadcast messages about the start or stop of playback.
I have never used broadcast. Do I need to use Indy to send broadcast message? Can you show me an example of how to send broadcast message in my case? If possible in Pascal because I have to create an application Android 64 bit.
rlebeau
BCBJ Author
BCBJ Author
Posts: 1715
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA
Contact:

Re: [Android]BASS

Post by rlebeau »

Lena wrote: Mon Oct 05, 2020 12:36 pm Is it my steps correctly?
I am not an Android developer, so this is WAY outside my area of expertise.
Lena wrote: Mon Oct 05, 2020 12:36 pm 2. And in the application, send broadcast messages about the start or stop of playback.
Unless you want other apps to control your service, using broadcast messages is not the best way to communicate between your UI and service. A Binding makes more sense, as it is a direct line of communication. See Embarcadero's TLocalServiceConnection and TRemoteServiceConnection classes, which have a BindService() method.

Creating Android Services
Lena wrote: Mon Oct 05, 2020 12:36 pm I have never used broadcast. Do I need to use Indy to send broadcast message?
No. Indy deals with network communications, not inter-process messaging.
Remy Lebeau (TeamB)
Lebeau Software
Lena
BCBJ Master
BCBJ Master
Posts: 696
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]BASS

Post by Lena »

Thank you.
Are my first steps in creating a service correct?

Code: Select all

unit UnitSrvice;

interface

uses
  System.SysUtils,
  System.Classes,
  System.Android.Service,
  AndroidApi.JNI.GraphicsContentViewText,
  Androidapi.JNI.Os, bass;

type
  TDM = class(TAndroidService)
    function AndroidServiceStartCommand(const Sender: TObject;
      const Intent: JIntent; Flags, StartId: Integer): Integer;
    function AndroidServiceBind(const Sender: TObject;
      const AnIntent: JIntent): JIBinder;
    function AndroidServiceUnBind(const Sender: TObject;
      const AnIntent: JIntent): Boolean;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  DM: TDM;

  str: HSTREAM;

implementation

{%CLASSGROUP 'FMX.Controls.TControl'}

{$R *.dfm}

uses
   Androidapi.JNI.App;


function TDM.AndroidServiceBind(const Sender: TObject;
  const AnIntent: JIntent): JIBinder;
begin
  BASS_Init(-1, 44100, 0, nil, nil); //Maybe this line of code in OnCreate AndroidService?
  BASS_StreamFree(str);
  str := BASS_StreamCreateURL(PChar('http://91.199.194.34:8000'), 0, BASS_UNICODE, nil, nil);
  //ShowMessage(IntToStr(BASS_ErrorGetCode));


  If BASS_ErrorGetCode = 0 Then
  Begin
     BASS_ChannelPlay(str, True); //start play
   End;

   Result := GetBinder;
end;

function TDM.AndroidServiceStartCommand(const Sender: TObject;
  const Intent: JIntent; Flags, StartId: Integer): Integer;
begin
   Result := TJService.JavaClass.START_STICKY;
end;

function TDM.AndroidServiceUnBind(const Sender: TObject;
  const AnIntent: JIntent): Boolean;
begin
  BASS_ChannelStop(str); //stop play
  BASS_StreamFree(str);

  JavaService.StopService(AnIntent);
  Result := False;

end;

end.

Lena
BCBJ Master
BCBJ Master
Posts: 696
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]BASS

Post by Lena »

My host app:

Code: Select all

unit UnitMain;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  System.Android.Service, FMX.Controls.Presentation, FMX.StdCtrls;

type
  TForm1 = class(TForm)
    ButtonPlay: TButton;
    ButtonStop: TButton;
    procedure ButtonPlayClick(Sender: TObject);
    procedure ButtonStopClick(Sender: TObject);
  private
    { Private declarations }
    FService: TLocalServiceConnection;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.ButtonPlayClick(Sender: TObject);
begin
  if FService = Nil Then Begin

  FService := TLocalServiceConnection.Create;
  FService.StartService('ServicPablicRadio');
  end;

  FService.BindService('ServicPablicRadio', 0);

end;

procedure TForm1.ButtonStopClick(Sender: TObject);
begin
  if FService <>  Nil then begin
    FService.UnbindService;
    FService := Nil;
  end;

end;

end.

In AndroidManifest.template.xml add:
<service android:exported="true" android:name="com.embarcadero.services.ServicPablicRadio" />
But the music still stops playing if you minimize the application. :(
rlebeau
BCBJ Author
BCBJ Author
Posts: 1715
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA
Contact:

Re: [Android]BASS

Post by rlebeau »

Lena wrote: Mon Oct 05, 2020 11:26 pm Are my first steps in creating a service correct?
Again, I direct you to the Android documentation, which discusses this in detail:

Media app architecture overview

Audio app overview

The key is to write a service that implements MediaBrowserService and contains a MediaSession, and write a UI app that contains a MediaBrowser and MediaController to communicate with the service.

This is the way Android prefers an audio app be written. How you translate that into Delphi, I don't know. I'm sure it will involve using Embarcadero's JNIBridge framework to access these Android classes, if Embarcadero hasn't already imported them into its Androidapi namespace.
Remy Lebeau (TeamB)
Lebeau Software
Lena
BCBJ Master
BCBJ Master
Posts: 696
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]BASS

Post by Lena »

Thanks for the additional information!
I think the error is also in RAD 10.3.3
1. I create new an Android service 64 bit.
2. I create new app 64 bit.
3. Not use bass.
4. Use this short video: https://www.youtube.com/watch?v=0mD3WLK8FYc

Code: Select all

function TDM.AndroidServiceStartCommand(const Sender: TObject;
  const Intent: JIntent; Flags, StartId: Integer): Integer;
begin
   Result := TJService.JavaClass.START_STICKY;
end;
Host app:

Code: Select all

System.Android.Service;

 private
    { Private declarations }
    FService: TLocalServiceConnection;
 //-------------------------------------------   

procedure TForm1.FormCreate(Sender: TObject);
begin
  FService := TLocalServiceConnection.Create;
  FService.StartService('serPublic');
end;
When I start the application, I see that it is running and the service is running too. Everything works perfectly.
When I overlap the application window with another program, then after two minutes both the application and the service stop.
Or if you remove the application from memory as in a video, then the service also stops.
I don't know why the service stops.
Phone Readmi 5A Android 8.1 MIUI 11.0.2 RAD 10.3.3
rlebeau
BCBJ Author
BCBJ Author
Posts: 1715
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA
Contact:

Re: [Android]BASS

Post by rlebeau »

Lena wrote: Fri Oct 09, 2020 4:22 am I don't know why the service stops.
Android service doesn't work when I close app
Remy Lebeau (TeamB)
Lebeau Software
Lena
BCBJ Master
BCBJ Master
Posts: 696
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]BASS

Post by Lena »

Thank you very much I will investigate!

P.S.
I also found this difficult code. But it does not compile for me. Delphi 10.3.3
Start Foreground service in Delphi 10.3

Code: Select all

unit Unit1;

interface

uses
  System.SysUtils,
  System.Classes,
  System.Android.Service,
  AndroidApi.JNI.GraphicsContentViewText,
  Androidapi.JNI.Os,
  Androidapi.JNI.App,
  Androidapi.JNI.Support,
  Androidapi.JNI.JavaTypes,
  Androidapi.Helpers,
  Androidapi.JNIBridge;


type
  TDM = class(TAndroidService)
    function AndroidServiceStartCommand(const Sender: TObject;
      const Intent: JIntent; Flags, StartId: Integer): Integer;

  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  DM: TDM;

implementation

{%CLASSGROUP 'FMX.Controls.TControl'}

{$R *.dfm}

function TDM.AndroidServiceStartCommand(const Sender: TObject;
  const Intent: JIntent; Flags, StartId: Integer): Integer;
var
  ExtraData: String;
  {$ifdef VER330}
  ServiceChannel: JNotificationChannel;
  NotificationManager: JNotificationManager;
  Obj: JObject;
  {$endif}
  NewIntent: JIntent;
  ncb: JNotificationCompat_Builder;
  ntf: JNotification;
  PendingIntent: JPendingIntent;

begin

   Result := TJService.JavaClass.START_NOT_STICKY;

   // can't ref .O on earlier phones, must hardcode
  if TJBuild_VERSION.JavaClass.SDK_INT > 26 then // JBuild_VERSION_CODES.JavaClass.O                    begin
    {$ifdef VER330}
    // new ways for SDK > 26 (won't be called when API < 26 anyways)
    ServiceChannel := TJNotificationChannel.JavaClass.init(
      StringtoJString(CHANNEL_ID),
      StrToJCharSequence('My Service Channel'),
      TJNotificationManager.JavaClass.IMPORTANCE_DEFAULT
    );

    Obj := TAndroidHelper.Context.getSystemService(
    TJContext.JavaClass.NOTIFICATION_SERVICE);
    NotificationManager := TJNotificationManager.Wrap(Obj);
    NotificationManager.createNotificationChannel(ServiceChannel);

    NewIntent:= TAndroidHelper.Context.getPackageManager().getLaunchIntentForPackage(
      TAndroidHelper.Context.getPackageName());
    NewIntent.setAction(TJIntent.JavaClass.ACTION_MAIN);
    NewIntent.addCategory(TJIntent.JavaClass.CATEGORY_LAUNCHER);
    NewIntent.addFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);

    PendingIntent := TJPendingIntent.JavaClass.getActivity(
      TAndroidHelper.Context, 0, NewIntent,
      TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK
    );

    ncb := TJNotificationCompat_Builder.JavaClass.init(
      TAndroidHelper.Context,
      StringToJString(CHANNEL_ID)
    );

   ncb.setContentTitle(StrToJCharSequence('MyService'));
   // ncb.setTicker(StrToJCharSequence('MyCommsService')); // can't remember why this is commented out to be honest
   ncb.setSmallIcon(JavaService.getApplicationInfo.icon);
   ncb.setContentIntent(PendingIntent);
   ncb.setOngoing(True);
   ntf := ncb.build;
   {$endif VER330}
 end
 else
 begin
   {$ifdef ORDINARY_NOTIFICATION}
    PendingIntent := TJPendingIntent.JavaClass.getActivity(
      JavaService.getApplicationContext, 0, Intent, 0
    );
    ntf := TJNotification.Create;
    ntf.icon := JavaService.getApplicationInfo.icon;
    ntf.setLatestEventInfo(
      JavaService.getApplicationContext,
      StrToJCharSequence('MyService'),
      StrToJCharSequence('MyCommsService'), PendingIntent);
    {$endif}
    {$ifdef CLICKABLE_NOTIFICATION}
    NewIntent:= TAndroidHelper.Context.getPackageManager().getLaunchIntentForPackage(
      TAndroidHelper.Context.getPackageName());
    NewIntent.setAction(TJIntent.JavaClass.ACTION_MAIN);
    NewIntent.addCategory(TJIntent.JavaClass.CATEGORY_LAUNCHER);
    NewIntent.addFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);

    PendingIntent := TJPendingIntent.JavaClass.getActivity(
      TAndroidHelper.Context, 0, NewIntent,
      TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK
    );

    ncb := TJNotificationCompat_Builder.JavaClass.init(TAndroidHelper.Context);
    ncb.setContentTitle(StrToJCharSequence('MyService'));
    ncb.setTicker(StrToJCharSequence('MyCommsService'));
    ncb.setSmallIcon(JavaService.getApplicationInfo.icon);
    ncb.setContentIntent(PendingIntent);
    ncb.setOngoing(True);
    ntf := ncb.build;
    {$endif}
  end;

  JavaService.startForeground(StartId, ntf);

  if Intent <> nil then
  begin
     ExtraData := TAndroidHelper.JStringToString(
       Intent.getStringExtra(TAndroidHelper.StringToJString('ExtraData')));
  end;
end;

end.
[DCC Error] Unit1.pas(63): E2003 Undeclared identifier: 'CHANNEL_ID'
[DCC Error] Unit1.pas(97): E2029 ';' expected but 'ELSE' found
[DCC Error] Unit1.pas(130): E2029 '.' expected but ';' found
[DCC Error] Unit1.pas(136): E2003 Undeclared identifier: 'ExtraData'
[DCC Error] Unit1.pas(137): E2003 Undeclared identifier: 'Intent'
[DCC Error] Unit1.pas(137): E2029 'END' expected but ')' found
[DCC Warning] Unit1.pas(138): W1011 Text after final 'END.' - ignored by compiler

What is CHANNEL_ID the author did not explain.
rlebeau
BCBJ Author
BCBJ Author
Posts: 1715
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA
Contact:

Re: [Android]BASS

Post by rlebeau »

Lena wrote: Tue Oct 20, 2020 2:42 am

Code: Select all

   // can't ref .O on earlier phones, must hardcode
  if TJBuild_VERSION.JavaClass.SDK_INT > 26 then // JBuild_VERSION_CODES.JavaClass.O                    begin
    {$ifdef VER330}
Since that 'begin' follows a comment, it should be on the next line:

Code: Select all

   // can't ref .O on earlier phones, must hardcode
  if TJBuild_VERSION.JavaClass.SDK_INT > 26 then // JBuild_VERSION_CODES.JavaClass.O
  begin
    {$ifdef VER330}
Lena wrote: Tue Oct 20, 2020 2:42 am [DCC Error] Unit1.pas(63): E2003 Undeclared identifier: 'CHANNEL_ID'
Use whatever string value you want. The documentation for the NotificationChannel constructor says:
The id of the channel. Must be unique per package. The value may be truncated if it is too long.
Lena wrote: Tue Oct 20, 2020 2:42 am [DCC Error] Unit1.pas(97): E2029 ';' expected but 'ELSE' found
[DCC Error] Unit1.pas(130): E2029 '.' expected but ';' found
[DCC Error] Unit1.pas(136): E2003 Undeclared identifier: 'ExtraData'
[DCC Error] Unit1.pas(137): E2003 Undeclared identifier: 'Intent'
[DCC Error] Unit1.pas(137): E2029 'END' expected but ')' found
[DCC Warning] Unit1.pas(138): W1011 Text after final 'END.' - ignored by compiler
I think those are just side-effects of the earlier errors.
Lena wrote: Tue Oct 20, 2020 2:42 am What is CHANNEL_ID the author did not explain.
It is just a unique ID that you create to refer to your channel. See https://developer.android.com/training/notify-user/channels.
Remy Lebeau (TeamB)
Lebeau Software
Lena
BCBJ Master
BCBJ Master
Posts: 696
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]BASS

Post by Lena »

Thank You rlebeau!
Now Buil OK.

Code: Select all

uses
  System.SysUtils,
  System.Classes,
  System.Android.Service,
  AndroidApi.JNI.GraphicsContentViewText,
  Androidapi.JNI.Os,
  Androidapi.JNI.App,
  Androidapi.JNI.Support,
  Androidapi.JNI.JavaTypes,
  Androidapi.Helpers,
  Androidapi.JNIBridge,
  Androidapi.Jni;


type
  TDM = class(TAndroidService)
    function AndroidServiceStartCommand(const Sender: TObject;
      const Intent: JIntent; Flags, StartId: Integer): Integer;

  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  DM: TDM;

implementation

{%CLASSGROUP 'FMX.Controls.TControl'}

{$R *.dfm}



function TDM.AndroidServiceStartCommand(const Sender: TObject;
  const Intent: JIntent; Flags, StartId: Integer): Integer;
var
  ExtraData: String;
  {$ifdef VER330}
  ServiceChannel: JNotificationChannel;
  NotificationManager: JNotificationManager;
  Obj: JObject;
  {$endif}
  NewIntent: JIntent;
  ncb: JNotificationCompat_Builder;
  ntf: JNotification;
  PendingIntent: JPendingIntent;

begin

   Result := TJService.JavaClass.START_NOT_STICKY;

   // can't ref .O on earlier phones, must hardcode
  if TJBuild_VERSION.JavaClass.SDK_INT > 26 then // JBuild_VERSION_CODES.JavaClass.O
  begin
    {$ifdef VER330}
    // new ways for SDK > 26 (won't be called when API < 26 anyways)
    ServiceChannel := TJNotificationChannel.JavaClass.init(
      StringtoJString('com.radio.one'), //CHANNEL_ID
      StrToJCharSequence('Public radio Channel'),
      TJNotificationManager.JavaClass.IMPORTANCE_DEFAULT
    );

    Obj := TAndroidHelper.Context.getSystemService(
    TJContext.JavaClass.NOTIFICATION_SERVICE);
    NotificationManager := TJNotificationManager.Wrap(Obj);
    NotificationManager.createNotificationChannel(ServiceChannel);

    NewIntent:= TAndroidHelper.Context.getPackageManager().getLaunchIntentForPackage(
      TAndroidHelper.Context.getPackageName());
    NewIntent.setAction(TJIntent.JavaClass.ACTION_MAIN);
    NewIntent.addCategory(TJIntent.JavaClass.CATEGORY_LAUNCHER);
    NewIntent.addFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);

    PendingIntent := TJPendingIntent.JavaClass.getActivity(
      TAndroidHelper.Context, 0, NewIntent,
      TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK
    );

    ncb := TJNotificationCompat_Builder.JavaClass.init(
      TAndroidHelper.Context,
      StringToJString('com.radio.one')
    );

   ncb.setContentTitle(StrToJCharSequence('PublicRadio'));
   // ncb.setTicker(StrToJCharSequence('MyCommsService')); // can't remember why this is commented out to be honest
   ncb.setSmallIcon(JavaService.getApplicationInfo.icon);
   ncb.setContentIntent(PendingIntent);
   ncb.setOngoing(True);
   ntf := ncb.build;
   {$endif VER330}
 end
 else
 begin
   {$ifdef ORDINARY_NOTIFICATION}
    PendingIntent := TJPendingIntent.JavaClass.getActivity(
      JavaService.getApplicationContext, 0, Intent, 0
    );
    ntf := TJNotification.Create;
    ntf.icon := JavaService.getApplicationInfo.icon;
    ntf.setLatestEventInfo(
      JavaService.getApplicationContext,
      StrToJCharSequence('PublicRadio'),
      StrToJCharSequence('PublicRadioService'), PendingIntent); //MyCommsService
    {$endif}
    {$ifdef CLICKABLE_NOTIFICATION}
    NewIntent:= TAndroidHelper.Context.getPackageManager().getLaunchIntentForPackage(
      TAndroidHelper.Context.getPackageName());
    NewIntent.setAction(TJIntent.JavaClass.ACTION_MAIN);
    NewIntent.addCategory(TJIntent.JavaClass.CATEGORY_LAUNCHER);
    NewIntent.addFlags(TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK);

    PendingIntent := TJPendingIntent.JavaClass.getActivity(
      TAndroidHelper.Context, 0, NewIntent,
      TJIntent.JavaClass.FLAG_ACTIVITY_NEW_TASK
    );

    ncb := TJNotificationCompat_Builder.JavaClass.init(TAndroidHelper.Context);
    ncb.setContentTitle(StrToJCharSequence('PublicRadio'));
    ncb.setTicker(StrToJCharSequence('PublicRadioService')); //MyCommsService
    ncb.setSmallIcon(JavaService.getApplicationInfo.icon);
    ncb.setContentIntent(PendingIntent);
    ncb.setOngoing(True);
    ntf := ncb.build;
    {$endif}
  end;

  JavaService.startForeground(StartId, ntf);

  if Intent <> nil then
  begin
     ExtraData := TAndroidHelper.JStringToString(
       Intent.getStringExtra(TAndroidHelper.StringToJString('ExtraData')));
  end;
end;

end.
2. Set Options app Uses Permissions -> Foreground service=true in service app.

3. Created a test application and service with the code above. It started. An application icon appeared in the notification bar at the top of the phone screen. Minimize the application from the screen and lock the screen. In about 20 minutes we wake up the phone. The application icon has not disappeared.
It looks like it works... :idea:
Lena
BCBJ Master
BCBJ Master
Posts: 696
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]BASS

Post by Lena »

Next step:
Added a service to a project with BASS. Everything works as it should. For an hour now, the music has been playing when the application is minimized and the screen is locked.
Lena
BCBJ Master
BCBJ Master
Posts: 696
Joined: Sun Feb 06, 2011 1:28 pm

Re: [Android]BASS

Post by Lena »

I use this code to close the application and stop the service:
MainActivity.finish;
It works.
Do I need any additional steps to stop the service?
Post Reply