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 00000000..3d85b375 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_ali_notification.png differ diff --git a/app/src/main/res/layout/drawer_menu.xml b/app/src/main/res/layout/drawer_menu.xml index 1157d299..6e22709d 100644 --- a/app/src/main/res/layout/drawer_menu.xml +++ b/app/src/main/res/layout/drawer_menu.xml @@ -15,7 +15,7 @@ + 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); + } + } + } } }