From cbd2600c336ca1680d08f09c027165e4fa55048e Mon Sep 17 00:00:00 2001
From: hyb1996 <946994919@qq.com>
Date: Tue, 31 Oct 2017 00:07:45 +0800
Subject: [PATCH] add: notification listener service
---
app/src/main/AndroidManifest.xml | 9 ++
.../ui/main/drawer/DrawerFragment.java | 26 +++++
.../drawable-xhdpi/ic_ali_notification.png | Bin 0 -> 1780 bytes
app/src/main/res/layout/drawer_menu.xml | 10 +-
app/src/main/res/values/strings.xml | 2 +
.../autojs/core/image/ScreenCapturer.java | 16 ++-
.../stardust/autojs/runtime/api/Events.java | 36 ++++---
.../stardust/autojs/runtime/api/Images.java | 1 -
.../stardust/notification/Notification.java | 92 ++++++++++++++++++
.../NotificationListenerService.java | 69 +++++++++++++
.../accessibility/NotificationListener.java | 54 ++++------
11 files changed, 255 insertions(+), 60 deletions(-)
create mode 100644 app/src/main/res/drawable-xhdpi/ic_ali_notification.png
create mode 100644 automator/src/main/java/com/stardust/notification/Notification.java
create mode 100644 automator/src/main/java/com/stardust/notification/NotificationListenerService.java
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b9ae1c9e..db3e1dee 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -212,6 +212,15 @@
+
+
+
+
+
+
diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/main/drawer/DrawerFragment.java b/app/src/main/java/com/stardust/scriptdroid/ui/main/drawer/DrawerFragment.java
index a71382e6..dede6d86 100644
--- a/app/src/main/java/com/stardust/scriptdroid/ui/main/drawer/DrawerFragment.java
+++ b/app/src/main/java/com/stardust/scriptdroid/ui/main/drawer/DrawerFragment.java
@@ -1,8 +1,12 @@
package com.stardust.scriptdroid.ui.main.drawer;
+import android.app.NotificationManager;
+import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Bundle;
+import android.provider.Settings;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.view.View;
@@ -13,6 +17,7 @@ import com.afollestad.materialdialogs.MaterialDialog;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.target.SimpleTarget;
import com.bumptech.glide.request.transition.Transition;
+import com.stardust.notification.NotificationListenerService;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.R;
@@ -55,6 +60,7 @@ import io.reactivex.schedulers.Schedulers;
/**
* Created by Stardust on 2017/1/30.
+ * TODO 侧拉菜单用RecyclerView
*/
@EFragment(R.layout.fragment_drawer)
public class DrawerFragment extends android.support.v4.app.Fragment {
@@ -67,6 +73,9 @@ public class DrawerFragment extends android.support.v4.app.Fragment {
@ViewById(R.id.accessibility_service)
DrawerMenuItem mAccessibilityServiceItem;
+ @ViewById(R.id.notification_service)
+ DrawerMenuItem mNotificationPermissionItem;
+
@ViewById(R.id.floating_window)
DrawerMenuItem mFloatingWindowItem;
@@ -201,6 +210,19 @@ public class DrawerFragment extends android.support.v4.app.Fragment {
}
}
+ @Click(R.id.notification_service)
+ void goToNotificationServiceSettings() {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
+ return;
+ }
+ boolean enabled = NotificationListenerService.getInstance() != null;
+ boolean checked = mNotificationPermissionItem.getSwitchCompat().isChecked();
+ if ((checked && !enabled) || (!checked && enabled)) {
+ startActivity(new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS));
+ }
+ }
+
+
private boolean isAccessibilityServiceEnabled() {
return AccessibilityServiceTool.isAccessibilityServiceEnabled(getActivity());
}
@@ -307,6 +329,10 @@ public class DrawerFragment extends android.support.v4.app.Fragment {
private void syncSwitchState() {
mAccessibilityServiceItem.getSwitchCompat().setChecked(
AccessibilityServiceTool.isAccessibilityServiceEnabled(getActivity()));
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ mNotificationPermissionItem.getSwitchCompat().setChecked(NotificationListenerService.getInstance() != null);
+ }
+
}
private void enableAccessibilityService() {
diff --git a/app/src/main/res/drawable-xhdpi/ic_ali_notification.png b/app/src/main/res/drawable-xhdpi/ic_ali_notification.png
new file mode 100644
index 0000000000000000000000000000000000000000..3d85b37501665584c04c6311cc52264e209434be
GIT binary patch
literal 1780
zcmd5-{a2EC7XLm-q@)=dP8K;vI+9tT6jquqrRAgLWND@tp%O&oOA_YlIzFJ)Xlfob
zQYX#oL2OiD#pJmlLsX5UMy{O4z`m|ocJw-}Yo;4rc8y9aZt$9tR)z%c35j3x5^{9wGw4iOxjn#%5Vtx-=zz{=-u%
z4v!D{d2VM=^~$0Y*0Ux&aKY88#(Pp^+US#mMLc&fW1F59>MIYAKX5B$ef1@Ph1X4(
zKVFi(KXJC4Ty6_`zSsyFtPe5!`@DF_siASBEr%zPC@=-NaA8GOv?hl<$RBAEfy!56
zY&ABAd2iV^<5%`g#NiEsUHfu*&-U>x{JXK=G5xMXpH^|&QRCigbI-CtpW)%=eOAiN
zhTYj5V*xt0%H@vsZ}Cfzs$rtP&6T)Ds^hdl$SFj{j%gDk*Tok6n^?TgGw+{0+%gqu
zG~)GlaqPGC&pkn(*N&gzz|
zvIQhV_ZP#jl<#!cnM3Qi_DI!^rT$u<*?DEhe$Dq+L<1`UqeFS;+$O%=qu&1P7xjmWw0~~V~2E7;?A)l
zZayj$MbP&EIi#1jT(}jaxv-5aCj*}RjrJ9eQdJ(dWmM)=8F{%vUc)D~LuK_Q=QtYU
zz8hOsQJc&qy#gHG4eVcD(axz3dhHAa)~}J}{Du$84i=evtKX|_w@GfxQk_4p$Q55*
zWIiGtd#Ta8@@VaE*xBK*%rI;g^0@Yb>cf{eXRHDirq_r
zKWIAs`9*t@Nx$Xv3-Y6FV1|?)7YYr&F~q?!M_G-E%vej8hjlOFJq3J^LHB+0dxuvc
z8kNcZ-5eM0%nH`o1>GY?E(r)P6}ml`az}iDV0fsX-Ocw=O=`T;0Mn|~$6LL=#3+w?
z-KBQF4~5N}^=A;-+hcHQYn`P!xyLa)^UIesLFr|}8-R|zEq_onz#VFFQ=}LY)T@w6
zh;tDwKQc7WJ|*Nqz))WCX=7$#C6vJZl5rg9rX@lGgfuRg@)rY>%U#|waQDe{VAdZX
zaSX@d363IdfkF5_c?Glggnt6tj?o%{oI%>)X#4^pVT1m8efMz+U_^9Cl%hHH!mU&f
zYt1vCt6OY>ZvbOop6@68)~4=2E5YpJ9@pcx2)%Q9S}fINz(nPJYwQXC5&9vhgYB2eKV7->9094S8SEIi
zm}cU5bg6jCD#TcCbt4nhZ(ao9%zmB>?y=}Q1`nG{I$vUwID=Sam&@Paf
z8V;UM1aur~K0=yXPp7V%YCg=ugWK?{usu-FTAwk$Fi_UwTf)^6fhJV9w&Zct8`s_+
zJFUG2$}?jVK8JET-qALFjrcMo!M?3|iNG^Bq{v_xlFzp&lwy+HvxmyX1_v2%UGI{z
z7KH^KoOjgI$D&~UyGxOKNS1IV(F^BP+
+ app:title="@string/text_service">
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index def955ea..9002e3d3 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -282,6 +282,8 @@
重置成功
无法读取文件: %s
已连接
+ 服务
+ 通知权限
diff --git a/autojs/src/main/java/com/stardust/autojs/core/image/ScreenCapturer.java b/autojs/src/main/java/com/stardust/autojs/core/image/ScreenCapturer.java
index f23c0af5..fd482038 100644
--- a/autojs/src/main/java/com/stardust/autojs/core/image/ScreenCapturer.java
+++ b/autojs/src/main/java/com/stardust/autojs/core/image/ScreenCapturer.java
@@ -27,11 +27,11 @@ import com.stardust.util.ScreenMetrics;
public class ScreenCapturer {
private static final String LOG_TAG = "ScreenCapturer";
+ private final Object mCachedImageLock = new Object();
private ImageReader mImageReader;
private MediaProjection mMediaProjection;
private VirtualDisplay mVirtualDisplay;
private volatile Looper mImageAcquireLooper;
- private final Object mImageWaitingLock = new Object();
private volatile Image mUnderUsingImage;
private volatile Image mCachedImage;
private final int mScreenWidth;
@@ -87,7 +87,13 @@ public class ScreenCapturer {
@Override
public void onImageAvailable(ImageReader reader) {
if (mCachedImage != null) {
- return;
+ synchronized (mCachedImageLock) {
+ if (mCachedImage != null) {
+ mCachedImage.close();
+ }
+ mCachedImage = reader.acquireLatestImage();
+ return;
+ }
}
mCachedImage = reader.acquireLatestImage();
}
@@ -99,8 +105,10 @@ public class ScreenCapturer {
if (mCachedImage != null) {
if (mUnderUsingImage != null)
mUnderUsingImage.close();
- mUnderUsingImage = mCachedImage;
- mCachedImage = null;
+ synchronized (mCachedImageLock) {
+ mUnderUsingImage = mCachedImage;
+ mCachedImage = null;
+ }
}
return mUnderUsingImage;
}
diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/Events.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/Events.java
index 86f08401..988e0d35 100644
--- a/autojs/src/main/java/com/stardust/autojs/runtime/api/Events.java
+++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/Events.java
@@ -1,9 +1,9 @@
package com.stardust.autojs.runtime.api;
import android.accessibilityservice.AccessibilityServiceInfo;
-import android.app.Notification;
import android.content.Context;
import android.graphics.Point;
+import android.os.Build;
import android.os.Handler;
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityEvent;
@@ -11,6 +11,8 @@ import android.view.accessibility.AccessibilityEvent;
import com.stardust.autojs.R;
import com.stardust.autojs.core.accessibility.AccessibilityBridge;
import com.stardust.autojs.core.eventloop.EventEmitter;
+import com.stardust.notification.Notification;
+import com.stardust.notification.NotificationListenerService;
import com.stardust.autojs.runtime.ScriptBridges;
import com.stardust.autojs.runtime.exception.ScriptException;
import com.stardust.autojs.core.inputevent.InputEventObserver;
@@ -136,9 +138,14 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver
mListeningNotification = true;
ensureHandler();
mLoopers.waitWhenIdle(true);
- mAccessibilityBridge.ensureServiceEnabled();
- mAccessibilityBridge.getNotificationObserver()
- .addListener(this);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2
+ && NotificationListenerService.getInstance() != null) {
+ NotificationListenerService.getInstance().addListener(this);
+ } else {
+ mAccessibilityBridge.ensureServiceEnabled();
+ mAccessibilityBridge.getNotificationObserver()
+ .addListener(this);
+ }
}
public Events onNotification(Object listener) {
@@ -165,6 +172,10 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver
}
if (mListeningNotification) {
mAccessibilityBridge.getNotificationObserver().removeListener(this);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2
+ && NotificationListenerService.getInstance() != null) {
+ NotificationListenerService.getInstance().removeListener(this);
+ }
}
}
@@ -201,24 +212,11 @@ public class Events extends EventEmitter implements OnKeyListener, TouchObserver
});
}
-
- @Override
- public void onNotification(final AccessibilityEvent event, final NotificationInfo notification) {
+ public void onNotification(final Notification notification) {
mHandler.post(new Runnable() {
@Override
public void run() {
- emit("toast", notification);
- }
- });
- }
-
- @Override
- public void onNotification(final AccessibilityEvent event, final Notification notification) {
- final NotificationInfo info = NotificationInfo.fromEvent(event);
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- emit("notification", info, notification);
+ emit("notification", notification);
}
});
diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/Images.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/Images.java
index e37a7307..7bfe2523 100644
--- a/autojs/src/main/java/com/stardust/autojs/runtime/api/Images.java
+++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/Images.java
@@ -99,7 +99,6 @@ public class Images {
Image image = mScreenCapturer.capture();
if (image != null) {
saveImage(image, path);
- image.close();
return true;
}
return false;
diff --git a/automator/src/main/java/com/stardust/notification/Notification.java b/automator/src/main/java/com/stardust/notification/Notification.java
new file mode 100644
index 00000000..747eb1d0
--- /dev/null
+++ b/automator/src/main/java/com/stardust/notification/Notification.java
@@ -0,0 +1,92 @@
+package com.stardust.notification;
+
+import android.app.PendingIntent;
+import android.os.Build;
+import android.os.Parcel;
+import android.support.annotation.RequiresApi;
+
+/**
+ * Created by Stardust on 2017/10/30.
+ */
+
+public class Notification extends android.app.Notification {
+
+ private String mPackageName;
+
+ private Notification(String packageName) {
+ mPackageName = packageName;
+ }
+
+ public static Notification create(android.app.Notification n, String packageName) {
+ Notification notification = new Notification(packageName);
+ clone(n, notification);
+ return notification;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.KITKAT)
+ public String getText() {
+ return extras.getString(EXTRA_TEXT);
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.KITKAT)
+ public String getTitle() {
+ return extras.getString(EXTRA_TITLE);
+ }
+
+ public void click() {
+ try {
+ this.contentIntent.send();
+ } catch (PendingIntent.CanceledException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void delete() {
+ try {
+ this.deleteIntent.send();
+ } catch (PendingIntent.CanceledException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static void clone(android.app.Notification from, android.app.Notification to) {
+ to.when = from.when;
+ to.icon = from.icon;
+ to.iconLevel = from.iconLevel;
+ to.number = from.number;
+ to.contentIntent = from.contentIntent;
+ to.deleteIntent = from.deleteIntent;
+ to.fullScreenIntent = from.fullScreenIntent;
+ to.tickerText = from.tickerText;
+ to.tickerView = from.tickerView;
+ to.contentView = from.contentView;
+ to.bigContentView = from.bigContentView;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ to.headsUpContentView = from.headsUpContentView;
+ to.audioAttributes = from.audioAttributes;
+ to.color = from.color;
+ to.visibility = from.visibility;
+ to.category = from.category;
+ to.publicVersion = from.publicVersion;
+ }
+ to.largeIcon = from.largeIcon;
+ to.sound = from.sound;
+ to.audioStreamType = from.audioStreamType;
+ to.vibrate = from.vibrate;
+ to.ledARGB = from.ledARGB;
+ to.ledOnMS = from.ledOnMS;
+ to.ledOffMS = from.ledOffMS;
+ to.defaults = from.defaults;
+ to.flags = from.flags;
+ to.priority = from.priority;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ to.extras = from.extras;
+ to.actions = from.actions;
+ }
+ }
+
+}
diff --git a/automator/src/main/java/com/stardust/notification/NotificationListenerService.java b/automator/src/main/java/com/stardust/notification/NotificationListenerService.java
new file mode 100644
index 00000000..242568a4
--- /dev/null
+++ b/automator/src/main/java/com/stardust/notification/NotificationListenerService.java
@@ -0,0 +1,69 @@
+package com.stardust.notification;
+
+import android.app.Notification;
+import android.os.Build;
+import android.os.Parcel;
+import android.service.notification.StatusBarNotification;
+import android.support.annotation.RequiresApi;
+
+import com.stardust.view.accessibility.NotificationListener;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Created by Stardust on 2017/10/30.
+ */
+
+@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN_MR2)
+public class NotificationListenerService extends android.service.notification.NotificationListenerService {
+
+ private CopyOnWriteArrayList mNotificationListeners = new CopyOnWriteArrayList<>();
+ private static NotificationListenerService sInstance;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ sInstance = this;
+ }
+
+ public static NotificationListenerService getInstance() {
+ return sInstance;
+ }
+
+ @Override
+ public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
+ onNotificationPosted(sbn);
+ }
+
+ @Override
+ public void onNotificationPosted(StatusBarNotification sbn) {
+ for (NotificationListener listener : mNotificationListeners) {
+ listener.onNotification(com.stardust.notification.Notification.create(
+ sbn.getNotification(), sbn.getPackageName()));
+ }
+ }
+
+ @Override
+ public void onNotificationRemoved(StatusBarNotification sbn) {
+ }
+
+ @Override
+ public void onNotificationRemoved(StatusBarNotification sbn, RankingMap rankingMap) {
+ }
+
+ public void addListener(NotificationListener listener) {
+ mNotificationListeners.add(listener);
+ }
+
+ public boolean removeListener(NotificationListener listener) {
+ return mNotificationListeners.remove(listener);
+ }
+
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ sInstance = null;
+ }
+}
diff --git a/automator/src/main/java/com/stardust/view/accessibility/NotificationListener.java b/automator/src/main/java/com/stardust/view/accessibility/NotificationListener.java
index 018a68ea..6cc19bce 100644
--- a/automator/src/main/java/com/stardust/view/accessibility/NotificationListener.java
+++ b/automator/src/main/java/com/stardust/view/accessibility/NotificationListener.java
@@ -1,12 +1,14 @@
package com.stardust.view.accessibility;
import android.accessibilityservice.AccessibilityService;
-import android.app.Notification;
import android.content.Context;
+import android.service.notification.StatusBarNotification;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
+import com.stardust.notification.Notification;
+
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -19,6 +21,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
public interface NotificationListener {
+
class NotificationInfo {
private String mPackageName;
@@ -73,9 +76,9 @@ public interface NotificationListener {
}
}
- void onNotification(AccessibilityEvent event, NotificationInfo notification);
- void onNotification(AccessibilityEvent event, Notification notification);
+ void onNotification(Notification notification);
+
class Observer implements NotificationListener, AccessibilityDelegate {
@@ -91,27 +94,6 @@ public interface NotificationListener {
mContext = context;
}
- @Override
- public void onNotification(AccessibilityEvent event, NotificationInfo notification) {
- for (NotificationListener listener : mNotificationListeners) {
- try {
- listener.onNotification(event, notification);
- } catch (Exception e) {
- Log.e(TAG, "Error onNotification: " + notification + " Listener: " + listener, e);
- }
- }
- }
-
- @Override
- public void onNotification(AccessibilityEvent event, Notification notification) {
- for (NotificationListener listener : mNotificationListeners) {
- try {
- listener.onNotification(event, notification);
- } catch (Exception e) {
- Log.e(TAG, "Error onNotification: " + notification + " Listener: " + listener, e);
- }
- }
- }
public void addListener(NotificationListener listener) {
mNotificationListeners.add(listener);
@@ -124,18 +106,9 @@ public interface NotificationListener {
@Override
public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) {
if (event.getParcelableData() instanceof Notification) {
- Notification notification = (Notification) event.getParcelableData();
+ android.app.Notification notification = (android.app.Notification) event.getParcelableData();
Log.d(TAG, "onNotification: " + notification + "; " + event);
- onNotification(event, notification);
- } else {
- List list = event.getText();
- Log.d(TAG, "onNotification: " + list + "; " + event);
- if (event.getPackageName().equals(mContext.getPackageName())) {
- return false;
- }
- if (list != null) {
- onNotification(event, new NotificationInfo(event.getPackageName(), list));
- }
+ onNotification(Notification.create(notification, event.getPackageName().toString()));
}
return false;
}
@@ -145,5 +118,16 @@ public interface NotificationListener {
public Set getEventTypes() {
return EVENT_TYPES;
}
+
+ @Override
+ public void onNotification(Notification notification) {
+ for (NotificationListener listener : mNotificationListeners) {
+ try {
+ listener.onNotification(notification);
+ } catch (Exception e) {
+ Log.e(TAG, "Error onNotification: " + notification + " Listener: " + listener, e);
+ }
+ }
+ }
}
}