简体   繁体   中英

How to make ActionBar Movable in “Accessibility Services”

I am new in the Topic "Accessibility Services".I am able to Scroll for button clicks, and able to switch off phone and many More things, but i want to make my layout(action_bar.xml) Movable So, can anybody tell me please How to Make action_bar Movable

Here is my

action_bar.xml::

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<Button
    android:id="@+id/power"
    android:layout_weight="1"
    android:text="@string/power"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
<Button
    android:id="@+id/volume_up"
    android:text="@string/volume"
    android:layout_weight="1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
<Button
    android:layout_weight="1"
    android:id="@+id/scroll"
    android:text="@string/scroll"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>
<Button
    android:id="@+id/swipe"
    android:text="@string/swipe"
    android:layout_weight="1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/>

And ActionBarService.Java ,Where i Have Used it:

@Override
protected void onServiceConnected() {
    // Create an overlay and display the action bar
    WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
    mLayout = new FrameLayout(this);
    WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
    lp.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
    lp.format = PixelFormat.TRANSLUCENT;
    lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
    lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
    lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
    lp.gravity = Gravity.TOP;
    LayoutInflater inflater = LayoutInflater.from(this);
    inflater.inflate(R.layout.action_bar, mLayout);
    wm.addView(mLayout, lp);  
}

The answer by @fishbone is mostly correct, EXCEPT for the little part where overlays in Accessibility Services are NOT the same. Certainly, making this assumption simplifies things, but it is ultimately false. There are very important considerations specific to accessibility services, and In most ways they behave exactly like activities, but the ways that Accessibility Services launch overlays is very important and different from the ways most views behave, particularly in how they intercept touch events. You have to be very careful with touch events. Ultimately you want the view to be touchable, BUT NOT, take up the whole window. The two types you can play around with for this would be:

TYPE_ACCESSIBILITY_OVERLAY

or

TYPE_APPLICATION_OVERLAY

These two behave similarly, but not. The latter may help you if you're stuck on receiving touch events in your actual view.

The core problem is, that the layout parameters available for WindowManager only allow you to either completely intercept touch events or to not do so at all. You can't have it do so for this part of your view, and not for others. The logic for attempting to pass events past your window through to a window that you don't own... just don't go there.

As such, what you have to do, is NOT have a complete layout view. You have to add your view as Touchable, but have your view ONLY take up the part of the screen that it actually has to overlay. This should obviously be as minimal as possible (becuase touch events won't be able to go beind this view). THEN you'll have to do a custom hold touch listener, and do the touch and drag logic to move your view around manually.

You could potentially use the touch/drag view ONLY for replacement. As in, have a button that enables "drag mode" to replace it. And have that by the only time the entire layout is added to the window, remove the layout, and just add the smaller, non-layout version that is just your toolbar view.

Unfortunately there is no simple answer here. This is not something that Accessibility APIs are intended to do. I would also feel obligated to warn you, that the new expectations for Accessibility Services that Google is mulling around would almost certainly result in an Accessibility Service that does something like this being removed/not allowed in the Google Play Store.

Overlays work by the same rules as the usual View on the Activity or Fragment . There is no magic property such android:movable="true" that can make an overlay movable. You should make it by yourself. Here is my example of a movable View :

action_bar.xml (Here I replace LinearLayout with custom DraggableLayout )

<?xml version="1.0" encoding="utf-8"?>
<your.package.name.DraggableLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <Button
        android:id="@+id/power"
        android:layout_weight="1"
        android:text="Power"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/volume_up"
        android:text="Volume"
        android:layout_weight="1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <Button
        android:layout_weight="1"
        android:id="@+id/scroll"
        android:text="Scroll"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <Button
        android:id="@+id/swipe"
        android:text="Swipe"
        android:layout_weight="1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</your.package.name.DraggableLayout>

DraggableLayout.class

public class DraggableLayout extends LinearLayout implements View.OnDragListener {

    GestureDetector mLongClickDetector;
    Point mPickPoint;

    public DraggableLayout(Context context) {
        this(context, null, 0);
    }

    public DraggableLayout(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DraggableLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // When user performs a long press, we begin dragging
        mLongClickDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
            public void onLongPress(MotionEvent e) {
                mPickPoint = new Point((int) e.getX(), (int) e.getY());
                View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(DraggableLayout.this) {
                    @Override
                    public void onProvideShadowMetrics(Point outShadowSize, Point outShadowTouchPoint) {
                        outShadowSize.set(getWidth(), getHeight());
                        outShadowTouchPoint.set(mPickPoint.x, mPickPoint.y);
                    }
                };
                startDrag(null, shadowBuilder, null, 0);
            }
        });
    }

    @Override
    protected void onAttachedToWindow() {
        // We should register this class as OnDragListener to parent view to catch DROP events from it
        ((ViewGroup) getParent()).setOnDragListener(this);
        super.onAttachedToWindow();
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //This is also an important point: we must intercept touch events before the child elements (Buttons and so on)
        return mLongClickDetector.onTouchEvent(ev);
    }

    @Override
    public boolean onDragEvent(DragEvent event) {
        if (event.getAction() == DragEvent.ACTION_DROP) {
            // And when user performs drop we change position of view
            setX(event.getX() - mPickPoint.x);
            setY(event.getY() - mPickPoint.y);
            return true;
        }
        return false;
    }

    @Override
    public boolean onDrag(View v, DragEvent event) {
        return onDragEvent(event);
    }
}

ActionBarService.Java

@Override
protected void onServiceConnected() {
    // Create an overlay and display the action bar
    WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
    mLayout = new FrameLayout(this);
    WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
        WindowManager.LayoutParams.MATCH_PARENT, // Overlay must be full screen otherwise my trick will not work
        WindowManager.LayoutParams.MATCH_PARENT,
        WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY,
        WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, // Allow another windows to receive touch events
        PixelFormat.TRANSLUCENT);
    LayoutInflater inflater = LayoutInflater.from(this);
    inflater.inflate(R.layout.action_bar, mLayout);
    wm.addView(mLayout, lp);  
}

It seems to be working, let me know if not that.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM