Is It Possible To Read The Notifications Of Other Applications On Android With Delphi?
Solution 1:
You can read the notifications of other Apps with the use of the NotificationListenerService
, and here is how
Step 1 : Getting everything ready
In your project folder create a folder called Java
Now inside that folder create a folder called src and inside it create a folder structure that is the same as your Apps package name E.g : src/com/embarcadero/$yourapp
inside src/com/embarcadero/$yourapp add the following Java file as NotificationService.java
package com.embarcadero.$Yourapp;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import android.support.v4.content.LocalBroadcastManager;
publicclassNotificationServiceextendsNotificationListenerService {
staticfinalStringTAG="NotificationService";
Context context;
@OverridepublicvoidonCreate() {
super.onCreate();
context = getApplicationContext();
Log.d(TAG,"Service has been started!");
}
@OverridepublicvoidonNotificationPosted(StatusBarNotification sbn) {
Log.d(TAG,"Notification Posted!");
Stringpack= sbn.getPackageName();
Bundleextras= sbn.getNotification().extras;
Stringtitle= extras.getString("android.title");
Stringtext= extras.getCharSequence("android.text").toString();
Stringid= sbn.getTag();
Log.i("Package", pack);
Log.i("Title",title);
Log.i("Text",text);
if (id != null){
Log.i("Key", id);
}
Intentmsgrcv=newIntent("Msg");
msgrcv.putExtra("package", pack);
msgrcv.putExtra("key", id);
msgrcv.putExtra("title", title);
msgrcv.putExtra("text", text);
LocalBroadcastManager.getInstance(context).sendBroadcast(msgrcv);
}
@OverridepublicvoidonNotificationRemoved(StatusBarNotification sbn) {
Log.d(TAG,"Notification Removed");
}
}
and Add the following files to the Java folder : android-support-v4.jar (Found under your SDK Path : android-sdk-windows\extras\android\support\v4)
Add the following batch file build.bat
@echo off
setlocal
REM Set the your paths as needed
SET PATH=%PATH%;C:\Users\Public\Documents\Embarcadero\Studio\17.0\PlatformSDKs\android-sdk-windows\build-tools\23.0.1
if x%ANDROID% == x set ANDROID=C:\Users\Public\Documents\Embarcadero\Studio\17.0\PlatformSDKs\android-sdk-windows
set ANDROID_PLATFORM=%ANDROID%\platforms\android-23
set DX_LIB=%ANDROID%\build-tools\23.0.1\lib
set EMBO_DEX="C:\Program Files (x86)\Embarcadero\Studio\17.0\lib\android\debug\classes.dex"set PROJ_DIR=C:\Users\PJJ\Documents\allesbeste\Mobiletest\java
REM the PROJ_DIR must point to your Java Folder inside your project folder
set VERBOSE=0
set JAVA="C:\Program Files\Java\jdk1.7.0_71\bin"echo.
echo Compiling the Java service activity source files
echo.
mkdir output 2> nul
mkdir output\classes 2> nul
if x%VERBOSE% == x1 SET VERBOSE_FLAG=-verbose
javac %VERBOSE_FLAG% -source 1.7 -target 1.7 -Xlint:deprecation -cp %ANDROID_PLATFORM%\android.jar;%EMBO_LIB%\fmx.jar;%PROJ_DIR%\android-support-v4.jar -d output\classes src\com\embarcadero\AllesbesteToep\NotificationService.java
echo.
echo Creating jar containing the new classes
echo.
mkdir output\jar 2> nul
if x%VERBOSE% == x1 SET VERBOSE_FLAG=v
jar c%VERBOSE_FLAG%f output\jar\Delphi_Service.jar -C output\classes com
echo.
echo Converting from jar to dex...
echo.
mkdir output\dex 2> nul
if x%VERBOSE% == x1 SET VERBOSE_FLAG=--verbose
call dx --dex %VERBOSE_FLAG% --output=%PROJ_DIR%\output\dex\test_classes.dex --positions=lines %PROJ_DIR%\output\jar\Delphi_Service.jar
echo.
echo Merging dex files
echo.
java -cp %DX_LIB%\dx.jar com.android.dx.merge.DexMerger %PROJ_DIR%\output\dex\classes.dex %PROJ_DIR%\output\dex\test_classes.dex %EMBO_DEX%
echo Tidying up
echo.
REM Just change these as needed
del output\classes\com\embarcadero\AllesbesteToep\NotificationService.class
del output\dex\test_classes.dex
rmdir output\classes\com\embarcadero\AllesbesteToep
rmdir output\classes\com\embarcadero
rmdir output\classes\com
rmdir output\classes
pause
echo.
echo Now we have the end result, which is output\jar\Delphi_Service.jar
:Exit
endlocal
Add the default classes.dex file located at C:\Program Files (x86)\Embarcadero\Studio\17.0\lib\android\debug
Add the fmx.jar also located at C:\Program Files (x86)\Embarcadero\Studio\17.0\lib\android\debug
And rename it to classes.jar
So your Java folder structure now looks like this
src //with the correct folder structure and your NotificationService.java inside
android-support-v4
- build
- classes.dex
- classes.jar
if all your variables are set correctly in the build file you can now run build.bat to generate the output folder (Special thanks to Brian Long who initially created it)
You will now have 2 folders inside your output folder one called dex and the other jar, Now you will have a new classes.dex file and Delphi_Service executable
Have your App use the new classes.dex filehttp://docwiki.embarcadero.com/RADStudio/XE8/en/Creating_and_Deploying_a_classes.dex_File_Manually
Add the Delphi_Service jar file to your Apphttp://docwiki.embarcadero.com/RADStudio/XE8/en/Adding_A_Java_Library_to_Your_Application_Using_the_Project_Manager
In your project file add the following unit Android.JNI.LocalBroadcastMan
//Created with the Java2OP tool
unit Android.JNI.LocalBroadcastMan;
interfaceuses
Androidapi.JNIBridge,
Androidapi.JNI.JavaTypes,
Androidapi.JNI.GraphicsContentViewText,
Androidapi.JNI.Util,
Androidapi.JNI.Os,
Androidapi.JNI.Net;
type
// ===== Forward declarations =====
JLocalBroadcastManager = interface;//android.support.v4.content.LocalBroadcastManager// ===== Interface declarations =====
JLocalBroadcastManagerClass = interface(JObjectClass)
['{03179F7E-C439-4369-93CC-AA2BADC54398}']
{class} function getInstance(context: JContext): JLocalBroadcastManager; cdecl;
end;
[JavaSignature('android/support/v4/content/LocalBroadcastManager')]
JLocalBroadcastManager = interface(JObject)
['{6C255CD6-D94E-40BC-A758-EC4965A40725}']
procedure registerReceiver(receiver: JBroadcastReceiver; filter: JIntentFilter); cdecl;
function sendBroadcast(intent: JIntent): Boolean; cdecl;
procedure sendBroadcastSync(intent: JIntent); cdecl;
procedure unregisterReceiver(receiver: JBroadcastReceiver); cdecl;
end;
TJLocalBroadcastManager = class(TJavaGenericImport<JLocalBroadcastManagerClass, JLocalBroadcastManager>) end;
implementation
procedure RegisterTypes;
begin
TRegTypes.RegisterType('Android.JNI.LocalBroadcastMan.JLocalBroadcastManager', TypeInfo(Android.JNI.LocalBroadcastMan.JLocalBroadcastManager));
end;
initialization
RegisterTypes;
end.
Download the Free broadcast receiver component from http://www.fmxexpress.com/free-broadcast-receiver-component-for-delphi-xe7-firemonkey-on-android/
Install the component, now add the BroadcastReceiver unit that come with it to your Project and add the following code in BroadcastReceiver:
Add to your uses : Android.JNI.LocalBroadcastMan
Change the TBroadcastReceiver.Add
procedure to
procedure TBroadcastReceiver.Add(Value: String);
{$IFDEF ANDROID}
var
Filter: JIntentFilter;
locMan : JLocalBroadcastManager;
{$ENDIF}
begin
{$IFDEF ANDROID}
if (FListener = nil) or (FReceiver = nil) thenbegin
Raise Exception.Create('First use RegisterReceive!');
Exit;
end;
{$ENDIF}
if FItems <> nilthenif FItems.IndexOf(Value) = -1thenbegin
{$IFDEF ANDROID}
filter := TJIntentFilter.Create;
filter.addAction(StringToJString(Value));
SharedActivityContext.registerReceiver(FReceiver,filter);
locMan := TJLocalBroadcastManager.JavaClass.getInstance(SharedActivityContext);
locMan.registerReceiver(FReceiver,Filter);
{$ENDIF}
FItems.Add(Value);
end;
end;
Add the following Procedure unRegisterReceive
procedure TBroadcastReceiver.unRegisterReceive;
var
locMan : JLocalBroadcastManager;
begin
locMan := TJLocalBroadcastManager.JavaClass.getInstance(SharedActivityContext);
locMan.unregisterReceiver(FReceiver);
end;
Add the following procedure regagain
procedure TBroadcastReceiver.regagain;
var
locMan : JLocalBroadcastManager;
filter : JIntentFilter;
begin
filter := TJIntentFilter.Create;
filter.addAction(StringToJString('Msg'));
locMan := TJLocalBroadcastManager.JavaClass.getInstance(SharedActivityContext);
locMan.registerReceiver(FReceiver,Filter);
end;
Edit your AndroidManifest.template.xml in your project dir to inlcude the service
<serviceandroid:name="com.embarcadero.$Yourapp.NotificationService"android:label="%label%"android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"><intent-filter><actionandroid:name="android.service.notification.NotificationListenerService" /></intent-filter></service>
Step 2 : Putting it all together
Add a broadcastreciever component to your project and
Add the following procedure to your project
procedure startService
var
ServiceIntent: JIntent;
begin
BroadcastReceiver1.RegisterReceive;
BroadcastReceiver1.Add('Msg');
ServiceIntent := TJIntent.JavaClass.init(SharedActivityContext,
TJLang_Class.JavaClass.forName(
StringToJString('com.embarcadero.$yourapp.NotificationService'),True,SharedActivity.getClassLoader));
SharedActivity.startService(serviceIntent)
end;
The BroadcastReceiver
has a onReceive
event which will get triggered once the localBroadcastManager broadcasts the intent from the onNotificationPosted
method
Thus in onReceive
you can now do with that intent whatever it is you want such as
var
NoteName,NoteTitle,pack : String;
begin
//Do whatever you want with the intent
NoteName := JStringToString(Intent.getStringExtra(StringToJString('key')));
NoteTitle := JStringToString(Intent.getStringExtra(StringToJString('title')));
pack := JStringToString(Intent.getStringExtra(StringToJString('package')));
ShowMessage('Notification: '+NoteName+' Title :'+ NoteTitle+' package :'+pack);
end;
Now we need to write some code to handle the life cycle of the localBroadcastManager when your App is not in the foreground or when it re-enters the foreground
If you don't use Appevents first take a look here http://www.fmxexpress.com/handle-android-and-ios-lifecycle-events-in-delphi-xe5-firemonkey/
Now in response to EnteredBackground
Appevent add the unRegisterReceive
procedure : BroadcastReceiver1.unRegisterReceive;
In response to WillBecomeForeground
Appevent add the regagain
procedure : BroadcastReceiver1.regagain;
Lastly in order for your App to be able to read the notifications of other Apps you need to explicitly give it permission to do so.
Go to your phones Settings and search for Notification Access and you will see your App listed there, simply say it is allowed
Update 1:
Added code to handle the App life cycle, the App should now happily go between foreground and background and continue working
Post a Comment for "Is It Possible To Read The Notifications Of Other Applications On Android With Delphi?"