是否可以用delphi阅读android上其他应用程序的通知?
Is it possible to read the notifications of other applications on android with delphi?
我正在尝试在 Rad Studio XE7 为 android 制作的应用程序上读取来自其他应用程序的通知。
抬头一看,在java中可以访问NotificationListenerService,但是在delphi中不能访问这个服务。可以吗?
您可以使用NotificationListenerService
阅读其他应用的通知,这里是
第 1 步:准备好一切
在您的项目文件夹中创建一个名为 Java
的文件夹
现在在该文件夹中创建一个名为 src 的文件夹,并在其中创建一个与您的 Apps 包名称相同的文件夹结构,例如:src/com/embarcadero/$yourapp
在 src/com/embarcadero/$yourapp 添加以下 Java 文件作为 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;
public class NotificationService extends NotificationListenerService {
static final String TAG = "NotificationService";
Context context;
@Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
Log.d(TAG,"Service has been started!");
}
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
Log.d(TAG,"Notification Posted!");
String pack = sbn.getPackageName();
Bundle extras = sbn.getNotification().extras;
String title = extras.getString("android.title");
String text = extras.getCharSequence("android.text").toString();
String id = sbn.getTag();
Log.i("Package", pack);
Log.i("Title",title);
Log.i("Text",text);
if (id != null){
Log.i("Key", id);
}
Intent msgrcv = new Intent("Msg");
msgrcv.putExtra("package", pack);
msgrcv.putExtra("key", id);
msgrcv.putExtra("title", title);
msgrcv.putExtra("text", text);
LocalBroadcastManager.getInstance(context).sendBroadcast(msgrcv);
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
Log.d(TAG,"Notification Removed");
}
}
并将以下文件添加到 Java 文件夹:android-support-v4.jar(在您的 SDK 路径下找到:android-sdk-windows\extras\android\support\v4)
添加以下批处理文件build.bat
@echo off
setlocal
REM Set the your paths as needed
SET PATH=%PATH%;C:\Users\Public\Documents\Embarcadero\Studio.0\PlatformSDKs\android-sdk-windows\build-tools.0.1
if x%ANDROID% == x set ANDROID=C:\Users\Public\Documents\Embarcadero\Studio.0\PlatformSDKs\android-sdk-windows
set ANDROID_PLATFORM=%ANDROID%\platforms\android-23
set DX_LIB=%ANDROID%\build-tools.0.1\lib
set EMBO_DEX="C:\Program Files (x86)\Embarcadero\Studio.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
添加 默认 classes.dex 文件位于 C:\Program Files (x86)\Embarcadero\Studio.0\lib\android\debug
Add fmx.jar 也位于 C:\Program Files (x86)\Embarcadero\Studio.0\lib\android\debug
和重命名为classes.jar
所以你的 Java 文件夹结构现在看起来像这样
src //具有正确的文件夹结构和您的 NotificationService.java inside
android-support-v4
- 建设
- classes.dex
- classes.jar
如果您在构建文件中正确设置了所有变量,您现在可以 运行 build.bat 生成输出文件夹(特别感谢最初创建它的 Brian Long)
现在您的输出文件夹中将有 2 个文件夹,一个名为 dex,另一个为 jar,现在您将有一个新的 classes.dex 文件和 Delphi_Service 可执行文件
让您的应用程序使用新的 classes.dex 文件
http://docwiki.embarcadero.com/RADStudio/XE8/en/Creating_and_Deploying_a_classes.dex_File_Manually
将 Delphi_Service jar 文件添加到您的应用程序
http://docwiki.embarcadero.com/RADStudio/XE8/en/Adding_A_Java_Library_to_Your_Application_Using_the_Project_Manager
在您的项目文件中添加以下单元 Android.JNI.LocalBroadcastMan
//Created with the Java2OP tool
unit Android.JNI.LocalBroadcastMan;
interface
uses
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.
从 http://www.fmxexpress.com/free-broadcast-receiver-component-for-delphi-xe7-firemonkey-on-android/
下载 免费广播接收器组件
安装组件,现在将自带的BroadcastReceiver单元添加到你的Project中,在BroadcastReceiver中添加如下代码:
添加到您的 uses : Android.JNI.LocalBroadcastMan
将 TBroadcastReceiver.Add
程序更改为
procedure TBroadcastReceiver.Add(Value: String);
{$IFDEF ANDROID}
var
Filter: JIntentFilter;
locMan : JLocalBroadcastManager;
{$ENDIF}
begin
{$IFDEF ANDROID}
if (FListener = nil) or (FReceiver = nil) then
begin
Raise Exception.Create('First use RegisterReceive!');
Exit;
end;
{$ENDIF}
if FItems <> nil then
if FItems.IndexOf(Value) = -1 then
begin
{$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;
添加以下过程unRegisterReceive
procedure TBroadcastReceiver.unRegisterReceive;
var
locMan : JLocalBroadcastManager;
begin
locMan := TJLocalBroadcastManager.JavaClass.getInstance(SharedActivityContext);
locMan.unregisterReceiver(FReceiver);
end;
添加以下程序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;
在项目目录中编辑 AndroidManifest.template.xml 以包含服务
<service android:name="com.embarcadero.$Yourapp.NotificationService"
android:label="%label%"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
第 2 步:将它们放在一起
将 broadcastreciever 组件添加到您的项目并且
将以下过程添加到您的项目中
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;
BroadcastReceiver
有一个 onReceive
事件,一旦 localBroadcastManager 从 onNotificationPosted
方法
广播意图,该事件就会被触发
因此,在 onReceive
中,您现在可以随心所欲地执行该意图,例如
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;
现在我们需要编写一些代码来处理 localBroadcastManager 的生命周期,当您的应用程序不在前台或 re-enters 前台
如果你不使用 Appevents 先看看这里 http://www.fmxexpress.com/handle-android-and-ios-lifecycle-events-in-delphi-xe5-firemonkey/
现在响应 EnteredBackground
Appevent 添加 unRegisterReceive
过程:BroadcastReceiver1.unRegisterReceive;
响应 WillBecomeForeground
事件添加 regagain
过程:BroadcastReceiver1.regagain;
最后,为了让您的应用能够读取其他应用的通知,您需要明确授予它这样做的权限。
转到您的手机设置并搜索通知访问,您会看到您的应用程序列在那里,只需说它是允许的
更新一:
添加了处理 App 生命周期的代码,App 现在应该可以愉快地在前台和后台之间切换并继续工作
我正在尝试在 Rad Studio XE7 为 android 制作的应用程序上读取来自其他应用程序的通知。 抬头一看,在java中可以访问NotificationListenerService,但是在delphi中不能访问这个服务。可以吗?
您可以使用NotificationListenerService
阅读其他应用的通知,这里是
第 1 步:准备好一切
在您的项目文件夹中创建一个名为 Java
的文件夹现在在该文件夹中创建一个名为 src 的文件夹,并在其中创建一个与您的 Apps 包名称相同的文件夹结构,例如:src/com/embarcadero/$yourapp
在 src/com/embarcadero/$yourapp 添加以下 Java 文件作为 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;
public class NotificationService extends NotificationListenerService {
static final String TAG = "NotificationService";
Context context;
@Override
public void onCreate() {
super.onCreate();
context = getApplicationContext();
Log.d(TAG,"Service has been started!");
}
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
Log.d(TAG,"Notification Posted!");
String pack = sbn.getPackageName();
Bundle extras = sbn.getNotification().extras;
String title = extras.getString("android.title");
String text = extras.getCharSequence("android.text").toString();
String id = sbn.getTag();
Log.i("Package", pack);
Log.i("Title",title);
Log.i("Text",text);
if (id != null){
Log.i("Key", id);
}
Intent msgrcv = new Intent("Msg");
msgrcv.putExtra("package", pack);
msgrcv.putExtra("key", id);
msgrcv.putExtra("title", title);
msgrcv.putExtra("text", text);
LocalBroadcastManager.getInstance(context).sendBroadcast(msgrcv);
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
Log.d(TAG,"Notification Removed");
}
}
并将以下文件添加到 Java 文件夹:android-support-v4.jar(在您的 SDK 路径下找到:android-sdk-windows\extras\android\support\v4)
添加以下批处理文件build.bat
@echo off
setlocal
REM Set the your paths as needed
SET PATH=%PATH%;C:\Users\Public\Documents\Embarcadero\Studio.0\PlatformSDKs\android-sdk-windows\build-tools.0.1
if x%ANDROID% == x set ANDROID=C:\Users\Public\Documents\Embarcadero\Studio.0\PlatformSDKs\android-sdk-windows
set ANDROID_PLATFORM=%ANDROID%\platforms\android-23
set DX_LIB=%ANDROID%\build-tools.0.1\lib
set EMBO_DEX="C:\Program Files (x86)\Embarcadero\Studio.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
添加 默认 classes.dex 文件位于 C:\Program Files (x86)\Embarcadero\Studio.0\lib\android\debug
Add fmx.jar 也位于 C:\Program Files (x86)\Embarcadero\Studio.0\lib\android\debug
和重命名为classes.jar
所以你的 Java 文件夹结构现在看起来像这样
src //具有正确的文件夹结构和您的 NotificationService.java inside
android-support-v4
- 建设
- classes.dex
- classes.jar
如果您在构建文件中正确设置了所有变量,您现在可以 运行 build.bat 生成输出文件夹(特别感谢最初创建它的 Brian Long)
现在您的输出文件夹中将有 2 个文件夹,一个名为 dex,另一个为 jar,现在您将有一个新的 classes.dex 文件和 Delphi_Service 可执行文件
让您的应用程序使用新的 classes.dex 文件 http://docwiki.embarcadero.com/RADStudio/XE8/en/Creating_and_Deploying_a_classes.dex_File_Manually
将 Delphi_Service jar 文件添加到您的应用程序 http://docwiki.embarcadero.com/RADStudio/XE8/en/Adding_A_Java_Library_to_Your_Application_Using_the_Project_Manager
在您的项目文件中添加以下单元 Android.JNI.LocalBroadcastMan
//Created with the Java2OP tool
unit Android.JNI.LocalBroadcastMan;
interface
uses
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.
从 http://www.fmxexpress.com/free-broadcast-receiver-component-for-delphi-xe7-firemonkey-on-android/
下载 免费广播接收器组件安装组件,现在将自带的BroadcastReceiver单元添加到你的Project中,在BroadcastReceiver中添加如下代码:
添加到您的 uses : Android.JNI.LocalBroadcastMan
将 TBroadcastReceiver.Add
程序更改为
procedure TBroadcastReceiver.Add(Value: String);
{$IFDEF ANDROID}
var
Filter: JIntentFilter;
locMan : JLocalBroadcastManager;
{$ENDIF}
begin
{$IFDEF ANDROID}
if (FListener = nil) or (FReceiver = nil) then
begin
Raise Exception.Create('First use RegisterReceive!');
Exit;
end;
{$ENDIF}
if FItems <> nil then
if FItems.IndexOf(Value) = -1 then
begin
{$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;
添加以下过程unRegisterReceive
procedure TBroadcastReceiver.unRegisterReceive;
var
locMan : JLocalBroadcastManager;
begin
locMan := TJLocalBroadcastManager.JavaClass.getInstance(SharedActivityContext);
locMan.unregisterReceiver(FReceiver);
end;
添加以下程序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;
在项目目录中编辑 AndroidManifest.template.xml 以包含服务
<service android:name="com.embarcadero.$Yourapp.NotificationService"
android:label="%label%"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
第 2 步:将它们放在一起
将 broadcastreciever 组件添加到您的项目并且
将以下过程添加到您的项目中
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;
BroadcastReceiver
有一个 onReceive
事件,一旦 localBroadcastManager 从 onNotificationPosted
方法
因此,在 onReceive
中,您现在可以随心所欲地执行该意图,例如
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;
现在我们需要编写一些代码来处理 localBroadcastManager 的生命周期,当您的应用程序不在前台或 re-enters 前台
如果你不使用 Appevents 先看看这里 http://www.fmxexpress.com/handle-android-and-ios-lifecycle-events-in-delphi-xe5-firemonkey/
现在响应 EnteredBackground
Appevent 添加 unRegisterReceive
过程:BroadcastReceiver1.unRegisterReceive;
响应 WillBecomeForeground
事件添加 regagain
过程:BroadcastReceiver1.regagain;
最后,为了让您的应用能够读取其他应用的通知,您需要明确授予它这样做的权限。
转到您的手机设置并搜索通知访问,您会看到您的应用程序列在那里,只需说它是允许的
更新一:
添加了处理 App 生命周期的代码,App 现在应该可以愉快地在前台和后台之间切换并继续工作