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); + } + } + } } }