是否可以用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 现在应该可以愉快地在前台和后台之间切换并继续工作