elc*_*lie 1 delphi notifications android firemonkey
我正在尝试从 Rad Studio XE7 for android 中制作的应用程序上读取来自其他应用程序的通知。抬头一看,发现在java中可以访问NotificationListenerService,但是如果用delphi可以访问这个服务就不行了。可以做到吗?
您可以使用 阅读其他应用程序的通知,NotificationListenerService方法如下
在您的项目文件夹中创建一个名为 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");
}
}
Run Code Online (Sandbox Code Playgroud)
并将以下文件添加到 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\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
Run Code Online (Sandbox Code Playgroud)
添加位于 C:\Program Files (x86)\Embarcadero\Studio\17.0\lib\android\debug 的默认 classes.dex 文件
添加同样位于 C:\Program Files (x86)\Embarcadero\Studio\17.0\lib\android\debug 的 fmx.jar
而其重命名为classes.jar
所以你的 Java 文件夹结构现在看起来像这样
src //带有正确的文件夹结构和你的 NotificationService.java 里面
android-support-v4
如果您在构建文件中正确设置了所有变量,您现在可以运行 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.
Run Code Online (Sandbox Code Playgroud)
从http://www.fmxexpress.com/free-broadcast-receiver-component-for-delphi-xe7-firemonkey-on-android/下载免费广播接收器组件
安装组件,现在将随附的 BroadcastReceiver 单元添加到您的项目中,并在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;
Run Code Online (Sandbox Code Playgroud)
添加以下程序 unRegisterReceive
procedure TBroadcastReceiver.unRegisterReceive;
var
locMan : JLocalBroadcastManager;
begin
locMan := TJLocalBroadcastManager.JavaClass.getInstance(SharedActivityContext);
locMan.unregisterReceiver(FReceiver);
end;
Run Code Online (Sandbox Code Playgroud)
添加以下程序 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;
Run Code Online (Sandbox Code Playgroud)
在您的项目目录中编辑您的 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>
Run Code Online (Sandbox Code Playgroud)
将广播接收器组件添加到您的项目中并
将以下过程添加到您的项目中
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;
Run Code Online (Sandbox Code Playgroud)
该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;
Run Code Online (Sandbox Code Playgroud)
现在我们需要写一些代码来处理当你的App不在前台或者重新进入前台时localBroadcastManager的生命周期
如果你不使用 Appevents 首先看看这里http://www.fmxexpress.com/handle-android-and-ios-lifecycle-events-in-delphi-xe5-firemonkey/
现在响应EnteredBackgroundAppevent 添加unRegisterReceive程序:BroadcastReceiver1.unRegisterReceive;
响应WillBecomeForegroundAppevent 添加regagain程序:BroadcastReceiver1.regagain;
最后,为了让您的应用程序能够读取其他应用程序的通知,您需要明确授予它这样做的权限。
转到您的手机设置并搜索通知访问,您将看到您的应用程序在那里列出,只需说它是允许的
更新 1:
添加了处理应用程序生命周期的代码,应用程序现在应该可以愉快地在前台和后台之间切换并继续工作