add: notification listener service

This commit is contained in:
hyb1996 2017-10-31 00:07:45 +08:00
parent bfd6a0c40a
commit cbd2600c33
11 changed files with 255 additions and 60 deletions

View File

@ -212,6 +212,15 @@
</service>
<service
android:name="com.stardust.notification.NotificationListenerService"
android:label="@string/_app_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService"/>
</intent-filter>
</service>
<receiver android:name=".external.widget.ScriptWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>

View File

@ -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() {

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -15,7 +15,7 @@
<com.stardust.scriptdroid.ui.main.drawer.DrawerMenuGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:title="@string/text_accessibility_service">
app:title="@string/text_service">
<com.stardust.scriptdroid.ui.main.drawer.DrawerMenuItem
android:id="@+id/accessibility_service"
@ -35,6 +35,14 @@
app:title="@string/text_stable_mode"
app:with_switch="true"/>
<com.stardust.scriptdroid.ui.main.drawer.DrawerMenuItem
android:id="@+id/notification_service"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:icon="@drawable/ic_ali_notification"
app:title="@string/text_notification_permission"
app:with_switch="true"/>
</com.stardust.scriptdroid.ui.main.drawer.DrawerMenuGroup>

View File

@ -282,6 +282,8 @@
<string name="text_reset_succeed">重置成功</string>
<string name="text_cannot_read_file" formatted="true">无法读取文件: %s</string>
<string name="text_connected">已连接</string>
<string name="text_service">服务</string>
<string name="text_notification_permission">通知权限</string>
<string-array name="ad_showing_mode_keys">

View File

@ -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;
}

View File

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

View File

@ -99,7 +99,6 @@ public class Images {
Image image = mScreenCapturer.capture();
if (image != null) {
saveImage(image, path);
image.close();
return true;
}
return false;

View File

@ -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;
}
}
}

View File

@ -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<NotificationListener> 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;
}
}

View File

@ -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<CharSequence> 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<Integer> 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);
}
}
}
}
}