package com.stardust.automator; import android.accessibilityservice.AccessibilityService; import android.util.Log; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import com.stardust.view.accessibility.AccessibilityDelegate; import java.util.LinkedList; import java.util.Queue; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.Executors; /** * Created by Stardust on 2017/3/9. */ public class AccessibilityEventCommandHost implements AccessibilityDelegate { public interface Command { void execute(AccessibilityService service, AccessibilityEvent event); boolean isValid(); void setValid(boolean valid); } public abstract static class AbstractCommand implements Command { private boolean mValid; public synchronized boolean isValid() { return mValid; } public synchronized void setValid(boolean valid) { mValid = valid; } } private static final String TAG = "CommandHostDelegate"; public static final int RUN_MODE_SINGLE_THREAD = 0; public static final int RUN_MODE_THREAD_POOL = 1; public static final int RUN_MODE_NEW_THREAD_EVERY_TIME = 2; private final Queue<Command> mCommands = new LinkedList<>(); private Executor mExecutor = Executors.newSingleThreadExecutor(); private int mRunMode = RUN_MODE_THREAD_POOL; @Override public boolean onAccessibilityEvent(final AccessibilityService service, final AccessibilityEvent event) { synchronized (mCommands) { if (!mCommands.isEmpty()) { Log.v(TAG, "will execute " + mCommands.size() + " commands"); } while (!mCommands.isEmpty()) { final Command command = mCommands.poll(); if (command.isValid()) { executeCommand(command, service, event); } } } return false; } @Override public Set<Integer> getEventTypes() { return null; } private void executeCommand(final Command command, final AccessibilityService service, final AccessibilityEvent event) { final Runnable r = new Runnable() { @Override public void run() { if (!command.isValid()) { return; } Log.v(TAG, "executing " + command); command.execute(service, event); synchronized (command) { command.notify(); } } }; if (mRunMode == RUN_MODE_SINGLE_THREAD) { r.run(); } else if (mRunMode == RUN_MODE_NEW_THREAD_EVERY_TIME) { new Thread(r).start(); } else { mExecutor.execute(r); } } public void executeAndWaitForEvent(Command command) { synchronized (mCommands) { mCommands.offer(command); } synchronized (command) { try { command.wait(); } catch (InterruptedException e) { command.setValid(false); throw new RuntimeException(e); } } } public void setRunMode(int mode) { mRunMode = mode; } }