2

I am following this official tutorial by Google, which shows adding a layout on top of a view. However, it adds the layout always, from the time accessibility service is activated.

I want to add this layout programmatically, after a certain action and remove it again, after some other action.

Here is my XML for accessibility service:

<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
    android:packageNames="com.whatsapp"
    android:accessibilityFeedbackType="feedbackSpoken"
    android:notificationTimeout="100"
    android:settingsActivity="com.example.android.apis.accessibility.TestBackActivity"
    android:canRetrieveWindowContent="true" />

Following is my code, which I have mostly borrowed from the tutorial:

public class GlobalActionBarService extends AccessibilityService {

    private static final String TAG = "AccessibilityService";
    private WindowManager mWindowManager;
    FrameLayout mLayout;

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        final int eventType = event.getEventType();
        String eventText = null;
        switch(eventType) {
            case AccessibilityEvent.TYPE_VIEW_CLICKED:
                eventText = "Clicked: ";
                break;
            case AccessibilityEvent.TYPE_VIEW_FOCUSED:
                eventText = "Focused: ";
                break;
        }

        eventText = eventText + event.getContentDescription();

        mLayout = new FrameLayout(this);
        mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
        lp.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
        lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
        lp.format = PixelFormat.TRANSLUCENT;
        lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
        lp.gravity = Gravity.TOP;
        LayoutInflater inflater = LayoutInflater.from(this);
        inflater.inflate(R.layout.action_bar, mLayout);

        if (eventText.toLowerCase().contains("test")) {
            Log.d(TAG, "Oh! Displaying the layout");
            mWindowManager.addView(mLayout, lp);
        }

        if (eventText.toLowerCase().contains("navigate")) {
            Log.d(TAG, "Removing the layout");
            if (mLayout != null && mLayout.getRootView() != null) {
                mWindowManager.removeView(mLayout.getRootView());
            }
        }
    }

    @Override
    public void onInterrupt() {

    }

} 

However, my code breaks and it is obviously wrong. It fails when it tries to remove the layout. (And also, is it adding the same layout multiple times?). And are there any performance issues I should consider?

Following is the exception which is thrown when I try to remove the frame layout:

03-08 22:28:10.375 24266-24266/im.avi.todolist E/AndroidRuntime: 
FATAL EXCEPTION: main
Process: im.avi.todolist, PID: 24266
java.lang.IllegalArgumentException: View=android.widget.FrameLayout{3695c2 V.E...... ......I. 0,0-0,0} not attached to window manager
 at android.view.WindowManagerGlobal.findViewLocked(WindowManagerGlobal.java:473)
 at android.view.WindowManagerGlobal.removeView(WindowManagerGlobal.java:382)
 at android.view.WindowManagerImpl.removeView(WindowManagerImpl.java:119)
 at im.avi.todolist.GlobalActionBarService.onAccessibilityEvent(GlobalActionBarService.java:77)
 at android.accessibilityservice.AccessibilityService$2.onAccessibilityEvent(AccessibilityService.java:1449)
 at android.accessibilityservice.AccessibilityService$IAccessibilityServiceClientWrapper.executeMessage(AccessibilityService.java:1585)
 at com.android.internal.os.HandlerCaller$MyHandler.handleMessage(HandlerCaller.java:37)
 at android.os.Handler.dispatchMessage(Handler.java:102)
 at android.os.Looper.loop(Looper.java:154)
 at android.app.ActivityThread.main(ActivityThread.java:6119)
 at java.lang.reflect.Method.invoke(Native Method)
 at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
avi
  • 9,292
  • 11
  • 47
  • 84

1 Answers1

4

I figured out the solution with a help of my friend. The mistake I was doing was adding the framelayout everytime. Following is the code which works now:

public class GlobalActionBarService extends AccessibilityService {

    private static final String TAG = "AccessibilityService";
    private WindowManager mWindowManager;
    FrameLayout mLayout;

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        final int eventType = event.getEventType();
        String eventText = null;
        switch(eventType) {
            case AccessibilityEvent.TYPE_VIEW_CLICKED:
                eventText = "Clicked: ";
                break;
            case AccessibilityEvent.TYPE_VIEW_FOCUSED:
                eventText = "Focused: ";
                break;
        }

        eventText = eventText + event.getContentDescription();

        if (eventText.toLowerCase().contains("test")) {
            Log.d(TAG, "Oh! Displaying the layout");
            if (mLayout == null) {
                mLayout = new FrameLayout(this);
                mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
                WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
                lp.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
                lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN;
                lp.format = PixelFormat.TRANSLUCENT;
                lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
                lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
                lp.gravity = Gravity.TOP;
                LayoutInflater inflater = LayoutInflater.from(this);
                inflater.inflate(R.layout.action_bar, mLayout);
                mWindowManager.addView(mLayout, lp);
            }
        }

        if (eventText.toLowerCase().contains("navigate")) {
            Log.d(TAG, "Removing the layout");
            if (mLayout != null && mLayout.getRootView() != null) {
                mWindowManager.removeView(mLayout);
                mLayout = null;
            }
        }
    }

    @Override
    public void onInterrupt() {

    }

} 
avi
  • 9,292
  • 11
  • 47
  • 84